From 97a470bd77b36e7177d5c62556865f76c1f7902c Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Tue, 19 Jun 2018 00:22:46 +0200 Subject: [PATCH 01/36] Upgrade to D v2.081.0-beta.1 --- CMakeLists.txt | 6 +- dmd/access.d | 21 +- dmd/aggregate.d | 25 +- dmd/aggregate.h | 32 +- dmd/apply.d | 2 +- dmd/arraytypes.d | 2 + dmd/arraytypes.h | 4 + dmd/astcodegen.d | 1 + dmd/canthrow.d | 2 +- dmd/clone.d | 411 +++++++++++++------ dmd/constfold.d | 153 +++---- dmd/cppmangle.d | 520 +++++++++++++++++------- dmd/cppmanglewin.d | 177 +++++++- dmd/ctfeexpr.d | 241 ++++++----- dmd/ctorflow.d | 168 +++++--- dmd/dcast.d | 13 +- dmd/dclass.d | 20 +- dmd/declaration.d | 6 +- dmd/declaration.h | 14 +- dmd/delegatize.d | 4 +- dmd/dinterpret.d | 727 +++++++++++++++++++-------------- dmd/dmangle.d | 7 +- dmd/dmodule.d | 32 -- dmd/doc.d | 34 +- dmd/dscope.d | 88 +--- dmd/dsymbol.d | 2 + dmd/dsymbolsem.d | 234 ++++++++--- dmd/dtemplate.d | 96 +++-- dmd/expression.d | 180 +++++++-- dmd/expressionsem.d | 84 ++-- dmd/func.d | 182 ++++++--- dmd/hdrgen.d | 90 ++++- dmd/id.d | 8 +- dmd/identifier.d | 62 ++- dmd/initsem.d | 5 +- dmd/inline.d | 3 +- dmd/inlinecost.d | 2 +- dmd/lambdacomp.d | 4 +- dmd/mars.d | 114 +++--- dmd/mtype.d | 91 +++-- dmd/nogc.d | 2 +- dmd/opover.d | 4 +- dmd/optimize.d | 3 +- dmd/parse.d | 325 +++++++++------ dmd/root/ctfloat.d | 19 +- dmd/root/filename.d | 4 +- dmd/root/longdouble.d | 843 +++++++++++++++++++++++++++++++++++++++ dmd/root/longdouble.h | 222 ++++++----- dmd/root/stringtable.d | 8 +- dmd/sapply.d | 2 +- dmd/semantic3.d | 23 +- dmd/sideeffect.d | 10 +- dmd/statement.d | 81 ++-- dmd/statement.h | 2 + dmd/statementsem.d | 10 +- dmd/target.d | 4 + dmd/target.h | 1 + dmd/template.h | 2 +- dmd/tokens.d | 2 +- dmd/traits.d | 104 ++++- dmd/transitivevisitor.d | 25 +- dmd/typesem.d | 448 +++++++++++---------- runtime/CMakeLists.txt | 19 - runtime/druntime | 2 +- runtime/phobos | 2 +- tests/d2/dmd-testsuite | 2 +- tests/d2/src/osmodel.mak | 2 +- 67 files changed, 4137 insertions(+), 1901 deletions(-) create mode 100644 dmd/root/longdouble.d diff --git a/CMakeLists.txt b/CMakeLists.txt index a3c55c2f4dd..8f2460794e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,11 +62,11 @@ endfunction() # # Version information -set(LDC_VERSION "1.10.0") # May be overridden by git hash tag +set(LDC_VERSION "1.11.0") # May be overridden by git hash tag set(DMDFE_MAJOR_VERSION 2) set(DMDFE_MINOR_VERSION 0) -set(DMDFE_PATCH_VERSION 80) -set(DMDFE_FIX_LEVEL 1) # Comment out if not used +set(DMDFE_PATCH_VERSION 81) +set(DMDFE_FIX_LEVEL 0) # Comment out if not used set(DMD_VERSION ${DMDFE_MAJOR_VERSION}.${DMDFE_MINOR_VERSION}${DMDFE_PATCH_VERSION}) if(DEFINED DMDFE_FIX_LEVEL) diff --git a/dmd/access.d b/dmd/access.d index a7cf13632ee..aeea0f2c7f0 100644 --- a/dmd/access.d +++ b/dmd/access.d @@ -543,7 +543,7 @@ extern (C++) bool checkSymbolAccess(Scope *sc, Dsymbol s) * but doesn't recurse nor resolve aliases because protection/visibility is an * attribute of the alias not the aliasee. */ -public Dsymbol mostVisibleOverload(Dsymbol s) +public Dsymbol mostVisibleOverload(Dsymbol s, Module mod = null) { if (!s.isOverloadable()) return s; @@ -612,7 +612,24 @@ public Dsymbol mostVisibleOverload(Dsymbol s) else break; - if (next && mostVisible.prot().isMoreRestrictiveThan(next.prot())) + /** + * Return the "effective" protection attribute of a symbol when accessed in a module. + * The effective protection attribute is the same as the regular protection attribute, + * except package() is "private" if the module is outside the package; + * otherwise, "public". + */ + static Prot protectionSeenFromModule(Dsymbol d, Module mod = null) + { + Prot prot = d.prot(); + if (mod && prot.kind == Prot.Kind.package_) + { + return hasPackageAccess(mod, d) ? Prot(Prot.Kind.public_) : Prot(Prot.Kind.private_); + } + return prot; + } + + if (next && + protectionSeenFromModule(mostVisible, mod).isMoreRestrictiveThan(protectionSeenFromModule(next, mod))) mostVisible = next; } return mostVisible; diff --git a/dmd/aggregate.d b/dmd/aggregate.d index ac2677798af..b25f4e2bae6 100644 --- a/dmd/aggregate.d +++ b/dmd/aggregate.d @@ -50,6 +50,22 @@ enum Baseok : int semanticdone, // all base classes semantic done } +/** + * The ClassKind enum is used in AggregateDeclaration AST nodes to + * specify the linkage type of the struct/class/interface or if it + * is an anonymous class. If the class is anonymous it is also + * considered to be a D class. + */ +enum ClassKind : int +{ + /// the aggregate is a d(efault) class + d, + /// the aggregate is a C++ struct/class/interface + cpp, + /// the aggregate is an Objective-C class/interface + objc, +} + /*********************************************************** */ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol @@ -64,6 +80,9 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol Dsymbol deferred; // any deferred semantic2() or semantic3() symbol bool isdeprecated; // true if deprecated + /// specifies whether this is a D, C++, Objective-C or anonymous struct/class/interface + ClassKind classKind; + /* !=null if is nested * pointing to the dsymbol that directly enclosing it. * 1. The function that enclosing it (nested struct and class) @@ -91,8 +110,10 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol Dsymbol aliasthis; // forward unresolved lookups to aliasthis bool noDefaultCtor; // no default construction - FuncDeclarations dtors; // Array of destructors - FuncDeclaration dtor; // aggregate destructor + DtorDeclarations dtors; // Array of destructors + DtorDeclaration dtor; // aggregate destructor + DtorDeclaration primaryDtor; // non-deleting C++ destructor, same as dtor for D + DtorDeclaration tidtor; // aggregate destructor used in TypeInfo (must have extern(D) ABI) Expression getRTInfo; // pointer to GC info generated by object.RTInfo(this) diff --git a/dmd/aggregate.h b/dmd/aggregate.h index 9d8a083f0fa..73b279bb9f4 100644 --- a/dmd/aggregate.h +++ b/dmd/aggregate.h @@ -72,9 +72,19 @@ FuncDeclaration *buildXopEquals(StructDeclaration *sd, Scope *sc); FuncDeclaration *buildXopCmp(StructDeclaration *sd, Scope *sc); FuncDeclaration *buildXtoHash(StructDeclaration *ad, Scope *sc); FuncDeclaration *buildPostBlit(StructDeclaration *sd, Scope *sc); -FuncDeclaration *buildDtor(AggregateDeclaration *ad, Scope *sc); +DtorDeclaration *buildDtor(AggregateDeclaration *ad, Scope *sc); FuncDeclaration *buildInv(AggregateDeclaration *ad, Scope *sc); +enum ClassKind +{ + /// the aggregate is a d(efault) struct/class/interface + d, + /// the aggregate is a C++ struct/class/interface + cpp, + /// the aggregate is an Objective-C class/interface + objc +}; + class AggregateDeclaration : public ScopeDsymbol { public: @@ -88,6 +98,8 @@ class AggregateDeclaration : public ScopeDsymbol Dsymbol *deferred; // any deferred semantic2() or semantic3() symbol bool isdeprecated; // true if deprecated + ClassKind classKind; // specifies the linkage type + /* !=NULL if is nested * pointing to the dsymbol that directly enclosing it. * 1. The function that enclosing it (nested struct and class) @@ -112,8 +124,10 @@ class AggregateDeclaration : public ScopeDsymbol Dsymbol *aliasthis; // forward unresolved lookups to aliasthis bool noDefaultCtor; // no default construction - FuncDeclarations dtors; // Array of destructors - FuncDeclaration *dtor; // aggregate destructor + DtorDeclarations dtors; // Array of destructors + DtorDeclaration *dtor; // aggregate destructor + DtorDeclaration *primaryDtor; // non-deleting C++ destructor, same as dtor for D + DtorDeclaration *tidtor; // aggregate destructor used in TypeInfo (must have extern(D) ABI) Expression *getRTInfo; // pointer to GC info generated by object.RTInfo(this) @@ -246,16 +260,6 @@ struct ClassFlags }; }; -enum ClassKind -{ - /// the class is a d(efault) class - d, - /// the class is a C++ interface - cpp, - /// the class is an Objective-C class/interface - objc -}; - class ClassDeclaration : public AggregateDeclaration { public: @@ -283,7 +287,7 @@ class ClassDeclaration : public AggregateDeclaration 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 - ClassKind classKind; // specifies the linkage type + int cppDtorVtblIndex; // slot reserved for the virtual destructor [extern(C++)] bool inuse; // to prevent recursive attempts bool isActuallyAnonymous; // true if this class has an identifier, but was originally declared anonymous // used in support of https://issues.dlang.org/show_bug.cgi?id=17371 diff --git a/dmd/apply.d b/dmd/apply.d index 5a19ef18edd..0dd76b7d23f 100644 --- a/dmd/apply.d +++ b/dmd/apply.d @@ -29,7 +29,7 @@ import dmd.visitor; */ private extern (C++) final class PostorderExpressionVisitor : StoppableVisitor { - alias visit = super.visit; + alias visit = typeof(super).visit; public: StoppableVisitor v; diff --git a/dmd/arraytypes.d b/dmd/arraytypes.d index 3cd800c9b25..c33f867490a 100644 --- a/dmd/arraytypes.d +++ b/dmd/arraytypes.d @@ -35,6 +35,7 @@ alias BaseClasses = Array!(BaseClass*); alias ClassDeclarations = Array!(ClassDeclaration); alias Dsymbols = Array!(Dsymbol); alias Objects = Array!(RootObject); +alias DtorDeclarations = Array!(DtorDeclaration); alias FuncDeclarations = Array!(FuncDeclaration); alias Parameters = Array!(Parameter); alias Initializers = Array!(Initializer); @@ -51,3 +52,4 @@ alias GotoCaseStatements = Array!(GotoCaseStatement); alias ReturnStatements = Array!(ReturnStatement); alias GotoStatements = Array!(GotoStatement); alias TemplateInstances = Array!(TemplateInstance); +alias Ensures = Array!(Ensure); diff --git a/dmd/arraytypes.h b/dmd/arraytypes.h index 73450d8728a..e87be9a7dec 100644 --- a/dmd/arraytypes.h +++ b/dmd/arraytypes.h @@ -32,6 +32,8 @@ typedef Array Dsymbols; typedef Array Objects; +typedef Array DtorDeclarations; + typedef Array FuncDeclarations; typedef Array Parameters; @@ -67,4 +69,6 @@ typedef Array GotoStatements; typedef Array TemplateInstances; +typedef Array Ensures; + #endif diff --git a/dmd/astcodegen.d b/dmd/astcodegen.d index 1f07e993c66..89b4e4cac37 100644 --- a/dmd/astcodegen.d +++ b/dmd/astcodegen.d @@ -37,6 +37,7 @@ struct ASTCodegen alias initializerToExpression = dmd.initsem.initializerToExpression; alias typeToExpression = dmd.typesem.typeToExpression; alias UserAttributeDeclaration = dmd.attrib.UserAttributeDeclaration; + alias Ensure = dmd.func.Ensure; // workaround for bug in older DMD frontends alias MODFlags = dmd.mtype.MODFlags; alias Type = dmd.mtype.Type; diff --git a/dmd/canthrow.d b/dmd/canthrow.d index b5003336cad..55d95b3d452 100644 --- a/dmd/canthrow.d +++ b/dmd/canthrow.d @@ -39,7 +39,7 @@ extern (C++) bool canThrow(Expression e, FuncDeclaration func, bool mustNotThrow // stop walking if we determine this expression can throw extern (C++) final class CanThrow : StoppableVisitor { - alias visit = super.visit; + alias visit = typeof(super).visit; FuncDeclaration func; bool mustNotThrow; diff --git a/dmd/clone.d b/dmd/clone.d index c3ee6850dca..1efc9b62c59 100644 --- a/dmd/clone.d +++ b/dmd/clone.d @@ -15,6 +15,7 @@ module dmd.clone; import core.stdc.stdio; import dmd.aggregate; import dmd.arraytypes; +import dmd.dclass; import dmd.declaration; import dmd.dscope; import dmd.dstruct; @@ -32,17 +33,25 @@ import dmd.mtype; import dmd.opover; import dmd.semantic2; import dmd.statement; +import dmd.target; import dmd.typesem; import dmd.tokens; /******************************************* * Merge function attributes pure, nothrow, @safe, @nogc, and @disable + * from f into s1. + * Params: + * s1 = storage class to merge into + * f = function + * Returns: + * merged storage class */ -extern (C++) StorageClass mergeFuncAttrs(StorageClass s1, FuncDeclaration f) +extern (C++) StorageClass mergeFuncAttrs(StorageClass s1, const FuncDeclaration f) pure { if (!f) return s1; StorageClass s2 = (f.storage_class & STC.disable); + TypeFunction tf = cast(TypeFunction)f.type; if (tf.trust == TRUST.safe) s2 |= STC.safe; @@ -50,15 +59,19 @@ extern (C++) StorageClass mergeFuncAttrs(StorageClass s1, FuncDeclaration f) s2 |= STC.system; else if (tf.trust == TRUST.trusted) s2 |= STC.trusted; + if (tf.purity != PURE.impure) s2 |= STC.pure_; if (tf.isnothrow) s2 |= STC.nothrow_; if (tf.isnogc) s2 |= STC.nogc; - StorageClass stc = 0; - StorageClass sa = s1 & s2; - StorageClass so = s1 | s2; + + const sa = s1 & s2; + const so = s1 | s2; + + StorageClass stc = (sa & (STC.pure_ | STC.nothrow_ | STC.nogc)) | (so & STC.disable); + if (so & STC.system) stc |= STC.system; else if (sa & STC.trusted) @@ -67,14 +80,7 @@ extern (C++) StorageClass mergeFuncAttrs(StorageClass s1, FuncDeclaration f) stc |= STC.trusted; else if (sa & STC.safe) stc |= STC.safe; - if (sa & STC.pure_) - stc |= STC.pure_; - if (sa & STC.nothrow_) - stc |= STC.nothrow_; - if (sa & STC.nogc) - stc |= STC.nogc; - if (so & STC.disable) - stc |= STC.disable; + return stc; } @@ -141,20 +147,26 @@ extern (C++) FuncDeclaration hasIdentityOpAssign(AggregateDeclaration ad, Scope* private bool needOpAssign(StructDeclaration sd) { //printf("StructDeclaration::needOpAssign() %s\n", sd.toChars()); + + static bool isNeeded() + { + //printf("\tneed\n"); + return true; + } + if (sd.isUnionDeclaration()) - return false; + return !isNeeded(); - if (sd.hasIdentityAssign) - goto Lneed; // because has identity==elaborate opAssign + if (sd.hasIdentityAssign || // because has identity==elaborate opAssign + sd.dtor || + sd.postblit) + return isNeeded(); - if (sd.dtor || sd.postblit) - goto Lneed; /* If any of the fields need an opAssign, then we * need it too. */ - for (size_t i = 0; i < sd.fields.dim; i++) + foreach (v; sd.fields) { - VarDeclaration v = sd.fields[i]; if (v.storage_class & STC.ref_) continue; if (v.overlapped) // if field of a union @@ -166,14 +178,10 @@ private bool needOpAssign(StructDeclaration sd) if (ts.sym.isUnionDeclaration()) continue; if (needOpAssign(ts.sym)) - goto Lneed; + return isNeeded(); } } - //printf("\tdontneed\n"); - return false; -Lneed: - //printf("\tneed\n"); - return true; + return !isNeeded(); } /****************************************** @@ -224,9 +232,8 @@ extern (C++) FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) // check for it. // In this event, it will be reflected by having `stc` (opAssign's // storage class) include `STC.disabled`. - for (size_t i = 0; i < sd.fields.dim; i++) + foreach (v; sd.fields) { - VarDeclaration v = sd.fields[i]; if (v.storage_class & STC.ref_) continue; if (v.overlapped) @@ -253,9 +260,10 @@ extern (C++) FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) auto fop = new FuncDeclaration(declLoc, Loc.initial, Id.assign, stc, tf); fop.storage_class |= STC.inference; fop.generated = true; - Expression e = null; + Expression e; if (stc & STC.disable) { + e = null; } else if (sd.dtor || sd.postblit) { @@ -263,32 +271,30 @@ extern (C++) FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) * __swap = this; this = s; __swap.dtor(); */ //printf("\tswap copy\n"); - Identifier idtmp = Identifier.generateId("__swap"); - VarDeclaration tmp = null; - AssignExp ec = null; if (sd.dtor) { TypeFunction tdtor = cast(TypeFunction)sd.dtor.type; assert(tdtor.ty == Tfunction); - tmp = new VarDeclaration(loc, sd.type, idtmp, new VoidInitializer(loc)); - tmp.storage_class |= STC.nodtor | STC.temp | STC.ctfe; + + auto idswap = Identifier.generateId("__swap"); + auto swap = new VarDeclaration(loc, sd.type, idswap, new VoidInitializer(loc)); + swap.storage_class |= STC.nodtor | STC.temp | STC.ctfe; if (tdtor.isscope) - tmp.storage_class |= STC.scope_; - e = new DeclarationExp(loc, tmp); - ec = new BlitExp(loc, new VarExp(loc, tmp), new ThisExp(loc)); - e = Expression.combine(e, ec); - } - ec = new BlitExp(loc, new ThisExp(loc), new IdentifierExp(loc, Id.p)); - e = Expression.combine(e, ec); - if (sd.dtor) - { + swap.storage_class |= STC.scope_; + auto e1 = new DeclarationExp(loc, swap); + + auto e2 = new BlitExp(loc, new VarExp(loc, swap), new ThisExp(loc)); + auto e3 = new BlitExp(loc, new ThisExp(loc), new IdentifierExp(loc, Id.p)); + /* Instead of running the destructor on s, run it - * on tmp. This avoids needing to copy tmp back in to s. + * on swap. This avoids needing to copy swap back in to s. */ - Expression ec2 = new DotVarExp(loc, new VarExp(loc, tmp), sd.dtor, false); - ec2 = new CallExp(loc, ec2); - e = Expression.combine(e, ec2); + auto e4 = new CallExp(loc, new DotVarExp(loc, new VarExp(loc, swap), sd.dtor, false)); + + e = Expression.combine(e1, e2, e3, e4); } + else + e = new BlitExp(loc, new ThisExp(loc), new IdentifierExp(loc, Id.p)); } else { @@ -300,9 +306,9 @@ extern (C++) FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) * In both cases, it will change the parent context. */ //printf("\tmemberwise copy\n"); - for (size_t i = 0; i < sd.fields.dim; i++) + e = null; + foreach (v; sd.fields) { - VarDeclaration v = sd.fields[i]; // this.v = s.v; auto ec = new AssignExp(loc, new DotVarExp(loc, new ThisExp(loc), v), @@ -316,15 +322,15 @@ extern (C++) FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) /* Add: * return this; */ - e = new ThisExp(loc); - Statement s2 = new ReturnStatement(loc, e); + auto er = new ThisExp(loc); + Statement s2 = new ReturnStatement(loc, er); fop.fbody = new CompoundStatement(loc, s1, s2); tf.isreturn = true; } sd.members.push(fop); fop.addMember(sc, sd); sd.hasIdentityAssign = true; // temporary mark identity assignable - uint errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it. + const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it. Scope* sc2 = sc.push(); sc2.stc = 0; sc2.linkage = LINK.d; @@ -796,7 +802,7 @@ extern (C++) FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc) * Close similarity with StructDeclaration::buildPostBlit(), * and the ordering changes (runs backward instead of forwards). */ -extern (C++) FuncDeclaration buildDtor(AggregateDeclaration ad, Scope* sc) +extern (C++) DtorDeclaration buildDtor(AggregateDeclaration ad, Scope* sc) { //printf("AggregateDeclaration::buildDtor() %s\n", ad.toChars()); if (ad.isUnionDeclaration()) @@ -806,95 +812,123 @@ extern (C++) FuncDeclaration buildDtor(AggregateDeclaration ad, Scope* sc) Loc declLoc = ad.dtors.dim ? ad.dtors[0].loc : ad.loc; Loc loc; // internal code should have no loc to prevent coverage - Expression e = null; - for (size_t i = 0; i < ad.fields.dim; i++) + // if the dtor is an extern(C++) prototype, then we expect it performs a full-destruction; we don't need to build a full-dtor + const bool dtorIsCppPrototype = ad.dtors.dim == 1 && ad.dtors[0].linkage == LINK.cpp && !ad.dtors[0].fbody; + if (!dtorIsCppPrototype) { - auto v = ad.fields[i]; - if (v.storage_class & STC.ref_) - continue; - if (v.overlapped) - continue; - auto tv = v.type.baseElemOf(); - if (tv.ty != Tstruct) - continue; - auto sdv = (cast(TypeStruct)tv).sym; - if (!sdv.dtor) - continue; - sdv.dtor.functionSemantic(); - - stc = mergeFuncAttrs(stc, sdv.dtor); - if (stc & STC.disable) + Expression e = null; + for (size_t i = 0; i < ad.fields.dim; i++) { - e = null; - break; - } + auto v = ad.fields[i]; + if (v.storage_class & STC.ref_) + continue; + if (v.overlapped) + continue; + auto tv = v.type.baseElemOf(); + if (tv.ty != Tstruct) + continue; + auto sdv = (cast(TypeStruct)tv).sym; + if (!sdv.dtor) + continue; + sdv.dtor.functionSemantic(); - Expression ex; - tv = v.type.toBasetype(); - if (tv.ty == Tstruct) - { - // this.v.__xdtor() + stc = mergeFuncAttrs(stc, sdv.dtor); + if (stc & STC.disable) + { + e = null; + break; + } + + Expression ex; + tv = v.type.toBasetype(); + if (tv.ty == Tstruct) + { + // this.v.__xdtor() - ex = new ThisExp(loc); - ex = new DotVarExp(loc, ex, v); + ex = new ThisExp(loc); + ex = new DotVarExp(loc, ex, v); - // This is a hack so we can call destructors on const/immutable objects. - // Do it as a type 'paint'. - ex = new CastExp(loc, ex, v.type.mutableOf()); - if (stc & STC.safe) - stc = (stc & ~STC.safe) | STC.trusted; + // This is a hack so we can call destructors on const/immutable objects. + // Do it as a type 'paint'. + ex = new CastExp(loc, ex, v.type.mutableOf()); + if (stc & STC.safe) + stc = (stc & ~STC.safe) | STC.trusted; - ex = new DotVarExp(loc, ex, sdv.dtor, false); - ex = new CallExp(loc, ex); + ex = new DotVarExp(loc, ex, sdv.dtor, false); + ex = new CallExp(loc, ex); + } + else + { + // __ArrayDtor((cast(S*)this.v.ptr)[0 .. n]) + + const n = tv.numberOfElems(loc); + if (n == 0) + continue; + + ex = new ThisExp(loc); + ex = new DotVarExp(loc, ex, v); + + // This is a hack so we can call destructors on const/immutable objects. + ex = new DotIdExp(loc, ex, Id.ptr); + ex = new CastExp(loc, ex, sdv.type.pointerTo()); + if (stc & STC.safe) + stc = (stc & ~STC.safe) | STC.trusted; + + ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t), + new IntegerExp(loc, n, Type.tsize_t)); + // Prevent redundant bounds check + (cast(SliceExp)ex).upperIsInBounds = true; + (cast(SliceExp)ex).lowerIsLessThanUpper = true; + + ex = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayDtor), ex); + } + e = Expression.combine(ex, e); // combine in reverse order } - else + + /* extern(C++) destructors call into super to destruct the full hierarchy + */ + ClassDeclaration cldec = ad.isClassDeclaration(); + if (cldec && cldec.classKind == ClassKind.cpp && cldec.baseClass && cldec.baseClass.primaryDtor) { - // _ArrayDtor((cast(S*)this.v.ptr)[0 .. n]) + // WAIT BUT: do I need to run `cldec.baseClass.dtor` semantic? would it have been run before? + cldec.baseClass.dtor.functionSemantic(); - uinteger_t n = 1; - while (tv.ty == Tsarray) + stc = mergeFuncAttrs(stc, cldec.baseClass.primaryDtor); + if (!(stc & STC.disable)) { - n *= (cast(TypeSArray)tv).dim.toUInteger(); - tv = tv.nextOf().toBasetype(); - } - if (n == 0) - continue; + // super.__xdtor() - ex = new ThisExp(loc); - ex = new DotVarExp(loc, ex, v); + Expression ex = new SuperExp(loc); - // This is a hack so we can call destructors on const/immutable objects. - ex = new DotIdExp(loc, ex, Id.ptr); - ex = new CastExp(loc, ex, sdv.type.pointerTo()); - if (stc & STC.safe) - stc = (stc & ~STC.safe) | STC.trusted; + // This is a hack so we can call destructors on const/immutable objects. + // Do it as a type 'paint'. + ex = new CastExp(loc, ex, cldec.baseClass.type.mutableOf()); + if (stc & STC.safe) + stc = (stc & ~STC.safe) | STC.trusted; - ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t), - new IntegerExp(loc, n, Type.tsize_t)); - // Prevent redundant bounds check - (cast(SliceExp)ex).upperIsInBounds = true; - (cast(SliceExp)ex).lowerIsLessThanUpper = true; + ex = new DotVarExp(loc, ex, cldec.baseClass.primaryDtor, false); + ex = new CallExp(loc, ex); - ex = new CallExp(loc, new IdentifierExp(loc, Id._ArrayDtor), ex); + e = Expression.combine(e, ex); // super dtor last + } } - e = Expression.combine(ex, e); // combine in reverse order - } - /* Build our own "destructor" which executes e - */ - if (e || (stc & STC.disable)) - { - //printf("Building __fieldDtor(), %s\n", e.toChars()); - auto dd = new DtorDeclaration(declLoc, Loc.initial, stc, Id.__fieldDtor); - dd.generated = true; - dd.storage_class |= STC.inference; - dd.fbody = new ExpStatement(loc, e); - ad.dtors.shift(dd); - ad.members.push(dd); - dd.dsymbolSemantic(sc); + /* Build our own "destructor" which executes e + */ + if (e || (stc & STC.disable)) + { + //printf("Building __fieldDtor(), %s\n", e.toChars()); + auto dd = new DtorDeclaration(declLoc, Loc.initial, stc, Id.__fieldDtor); + dd.generated = true; + dd.storage_class |= STC.inference; + dd.fbody = new ExpStatement(loc, e); + ad.dtors.shift(dd); + ad.members.push(dd); + dd.dsymbolSemantic(sc); + } } - FuncDeclaration xdtor = null; + DtorDeclaration xdtor = null; switch (ad.dtors.dim) { case 0: @@ -905,6 +939,8 @@ extern (C++) FuncDeclaration buildDtor(AggregateDeclaration ad, Scope* sc) break; default: + assert(!dtorIsCppPrototype); + Expression e = null; e = null; stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; for (size_t i = 0; i < ad.dtors.dim; i++) @@ -931,6 +967,11 @@ extern (C++) FuncDeclaration buildDtor(AggregateDeclaration ad, Scope* sc) break; } + ad.primaryDtor = xdtor; + + if (xdtor && xdtor.linkage == LINK.cpp && !Target.twoDtorInVtable) + xdtor = buildWindowsCppDtor(ad, xdtor, sc); + // Add an __xdtor alias to make the inclusive dtor accessible if (xdtor) { @@ -943,6 +984,108 @@ extern (C++) FuncDeclaration buildDtor(AggregateDeclaration ad, Scope* sc) return xdtor; } +/** + * build a shim function around the compound dtor that accepts an argument + * that is used to implement the deleting C++ destructor + * + * Params: + * ad = the aggregate that contains the destructor to wrap + * dtor = the destructor to wrap + * sc = the scope in which to analyze the new function + * + * Returns: + * the shim destructor, semantically analyzed and added to the class as a member + */ +private DtorDeclaration buildWindowsCppDtor(AggregateDeclaration ad, DtorDeclaration dtor, Scope* sc) +{ + auto cldec = ad.isClassDeclaration(); + if (!cldec || cldec.cppDtorVtblIndex == -1) // scalar deleting dtor not built for non-virtual dtors + return dtor; + + // generate deleting C++ destructor corresponding to: + // void* C::~C(int del) + // { + // this->~C(); + // // TODO: if (del) delete (char*)this; + // return (void*) this; + // } + Parameter delparam = new Parameter(STC.undefined_, Type.tuns32, Identifier.idPool("del"), new IntegerExp(dtor.loc, 0, Type.tuns32)); + Parameters* params = new Parameters; + params.push(delparam); + auto ftype = new TypeFunction(params, Type.tvoidptr, false, LINK.cpp, dtor.storage_class); + auto func = new DtorDeclaration(dtor.loc, dtor.loc, dtor.storage_class, Id.cppdtor); + func.type = ftype; + if (dtor.fbody) + { + const loc = dtor.loc; + auto stmts = new Statements; + auto call = new CallExp(loc, dtor, null); + call.directcall = true; + stmts.push(new ExpStatement(loc, call)); + stmts.push(new ReturnStatement(loc, new CastExp(loc, new ThisExp(loc), Type.tvoidptr))); + func.fbody = new CompoundStatement(loc, stmts); + func.generated = true; + } + + auto sc2 = sc.push(); + sc2.stc &= ~STC.static_; // not a static destructor + sc2.linkage = LINK.cpp; + + ad.members.push(func); + func.addMember(sc2, ad); + func.dsymbolSemantic(sc2); + + sc2.pop(); + return func; +} + +/** + * build a shim function around the compound dtor that translates + * a C++ destructor to a destructor with extern(D) calling convention + * + * Params: + * ad = the aggregate that contains the destructor to wrap + * sc = the scope in which to analyze the new function + * + * Returns: + * the shim destructor, semantically analyzed and added to the class as a member + */ +DtorDeclaration buildExternDDtor(AggregateDeclaration ad, Scope* sc) +{ + auto dtor = ad.primaryDtor; + if (!dtor) + return null; + + // the only ABI known to be incompatible is Windows/x86 + if (ad.classKind != ClassKind.cpp || !global.params.isWindows || global.params.is64bit) + return dtor; + + // generate member function that adjusts calling convention (EAX used instead of ECX for 'this'): + // extern(D) void __ticppdtor() + // { + // Class.__dtor(); + // } + auto ftype = new TypeFunction(null, Type.tvoid, false, LINK.d, dtor.storage_class); + auto func = new DtorDeclaration(dtor.loc, dtor.loc, dtor.storage_class, Id.ticppdtor); + func.type = ftype; + + auto call = new CallExp(dtor.loc, dtor, null); + call.directcall = true; // non-virtual call Class.__dtor(); + func.fbody = new ExpStatement(dtor.loc, call); + func.generated = true; + + auto sc2 = sc.push(); + sc2.stc &= ~STC.static_; // not a static destructor + sc2.linkage = LINK.d; + + ad.members.push(func); + func.addMember(sc2, ad); + func.dsymbolSemantic(sc2); + + sc2.pop(); + return func; +} + /****************************************** * Create inclusive invariant for struct/class by aggregating * all the invariants in invs[]. @@ -953,27 +1096,28 @@ extern (C++) FuncDeclaration buildDtor(AggregateDeclaration ad, Scope* sc) */ extern (C++) FuncDeclaration buildInv(AggregateDeclaration ad, Scope* sc) { - StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; - Loc declLoc = ad.loc; - Loc loc; // internal code should have no loc to prevent coverage switch (ad.invs.dim) { case 0: return null; + case 1: // Don't return invs[0] so it has uniquely generated name. - /* fall through */ + goto default; + default: Expression e = null; StorageClass stcx = 0; - for (size_t i = 0; i < ad.invs.dim; i++) + StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; + foreach (i, inv; ad.invs) { - stc = mergeFuncAttrs(stc, ad.invs[i]); + stc = mergeFuncAttrs(stc, inv); if (stc & STC.disable) { // What should do? } - StorageClass stcy = (ad.invs[i].storage_class & STC.synchronized_) | (ad.invs[i].type.mod & MODFlags.shared_ ? STC.shared_ : 0); + const stcy = (inv.storage_class & STC.synchronized_) | + (inv.type.mod & MODFlags.shared_ ? STC.shared_ : 0); if (i == 0) stcx = stcy; else if (stcx ^ stcy) @@ -981,14 +1125,15 @@ extern (C++) FuncDeclaration buildInv(AggregateDeclaration ad, Scope* sc) version (all) { // currently rejects - ad.error(ad.invs[i].loc, "mixing invariants with different `shared`/`synchronized` qualifiers is not supported"); + ad.error(inv.loc, "mixing invariants with different `shared`/`synchronized` qualifiers is not supported"); e = null; break; } } - e = Expression.combine(e, new CallExp(loc, new VarExp(loc, ad.invs[i], false))); + e = Expression.combine(e, new CallExp(Loc.initial, new VarExp(Loc.initial, inv, false))); } - auto inv = new InvariantDeclaration(declLoc, Loc.initial, stc | stcx, Id.classInvariant, new ExpStatement(loc, e)); + auto inv = new InvariantDeclaration(ad.loc, Loc.initial, stc | stcx, + Id.classInvariant, new ExpStatement(Loc.initial, e)); ad.members.push(inv); inv.dsymbolSemantic(sc); return inv; diff --git a/dmd/constfold.d b/dmd/constfold.d index 3d76771e25f..ec7396a122d 100644 --- a/dmd/constfold.d +++ b/dmd/constfold.d @@ -48,7 +48,10 @@ private Expression expType(Type type, Expression e) return e; } -/* ================================== isConst() ============================== */ +/************************************ + * Returns: + * true if e is a constant + */ extern (C++) int isConst(Expression e) { //printf("Expression::isConst(): %s\n", e.toChars()); @@ -79,6 +82,16 @@ version(IN_LLVM) assert(0); } +/********************************** + * Initialize a TOK.cantExpression Expression. + * Params: + * ue = where to write it + */ +void cantExp(out UnionExp ue) +{ + emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); +} + /* =============================== constFold() ============================== */ /* The constFold() functions were redundant with the optimize() ones, * and so have been folded in with them. @@ -86,7 +99,7 @@ version(IN_LLVM) /* ========================================================================== */ extern (C++) UnionExp Neg(Type type, Expression e1) { - UnionExp ue; + UnionExp ue = void; Loc loc = e1.loc; if (e1.type.isreal()) { @@ -109,7 +122,7 @@ extern (C++) UnionExp Neg(Type type, Expression e1) extern (C++) UnionExp Com(Type type, Expression e1) { - UnionExp ue; + UnionExp ue = void; Loc loc = e1.loc; emplaceExp!(IntegerExp)(&ue, loc, ~e1.toInteger(), type); return ue; @@ -117,7 +130,7 @@ extern (C++) UnionExp Com(Type type, Expression e1) extern (C++) UnionExp Not(Type type, Expression e1) { - UnionExp ue; + UnionExp ue = void; Loc loc = e1.loc; emplaceExp!(IntegerExp)(&ue, loc, e1.isBool(false) ? 1 : 0, type); return ue; @@ -125,7 +138,7 @@ extern (C++) UnionExp Not(Type type, Expression e1) private UnionExp Bool(Type type, Expression e1) { - UnionExp ue; + UnionExp ue = void; Loc loc = e1.loc; emplaceExp!(IntegerExp)(&ue, loc, e1.isBool(true) ? 1 : 0, type); return ue; @@ -133,7 +146,7 @@ private UnionExp Bool(Type type, Expression e1) extern (C++) UnionExp Add(const ref Loc loc, Type type, Expression e1, Expression e2) { - UnionExp ue; + UnionExp ue = void; static if (LOG) { printf("Add(e1 = %s, e2 = %s)\n", e1.toChars(), e2.toChars()); @@ -240,7 +253,7 @@ extern (C++) UnionExp Add(const ref Loc loc, Type type, Expression e1, Expressio extern (C++) UnionExp Min(const ref Loc loc, Type type, Expression e1, Expression e2) { - UnionExp ue; + UnionExp ue = void; if (type.isreal()) { emplaceExp!(RealExp)(&ue, loc, e1.toReal() - e2.toReal(), type); @@ -339,7 +352,7 @@ extern (C++) UnionExp Min(const ref Loc loc, Type type, Expression e1, Expressio extern (C++) UnionExp Mul(const ref Loc loc, Type type, Expression e1, Expression e2) { - UnionExp ue; + UnionExp ue = void; if (type.isfloating()) { auto c = complex_t(CTFloat.zero); @@ -388,7 +401,7 @@ extern (C++) UnionExp Mul(const ref Loc loc, Type type, Expression e1, Expressio extern (C++) UnionExp Div(const ref Loc loc, Type type, Expression e1, Expression e2) { - UnionExp ue; + UnionExp ue = void; if (type.isfloating()) { auto c = complex_t(CTFloat.zero); @@ -479,7 +492,7 @@ extern (C++) UnionExp Div(const ref Loc loc, Type type, Expression e1, Expressio extern (C++) UnionExp Mod(const ref Loc loc, Type type, Expression e1, Expression e2) { - UnionExp ue; + UnionExp ue = void; if (type.isfloating()) { auto c = complex_t(CTFloat.zero); @@ -544,6 +557,7 @@ extern (C++) UnionExp Mod(const ref Loc loc, Type type, Expression e1, Expressio extern (C++) UnionExp Pow(const ref Loc loc, Type type, Expression e1, Expression e2) { + //printf("Pow()\n"); UnionExp ue; // Handle integer power operations. if (e2.type.isintegral()) @@ -554,7 +568,7 @@ extern (C++) UnionExp Pow(const ref Loc loc, Type type, Expression e1, Expressio { if (e1.type.isintegral()) { - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); + cantExp(ue); return ue; } // Don't worry about overflow, from now on n is unsigned. @@ -614,23 +628,23 @@ extern (C++) UnionExp Pow(const ref Loc loc, Type type, Expression e1, Expressio emplaceExp!(RealExp)(&ue, loc, Target.RealProperties.nan, type); } else - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); + cantExp(ue); } else - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); + cantExp(ue); return ue; } extern (C++) UnionExp Shl(const ref Loc loc, Type type, Expression e1, Expression e2) { - UnionExp ue; + UnionExp ue = void; emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() << e2.toInteger(), type); return ue; } extern (C++) UnionExp Shr(const ref Loc loc, Type type, Expression e1, Expression e2) { - UnionExp ue; + UnionExp ue = void; dinteger_t value = e1.toInteger(); dinteger_t dcount = e2.toInteger(); assert(dcount <= 0xFFFFFFFF); @@ -676,7 +690,7 @@ extern (C++) UnionExp Shr(const ref Loc loc, Type type, Expression e1, Expressio extern (C++) UnionExp Ushr(const ref Loc loc, Type type, Expression e1, Expression e2) { - UnionExp ue; + UnionExp ue = void; dinteger_t value = e1.toInteger(); dinteger_t dcount = e2.toInteger(); assert(dcount <= 0xFFFFFFFF); @@ -716,14 +730,14 @@ extern (C++) UnionExp Ushr(const ref Loc loc, Type type, Expression e1, Expressi extern (C++) UnionExp And(const ref Loc loc, Type type, Expression e1, Expression e2) { - UnionExp ue; + UnionExp ue = void; emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() & e2.toInteger(), type); return ue; } extern (C++) UnionExp Or(const ref Loc loc, Type type, Expression e1, Expression e2) { - UnionExp ue; + UnionExp ue = void; emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() | e2.toInteger(), type); return ue; } @@ -731,7 +745,7 @@ extern (C++) UnionExp Or(const ref Loc loc, Type type, Expression e1, Expression extern (C++) UnionExp Xor(const ref Loc loc, Type type, Expression e1, Expression e2) { //printf("Xor(linnum = %d, e1 = %s, e2 = %s)\n", loc.linnum, e1.toChars(), e2.toChars()); - UnionExp ue; + UnionExp ue = void; emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() ^ e2.toInteger(), type); return ue; } @@ -740,7 +754,7 @@ extern (C++) UnionExp Xor(const ref Loc loc, Type type, Expression e1, Expressio */ extern (C++) UnionExp Equal(TOK op, const ref Loc loc, Type type, Expression e1, Expression e2) { - UnionExp ue; + UnionExp ue = void; int cmp = 0; real_t r1 = CTFloat.zero; real_t r2 = CTFloat.zero; @@ -762,7 +776,7 @@ extern (C++) UnionExp Equal(TOK op, const ref Loc loc, Type type, Expression e1, } else { - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); + cantExp(ue); return ue; } } @@ -780,7 +794,7 @@ extern (C++) UnionExp Equal(TOK op, const ref Loc loc, Type type, Expression e1, } else { - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); + cantExp(ue); return ue; } } @@ -791,7 +805,7 @@ extern (C++) UnionExp Equal(TOK op, const ref Loc loc, Type type, Expression e1, if (es1.sz != es2.sz) { assert(global.errors); - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); + cantExp(ue); return ue; } if (es1.len == es2.len && memcmp(es1.string, es2.string, es1.sz * es1.len) == 0) @@ -850,7 +864,7 @@ extern (C++) UnionExp Equal(TOK op, const ref Loc loc, Type type, Expression e1, auto ee2 = es2.getElement(i); if (ee2.isConst() != 1) { - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); + cantExp(ue); return ue; } cmp = (c == ee2.toInteger()); @@ -896,7 +910,7 @@ extern (C++) UnionExp Equal(TOK op, const ref Loc loc, Type type, Expression e1, } else if (e1.isConst() != 1 || e2.isConst() != 1) { - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); + cantExp(ue); return ue; } else if (e1.type.isreal()) @@ -929,7 +943,7 @@ extern (C++) UnionExp Equal(TOK op, const ref Loc loc, Type type, Expression e1, } else { - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); + cantExp(ue); return ue; } if (op == TOK.notEqual) @@ -940,7 +954,7 @@ extern (C++) UnionExp Equal(TOK op, const ref Loc loc, Type type, Expression e1, extern (C++) UnionExp Identity(TOK op, const ref Loc loc, Type type, Expression e1, Expression e2) { - UnionExp ue; + UnionExp ue = void; int cmp; if (e1.op == TOK.null_) { @@ -986,7 +1000,7 @@ extern (C++) UnionExp Identity(TOK op, const ref Loc loc, Type type, Expression extern (C++) UnionExp Cmp(TOK op, const ref Loc loc, Type type, Expression e1, Expression e2) { - UnionExp ue; + UnionExp ue = void; dinteger_t n; real_t r1 = CTFloat.zero; real_t r2 = CTFloat.zero; @@ -1007,7 +1021,7 @@ extern (C++) UnionExp Cmp(TOK op, const ref Loc loc, Type type, Expression e1, E } else if (e1.isConst() != 1 || e2.isConst() != 1) { - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); + cantExp(ue); return ue; } else if (e1.type.isreal()) @@ -1048,7 +1062,7 @@ extern (C++) UnionExp Cmp(TOK op, const ref Loc loc, Type type, Expression e1, E */ extern (C++) UnionExp Cast(const ref Loc loc, Type type, Type to, Expression e1) { - UnionExp ue; + UnionExp ue = void; Type tb = to.toBasetype(); Type typeb = type.toBasetype(); //printf("Cast(type = %s, to = %s, e1 = %s)\n", type.toChars(), to.toChars(), e1.toChars()); @@ -1093,7 +1107,7 @@ extern (C++) UnionExp Cast(const ref Loc loc, Type type, Type to, Expression e1) } if (e1.isConst() != 1) { - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); + cantExp(ue); } else if (tb.ty == Tbool) { @@ -1165,7 +1179,7 @@ extern (C++) UnionExp Cast(const ref Loc loc, Type type, Type to, Expression e1) } else if (tb.ty == Tvoid) { - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); + cantExp(ue); } else if (tb.ty == Tstruct && e1.op == TOK.int64) { @@ -1201,7 +1215,7 @@ extern (C++) UnionExp Cast(const ref Loc loc, Type type, Type to, Expression e1) extern (C++) UnionExp ArrayLength(Type type, Expression e1) { - UnionExp ue; + UnionExp ue = void; Loc loc = e1.loc; if (e1.op == TOK.string_) { @@ -1226,7 +1240,7 @@ extern (C++) UnionExp ArrayLength(Type type, Expression e1) emplaceExp!(UnionExp)(&ue, e); } else - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); + cantExp(ue); return ue; } @@ -1234,7 +1248,7 @@ extern (C++) UnionExp ArrayLength(Type type, Expression e1) */ extern (C++) UnionExp Index(Type type, Expression e1, Expression e2) { - UnionExp ue; + UnionExp ue = void; Loc loc = e1.loc; //printf("Index(e1 = %s, e2 = %s)\n", e1.toChars(), e2.toChars()); assert(e1.type); @@ -1269,12 +1283,12 @@ extern (C++) UnionExp Index(Type type, Expression e1, Expression e2) e.type = type; e.loc = loc; if (hasSideEffect(e)) - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); + cantExp(ue); else emplaceExp!(UnionExp)(&ue, e); } else - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); + cantExp(ue); } else if (e1.type.toBasetype().ty == Tarray && e2.op == TOK.int64) { @@ -1293,13 +1307,13 @@ extern (C++) UnionExp Index(Type type, Expression e1, Expression e2) e.type = type; e.loc = loc; if (hasSideEffect(e)) - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); + cantExp(ue); else emplaceExp!(UnionExp)(&ue, e); } } else - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); + cantExp(ue); } else if (e1.op == TOK.assocArrayLiteral) { @@ -1319,16 +1333,16 @@ extern (C++) UnionExp Index(Type type, Expression e1, Expression e2) e.type = type; e.loc = loc; if (hasSideEffect(e)) - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); + cantExp(ue); else emplaceExp!(UnionExp)(&ue, e); return ue; } } - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); + cantExp(ue); } else - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); + cantExp(ue); return ue; } @@ -1336,7 +1350,7 @@ extern (C++) UnionExp Index(Type type, Expression e1, Expression e2) */ extern (C++) UnionExp Slice(Type type, Expression e1, Expression lwr, Expression upr) { - UnionExp ue; + UnionExp ue = void; Loc loc = e1.loc; static if (LOG) { @@ -1348,25 +1362,28 @@ extern (C++) UnionExp Slice(Type type, Expression e1, Expression lwr, Expression printf("\tupr = %s\n", upr.toChars()); } } + + static bool sliceBoundsCheck(uinteger_t lwr, uinteger_t upr, uinteger_t newlwr, uinteger_t newupr) pure + { + assert(lwr <= upr); + return !(newlwr <= newupr && + lwr <= newlwr && + newupr <= upr); + } + if (e1.op == TOK.string_ && lwr.op == TOK.int64 && upr.op == TOK.int64) { StringExp es1 = cast(StringExp)e1; - uinteger_t ilwr = lwr.toInteger(); - uinteger_t iupr = upr.toInteger(); - if (iupr > es1.len || ilwr > iupr) - { - // https://issues.dlang.org/show_bug.cgi?id=18115 - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); - return ue; - //e1.error("string slice `[%llu .. %llu]` is out of bounds", ilwr, iupr); - //emplaceExp!(ErrorExp)(&ue); - } + const uinteger_t ilwr = lwr.toInteger(); + const uinteger_t iupr = upr.toInteger(); + if (sliceBoundsCheck(0, es1.len, ilwr, iupr)) + cantExp(ue); // https://issues.dlang.org/show_bug.cgi?id=18115 else { - size_t len = cast(size_t)(iupr - ilwr); - ubyte sz = es1.sz; + const len = cast(size_t)(iupr - ilwr); + const sz = es1.sz; void* s = mem.xmalloc(len * sz); - memcpy(cast(char*)s, es1.string + ilwr * sz, len * sz); + memcpy(s, es1.string + ilwr * sz, len * sz); emplaceExp!(StringExp)(&ue, loc, s, len, es1.postfix); StringExp es = cast(StringExp)ue.exp(); es.sz = sz; @@ -1377,13 +1394,10 @@ extern (C++) UnionExp Slice(Type type, Expression e1, Expression lwr, Expression else if (e1.op == TOK.arrayLiteral && lwr.op == TOK.int64 && upr.op == TOK.int64 && !hasSideEffect(e1)) { ArrayLiteralExp es1 = cast(ArrayLiteralExp)e1; - uinteger_t ilwr = lwr.toInteger(); - uinteger_t iupr = upr.toInteger(); - if (iupr > es1.elements.dim || ilwr > iupr) - { - e1.error("array slice `[%llu .. %llu]` is out of bounds", ilwr, iupr); - emplaceExp!(ErrorExp)(&ue); - } + const uinteger_t ilwr = lwr.toInteger(); + const uinteger_t iupr = upr.toInteger(); + if (sliceBoundsCheck(0, es1.elements.dim, ilwr, iupr)) + cantExp(ue); else { auto elements = new Expressions(); @@ -1394,8 +1408,7 @@ extern (C++) UnionExp Slice(Type type, Expression e1, Expression lwr, Expression } } else - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); - assert(ue.exp().type); + cantExp(ue); return ue; } @@ -1466,7 +1479,7 @@ extern (C++) int sliceCmpStringWithArray(const StringExp se1, ArrayLiteralExp ae */ extern (C++) UnionExp Cat(Type type, Expression e1, Expression e2) { - UnionExp ue; + UnionExp ue = void; Expression e = CTFEExp.cantexp; Loc loc = e1.loc; Type t; @@ -1557,7 +1570,7 @@ extern (C++) UnionExp Cat(Type type, Expression e1, Expression e2) * auto s = "foo"d ~ "bar"c; */ assert(global.errors); - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); + cantExp(ue); assert(ue.exp().type); return ue; } @@ -1763,7 +1776,7 @@ extern (C++) UnionExp Cat(Type type, Expression e1, Expression e2) } } else - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); + cantExp(ue); assert(ue.exp().type); return ue; } @@ -1771,7 +1784,7 @@ extern (C++) UnionExp Cat(Type type, Expression e1, Expression e2) extern (C++) UnionExp Ptr(Type type, Expression e1) { //printf("Ptr(e1 = %s)\n", e1.toChars()); - UnionExp ue; + UnionExp ue = void; if (e1.op == TOK.add) { AddExp ae = cast(AddExp)e1; @@ -1791,6 +1804,6 @@ extern (C++) UnionExp Ptr(Type type, Expression e1) } } } - emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); + cantExp(ue); return ue; } diff --git a/dmd/cppmangle.d b/dmd/cppmangle.d index 6c428a47815..ab7e2f49cc2 100644 --- a/dmd/cppmangle.d +++ b/dmd/cppmangle.d @@ -36,6 +36,7 @@ import dmd.globals; import dmd.id; import dmd.identifier; import dmd.mtype; +import dmd.nspace; import dmd.root.outbuffer; import dmd.root.rootobject; import dmd.target; @@ -45,6 +46,21 @@ import dmd.visitor; extern (C++): +// helper to check if an identifier is a C++ operator +enum CppOperator { Cast, Assign, Eq, Index, Call, Unary, Binary, OpAssign, Unknown } +package CppOperator isCppOperator(Identifier id) +{ + __gshared const(Identifier)[] operators = null; + if (!operators) + operators = [Id._cast, Id.assign, Id.eq, Id.index, Id.call, Id.opUnary, Id.opBinary, Id.opOpAssign]; + foreach (i, op; operators) + { + if (op == id) + return cast(CppOperator)i; + } + return CppOperator.Unknown; +} + const(char)* toCppMangleItanium(Dsymbol s) { //printf("toCppMangleItanium(%s)\n", s.toChars()); @@ -64,6 +80,24 @@ const(char)* cppTypeInfoMangleItanium(Dsymbol s) return buf.extractString(); } +/****************************** + * Determine if sym is the 'primary' destructor, that is, + * the most-aggregate destructor (the one that is defined as __xdtor) + * Params: + * sym = Dsymbol + * Returns: + * true if sym is the primary destructor for an aggregate + */ +bool isPrimaryDtor(const Dsymbol sym) +{ + const dtor = sym.isDtorDeclaration(); + if (!dtor) + return false; + const ad = dtor.isMember(); + assert(ad); + return dtor == ad.primaryDtor; +} + private final class CppMangleVisitor : Visitor { alias visit = Visitor.visit; @@ -106,12 +140,24 @@ private final class CppMangleVisitor : Visitor /****** * See if `p` exists in components[] + * + * Note that components can contain `null` entries, + * as the index used in mangling is based on the index in the array. + * + * If called with an object whose dynamic type is `Nspace`, + * calls the `find(Nspace)` overload. + * * Returns: * index if found, -1 if not */ int find(RootObject p) { //printf("find %p %d %s\n", p, p.dyncast(), p ? p.toChars() : null); + + if (p.dyncast() == DYNCAST.dsymbol) + if (auto ns = (cast(Dsymbol)p).isNspace()) + return find(ns); + foreach (i, component; components) { if (p == component) @@ -120,6 +166,40 @@ private final class CppMangleVisitor : Visitor return -1; } + /** + * Overload which accepts a Namespace + * + * It is very common for large C++ projects to have multiple files sharing + * the same `namespace`. If any D project adopts the same approach + * (e.g. separating data structures from functions), it will lead to two + * `Nspace` objects being instantiated, with different addresses. + * At the same time, we cannot compare just any Dsymbol via identifier, + * because it messes with templates. + * + * See_Also: + * https://issues.dlang.org/show_bug.cgi?id=18922 + * + * Params: + * ns = C++ namespace to do substitution for + * + * Returns: + * Index of the entry, if found, or `-1` otherwise + */ + int find(Nspace ns) + { + foreach (i, component; components) + { + if (ns == component) + return cast(int)i; + + if (component && component.dyncast() == DYNCAST.dsymbol) + if (auto ons = (cast(Dsymbol)component).isNspace()) + if (ns.equals(ons)) + return cast(int)i; + } + return -1; + } + /********************* * Append p to components[] */ @@ -144,19 +224,112 @@ private final class CppMangleVisitor : Visitor !getQualifier(s)); // at global level } + /****************************** + * Write the mangled representation of a template argument. + * Params: + * ti = the template instance + * arg = the template argument index + */ + void template_arg(TemplateInstance ti, size_t arg) + { + TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration(); + assert(td); + TemplateParameter tp = (*td.parameters)[arg]; + RootObject o = (*ti.tiargs)[arg]; + + if (tp.isTemplateTypeParameter()) + { + Type t = isType(o); + assert(t); + t.accept(this); + } + else if (TemplateValueParameter tv = tp.isTemplateValueParameter()) + { + // ::= L E # integer literal + if (tv.valType.isintegral()) + { + Expression e = isExpression(o); + assert(e); + buf.writeByte('L'); + tv.valType.accept(this); + auto val = e.toUInteger(); + if (!tv.valType.isunsigned() && cast(sinteger_t)val < 0) + { + val = -val; + buf.writeByte('n'); + } + buf.print(val); + buf.writeByte('E'); + } + else + { + ti.error("Internal Compiler Error: C++ `%s` template value parameter is not supported", tv.valType.toChars()); + fatal(); + } + } + else if (tp.isTemplateAliasParameter()) + { + Dsymbol d = isDsymbol(o); + Expression e = isExpression(o); + if (d && d.isFuncDeclaration()) + { + bool is_nested = d.toParent() && + !d.toParent().isModule() && + (cast(TypeFunction)d.isFuncDeclaration().type).linkage == LINK.cpp; + if (is_nested) + buf.writeByte('X'); + buf.writeByte('L'); + mangle_function(d.isFuncDeclaration()); + buf.writeByte('E'); + if (is_nested) + buf.writeByte('E'); + } + else if (e && e.op == TOK.variable && (cast(VarExp)e).var.isVarDeclaration()) + { + VarDeclaration vd = (cast(VarExp)e).var.isVarDeclaration(); + buf.writeByte('L'); + mangle_variable(vd, true); + buf.writeByte('E'); + } + else if (d && d.isTemplateDeclaration() && d.isTemplateDeclaration().onemember) + { + if (!substitute(d)) + { + cpp_mangle_name(d, false); + } + } + else + { + ti.error("Internal Compiler Error: C++ `%s` template alias parameter is not supported", o.toChars()); + fatal(); + } + } + else if (tp.isTemplateThisParameter()) + { + ti.error("Internal Compiler Error: C++ `%s` template this parameter is not supported", o.toChars()); + fatal(); + } + else + { + assert(0); + } + } + /****************************** * Write the mangled representation of the template arguments. * Params: * ti = the template instance + * Returns: + * true if any arguments were written */ - void template_args(TemplateInstance ti) + bool template_args(TemplateInstance ti, int firstArg = 0) { /* ::= I + E */ - if (!ti) // could happen if std::basic_string is not a template - return; + if (!ti || ti.tiargs.dim <= firstArg) // could happen if std::basic_string is not a template + return false; buf.writeByte('I'); - foreach (i, o; *ti.tiargs) + foreach (i; firstArg .. ti.tiargs.dim) { TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration(); assert(td); @@ -184,84 +357,10 @@ private final class CppMangleVisitor : Visitor break; } - if (tp.isTemplateTypeParameter()) - { - Type t = isType(o); - assert(t); - t.accept(this); - } - else if (TemplateValueParameter tv = tp.isTemplateValueParameter()) - { - // ::= L E # integer literal - if (tv.valType.isintegral()) - { - Expression e = isExpression(o); - assert(e); - buf.writeByte('L'); - tv.valType.accept(this); - auto val = e.toUInteger(); - if (!tv.valType.isunsigned() && cast(sinteger_t)val < 0) - { - val = -val; - buf.writeByte('n'); - } - buf.print(val); - buf.writeByte('E'); - } - else - { - ti.error("Internal Compiler Error: C++ `%s` template value parameter is not supported", tv.valType.toChars()); - fatal(); - } - } - else if (tp.isTemplateAliasParameter()) - { - Dsymbol d = isDsymbol(o); - Expression e = isExpression(o); - if (d && d.isFuncDeclaration()) - { - bool is_nested = d.toParent() && - !d.toParent().isModule() && - (cast(TypeFunction)d.isFuncDeclaration().type).linkage == LINK.cpp; - if (is_nested) - buf.writeByte('X'); - buf.writeByte('L'); - mangle_function(d.isFuncDeclaration()); - buf.writeByte('E'); - if (is_nested) - buf.writeByte('E'); - } - else if (e && e.op == TOK.variable && (cast(VarExp)e).var.isVarDeclaration()) - { - VarDeclaration vd = (cast(VarExp)e).var.isVarDeclaration(); - buf.writeByte('L'); - mangle_variable(vd, true); - buf.writeByte('E'); - } - else if (d && d.isTemplateDeclaration() && d.isTemplateDeclaration().onemember) - { - if (!substitute(d)) - { - cpp_mangle_name(d, false); - } - } - else - { - ti.error("Internal Compiler Error: C++ `%s` template alias parameter is not supported", o.toChars()); - fatal(); - } - } - else if (tp.isTemplateThisParameter()) - { - ti.error("Internal Compiler Error: C++ `%s` template this parameter is not supported", o.toChars()); - fatal(); - } - else - { - assert(0); - } + template_arg(ti, i); } buf.writeByte('E'); + return true; } @@ -382,70 +481,70 @@ private final class CppMangleVisitor : Visitor void prefix_name(Dsymbol s) { //printf("prefix_name(%s)\n", s.toChars()); - if (!substitute(s)) + if (substitute(s)) + return; + + auto si = getInstance(s); + Dsymbol p = getQualifier(si); + if (p) { - auto si = getInstance(s); - Dsymbol p = getQualifier(si); - if (p) + if (isStd(p)) { - if (isStd(p)) + TemplateInstance ti = si.isTemplateInstance(); + if (ti) { - TemplateInstance ti = si.isTemplateInstance(); - if (ti) + if (s.ident == Id.allocator) { - if (s.ident == Id.allocator) - { - buf.writestring("Sa"); - template_args(ti); - append(ti); - return; - } - if (s.ident == Id.basic_string) - { - // ::std::basic_string, ::std::allocator> - if (ti.tiargs.dim == 3 && - isChar((*ti.tiargs)[0]) && - isChar_traits_char((*ti.tiargs)[1]) && - isAllocator_char((*ti.tiargs)[2])) + buf.writestring("Sa"); + template_args(ti); + append(ti); + return; + } + if (s.ident == Id.basic_string) + { + // ::std::basic_string, ::std::allocator> + if (ti.tiargs.dim == 3 && + isChar((*ti.tiargs)[0]) && + isChar_traits_char((*ti.tiargs)[1]) && + isAllocator_char((*ti.tiargs)[2])) - { - buf.writestring("Ss"); - return; - } - buf.writestring("Sb"); // ::std::basic_string - template_args(ti); - append(ti); + { + buf.writestring("Ss"); return; } + buf.writestring("Sb"); // ::std::basic_string + template_args(ti); + append(ti); + return; + } - // ::std::basic_istream> - if (s.ident == Id.basic_istream && - char_std_char_traits_char(ti, "Si")) - return; + // ::std::basic_istream> + if (s.ident == Id.basic_istream && + char_std_char_traits_char(ti, "Si")) + return; - // ::std::basic_ostream> - if (s.ident == Id.basic_ostream && - char_std_char_traits_char(ti, "So")) - return; + // ::std::basic_ostream> + if (s.ident == Id.basic_ostream && + char_std_char_traits_char(ti, "So")) + return; - // ::std::basic_iostream> - if (s.ident == Id.basic_iostream && - char_std_char_traits_char(ti, "Sd")) - return; - } - buf.writestring("St"); + // ::std::basic_iostream> + if (s.ident == Id.basic_iostream && + char_std_char_traits_char(ti, "Sd")) + return; } - else - prefix_name(p); + buf.writestring("St"); } - source_name(si); - if (!isStd(si)) - /* Do this after the source_name() call to keep components[] - * in the right order. - * https://issues.dlang.org/show_bug.cgi?id=17947 - */ - append(si); + else + prefix_name(p); } + source_name(si); + if (!isStd(si)) + /* Do this after the source_name() call to keep components[] + * in the right order. + * https://issues.dlang.org/show_bug.cgi?id=17947 + */ + append(si); } @@ -519,7 +618,12 @@ private final class CppMangleVisitor : Visitor { buf.writeByte('N'); if (write_prefix) - prefix_name(p); + { + if (isStd(p)) + buf.writestring("St"); + else + prefix_name(p); + } source_name(se); buf.writeByte('E'); } @@ -584,21 +688,135 @@ private final class CppMangleVisitor : Visitor */ TemplateInstance ti = d.parent.isTemplateInstance(); assert(ti); + bool appendReturnType = true; Dsymbol p = ti.toParent(); if (p && !p.isModule() && tf.linkage == LINK.cpp) { buf.writeByte('N'); CV_qualifiers(d.type); prefix_name(p); - if (d.isDtorDeclaration()) + if (d.isCtorDeclaration()) + { + buf.writestring("C1"); + appendReturnType = false; + } + else if (d.isPrimaryDtor()) + { buf.writestring("D1"); + appendReturnType = false; + } else - source_name(ti); + { + int firstTemplateArg = 0; + bool isConvertFunc = false; + string symName; + + // test for special symbols + CppOperator whichOp = isCppOperator(ti.name); + final switch (whichOp) + { + case CppOperator.Unknown: + break; + case CppOperator.Cast: + symName = "cv"; + firstTemplateArg = 1; + isConvertFunc = true; + appendReturnType = false; + break; + case CppOperator.Assign: + symName = "aS"; + break; + case CppOperator.Eq: + symName = "eq"; + break; + case CppOperator.Index: + symName = "ix"; + break; + case CppOperator.Call: + symName = "cl"; + break; + case CppOperator.Unary: + case CppOperator.Binary: + case CppOperator.OpAssign: + TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration(); + assert(td); + assert(ti.tiargs.dim >= 1); + TemplateParameter tp = (*td.parameters)[0]; + TemplateValueParameter tv = tp.isTemplateValueParameter(); + if (!tv || !tv.valType.isString()) + break; // expecting a string argument to operators! + Expression exp = (*ti.tiargs)[0].isExpression(); + StringExp str = exp.toStringExp(); + switch (whichOp) + { + case CppOperator.Unary: + switch (str.peekSlice()) + { + case "*": symName = "de"; goto continue_template; + case "++": symName = "pp"; goto continue_template; + case "--": symName = "mm"; goto continue_template; + case "-": symName = "ng"; goto continue_template; + case "+": symName = "ps"; goto continue_template; + case "~": symName = "co"; goto continue_template; + default: break; + } + break; + case CppOperator.Binary: + switch (str.peekSlice()) + { + case ">>": symName = "rs"; goto continue_template; + case "<<": symName = "ls"; goto continue_template; + case "*": symName = "ml"; goto continue_template; + case "-": symName = "mi"; goto continue_template; + case "+": symName = "pl"; goto continue_template; + case "&": symName = "an"; goto continue_template; + case "/": symName = "dv"; goto continue_template; + case "%": symName = "rm"; goto continue_template; + case "^": symName = "eo"; goto continue_template; + case "|": symName = "or"; goto continue_template; + default: break; + } + break; + case CppOperator.OpAssign: + switch (str.peekSlice()) + { + case "*": symName = "mL"; goto continue_template; + case "+": symName = "pL"; goto continue_template; + case "-": symName = "mI"; goto continue_template; + case "/": symName = "dV"; goto continue_template; + case "%": symName = "rM"; goto continue_template; + case ">>": symName = "rS"; goto continue_template; + case "<<": symName = "lS"; goto continue_template; + case "&": symName = "aN"; goto continue_template; + case "|": symName = "oR"; goto continue_template; + case "^": symName = "eO"; goto continue_template; + default: break; + } + break; + default: + assert(0); + continue_template: + firstTemplateArg = 1; + break; + } + break; + } + if (symName.length == 0) + source_name(ti); + else + { + buf.writestring(symName); + if (isConvertFunc) + template_arg(ti, 0); + appendReturnType = template_args(ti, firstTemplateArg) && appendReturnType; + } + } buf.writeByte('E'); } else source_name(ti); - headOfType(tf.nextOf()); // mangle return type + if (appendReturnType) + headOfType(tf.nextOf()); // mangle return type } else { @@ -621,14 +839,20 @@ private final class CppMangleVisitor : Visitor prefix_name(p); //printf("p: %s\n", buf.peekString()); - if (d.isDtorDeclaration()) - { + if (d.isCtorDeclaration()) + buf.writestring("C1"); + else if (d.isPrimaryDtor()) buf.writestring("D1"); - } + else if (d.ident && d.ident == Id.assign) + buf.writestring("aS"); + else if (d.ident && d.ident == Id.eq) + buf.writestring("eq"); + else if (d.ident && d.ident == Id.index) + buf.writestring("ix"); + else if (d.ident && d.ident == Id.call) + buf.writestring("cl"); else - { source_name(d); - } buf.writeByte('E'); } else @@ -761,6 +985,14 @@ public: buf.writeByte(c); } + override void visit(TypeNull t) + { + if (t.isImmutable() || t.isShared()) + return error(t); + + writeBasicType(t, 'D', 'n'); + } + override void visit(TypeBasic t) { if (t.isImmutable() || t.isShared()) @@ -956,14 +1188,18 @@ public: if (t.isImmutable() || t.isShared()) return error(t); - /* __c_long and __c_ulong get special mangling + /* __c_(u)long(long) get special mangling */ const id = t.sym.ident; - //printf("struct id = '%s'\n", id.toChars()); + //printf("enum id = '%s'\n", id.toChars()); if (id == Id.__c_long) return writeBasicType(t, 0, 'l'); else if (id == Id.__c_ulong) return writeBasicType(t, 0, 'm'); + else if (id == Id.__c_longlong) + return writeBasicType(t, 0, 'x'); + else if (id == Id.__c_ulonglong) + return writeBasicType(t, 0, 'y'); //printf("TypeStruct %s\n", t.toChars()); doSymbol(t); diff --git a/dmd/cppmanglewin.d b/dmd/cppmanglewin.d index 3cda37d4eb8..587357f201f 100644 --- a/dmd/cppmanglewin.d +++ b/dmd/cppmanglewin.d @@ -15,6 +15,7 @@ import core.stdc.string; import core.stdc.stdio; import dmd.arraytypes; +import dmd.cppmangle : isPrimaryDtor, isCppOperator, CppOperator; import dmd.declaration; import dmd.dsymbol; import dmd.dtemplate; @@ -125,6 +126,18 @@ public: fatal(); //Fatal, because this error should be handled in frontend } + override void visit(TypeNull type) + { + if (type.isImmutable() || type.isShared()) + { + visit(cast(Type)type); + return; + } + buf.writestring("$$T"); + flags &= ~IS_NOT_TOP_TYPE; + flags &= ~IGNORE_CONST; + } + override void visit(TypeBasic type) { //printf("visit(TypeBasic); is_not_top_type = %d\n", (int)(flags & IS_NOT_TOP_TYPE)); @@ -374,16 +387,18 @@ public: override void visit(TypeStruct type) { const id = type.sym.ident; - char c; + string c; if (id == Id.__c_long_double) - c = 'O'; // VC++ long double + c = "O"; // VC++ long double else if (id == Id.__c_long) - c = 'J'; // VC++ long + c = "J"; // VC++ long else if (id == Id.__c_ulong) - c = 'K'; // VC++ unsigned long - else - c = 0; - if (c) + c = "K"; // VC++ unsigned long + else if (id == Id.__c_longlong) + c = "_J"; // VC++ long long + else if (id == Id.__c_ulonglong) + c = "_K"; // VC++ unsigned long long + if (c.length) { if (type.isImmutable() || type.isShared()) { @@ -396,7 +411,7 @@ public: return; } mangleModifier(type); - buf.writeByte(c); + buf.writestring(c); } else { @@ -541,7 +556,7 @@ private: // Pivate methods always non-virtual in D and it should be mangled as non-virtual in C++ //printf("%s: isVirtualMethod = %d, isVirtual = %d, vtblIndex = %d, interfaceVirtual = %p\n", //d.toChars(), d.isVirtualMethod(), d.isVirtual(), cast(int)d.vtblIndex, d.interfaceVirtual); - if (d.isVirtual() && (d.vtblIndex != -1 || d.interfaceVirtual || d.overrideInterface())) + if ((d.isVirtual() && (d.vtblIndex != -1 || d.interfaceVirtual || d.overrideInterface())) || (d.isDtorDeclaration() && d.parent.isClassDeclaration() && !d.isFinal())) { switch (d.protection.kind) { @@ -603,7 +618,7 @@ private: // ::= Y buf.writeByte('Y'); } - const(char)* args = mangleFunctionType(cast(TypeFunction)d.type, d.needThis(), d.isCtorDeclaration() || d.isDtorDeclaration()); + const(char)* args = mangleFunctionType(cast(TypeFunction)d.type, d.needThis(), d.isCtorDeclaration() || isPrimaryDtor(d)); buf.writestring(args); } @@ -674,26 +689,158 @@ private: //printf("mangleName('%s')\n", sym.toChars()); const(char)* name = null; bool is_dmc_template = false; - if (sym.isDtorDeclaration()) + if (sym.isCtorDeclaration()) + { + buf.writestring("?0"); + return; + } + else if (sym.isPrimaryDtor()) { buf.writestring("?1"); return; } + else if (sym.ident) + { + if (sym.ident == Id.assign) + { + buf.writestring("?4"); + return; + } + else if (sym.ident == Id.eq) + { + buf.writestring("?8"); + return; + } + else if (sym.ident == Id.index) + { + buf.writestring("?A"); + return; + } + else if (sym.ident == Id.call) + { + buf.writestring("?R"); + return; + } + else if (sym.ident == Id.cppdtor) + { + buf.writestring("?_G"); + return; + } + } if (TemplateInstance ti = sym.isTemplateInstance()) { + auto id = ti.tempdecl.ident; + const(char)* symName = id.toChars(); + + int firstTemplateArg = 0; + + // test for special symbols + CppOperator whichOp = isCppOperator(ti.name); + final switch (whichOp) + { + case CppOperator.Unknown: + break; + case CppOperator.Cast: + buf.writestring("?B"); + return; + case CppOperator.Assign: + symName = "?4"; + break; + case CppOperator.Eq: + symName = "?8"; + break; + case CppOperator.Index: + symName = "?A"; + break; + case CppOperator.Call: + symName = "?R"; + break; + case CppOperator.Unary: + case CppOperator.Binary: + case CppOperator.OpAssign: + TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration(); + assert(td); + assert(ti.tiargs.dim >= 1); + TemplateParameter tp = (*td.parameters)[0]; + TemplateValueParameter tv = tp.isTemplateValueParameter(); + if (!tv || !tv.valType.isString()) + break; // expecting a string argument to operators! + Expression exp = (*ti.tiargs)[0].isExpression(); + StringExp str = exp.toStringExp(); + switch (whichOp) + { + case CppOperator.Unary: + switch (str.peekSlice()) + { + 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: break; + } + break; + case CppOperator.Binary: + switch (str.peekSlice()) + { + 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: break; + } + break; + case CppOperator.OpAssign: + switch (str.peekSlice()) + { + 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: break; + } + break; + default: + assert(0); + continue_template: + if (ti.tiargs.dim == 1) + { + buf.writestring(symName); + return; + } + firstTemplateArg = 1; + break; + } + break; + } + scope VisualCPPMangler tmp = new VisualCPPMangler((flags & IS_DMC) ? true : false); tmp.buf.writeByte('?'); tmp.buf.writeByte('$'); - tmp.buf.writestring(ti.name.toChars()); - tmp.saved_idents[0] = ti.name.toChars(); - tmp.buf.writeByte('@'); + tmp.buf.writestring(symName); + tmp.saved_idents[0] = symName; + if (symName == id.toChars()) + tmp.buf.writeByte('@'); if (flags & IS_DMC) { tmp.mangleIdent(sym.parent, true); is_dmc_template = true; } bool is_var_arg = false; - for (size_t i = 0; i < ti.tiargs.dim; i++) + for (size_t i = firstTemplateArg; i < ti.tiargs.dim; i++) { RootObject o = (*ti.tiargs)[i]; TemplateParameter tp = null; diff --git a/dmd/ctfeexpr.d b/dmd/ctfeexpr.d index bf683eef116..74bd6b46c2f 100644 --- a/dmd/ctfeexpr.d +++ b/dmd/ctfeexpr.d @@ -144,13 +144,16 @@ extern (C++) final class VoidInitExp : Expression } } -// Return index of the field, or -1 if not found -// Same as getFieldIndex, but checks for a direct match with the VarDeclaration -extern (C++) int findFieldIndexByName(StructDeclaration sd, VarDeclaration v) +/************************* + * Same as getFieldIndex, but checks for a direct match with the VarDeclaration + * Returns: + * index of the field, or -1 if not found + */ +extern (C++) int findFieldIndexByName(const StructDeclaration sd, const VarDeclaration v) pure { - for (size_t i = 0; i < sd.fields.dim; ++i) + foreach (i, field; sd.fields) { - if (sd.fields[i] == v) + if (field == v) return cast(int)i; } return -1; @@ -179,7 +182,8 @@ extern (C++) final class ThrownExceptionExp : Expression // Generate an error message when this exception is not caught void generateUncaughtError() { - Expression e = resolveSlice((*thrown.value.elements)[0]); + UnionExp ue = void; + Expression e = resolveSlice((*thrown.value.elements)[0], &ue); StringExp se = e.toStringExp(); thrown.error("uncaught CTFE exception `%s(%s)`", thrown.type.toChars(), se ? se.toChars() : e.toChars()); /* Also give the line where the throw statement was. We won't have it @@ -232,19 +236,19 @@ extern (C++) final class CTFEExp : Expression extern (C++) static __gshared CTFEExp continueexp; extern (C++) static __gshared CTFEExp gotoexp; - static bool isCantExp(Expression e) + static bool isCantExp(const Expression e) { return e && e.op == TOK.cantExpression; } - static bool isGotoExp(Expression e) + static bool isGotoExp(const Expression e) { return e && e.op == TOK.goto_; } } // True if 'e' is CTFEExp::cantexp, or an exception -extern (C++) bool exceptionOrCantInterpret(Expression e) +extern (C++) bool exceptionOrCantInterpret(const Expression e) { return e && (e.op == TOK.cantExpression || e.op == TOK.thrownException); } @@ -252,18 +256,19 @@ extern (C++) bool exceptionOrCantInterpret(Expression e) /************** Aggregate literals (AA/string/array/struct) ******************/ // Given expr, which evaluates to an array/AA/string literal, // return true if it needs to be copied -extern (C++) bool needToCopyLiteral(Expression expr) +extern (C++) bool needToCopyLiteral(const Expression expr) { + Expression e = cast()expr; for (;;) { - switch (expr.op) + switch (e.op) { case TOK.arrayLiteral: - return (cast(ArrayLiteralExp)expr).ownedByCtfe == OwnedBy.code; + return (cast(ArrayLiteralExp)e).ownedByCtfe == OwnedBy.code; case TOK.assocArrayLiteral: - return (cast(AssocArrayLiteralExp)expr).ownedByCtfe == OwnedBy.code; + return (cast(AssocArrayLiteralExp)e).ownedByCtfe == OwnedBy.code; case TOK.structLiteral: - return (cast(StructLiteralExp)expr).ownedByCtfe == OwnedBy.code; + return (cast(StructLiteralExp)e).ownedByCtfe == OwnedBy.code; case TOK.string_: case TOK.this_: case TOK.variable: @@ -274,14 +279,14 @@ extern (C++) bool needToCopyLiteral(Expression expr) case TOK.dotVariable: case TOK.slice: case TOK.cast_: - expr = (cast(UnaExp)expr).e1; + e = (cast(UnaExp)e).e1; continue; case TOK.concatenate: - return needToCopyLiteral((cast(BinExp)expr).e1) || needToCopyLiteral((cast(BinExp)expr).e2); + return needToCopyLiteral((cast(BinExp)e).e1) || needToCopyLiteral((cast(BinExp)e).e2); case TOK.concatenateAssign: case TOK.concatenateElemAssign: case TOK.concatenateDcharAssign: - expr = (cast(BinExp)expr).e2; + e = (cast(BinExp)e).e2; continue; default: return false; @@ -296,12 +301,9 @@ private Expressions* copyLiteralArray(Expressions* oldelems, Expression basis = CtfeStatus.numArrayAllocs++; auto newelems = new Expressions(); newelems.setDim(oldelems.dim); - for (size_t i = 0; i < oldelems.dim; i++) + foreach (i, el; *oldelems) { - auto el = (*oldelems)[i]; - if (!el) - el = basis; - (*newelems)[i] = copyLiteral(el).copy(); + (*newelems)[i] = copyLiteral(el ? el : basis).copy(); } return newelems; } @@ -529,14 +531,28 @@ private UnionExp paintTypeOntoLiteralCopy(Type type, Expression lit) return ue; } -extern (C++) Expression resolveSlice(Expression e) +/************************************* + * If e is a SliceExp, constant fold it. + * Params: + * e = expression to resolve + * pue = if not null, store resulting expression here + * Returns: + * resulting expression + */ +extern (C++) Expression resolveSlice(Expression e, UnionExp* pue = null) { if (e.op != TOK.slice) return e; SliceExp se = cast(SliceExp)e; if (se.e1.op == TOK.null_) return se.e1; - return Slice(e.type, se.e1, se.lwr, se.upr).copy(); + if (pue) + { + *pue = Slice(e.type, se.e1, se.lwr, se.upr); + return pue.exp(); + } + else + return Slice(e.type, se.e1, se.lwr, se.upr).copy(); } /* Determine the array length, without interpreting it. @@ -544,33 +560,41 @@ extern (C++) Expression resolveSlice(Expression e) * It's very wasteful to resolve the slice when we only * need the length. */ -extern (C++) uinteger_t resolveArrayLength(Expression e) +extern (C++) uinteger_t resolveArrayLength(const Expression e) { - if (e.op == TOK.vector) - e = (cast(VectorExp)e).e1; - if (e.op == TOK.null_) - return 0; - if (e.op == TOK.slice) - { - uinteger_t ilo = (cast(SliceExp)e).lwr.toInteger(); - uinteger_t iup = (cast(SliceExp)e).upr.toInteger(); - return iup - ilo; - } - if (e.op == TOK.string_) - { - return (cast(StringExp)e).len; - } - if (e.op == TOK.arrayLiteral) - { - ArrayLiteralExp ale = cast(ArrayLiteralExp)e; - return ale.elements ? ale.elements.dim : 0; - } - if (e.op == TOK.assocArrayLiteral) + switch (e.op) { - AssocArrayLiteralExp ale = cast(AssocArrayLiteralExp)e; - return ale.keys.dim; + case TOK.vector: + return resolveArrayLength((cast(VectorExp)e).e1); + + case TOK.null_: + return 0; + + case TOK.slice: + { + const ilo = (cast(SliceExp)e).lwr.toInteger(); + const iup = (cast(SliceExp)e).upr.toInteger(); + return iup - ilo; + } + + case TOK.string_: + return (cast(StringExp)e).len; + + case TOK.arrayLiteral: + { + const ale = cast(ArrayLiteralExp)e; + return ale.elements ? ale.elements.dim : 0; + } + + case TOK.assocArrayLiteral: + { + const ale = cast(AssocArrayLiteralExp)e; + return ale.keys.dim; + } + + default: + assert(0); } - assert(0); } /****************************** @@ -590,12 +614,12 @@ extern (C++) ArrayLiteralExp createBlockDuplicatedArrayLiteral(const ref Loc loc { // If it is a multidimensional array literal, do it recursively auto tsa = cast(TypeSArray)type.nextOf(); - auto len = cast(size_t)tsa.dim.toInteger(); + const len = cast(size_t)tsa.dim.toInteger(); elem = createBlockDuplicatedArrayLiteral(loc, type.nextOf(), elem, len); } // Buzilla 15681 - auto tb = elem.type.toBasetype(); + const tb = elem.type.toBasetype(); const mustCopy = tb.ty == Tstruct || tb.ty == Tsarray; auto elements = new Expressions(); @@ -662,7 +686,7 @@ extern (C++) TypeAArray toBuiltinAAType(Type t) /************** TypeInfo operations ************************************/ // Return true if type is TypeInfo_Class -extern (C++) bool isTypeInfo_Class(Type type) +extern (C++) bool isTypeInfo_Class(const Type type) { return type.ty == Tclass && (Type.dtypeinfo == (cast(TypeClass)type).sym || Type.dtypeinfo.isBaseOf((cast(TypeClass)type).sym, null)); } @@ -727,16 +751,17 @@ extern (C++) Expression getAggregateFromPointer(Expression e, dinteger_t* ofs) *ofs = (cast(SymOffExp)e).offset; if (e.op == TOK.dotVariable) { - Expression ex = (cast(DotVarExp)e).e1; - VarDeclaration v = (cast(DotVarExp)e).var.isVarDeclaration(); + const ex = (cast(DotVarExp)e).e1; + const v = (cast(DotVarExp)e).var.isVarDeclaration(); assert(v); - StructLiteralExp se = ex.op == TOK.classReference ? (cast(ClassReferenceExp)ex).value : cast(StructLiteralExp)ex; + StructLiteralExp se = (ex.op == TOK.classReference) + ? (cast(ClassReferenceExp)ex).value + : cast(StructLiteralExp)ex; + // We can't use getField, because it makes a copy - uint i; - if (ex.op == TOK.classReference) - i = (cast(ClassReferenceExp)ex).getFieldIndex(e.type, v.offset); - else - i = se.getFieldIndex(e.type, v.offset); + const i = (ex.op == TOK.classReference) + ? (cast(ClassReferenceExp)ex).getFieldIndex(e.type, v.offset) + : se.getFieldIndex(e.type, v.offset); e = (*se.elements)[i]; } if (e.op == TOK.index) @@ -789,26 +814,25 @@ extern (C++) bool pointToSameMemoryBlock(Expression agg1, Expression agg2) // return e1 - e2 as an integer, or error if not possible extern (C++) UnionExp pointerDifference(const ref Loc loc, Type type, Expression e1, Expression e2) { - UnionExp ue; + UnionExp ue = void; dinteger_t ofs1, ofs2; Expression agg1 = getAggregateFromPointer(e1, &ofs1); Expression agg2 = getAggregateFromPointer(e2, &ofs2); if (agg1 == agg2) { Type pointee = (cast(TypePointer)agg1.type).next; - dinteger_t sz = pointee.size(); + const sz = pointee.size(); emplaceExp!(IntegerExp)(&ue, loc, (ofs1 - ofs2) * sz, type); } - else if (agg1.op == TOK.string_ && agg2.op == TOK.string_) + else if (agg1.op == TOK.string_ && agg2.op == TOK.string_ && + (cast(StringExp)agg1).string == (cast(StringExp)agg2).string) { - if ((cast(StringExp)agg1).string == (cast(StringExp)agg2).string) - { - Type pointee = (cast(TypePointer)agg1.type).next; - dinteger_t sz = pointee.size(); - emplaceExp!(IntegerExp)(&ue, loc, (ofs1 - ofs2) * sz, type); - } + Type pointee = (cast(TypePointer)agg1.type).next; + const sz = pointee.size(); + emplaceExp!(IntegerExp)(&ue, loc, (ofs1 - ofs2) * sz, type); } - else if (agg1.op == TOK.symbolOffset && agg2.op == TOK.symbolOffset && (cast(SymOffExp)agg1).var == (cast(SymOffExp)agg2).var) + else if (agg1.op == TOK.symbolOffset && agg2.op == TOK.symbolOffset && + (cast(SymOffExp)agg1).var == (cast(SymOffExp)agg2).var) { emplaceExp!(IntegerExp)(&ue, loc, ofs1 - ofs2, type); } @@ -832,9 +856,9 @@ extern (C++) UnionExp pointerArithmetic(const ref Loc loc, TOK op, Type type, Ex emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); return ue; } - dinteger_t ofs1; if (eptr.op == TOK.address) eptr = (cast(AddrExp)eptr).e1; + dinteger_t ofs1; Expression agg1 = getAggregateFromPointer(eptr, &ofs1); if (agg1.op == TOK.symbolOffset) { @@ -945,8 +969,8 @@ extern (C++) int comparePointers(TOK op, Expression agg1, dinteger_t ofs1, Expre } return n; } - bool null1 = (agg1.op == TOK.null_); - bool null2 = (agg2.op == TOK.null_); + const null1 = (agg1.op == TOK.null_); + const null2 = (agg2.op == TOK.null_); int cmp; if (null1 || null2) { @@ -1129,14 +1153,14 @@ private int ctfeCmpArrays(const ref Loc loc, Expression e1, Expression e2, uinte // Comparing two array literals. This case is potentially recursive. // If they aren't strings, we just need an equality check rather than // a full cmp. - bool needCmp = ae1.type.nextOf().isintegral(); - for (size_t i = 0; i < cast(size_t)len; i++) + const bool needCmp = ae1.type.nextOf().isintegral(); + foreach (size_t i; 0 .. cast(size_t)len) { Expression ee1 = (*ae1.elements)[cast(size_t)(lo1 + i)]; Expression ee2 = (*ae2.elements)[cast(size_t)(lo2 + i)]; if (needCmp) { - sinteger_t c = ee1.toInteger() - ee2.toInteger(); + const sinteger_t c = ee1.toInteger() - ee2.toInteger(); if (c > 0) return 1; if (c < 0) @@ -1165,7 +1189,7 @@ private FuncDeclaration funcptrOf(Expression e) return null; } -private bool isArray(Expression e) +private bool isArray(const Expression e) { return e.op == TOK.arrayLiteral || e.op == TOK.string_ || e.op == TOK.slice || e.op == TOK.null_; } @@ -1232,15 +1256,15 @@ private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2) } if (isArray(e1) && isArray(e2)) { - uinteger_t len1 = resolveArrayLength(e1); - uinteger_t len2 = resolveArrayLength(e2); + const uinteger_t len1 = resolveArrayLength(e1); + const uinteger_t len2 = resolveArrayLength(e2); // workaround for dmc optimizer bug calculating wrong len for // uinteger_t len = (len1 < len2 ? len1 : len2); // if (len == 0) ... if (len1 > 0 && len2 > 0) { - uinteger_t len = (len1 < len2 ? len1 : len2); - int res = ctfeCmpArrays(loc, e1, e2, len); + const uinteger_t len = (len1 < len2 ? len1 : len2); + const int res = ctfeCmpArrays(loc, e1, e2, len); if (res != 0) return res; } @@ -1282,7 +1306,7 @@ private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2) return 1; else { - for (size_t i = 0; i < es1.elements.dim; i++) + foreach (size_t i; 0 .. es1.elements.dim) { Expression ee1 = (*es1.elements)[i]; Expression ee2 = (*es2.elements)[i]; @@ -1290,7 +1314,7 @@ private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2) continue; if (!ee1 || !ee2) return 1; - int cmp = ctfeRawCmp(loc, ee1, ee2); + const int cmp = ctfeRawCmp(loc, ee1, ee2); if (cmp) return 1; } @@ -1306,12 +1330,12 @@ private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2) return 1; bool* used = cast(bool*)mem.xmalloc(bool.sizeof * dim); memset(used, 0, bool.sizeof * dim); - for (size_t i = 0; i < dim; ++i) + foreach (size_t i; 0 .. dim) { Expression k1 = (*es1.keys)[i]; Expression v1 = (*es1.values)[i]; Expression v2 = null; - for (size_t j = 0; j < dim; ++j) + foreach (size_t j; 0 .. dim) { if (used[j]) continue; @@ -1338,10 +1362,7 @@ private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2) /// Evaluate ==, !=. Resolves slices before comparing. Returns 0 or 1 extern (C++) int ctfeEqual(const ref Loc loc, TOK op, Expression e1, Expression e2) { - int cmp = !ctfeRawCmp(loc, e1, e2); - if (op == TOK.notEqual) - cmp ^= 1; - return cmp; + return !ctfeRawCmp(loc, e1, e2) ^ (op == TOK.notEqual); } /// Evaluate is, !is. Resolves slices before comparing. Returns 0 or 1 @@ -1409,11 +1430,11 @@ extern (C++) UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expre // [chars] ~ string => string (only valid for CTFE) StringExp es1 = cast(StringExp)e2; ArrayLiteralExp es2 = cast(ArrayLiteralExp)e1; - size_t len = es1.len + es2.elements.dim; - ubyte sz = es1.sz; + const len = es1.len + es2.elements.dim; + const sz = es1.sz; void* s = mem.xmalloc((len + 1) * sz); memcpy(cast(char*)s + sz * es2.elements.dim, es1.string, es1.len * sz); - for (size_t i = 0; i < es2.elements.dim; i++) + foreach (size_t i; 0 .. es2.elements.dim) { Expression es2e = (*es2.elements)[i]; if (es2e.op != TOK.int64) @@ -1439,11 +1460,11 @@ extern (C++) UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expre // Concatenate the strings StringExp es1 = cast(StringExp)e1; ArrayLiteralExp es2 = cast(ArrayLiteralExp)e2; - size_t len = es1.len + es2.elements.dim; - ubyte sz = es1.sz; + const len = es1.len + es2.elements.dim; + const sz = es1.sz; void* s = mem.xmalloc((len + 1) * sz); memcpy(s, es1.string, es1.len * sz); - for (size_t i = 0; i < es2.elements.dim; i++) + foreach (size_t i; 0 .. es2.elements.dim) { Expression es2e = (*es2.elements)[i]; if (es2e.op != TOK.int64) @@ -1451,7 +1472,7 @@ extern (C++) UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expre emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); return ue; } - dinteger_t v = es2e.toInteger(); + const v = es2e.toInteger(); Port.valcpy(cast(char*)s + (es1.len + i) * sz, v, sz); } // Add terminating 0 @@ -1499,9 +1520,9 @@ extern (C++) Expression findKeyInAA(const ref Loc loc, AssocArrayLiteralExp ae, */ for (size_t i = ae.keys.dim; i;) { - i--; + --i; Expression ekey = (*ae.keys)[i]; - int eq = ctfeEqual(loc, TOK.equal, ekey, e2); + const int eq = ctfeEqual(loc, TOK.equal, ekey, e2); if (eq) { return (*ae.values)[i]; @@ -1631,7 +1652,7 @@ extern (C++) void assignInPlace(Expression dest, Expression src) else assert(0); assert(oldelems.dim == newelems.dim); - for (size_t i = 0; i < oldelems.dim; ++i) + foreach (size_t i; 0 .. oldelems.dim) { Expression e = (*newelems)[i]; Expression o = (*oldelems)[i]; @@ -1703,8 +1724,8 @@ extern (C++) UnionExp changeArrayLiteralLength(const ref Loc loc, TypeArray arra StringExp oldse = cast(StringExp)oldval; void* s = mem.xcalloc(newlen + 1, oldse.sz); memcpy(s, oldse.string, copylen * oldse.sz); - uint defaultValue = cast(uint)defaultElem.toInteger(); - for (size_t elemi = copylen; elemi < newlen; ++elemi) + const defaultValue = cast(uint)defaultElem.toInteger(); + foreach (size_t elemi; copylen .. newlen) { switch (oldse.sz) { @@ -1734,7 +1755,7 @@ extern (C++) UnionExp changeArrayLiteralLength(const ref Loc loc, TypeArray arra { assert(oldval.op == TOK.arrayLiteral); ArrayLiteralExp ae = cast(ArrayLiteralExp)oldval; - for (size_t i = 0; i < copylen; i++) + foreach (size_t i; 0 .. copylen) (*elements)[i] = (*ae.elements)[indxlo + i]; } if (elemType.ty == Tstruct || elemType.ty == Tsarray) @@ -1742,12 +1763,12 @@ extern (C++) UnionExp changeArrayLiteralLength(const ref Loc loc, TypeArray arra /* If it is an aggregate literal representing a value type, * we need to create a unique copy for each element */ - for (size_t i = copylen; i < newlen; i++) + foreach (size_t i; copylen .. newlen) (*elements)[i] = copyLiteral(defaultElem).copy(); } else { - for (size_t i = copylen; i < newlen; i++) + foreach (size_t i; copylen .. newlen) (*elements)[i] = defaultElem; } emplaceExp!(ArrayLiteralExp)(&ue, loc, elements); @@ -1811,7 +1832,7 @@ extern (C++) bool isCtfeValueValid(Expression newval) if (newval.op == TOK.slice) { // e1 should be an array aggregate - SliceExp se = cast(SliceExp)newval; + const SliceExp se = cast(SliceExp)newval; assert(se.lwr && se.lwr.op == TOK.int64); assert(se.upr && se.upr.op == TOK.int64); return (tb.ty == Tarray || tb.ty == Tsarray) && (se.e1.op == TOK.string_ || se.e1.op == TOK.arrayLiteral); @@ -1828,14 +1849,14 @@ extern (C++) bool isCtfeReferenceValid(Expression newval) return true; if (newval.op == TOK.variable) { - VarDeclaration v = (cast(VarExp)newval).var.isVarDeclaration(); + const VarDeclaration v = (cast(VarExp)newval).var.isVarDeclaration(); assert(v); // Must not be a reference to a reference return true; } if (newval.op == TOK.index) { - Expression eagg = (cast(IndexExp)newval).e1; + const Expression eagg = (cast(IndexExp)newval).e1; return eagg.op == TOK.string_ || eagg.op == TOK.arrayLiteral || eagg.op == TOK.assocArrayLiteral; } if (newval.op == TOK.dotVariable) @@ -1967,11 +1988,11 @@ extern (C++) UnionExp voidInitLiteral(Type t, VarDeclaration var) Expression elem = voidInitLiteral(tsa.next, var).copy(); // For aggregate value types (structs, static arrays) we must // create an a separate copy for each element. - bool mustCopy = (elem.op == TOK.arrayLiteral || elem.op == TOK.structLiteral); + const mustCopy = (elem.op == TOK.arrayLiteral || elem.op == TOK.structLiteral); auto elements = new Expressions(); - size_t d = cast(size_t)tsa.dim.toInteger(); + const d = cast(size_t)tsa.dim.toInteger(); elements.setDim(d); - for (size_t i = 0; i < d; i++) + foreach (i; 0 .. d) { if (mustCopy && i > 0) elem = copyLiteral(elem).copy(); @@ -1987,7 +2008,7 @@ extern (C++) UnionExp voidInitLiteral(Type t, VarDeclaration var) TypeStruct ts = cast(TypeStruct)t; auto exps = new Expressions(); exps.setDim(ts.sym.fields.dim); - for (size_t i = 0; i < ts.sym.fields.dim; i++) + foreach (size_t i; 0 .. ts.sym.fields.dim) { (*exps)[i] = voidInitLiteral(ts.sym.fields[i].type, ts.sym.fields[i]).copy(); } diff --git a/dmd/ctorflow.d b/dmd/ctorflow.d index 7c87c211242..2e06e5c1ab2 100644 --- a/dmd/ctorflow.d +++ b/dmd/ctorflow.d @@ -23,13 +23,11 @@ enum CSX : ushort none = 0, this_ctor = 0x01, /// called this() super_ctor = 0x02, /// called super() - this_ = 0x04, /// referenced this - super_ = 0x08, /// referenced super - label = 0x10, /// seen a label - return_ = 0x20, /// seen a return statement - any_ctor = 0x40, /// either this() or super() was called - halt = 0x80, /// assert(0) - deprecate_18719 = 0x100, // issue deprecation for Issue 18719 - delete when deprecation period is over + label = 0x04, /// seen a label + return_ = 0x08, /// seen a return statement + any_ctor = 0x10, /// either this() or super() was called + halt = 0x20, /// assert(0) + deprecate_18719 = 0x40, // issue deprecation for Issue 18719 - delete when deprecation period is over } /*********** @@ -100,7 +98,7 @@ else * Params: * ctorflow = bits to OR in */ - void OR(const ref CtorFlow ctorflow) + void OR(const ref CtorFlow ctorflow) pure nothrow { callSuper |= ctorflow.callSuper; if (fieldinit.length && ctorflow.fieldinit.length) @@ -114,56 +112,120 @@ else /**************************************** - * Merge fi flow analysis results into fieldInit. + * Merge `b` flow analysis results into `a`. * Params: - * fieldInit = the path to merge fi into - * fi = the other path + * a = the path to merge `b` into + * b = the other path * Returns: - * false means either fieldInit or fi skips initialization + * false means one of the paths skips construction */ -bool mergeFieldInitX(ref CSX fieldInit, const CSX fi) +bool mergeCallSuper(ref CSX a, const CSX b) pure nothrow { - if (fi != fieldInit) - { - // Have any branches returned? - bool aRet = (fi & CSX.return_) != 0; - bool bRet = (fieldInit & CSX.return_) != 0; - // Have any branches halted? - bool aHalt = (fi & CSX.halt) != 0; - bool bHalt = (fieldInit & CSX.halt) != 0; - bool ok; - if (aHalt && bHalt) - { - ok = true; - fieldInit = CSX.halt; - } - else if (!aHalt && aRet) - { - ok = (fi & CSX.this_ctor); - fieldInit = fieldInit; - } - else if (!bHalt && bRet) - { - ok = (fieldInit & CSX.this_ctor); - fieldInit = fi; - } - else if (aHalt) - { - ok = (fieldInit & CSX.this_ctor); - fieldInit = fieldInit; - } - else if (bHalt) - { - ok = (fi & CSX.this_ctor); - fieldInit = fi; - } - else - { - ok = !((fieldInit ^ fi) & CSX.this_ctor); - fieldInit |= fi; - } - return ok; + // This does a primitive flow analysis to support the restrictions + // regarding when and how constructors can appear. + // It merges the results of two paths. + // The two paths are `a` and `b`; the result is merged into `a`. + if (b == a) + return true; + + // Have ALL branches called a constructor? + const aAll = (a & (CSX.this_ctor | CSX.super_ctor)) != 0; + const bAll = (b & (CSX.this_ctor | CSX.super_ctor)) != 0; + // Have ANY branches called a constructor? + const aAny = (a & CSX.any_ctor) != 0; + const bAny = (b & CSX.any_ctor) != 0; + // Have any branches returned? + const aRet = (a & CSX.return_) != 0; + const bRet = (b & CSX.return_) != 0; + // Have any branches halted? + const aHalt = (a & CSX.halt) != 0; + const bHalt = (b & CSX.halt) != 0; + if (aHalt && bHalt) + { + a = CSX.halt; + } + else if ((!bHalt && bRet && !bAny && aAny) || (!aHalt && aRet && !aAny && bAny)) + { + // If one has returned without a constructor call, there must not + // be ctor calls in the other. + return false; + } + else if (bHalt || bRet && bAll) + { + // If one branch has called a ctor and then exited, anything the + // other branch has done is OK (except returning without a + // ctor call, but we already checked that). + a |= b & (CSX.any_ctor | CSX.label); + } + else if (aHalt || aRet && aAll) + { + a = cast(CSX)(b | (a & (CSX.any_ctor | CSX.label))); + } + else if (aAll != bAll) // both branches must have called ctors, or both not + return false; + else + { + // If one returned without a ctor, remember that + if (bRet && !bAny) + a |= CSX.return_; + a |= b & (CSX.any_ctor | CSX.label); } return true; } + +/**************************************** + * Merge `b` flow analysis results into `a`. + * Params: + * a = the path to merge `b` into + * b = the other path + * Returns: + * false means either `a` or `b` skips initialization + */ +bool mergeFieldInit(ref CSX a, const CSX b) pure nothrow +{ + if (b == a) + return true; + + // Have any branches returned? + const aRet = (a & CSX.return_) != 0; + const bRet = (b & CSX.return_) != 0; + // Have any branches halted? + const aHalt = (a & CSX.halt) != 0; + const bHalt = (b & CSX.halt) != 0; + + if (aHalt && bHalt) + { + a = CSX.halt; + return true; + } + + bool ok; + if (!bHalt && bRet) + { + ok = (b & CSX.this_ctor); + a = a; + } + else if (!aHalt && aRet) + { + ok = (a & CSX.this_ctor); + a = b; + } + else if (bHalt) + { + ok = (a & CSX.this_ctor); + a = a; + } + else if (aHalt) + { + ok = (b & CSX.this_ctor); + a = b; + } + else + { + ok = !((a ^ b) & CSX.this_ctor); + a |= b; + } + return ok; +} + diff --git a/dmd/dcast.d b/dmd/dcast.d index e6fe14db6c0..b85629ff713 100644 --- a/dmd/dcast.d +++ b/dmd/dcast.d @@ -1440,6 +1440,13 @@ extern (C++) Expression castTo(Expression e, Scope* sc, Type t) if (v && v.storage_class & STC.manifest) { result = e.ctfeInterpret(); + /* https://issues.dlang.org/show_bug.cgi?id=18236 + * + * The expression returned by ctfeInterpret points + * to the line where the manifest constant was declared + * so we need to update the location before trying to cast + */ + result.loc = e.loc; result = result.castTo(sc, t); return; } @@ -1555,8 +1562,7 @@ extern (C++) Expression castTo(Expression e, Scope* sc, Type t) { // T[n] sa; // cast(U*)sa; // ==> cast(U*)sa.ptr; - result = new AddrExp(e.loc, e); - result.type = t; + result = new AddrExp(e.loc, e, t); return; } if (tob.ty == Tarray && t1b.ty == Tsarray) @@ -2022,8 +2028,7 @@ extern (C++) Expression castTo(Expression e, Scope* sc, Type t) { result = new VarExp(e.loc, f, false); result.type = f.type; - result = new AddrExp(e.loc, result); - result.type = t; + result = new AddrExp(e.loc, result, t); return; } } diff --git a/dmd/dclass.d b/dmd/dclass.d index 6beb8058372..b2349548f2c 100644 --- a/dmd/dclass.d +++ b/dmd/dclass.d @@ -175,22 +175,6 @@ struct ClassFlags alias hasDtor = Enum.hasDtor; } -/** - * The ClassKind enum is used in ClassDeclaration AST nodes - * to specify the linkage type of the class or if it is an - * anonymous class. If the class is anonymous it is also - * considered to be a D class. - */ -enum ClassKind : int -{ - /// the class is a d(efault) class - d, - /// the class is a C++ interface - cpp, - /// the class is an Objective-C class/interface - objc, -} - /*********************************************************** */ extern (C++) class ClassDeclaration : AggregateDeclaration @@ -230,8 +214,8 @@ extern (C++) class ClassDeclaration : AggregateDeclaration /// true if this is a scope class bool stack; - /// specifies whether this is a D, C++, Objective-C or anonymous class/interface - ClassKind classKind; + /// if this is a C++ class, this is the slot reserved for the virtual destructor + int cppDtorVtblIndex = -1; /// to prevent recursive attempts private bool inuse; diff --git a/dmd/declaration.d b/dmd/declaration.d index 507b35440b8..19c8f9000f3 100644 --- a/dmd/declaration.d +++ b/dmd/declaration.d @@ -76,7 +76,7 @@ extern (C++) bool checkFrameAccess(Loc loc, Scope* sc, AggregateDeclaration ad, * Mark variable v as modified if it is inside a constructor that var * is a field in. */ -private int modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1) +bool modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1) { //printf("modifyFieldVar(var = %s)\n", var.toChars()); Dsymbol s = sc.func; @@ -1396,7 +1396,7 @@ extern (C++) class VarDeclaration : Declaration } else { - // _ArrayDtor(v[0 .. n]) + // __ArrayDtor(v[0 .. n]) e = new VarExp(loc, this); const sdsz = sd.type.size(); @@ -1411,7 +1411,7 @@ extern (C++) class VarDeclaration : Declaration // This is a hack so we can call destructors on const/immutable objects. e.type = sd.type.arrayOf(); - e = new CallExp(loc, new IdentifierExp(loc, Id._ArrayDtor), e); + e = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayDtor), e); } return e; } diff --git a/dmd/declaration.h b/dmd/declaration.h index 4767be68180..c017b3f0eec 100644 --- a/dmd/declaration.h +++ b/dmd/declaration.h @@ -25,6 +25,11 @@ class LabelDsymbol; class Initializer; class Module; class ForeachStatement; +struct Ensure +{ + Identifier *id; + Statement *ensure; +}; class FuncDeclaration; class ExpInitializer; class StructDeclaration; @@ -498,8 +503,10 @@ class FuncDeclaration : public Declaration { public: Types *fthrows; // Array of Type's of exceptions (not used) - Statement *frequire; - Statement *fensure; + Statements *frequires; // in contracts + Ensures *fensures; // out contracts + Statement *frequire; // lowered in contract + Statement *fensure; // lowered out contract Statement *fbody; FuncDeclarations foverrides; // functions this function overrides @@ -528,8 +535,7 @@ class FuncDeclaration : public Declaration bool emitInstrumentation; #endif - Identifier *outId; // identifier for out statement - VarDeclaration *vresult; // variable corresponding to outId + VarDeclaration *vresult; // result variable for out contracts LabelDsymbol *returnLabel; // where the return goes // used to prevent symbols in different diff --git a/dmd/delegatize.d b/dmd/delegatize.d index a4945dac6fe..ddf96304f5d 100644 --- a/dmd/delegatize.d +++ b/dmd/delegatize.d @@ -82,7 +82,7 @@ private void lambdaSetParent(Expression e, FuncDeclaration fd) { extern (C++) final class LambdaSetParent : StoppableVisitor { - alias visit = super.visit; + alias visit = typeof(super).visit; FuncDeclaration fd; public: @@ -137,7 +137,7 @@ extern (C++) bool lambdaCheckForNestedRef(Expression e, Scope* sc) { extern (C++) final class LambdaCheckForNestedRef : StoppableVisitor { - alias visit = super.visit; + alias visit = typeof(super).visit; public: Scope* sc; bool result; diff --git a/dmd/dinterpret.d b/dmd/dinterpret.d index f326f7d103c..de931283e63 100644 --- a/dmd/dinterpret.d +++ b/dmd/dinterpret.d @@ -43,6 +43,121 @@ import dmd.tokens; import dmd.utf; import dmd.visitor; +/************************************* + * Entry point for CTFE. + * A compile-time result is required. Give an error if not possible. + * + * `e` must be semantically valid expression. In other words, it should not + * contain any `ErrorExp`s in it. But, CTFE interpretation will cross over + * functions and may invoke a function that contains `ErrorStatement` in its body. + * If that, the "CTFE failed because of previous errors" error is raised. + */ +public extern (C++) Expression ctfeInterpret(Expression e) +{ + if (e.op == TOK.error) + return e; + assert(e.type); // https://issues.dlang.org/show_bug.cgi?id=14642 + //assert(e.type.ty != Terror); // FIXME + if (e.type.ty == Terror) + return new ErrorExp(); + + // This code is outside a function, but still needs to be compiled + // (there are compiler-generated temporary variables such as __dollar). + // However, this will only be run once and can then be discarded. + auto ctfeCodeGlobal = CompiledCtfeFunction(null); + ctfeCodeGlobal.callingloc = e.loc; + ctfeCodeGlobal.onExpression(e); + + Expression result = interpret(e, null); + + if (!CTFEExp.isCantExp(result)) + result = scrubReturnValue(e.loc, result); + if (CTFEExp.isCantExp(result)) + result = new ErrorExp(); + + return result; +} + +/* Run CTFE on the expression, but allow the expression to be a TypeExp + * or a tuple containing a TypeExp. (This is required by pragma(msg)). + */ +public extern (C++) Expression ctfeInterpretForPragmaMsg(Expression e) +{ + if (e.op == TOK.error || e.op == TOK.type) + return e; + + // It's also OK for it to be a function declaration (happens only with + // __traits(getOverloads)) + if (e.op == TOK.variable && (cast(VarExp)e).var.isFuncDeclaration()) + { + return e; + } + + if (e.op != TOK.tuple) + return e.ctfeInterpret(); + + // Tuples need to be treated separately, since they are + // allowed to contain a TypeExp in this case. + + TupleExp tup = cast(TupleExp)e; + Expressions* expsx = null; + for (size_t i = 0; i < tup.exps.dim; ++i) + { + Expression g = (*tup.exps)[i]; + Expression h = g; + h = ctfeInterpretForPragmaMsg(g); + if (h != g) + { + if (!expsx) + { + expsx = new Expressions(); + expsx.setDim(tup.exps.dim); + for (size_t j = 0; j < tup.exps.dim; j++) + (*expsx)[j] = (*tup.exps)[j]; + } + (*expsx)[i] = h; + } + } + if (expsx) + { + auto te = new TupleExp(e.loc, expsx); + expandTuples(te.exps); + te.type = new TypeTuple(te.exps); + return te; + } + return e; +} + +public extern (C++) Expression getValue(VarDeclaration vd) +{ + return ctfeStack.getValue(vd); +} + + +// CTFE diagnostic information +public extern (C++) void printCtfePerformanceStats() +{ + debug (SHOWPERFORMANCE) + { + printf(" ---- CTFE Performance ----\n"); + printf("max call depth = %d\tmax stack = %d\n", CtfeStatus.maxCallDepth, ctfeStack.maxStackUsage()); + printf("array allocs = %d\tassignments = %d\n\n", CtfeStatus.numArrayAllocs, CtfeStatus.numAssignments); + } +} + +/********* + * Typesafe PIMPL idiom so we can keep CompiledCtfeFunction private. + */ +struct CompiledCtfeFunctionPimpl +{ + private CompiledCtfeFunction* pimpl; + private alias pimpl this; +} + +/* ================================================ Implementation ======================================= */ + +private: + enum CtfeGoal : int { ctfeNeedRvalue, // Must return an Rvalue (== CTFE value) @@ -224,17 +339,6 @@ private struct InterState extern (C++) __gshared CtfeStack ctfeStack; -// CTFE diagnostic information -extern (C++) void printCtfePerformanceStats() -{ - debug (SHOWPERFORMANCE) - { - printf(" ---- CTFE Performance ----\n"); - printf("max call depth = %d\tmax stack = %d\n", CtfeStatus.maxCallDepth, ctfeStack.maxStackUsage()); - printf("array allocs = %d\tassignments = %d\n\n", CtfeStatus.numArrayAllocs, CtfeStatus.numAssignments); - } -} - /*********************************************************** * CTFE-object code for a single function * @@ -261,7 +365,7 @@ struct CompiledCtfeFunction { extern (C++) final class VarWalker : StoppableVisitor { - alias visit = super.visit; + alias visit = typeof(super).visit; public: CompiledCtfeFunction* ccf; @@ -635,91 +739,6 @@ private void ctfeCompile(FuncDeclaration fd) v.ctfeCompile(fd.fbody); } -/************************************* - * Entry point for CTFE. - * A compile-time result is required. Give an error if not possible. - * - * `e` must be semantically valid expression. In other words, it should not - * contain any `ErrorExp`s in it. But, CTFE interpretation will cross over - * functions and may invoke a function that contains `ErrorStatement` in its body. - * If that, the "CTFE failed because of previous errors" error is raised. - */ -extern (C++) Expression ctfeInterpret(Expression e) -{ - if (e.op == TOK.error) - return e; - assert(e.type); // https://issues.dlang.org/show_bug.cgi?id=14642 - //assert(e.type.ty != Terror); // FIXME - if (e.type.ty == Terror) - return new ErrorExp(); - - // This code is outside a function, but still needs to be compiled - // (there are compiler-generated temporary variables such as __dollar). - // However, this will only be run once and can then be discarded. - auto ctfeCodeGlobal = CompiledCtfeFunction(null); - ctfeCodeGlobal.callingloc = e.loc; - ctfeCodeGlobal.onExpression(e); - - Expression result = interpret(e, null); - - if (!CTFEExp.isCantExp(result)) - result = scrubReturnValue(e.loc, result); - if (CTFEExp.isCantExp(result)) - result = new ErrorExp(); - - return result; -} - -/* Run CTFE on the expression, but allow the expression to be a TypeExp - * or a tuple containing a TypeExp. (This is required by pragma(msg)). - */ -extern (C++) Expression ctfeInterpretForPragmaMsg(Expression e) -{ - if (e.op == TOK.error || e.op == TOK.type) - return e; - - // It's also OK for it to be a function declaration (happens only with - // __traits(getOverloads)) - if (e.op == TOK.variable && (cast(VarExp)e).var.isFuncDeclaration()) - { - return e; - } - - if (e.op != TOK.tuple) - return e.ctfeInterpret(); - - // Tuples need to be treated separately, since they are - // allowed to contain a TypeExp in this case. - - TupleExp tup = cast(TupleExp)e; - Expressions* expsx = null; - for (size_t i = 0; i < tup.exps.dim; ++i) - { - Expression g = (*tup.exps)[i]; - Expression h = g; - h = ctfeInterpretForPragmaMsg(g); - if (h != g) - { - if (!expsx) - { - expsx = new Expressions(); - expsx.setDim(tup.exps.dim); - for (size_t j = 0; j < tup.exps.dim; j++) - (*expsx)[j] = (*tup.exps)[j]; - } - (*expsx)[i] = h; - } - } - if (expsx) - { - auto te = new TupleExp(e.loc, expsx); - expandTuples(te.exps); - te.type = new TypeTuple(te.exps); - return te; - } - return e; -} - /************************************* * Attempt to interpret a function given the arguments. * Input: @@ -730,7 +749,7 @@ extern (C++) Expression ctfeInterpretForPragmaMsg(Expression e) * Return result expression if successful, TOK.cantExpression if not, * or CTFEExp if function returned void. */ -private Expression interpret(FuncDeclaration fd, InterState* istate, Expressions* arguments, Expression thisarg) +private Expression interpretFunction(FuncDeclaration fd, InterState* istate, Expressions* arguments, Expression thisarg) { debug (LOG) { @@ -989,9 +1008,11 @@ public: InterState* istate; CtfeGoal goal; Expression result; + UnionExp* pue; // storage for `result` - extern (D) this(InterState* istate, CtfeGoal goal) + extern (D) this(UnionExp* pue, InterState* istate, CtfeGoal goal) { + this.pue = pue; this.istate = istate; this.goal = goal; } @@ -1002,7 +1023,8 @@ public: { if (exceptionOrCantInterpret(e)) { - result = e; + // Make sure e is not pointing to a stack temporary + result = (e.op == TOK.cantExpression) ? CTFEExp.cantexp : e; return true; } return false; @@ -1053,7 +1075,7 @@ public: istate.start = null; } - Expression e = interpret(s.exp, istate, ctfeNeedNothing); + Expression e = interpret(pue, s.exp, istate, ctfeNeedNothing); if (exceptionOrCant(e)) return; } @@ -1067,11 +1089,11 @@ public: if (istate.start == s) istate.start = null; - size_t dim = s.statements ? s.statements.dim : 0; - for (size_t i = 0; i < dim; i++) + const dim = s.statements ? s.statements.dim : 0; + foreach (i; 0 .. dim) { Statement sx = (*s.statements)[i]; - result = interpret(sx, istate); + result = interpret(pue, sx, istate); if (result) break; } @@ -1090,11 +1112,11 @@ public: if (istate.start == s) istate.start = null; - size_t dim = s.statements ? s.statements.dim : 0; - for (size_t i = 0; i < dim; i++) + const dim = s.statements ? s.statements.dim : 0; + foreach (i; 0 .. dim) { Statement sx = (*s.statements)[i]; - Expression e = interpret(sx, istate); + Expression e = interpret(pue, sx, istate); if (!e) // succeeds to interpret, or goto target was not found continue; if (exceptionOrCant(e)) @@ -1145,15 +1167,16 @@ public: return; } - Expression e = interpret(s.condition, istate); + UnionExp ue = void; + Expression e = interpret(&ue, s.condition, istate); assert(e); if (exceptionOrCant(e)) return; if (isTrueBool(e)) - result = interpret(s.ifbody, istate); + result = interpret(pue, s.ifbody, istate); else if (e.isBool(false)) - result = interpret(s.elsebody, istate); + result = interpret(pue, s.elsebody, istate); else { // no error, or assert(0)? @@ -1170,17 +1193,18 @@ public: if (istate.start == s) istate.start = null; - result = interpret(s.statement, istate); + 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. - Return true if it is safe to return, false if an error was generated. 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) { @@ -1272,7 +1296,7 @@ public: */ if (tf.isref) { - result = interpret(s.exp, istate, ctfeNeedLvalue); + result = interpret(pue, s.exp, istate, ctfeNeedLvalue); return; } if (tf.next && tf.next.ty == Tdelegate && istate.fd.closureVars.dim > 0) @@ -1286,7 +1310,7 @@ public: // We need to treat pointers specially, because TOK.symbolOffset can be used to // return a value OR a pointer - Expression e = interpret(s.exp, istate); + Expression e = interpret(pue, s.exp, istate); if (exceptionOrCant(e)) return; @@ -1407,7 +1431,8 @@ public: return; } - e = interpret(s.condition, istate); + UnionExp ue = void; + e = interpret(&ue, s.condition, istate); if (exceptionOrCant(e)) return; if (!e.isConst()) @@ -1431,7 +1456,8 @@ public: if (istate.start == s) istate.start = null; - Expression ei = interpret(s._init, istate); + UnionExp ueinit = void; + Expression ei = interpret(&ueinit, s._init, istate); if (exceptionOrCant(ei)) return; assert(!ei); // s.init never returns from function, or jumps out from it @@ -1440,7 +1466,8 @@ public: { if (s.condition && !istate.start) { - Expression e = interpret(s.condition, istate); + UnionExp ue = void; + Expression e = interpret(&ue, s.condition, istate); if (exceptionOrCant(e)) return; if (e.isBool(false)) @@ -1448,7 +1475,7 @@ public: assert(isTrueBool(e)); } - Expression e = interpret(s._body, istate); + Expression e = interpret(pue, s._body, istate); if (!e && istate.start) // goto target was not found return; assert(!istate.start); @@ -1481,7 +1508,8 @@ public: return; } - e = interpret(s.increment, istate, ctfeNeedNothing); + UnionExp uei = void; + e = interpret(&uei, s.increment, istate, ctfeNeedNothing); if (exceptionOrCant(e)) return; } @@ -1527,7 +1555,8 @@ public: return; } - Expression econdition = interpret(s.condition, istate); + UnionExp uecond = void; + Expression econdition = interpret(&uecond, s.condition, istate); if (exceptionOrCant(econdition)) return; @@ -1536,7 +1565,8 @@ public: for (size_t i = 0; i < dim; i++) { CaseStatement cs = (*s.cases)[i]; - Expression ecase = interpret(cs.exp, istate); + UnionExp uecase = void; + Expression ecase = interpret(&uecase, cs.exp, istate); if (exceptionOrCant(ecase)) return; if (ctfeEqual(cs.exp.loc, TOK.equal, econdition, ecase)) @@ -1557,7 +1587,7 @@ public: /* Jump to scase */ istate.start = scase; - Expression e = interpret(s._body, istate); + Expression e = interpret(pue, s._body, istate); assert(!istate.start); // jump must not fail if (e && e.op == TOK.break_) { @@ -1581,7 +1611,7 @@ public: if (istate.start == s) istate.start = null; - result = interpret(s.statement, istate); + result = interpret(pue, s.statement, istate); } override void visit(DefaultStatement s) @@ -1593,7 +1623,7 @@ public: if (istate.start == s) istate.start = null; - result = interpret(s.statement, istate); + result = interpret(pue, s.statement, istate); } override void visit(GotoStatement s) @@ -1659,7 +1689,7 @@ public: if (istate.start == s) istate.start = null; - result = interpret(s.statement, istate); + result = interpret(pue, s.statement, istate); } override void visit(TryCatchStatement s) @@ -1673,13 +1703,13 @@ public: if (istate.start) { Expression e = null; - e = interpret(s._body, istate); + e = interpret(pue, s._body, istate); for (size_t i = 0; i < s.catches.dim; i++) { if (e || !istate.start) // goto target was found break; Catch ca = (*s.catches)[i]; - e = interpret(ca.handler, istate); + e = interpret(pue, ca.handler, istate); } result = e; return; @@ -1780,7 +1810,7 @@ public: if (istate.start) { Expression e = null; - e = interpret(s._body, istate); + e = interpret(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; @@ -1876,7 +1906,7 @@ public: // If it is with(Enum) {...}, just execute the body. if (s.exp.op == TOK.scope_ || s.exp.op == TOK.type) { - result = interpret(s._body, istate); + result = interpret(pue, s._body, istate); return; } @@ -1886,8 +1916,7 @@ public: if (s.wthis.type.ty == Tpointer && s.exp.type.ty != Tpointer) { - e = new AddrExp(s.loc, e); - e.type = s.wthis.type; + e = new AddrExp(s.loc, e, s.wthis.type); } ctfeStack.push(s.wthis); setValue(s.wthis, e); @@ -2092,8 +2121,8 @@ public: // It's OK to cast from fixed length to dynamic array, eg &int[3] to int[]* if (val.type.ty == Tsarray && pointee.ty == Tarray && elemsize == pointee.nextOf().size()) { - result = new AddrExp(e.loc, val); - result.type = e.type; + emplaceExp!(AddrExp)(pue, e.loc, val, e.type); + result = pue.exp(); return; } @@ -2105,10 +2134,10 @@ public: Expression eupr = new IntegerExp(e.loc, e.offset / elemsize + d, Type.tsize_t); // Create a CTFE pointer &val[ofs..ofs+d] - result = new SliceExp(e.loc, val, elwr, eupr); - result.type = pointee; - result = new AddrExp(e.loc, result); - result.type = e.type; + auto se = new SliceExp(e.loc, val, elwr, eupr); + se.type = pointee; + emplaceExp!(AddrExp)(pue, e.loc, se, e.type); + result = pue.exp(); return; } @@ -2118,10 +2147,10 @@ public: if (e.offset == 0 && isSafePointerCast(e.var.type, pointee)) { // Create a CTFE pointer &var - result = new VarExp(e.loc, e.var); - result.type = elemtype; - result = new AddrExp(e.loc, result); - result.type = e.type; + auto ve = new VarExp(e.loc, e.var); + ve.type = elemtype; + emplaceExp!(AddrExp)(pue, e.loc, ve, e.type); + result = pue.exp(); return; } e.error("reinterpreting cast from `%s` to `%s` is not supported in CTFE", val.type.toChars(), e.type.toChars()); @@ -2129,7 +2158,7 @@ public: return; } - dinteger_t sz = pointee.size(); + const dinteger_t sz = pointee.size(); dinteger_t indx = e.offset / sz; assert(sz * indx == e.offset); Expression aggregate = null; @@ -2140,17 +2169,18 @@ public: else if (val.op == TOK.slice) { aggregate = (cast(SliceExp)val).e1; - Expression lwr = interpret((cast(SliceExp)val).lwr, istate); + UnionExp uelwr = void; + Expression lwr = interpret(&uelwr, (cast(SliceExp)val).lwr, istate); indx += lwr.toInteger(); } if (aggregate) { // Create a CTFE pointer &aggregate[ofs] auto ofs = new IntegerExp(e.loc, indx, Type.tsize_t); - result = new IndexExp(e.loc, aggregate, ofs); - result.type = elemtype; - result = new AddrExp(e.loc, result); - result.type = e.type; + auto ei = new IndexExp(e.loc, aggregate, ofs); + ei.type = elemtype; + emplaceExp!(AddrExp)(pue, e.loc, ei, e.type); + result = pue.exp(); return; } } @@ -2159,8 +2189,8 @@ public: // Create a CTFE pointer &var auto ve = new VarExp(e.loc, e.var); ve.type = e.var.type; - result = new AddrExp(e.loc, ve); - result.type = e.type; + emplaceExp!(AddrExp)(pue, e.loc, ve, e.type); + result = pue.exp(); return; } @@ -2193,15 +2223,15 @@ public: return; } } - result = interpret(e.e1, istate, ctfeNeedLvalue); - if (result.op == TOK.variable && (cast(VarExp)result).var == istate.fd.vthis) - result = interpret(result, istate); - if (exceptionOrCant(result)) + auto er = interpret(e.e1, istate, ctfeNeedLvalue); + if (er.op == TOK.variable && (cast(VarExp)er).var == istate.fd.vthis) + er = interpret(er, istate); + if (exceptionOrCant(er)) return; // Return a simplified address expression - result = new AddrExp(e.loc, result); - result.type = e.type; + emplaceExp!(AddrExp)(pue, e.loc, er, e.type); + result = pue.exp(); } override void visit(DelegateExp e) @@ -2221,17 +2251,18 @@ public: return; } - result = interpret(e.e1, istate); - if (exceptionOrCant(result)) + auto er = interpret(e.e1, istate); + if (exceptionOrCant(er)) return; - if (result == e.e1) + if (er == e.e1) { // If it has already been CTFE'd, just return it result = e; } else { - result = new DelegateExp(e.loc, result, e.func, false); + emplaceExp!(DelegateExp)(pue, e.loc, er, e.func, false); + result = pue.exp(); result.type = e.type; } } @@ -2404,7 +2435,7 @@ public: Expression ev = getValue(v); if (ev.op == TOK.variable || ev.op == TOK.index || ev.op == TOK.dotVariable) { - result = interpret(ev, istate, goal); + result = interpret(pue, ev, istate, goal); return; } } @@ -2580,7 +2611,8 @@ public: ClassDeclaration cd = (cast(ClassReferenceExp)result).originalClass(); assert(cd); - result = new TypeidExp(e.loc, cd.type); + emplaceExp!(TypeidExp)(pue, e.loc, cd.type); + result = pue.exp(); result.type = e.type; return; } @@ -2625,9 +2657,9 @@ public: if (expsx !is e.exps) { expandTuples(expsx); - auto te = new TupleExp(e.loc, expsx); - te.type = new TypeTuple(te.exps); - result = te; + emplaceExp!(TupleExp)(pue, e.loc, expsx); + result = pue.exp(); + result.type = new TypeTuple(expsx); } else result = e; @@ -2698,7 +2730,8 @@ public: result = CTFEExp.cantexp; return; } - auto ale = new ArrayLiteralExp(e.loc, basis, expsx); + emplaceExp!(ArrayLiteralExp)(pue, e.loc, basis, expsx); + auto ale = cast(ArrayLiteralExp)pue.exp(); ale.type = e.type; ale.ownedByCtfe = OwnedBy.ctfe; result = ale; @@ -2869,7 +2902,8 @@ public: result = CTFEExp.cantexp; return; } - auto sle = new StructLiteralExp(e.loc, e.sd, expsx); + emplaceExp!(StructLiteralExp)(pue, e.loc, e.sd, expsx); + auto sle = cast(StructLiteralExp)pue.exp(); sle.type = e.type; sle.ownedByCtfe = OwnedBy.ctfe; result = sle; @@ -2946,7 +2980,7 @@ public: se = interpret(se, istate); if (exceptionOrCant(se)) return; - result = interpret(e.member, istate, e.arguments, se); + result = interpretFunction(e.member, istate, e.arguments, se); // Repaint as same as CallExp::interpret() does. result.loc = e.loc; @@ -2977,8 +3011,8 @@ public: } if (exceptionOrCant(result)) return; - result = new AddrExp(e.loc, result); - result.type = e.type; + emplaceExp!(AddrExp)(pue, e.loc, result, e.type); + result = pue.exp(); return; } if (e.newtype.toBasetype().ty == Tclass) @@ -3039,7 +3073,7 @@ public: result = CTFEExp.cantexp; return; } - Expression ctorfail = interpret(e.member, istate, e.arguments, eref); + Expression ctorfail = interpretFunction(e.member, istate, e.arguments, eref); if (exceptionOrCant(ctorfail)) return; @@ -3072,10 +3106,10 @@ public: ae.type = e.newtype.arrayOf(); ae.ownedByCtfe = OwnedBy.ctfe; - result = new IndexExp(e.loc, ae, new IntegerExp(Loc.initial, 0, Type.tsize_t)); - result.type = e.newtype; - result = new AddrExp(e.loc, result); - result.type = e.type; + auto ei = new IndexExp(e.loc, ae, new IntegerExp(Loc.initial, 0, Type.tsize_t)); + ei.type = e.newtype; + emplaceExp!(AddrExp)(pue, e.loc, ei, e.type); + result = pue.exp(); return; } e.error("cannot interpret `%s` at compile time", e.toChars()); @@ -3088,22 +3122,22 @@ public: { printf("%s UnaExp::interpret() %s\n", e.loc.toChars(), e.toChars()); } - Expression e1 = interpret(e.e1, istate); + UnionExp ue = void; + Expression e1 = interpret(&ue, e.e1, istate); if (exceptionOrCant(e1)) return; - UnionExp ue; switch (e.op) { case TOK.negate: - ue = Neg(e.type, e1); + *pue = Neg(e.type, e1); break; case TOK.tilde: - ue = Com(e.type, e1); + *pue = Com(e.type, e1); break; case TOK.not: - ue = Not(e.type, e1); + *pue = Not(e.type, e1); break; case TOK.vector: @@ -3113,7 +3147,7 @@ public: default: assert(0); } - result = ue.copy(); + result = (*pue).exp(); } override void visit(DotTypeExp e) @@ -3122,15 +3156,17 @@ public: { printf("%s DotTypeExp::interpret() %s\n", e.loc.toChars(), e.toChars()); } - Expression e1 = interpret(e.e1, istate); + UnionExp ue = void; + Expression e1 = interpret(&ue, e.e1, istate); if (exceptionOrCant(e1)) return; if (e1 == e.e1) result = e; // optimize: reuse this CTFE reference else { - result = e.copy(); - (cast(DotTypeExp)result).e1 = e1; + auto edt = cast(DotTypeExp)e.copy(); + edt.e1 = (e1 == ue.exp()) ? e1.copy() : e1; // don't return pointer to ue + result = edt; } } @@ -3142,35 +3178,44 @@ public: } if (e.e1.type.ty == Tpointer && e.e2.type.ty == Tpointer && e.op == TOK.min) { - Expression e1 = interpret(e.e1, istate); + UnionExp ue1 = void; + Expression e1 = interpret(&ue1, e.e1, istate); if (exceptionOrCant(e1)) return; - Expression e2 = interpret(e.e2, istate); + UnionExp ue2 = void; + Expression e2 = interpret(&ue2, e.e2, istate); if (exceptionOrCant(e2)) return; - result = pointerDifference(e.loc, e.type, e1, e2).copy(); + *pue = pointerDifference(e.loc, e.type, e1, e2); + result = (*pue).exp(); return; } if (e.e1.type.ty == Tpointer && e.e2.type.isintegral()) { - Expression e1 = interpret(e.e1, istate); + UnionExp ue1 = void; + Expression e1 = interpret(&ue1, e.e1, istate); if (exceptionOrCant(e1)) return; - Expression e2 = interpret(e.e2, istate); + UnionExp ue2 = void; + Expression e2 = interpret(&ue2, e.e2, istate); if (exceptionOrCant(e2)) return; - result = pointerArithmetic(e.loc, e.op, e.type, e1, e2).copy(); + *pue = pointerArithmetic(e.loc, e.op, e.type, e1, e2); + result = (*pue).exp(); return; } if (e.e2.type.ty == Tpointer && e.e1.type.isintegral() && e.op == TOK.add) { - Expression e1 = interpret(e.e1, istate); + UnionExp ue1 = void; + Expression e1 = interpret(&ue1, e.e1, istate); if (exceptionOrCant(e1)) return; - Expression e2 = interpret(e.e2, istate); + UnionExp ue2 = void; + Expression e2 = interpret(&ue2, e.e2, istate); if (exceptionOrCant(e2)) return; - result = pointerArithmetic(e.loc, e.op, e.type, e2, e1).copy(); + *pue = pointerArithmetic(e.loc, e.op, e.type, e2, e1); + result = (*pue).exp(); return; } if (e.e1.type.ty == Tpointer || e.e2.type.ty == Tpointer) @@ -3180,9 +3225,9 @@ public: return; } - bool evalOperand(Expression ex, out Expression er) + bool evalOperand(UnionExp* pue, Expression ex, out Expression er) { - er = interpret(ex, istate); + er = interpret(pue, ex, istate); if (exceptionOrCant(er)) return false; if (er.isConst() != 1) @@ -3198,18 +3243,20 @@ public: return true; } + UnionExp ue1 = void; Expression e1; - if (!evalOperand(e.e1, e1)) + if (!evalOperand(&ue1, e.e1, e1)) return; + UnionExp ue2 = void; Expression e2; - if (!evalOperand(e.e2, e2)) + if (!evalOperand(&ue2, e.e2, e2)) return; if (e.op == TOK.rightShift || e.op == TOK.leftShift || e.op == TOK.unsignedRightShift) { - sinteger_t i2 = e2.toInteger(); - d_uns64 sz = e1.type.size() * 8; + const sinteger_t i2 = e2.toInteger(); + const d_uns64 sz = e1.type.size() * 8; if (i2 < 0 || i2 >= sz) { e.error("shift by %lld is outside the range 0..%llu", i2, cast(ulong)sz - 1); @@ -3217,7 +3264,8 @@ public: return; } } - result = (*fp)(e.loc, e.type, e1, e2).copy(); + *pue = (*fp)(e.loc, e.type, e1, e2); + result = (*pue).exp(); if (CTFEExp.isCantExp(result)) e.error("`%s` cannot be interpreted at compile time", e.toChars()); } @@ -3228,12 +3276,14 @@ public: { printf("%s BinExp::interpretCompareCommon() %s\n", e.loc.toChars(), e.toChars()); } + UnionExp ue1 = void; + UnionExp ue2 = void; if (e.e1.type.ty == Tpointer && e.e2.type.ty == Tpointer) { - Expression e1 = interpret(e.e1, istate); + Expression e1 = interpret(&ue1, e.e1, istate); if (exceptionOrCant(e1)) return; - Expression e2 = interpret(e.e2, istate); + Expression e2 = interpret(&ue2, e.e2, istate); if (exceptionOrCant(e2)) return; //printf("e1 = %s %s, e2 = %s %s\n", e1.type.toChars(), e1.toChars(), e2.type.toChars(), e2.toChars()); @@ -3241,7 +3291,7 @@ public: Expression agg1 = getAggregateFromPointer(e1, &ofs1); Expression agg2 = getAggregateFromPointer(e2, &ofs2); //printf("agg1 = %p %s, agg2 = %p %s\n", agg1, agg1.toChars(), agg2, agg2.toChars()); - int cmp = comparePointers(e.op, agg1, ofs1, agg2, ofs2); + const cmp = comparePointers(e.op, agg1, ofs1, agg2, ofs2); if (cmp == -1) { char dir = (e.op == TOK.greaterThan || e.op == TOK.greaterOrEqual) ? '<' : '>'; @@ -3249,10 +3299,11 @@ public: result = CTFEExp.cantexp; return; } - result = new IntegerExp(e.loc, cmp, e.type); + emplaceExp!(IntegerExp)(pue, e.loc, cmp, e.type); + result = (*pue).exp(); return; } - Expression e1 = interpret(e.e1, istate); + Expression e1 = interpret(&ue1, e.e1, istate); if (exceptionOrCant(e1)) return; if (!isCtfeComparable(e1)) @@ -3261,7 +3312,7 @@ public: result = CTFEExp.cantexp; return; } - Expression e2 = interpret(e.e2, istate); + Expression e2 = interpret(&ue2, e.e2, istate); if (exceptionOrCant(e2)) return; if (!isCtfeComparable(e2)) @@ -3270,8 +3321,9 @@ public: result = CTFEExp.cantexp; return; } - int cmp = (*fp)(e.loc, e.op, e1, e2); - result = new IntegerExp(e.loc, cmp, e.type); + const cmp = (*fp)(e.loc, e.op, e1, e2); + emplaceExp!(IntegerExp)(pue, e.loc, cmp, e.type); + result = (*pue).exp(); } override void visit(BinExp e) @@ -3504,7 +3556,8 @@ public: Expression ekey = interpret(xe.e2, istate); if (exceptionOrCant(ekey)) return; - ekey = resolveSlice(ekey); // only happens with AA assignment + UnionExp ekeyTmp = void; + ekey = resolveSlice(ekey, &ekeyTmp); // only happens with AA assignment // Look up this index in it up in the existing AA, to get the next level of AA. AssocArrayLiteralExp newAA = cast(AssocArrayLiteralExp)findKeyInAA(e.loc, existingAA, ekey); @@ -4713,7 +4766,8 @@ public: res = !andand; else if (andand ? isTrueBool(result) : result.isBool(false)) { - result = interpret(e.e2, istate); + UnionExp ue2; + result = interpret(&ue2, e.e2, istate); if (exceptionOrCant(result)) return; if (result.op == TOK.voidExpression) @@ -4740,7 +4794,10 @@ public: return; } if (goal != ctfeNeedNothing) - result = new IntegerExp(e.loc, res, e.type); + { + emplaceExp!(IntegerExp)(pue, e.loc, res, e.type); + result = pue.exp(); + } } @@ -4823,7 +4880,7 @@ public: fd = (cast(VarExp)ecall).var.isFuncDeclaration(); assert(fd); - if (fd.ident == Id._ArrayPostblit || fd.ident == Id._ArrayDtor) + if (fd.ident == Id.__ArrayPostblit || fd.ident == Id.__ArrayDtor) { assert(e.arguments.dim == 1); Expression ea = (*e.arguments)[0]; @@ -4849,7 +4906,7 @@ public: if (CTFEExp.isCantExp(result)) return; - if (fd.ident == Id._ArrayPostblit) + if (fd.ident == Id.__ArrayPostblit) result = evaluatePostblit(istate, result); else result = evaluateDtor(istate, result); @@ -4950,7 +5007,7 @@ public: return; } - result = interpret(fd, istate, e.arguments, pthis); + result = interpretFunction(fd, istate, e.arguments, pthis); if (result.op == TOK.voidExpression) return; if (!exceptionOrCantInterpret(result)) @@ -4973,19 +5030,23 @@ public: { printf("%s CommaExp::interpret() %s\n", e.loc.toChars(), e.toChars()); } - CommaExp firstComma = e; - while (firstComma.e1.op == TOK.comma) - firstComma = cast(CommaExp)firstComma.e1; // If it creates a variable, and there's no context for // the variable to be created in, we need to create one now. InterState istateComma; - if (!istate && firstComma.e1.op == TOK.declaration) + if (!istate && firstComma(e.e1).op == TOK.declaration) { ctfeStack.startFrame(null); istate = &istateComma; } + void endTempStackFrame() + { + // If we created a temporary stack frame, end it now. + if (istate == &istateComma) + ctfeStack.endFrame(); + } + result = CTFEExp.cantexp; // If the comma returns a temporary variable, it needs to be an lvalue @@ -5010,26 +5071,23 @@ public: // through a reference parameter instead). newval = interpret(newval, istate); if (exceptionOrCant(newval)) - goto Lfin; + return endTempStackFrame(); if (newval.op != TOK.voidExpression) { // v isn't necessarily null. setValueWithoutChecking(v, copyLiteral(newval).copy()); } } - result = interpret(e.e2, istate, goal); } else { - result = interpret(e.e1, istate, ctfeNeedNothing); - if (exceptionOrCant(result)) - goto Lfin; - result = interpret(e.e2, istate, goal); + UnionExp ue = void; + auto e1 = interpret(&ue, e.e1, istate, ctfeNeedNothing); + if (exceptionOrCant(e1)) + return endTempStackFrame(); } - Lfin: - // If we created a temporary stack frame, end it now. - if (istate == &istateComma) - ctfeStack.endFrame(); + result = interpret(pue, e.e2, istate, goal); + return endTempStackFrame(); } override void visit(CondExp e) @@ -5038,22 +5096,25 @@ public: { printf("%s CondExp::interpret() %s\n", e.loc.toChars(), e.toChars()); } + UnionExp uecond = void; + Expression econd; + econd = interpret(&uecond, e.econd, istate); + if (exceptionOrCant(econd)) + return; + if (isPointer(e.econd.type)) { - result = interpret(e.econd, istate); - if (exceptionOrCant(result)) - return; - if (result.op != TOK.null_) - result = new IntegerExp(e.loc, 1, Type.tbool); + if (econd.op != TOK.null_) + { + emplaceExp!(IntegerExp)(&uecond, e.loc, 1, Type.tbool); + econd = uecond.exp(); + } } - else - result = interpret(e.econd, istate); - if (exceptionOrCant(result)) - return; - if (isTrueBool(result)) - result = interpret(e.e1, istate, goal); - else if (result.isBool(false)) - result = interpret(e.e2, istate, goal); + + if (isTrueBool(econd)) + result = interpret(pue, e.e1, istate, goal); + else if (econd.isBool(false)) + result = interpret(pue, e.e2, istate, goal); else { e.error("`%s` does not evaluate to boolean result at compile time", e.econd.toChars()); @@ -5067,7 +5128,8 @@ public: { printf("%s ArrayLengthExp::interpret() %s\n", e.loc.toChars(), e.toChars()); } - Expression e1 = interpret(e.e1, istate); + UnionExp ue1; + Expression e1 = interpret(&ue1, e.e1, istate); assert(e1); if (exceptionOrCant(e1)) return; @@ -5077,7 +5139,8 @@ public: result = CTFEExp.cantexp; return; } - result = new IntegerExp(e.loc, resolveArrayLength(e1), e.type); + emplaceExp!(IntegerExp)(pue, e.loc, resolveArrayLength(e1), e.type); + result = pue.exp(); } override void visit(DelegatePtrExp e) @@ -5086,7 +5149,7 @@ public: { printf("%s DelegatePtrExp::interpret() %s\n", e.loc.toChars(), e.toChars()); } - Expression e1 = interpret(e.e1, istate); + Expression e1 = interpret(pue, e.e1, istate); assert(e1); if (exceptionOrCant(e1)) return; @@ -5100,7 +5163,7 @@ public: { printf("%s DelegateFuncptrExp::interpret() %s\n", e.loc.toChars(), e.toChars()); } - Expression e1 = interpret(e.e1, istate); + Expression e1 = interpret(pue, e.e1, istate); assert(e1); if (exceptionOrCant(e1)) return; @@ -5251,7 +5314,8 @@ public: { // if we need a reference, IndexExp shouldn't be interpreting // the expression to a value, it should stay as a reference - result = new IndexExp(e.loc, agg, new IntegerExp(e.e2.loc, indexToAccess, e.e2.type)); + emplaceExp!(IndexExp)(pue, e.loc, agg, new IntegerExp(e.e2.loc, indexToAccess, e.e2.type)); + result = pue.exp(); result.type = e.type; return; } @@ -5295,14 +5359,16 @@ public: result = e; else { - result = new IndexExp(e.loc, e1, e2); + emplaceExp!(IndexExp)(pue, e.loc, e1, e2); + result = pue.exp(); result.type = e.type; } return; } assert(e1.op == TOK.assocArrayLiteral); - e2 = resolveSlice(e2); + UnionExp e2tmp = void; + e2 = resolveSlice(e2, &e2tmp); result = findKeyInAA(e.loc, cast(AssocArrayLiteralExp)e1, e2); if (!result) { @@ -5323,7 +5389,8 @@ public: if (goal == ctfeNeedLvalue) { Expression e2 = new IntegerExp(e.e2.loc, indexToAccess, Type.tsize_t); - result = new IndexExp(e.loc, agg, e2); + emplaceExp!(IndexExp)(pue, e.loc, agg, e2); + result = pue.exp(); result.type = e.type; return; } @@ -5413,7 +5480,8 @@ public: lwr = new IntegerExp(e.loc, ilwr, lwr.type); upr = new IntegerExp(e.loc, iupr, upr.type); } - result = new SliceExp(e.loc, agg, lwr, upr); + emplaceExp!(SliceExp)(pue, e.loc, agg, lwr, upr); + result = pue.exp(); result.type = e.type; return; } @@ -5491,7 +5559,8 @@ public: } ilwr += lo1; iupr += lo1; - result = new SliceExp(e.loc, se.e1, new IntegerExp(e.loc, ilwr, lwr.type), new IntegerExp(e.loc, iupr, upr.type)); + emplaceExp!(SliceExp)(pue, e.loc, se.e1, new IntegerExp(e.loc, ilwr, lwr.type), new IntegerExp(e.loc, iupr, upr.type)); + result = pue.exp(); result.type = e.type; return; } @@ -5504,7 +5573,8 @@ public: return; } } - result = new SliceExp(e.loc, e1, lwr, upr); + emplaceExp!(SliceExp)(pue, e.loc, e1, lwr, upr); + result = pue.exp(); result.type = e.type; } @@ -5522,7 +5592,8 @@ public: return; if (e2.op == TOK.null_) { - result = new NullExp(e.loc, e.type); + emplaceExp!(NullExp)(pue, e.loc, e.type); + result = pue.exp(); return; } if (e2.op != TOK.assocArrayLiteral) @@ -5538,15 +5609,16 @@ public: return; if (!result) { - result = new NullExp(e.loc, e.type); + emplaceExp!(NullExp)(pue, e.loc, e.type); + result = pue.exp(); } else { // Create a CTFE pointer &aa[index] result = new IndexExp(e.loc, e2, e1); result.type = e.type.nextOf(); - result = new AddrExp(e.loc, result); - result.type = e.type; + emplaceExp!(AddrExp)(pue, e.loc, result, e.type); + result = pue.exp(); } } @@ -5556,6 +5628,7 @@ public: { printf("%s CatExp::interpret() %s\n", e.loc.toChars(), e.toChars()); } + { Expression e1 = interpret(e.e1, istate); if (exceptionOrCant(e1)) return; @@ -5563,9 +5636,12 @@ public: if (exceptionOrCant(e2)) return; - e1 = resolveSlice(e1); - e2 = resolveSlice(e2); + UnionExp e1tmp = void; + e1 = resolveSlice(e1, &e1tmp); + UnionExp e2tmp = void; + e2 = resolveSlice(e2, &e2tmp); result = ctfeCat(e.loc, e.type, e1, e2).copy(); + } if (CTFEExp.isCantExp(result)) { e.error("`%s` cannot be interpreted at compile time", e.toChars()); @@ -5627,7 +5703,7 @@ public: if (cd.dtor) { - result = interpret(cd.dtor, istate, null, cre); + result = interpretFunction(cd.dtor, istate, null, cre); if (exceptionOrCant(result)) return; } @@ -5656,7 +5732,7 @@ public: if (sd.dtor) { - result = interpret(sd.dtor, istate, null, sle); + result = interpretFunction(sd.dtor, istate, null, sle); if (exceptionOrCant(result)) return; } @@ -5687,7 +5763,7 @@ public: auto ale = cast(ArrayLiteralExp)result; foreach (el; *ale.elements) { - result = interpret(sd.dtor, istate, null, el); + result = interpretFunction(sd.dtor, istate, null, el); if (exceptionOrCant(result)) return; } @@ -5772,19 +5848,19 @@ public: return; } // Create a CTFE pointer &aggregate[1..2] - result = new IndexExp(e.loc, (cast(SliceExp)e1).e1, (cast(SliceExp)e1).lwr); - result.type = e.type.nextOf(); - result = new AddrExp(e.loc, result); - result.type = e.type; + auto ei = new IndexExp(e.loc, (cast(SliceExp)e1).e1, (cast(SliceExp)e1).lwr); + ei.type = e.type.nextOf(); + emplaceExp!(AddrExp)(pue, e.loc, ei, e.type); + result = pue.exp(); return; } if (e1.op == TOK.arrayLiteral || e1.op == TOK.string_) { // Create a CTFE pointer &[1,2,3][0] or &"abc"[0] - result = new IndexExp(e.loc, e1, new IntegerExp(e.loc, 0, Type.tsize_t)); - result.type = e.type.nextOf(); - result = new AddrExp(e.loc, result); - result.type = e.type; + auto ei = new IndexExp(e.loc, e1, new IntegerExp(e.loc, 0, Type.tsize_t)); + ei.type = e.type.nextOf(); + emplaceExp!(AddrExp)(pue, e.loc, ei, e.type); + result = pue.exp(); return; } if (e1.op == TOK.index && !(cast(IndexExp)e1).e1.type.equals(e1.type)) @@ -5797,20 +5873,23 @@ public: // get the original type. For strings, it's just the type... Type origType = ie.e1.type.nextOf(); // ..but for arrays of type void*, it's the type of the element - Expression xx = null; if (ie.e1.op == TOK.arrayLiteral && ie.e2.op == TOK.int64) { ArrayLiteralExp ale = cast(ArrayLiteralExp)ie.e1; size_t indx = cast(size_t)ie.e2.toInteger(); if (indx < ale.elements.dim) - xx = (*ale.elements)[indx]; + { + if (Expression xx = (*ale.elements)[indx]) + { + if (xx.op == TOK.index) + origType = (cast(IndexExp)xx).e1.type.nextOf(); + else if (xx.op == TOK.address) + origType = (cast(AddrExp)xx).e1.type; + else if (xx.op == TOK.variable) + origType = (cast(VarExp)xx).var.type; + } + } } - if (xx && xx.op == TOK.index) - origType = (cast(IndexExp)xx).e1.type.nextOf(); - else if (xx && xx.op == TOK.address) - origType = (cast(AddrExp)xx).e1.type; - else if (xx && xx.op == TOK.variable) - origType = (cast(VarExp)xx).var.type; if (!isSafePointerCast(origType, pointee)) { e.error("using `void*` to reinterpret cast from `%s*` to `%s*` is not supported in CTFE", origType.toChars(), pointee.toChars()); @@ -5826,8 +5905,8 @@ public: Type origType = (cast(AddrExp)e1).e1.type; if (isSafePointerCast(origType, pointee)) { - result = new AddrExp(e.loc, (cast(AddrExp)e1).e1); - result.type = e.type; + emplaceExp!(AddrExp)(pue, e.loc, (cast(AddrExp)e1).e1, e.type); + result = pue.exp(); return; } if (castToSarrayPointer && pointee.toBasetype().ty == Tsarray && (cast(AddrExp)e1).e1.op == TOK.index) @@ -5839,10 +5918,10 @@ public: Expression upr = new IntegerExp(ie.e2.loc, ie.e2.toInteger() + dim, Type.tsize_t); // Create a CTFE pointer &val[idx..idx+dim] - result = new SliceExp(e.loc, ie.e1, lwr, upr); - result.type = pointee; - result = new AddrExp(e.loc, result); - result.type = e.type; + auto er = new SliceExp(e.loc, ie.e1, lwr, upr); + er.type = pointee; + emplaceExp!(AddrExp)(pue, e.loc, er, e.type); + result = pue.exp(); return; } } @@ -5857,9 +5936,10 @@ public: return; } if (e1.op == TOK.variable) - result = new VarExp(e.loc, (cast(VarExp)e1).var); + emplaceExp!(VarExp)(pue, e.loc, (cast(VarExp)e1).var); else - result = new SymOffExp(e.loc, (cast(SymOffExp)e1).var, (cast(SymOffExp)e1).offset); + emplaceExp!(SymOffExp)(pue, e.loc, (cast(SymOffExp)e1).var, (cast(SymOffExp)e1).offset); + result = pue.exp(); result.type = e.to; return; } @@ -5893,9 +5973,9 @@ public: result = CTFEExp.cantexp; return; } - e1 = new SliceExp(e1.loc, se.e1, se.lwr, se.upr); - e1.type = e.to; - result = e1; + emplaceExp!(SliceExp)(pue, e1.loc, se.e1, se.lwr, se.upr); + result = pue.exp(); + result.type = e.to; return; } // Disallow array type painting, except for conversions between built-in @@ -5910,7 +5990,8 @@ public: e1 = resolveSlice(e1); if (e.to.toBasetype().ty == Tbool && e1.type.ty == Tpointer) { - result = new IntegerExp(e.loc, e1.op != TOK.null_, e.to); + emplaceExp!(IntegerExp)(pue, e.loc, e1.op != TOK.null_, e.to); + result = pue.exp(); return; } result = ctfeCast(e.loc, e.type, e.to, e1); @@ -5922,7 +6003,7 @@ public: { printf("%s AssertExp::interpret() %s\n", e.loc.toChars(), e.toChars()); } - Expression e1 = interpret(e.e1, istate); + Expression e1 = interpret(pue, e.e1, istate); if (exceptionOrCant(e1)) return; if (isTrueBool(e1)) @@ -5932,7 +6013,8 @@ public: { if (e.msg) { - result = interpret(e.msg, istate); + UnionExp ue = void; + result = interpret(&ue, e.msg, istate); if (exceptionOrCant(result)) return; e.error("`%s`", result.toChars()); @@ -6063,7 +6145,8 @@ public: result = e; // optimize: reuse this CTFE reference else { - result = new DotVarExp(e.loc, ex, f, false); + emplaceExp!(DotVarExp)(pue, e.loc, ex, f, false); + result = pue.exp(); result.type = e.type; } return; @@ -6124,7 +6207,8 @@ public: result = e; else { - result = new DotVarExp(e.loc, ex, v); + emplaceExp!(DotVarExp)(pue, e.loc, ex, v); + result = pue.exp(); result.type = e.type; } return; @@ -6204,7 +6288,8 @@ public: } valuesx.dim = valuesx.dim - removed; keysx.dim = keysx.dim - removed; - result = new IntegerExp(e.loc, removed ? 1 : 0, Type.tbool); + emplaceExp!(IntegerExp)(pue, e.loc, removed ? 1 : 0, Type.tbool); + result = pue.exp(); } override void visit(ClassReferenceExp e) @@ -6225,33 +6310,68 @@ public: } } -private Expression interpret(Expression e, InterState* istate, CtfeGoal goal = ctfeNeedRvalue) +/******************************************** + * Interpret the expression. + * Params: + * pue = non-null pointer to temporary storage that can be used to store the return value + * e = Expression to interpret + * istate = context + * goal = what the result will be used for + * Returns: + * resulting expression + */ + +Expression interpret(UnionExp* pue, Expression e, InterState* istate, CtfeGoal goal = ctfeNeedRvalue) { if (!e) return null; - scope Interpreter v = new Interpreter(istate, goal); + scope Interpreter v = new Interpreter(pue, istate, goal); e.accept(v); Expression ex = v.result; assert(goal == ctfeNeedNothing || ex !is null); return ex; } +/// +Expression interpret(Expression e, InterState* istate, CtfeGoal goal = ctfeNeedRvalue) +{ + UnionExp ue = void; + auto result = interpret(&ue, e, istate, goal); + if (result == ue.exp()) + result = ue.copy(); + return result; +} + /*********************************** * 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 * TOK.cantExpression cannot interpret statement at compile time * !NULL expression from return statement, or thrown exception */ -private Expression interpret(Statement s, InterState* istate) +Expression interpret(UnionExp* pue, Statement s, InterState* istate) { if (!s) return null; - scope Interpreter v = new Interpreter(istate, ctfeNeedNothing); + scope Interpreter v = new Interpreter(pue, istate, ctfeNeedNothing); s.accept(v); return v.result; } +/// +Expression interpret(Statement s, InterState* istate) +{ + UnionExp ue = void; + auto result = interpret(&ue, s, istate); + if (result == ue.exp()) + result = ue.copy(); + return result; +} + /* All results destined for use outside of CTFE need to have their CTFE-specific * features removed. * In particular, all slices must be resolved. @@ -6559,7 +6679,7 @@ private Expression interpret_aaApply(InterState* istate, Expression aa, Expressi if (numParams == 2) args[0] = ekey; - eresult = interpret(fd, istate, &args, pthis); + eresult = interpretFunction(fd, istate, &args, pthis); if (exceptionOrCantInterpret(eresult)) return eresult; @@ -6816,7 +6936,7 @@ private Expression foreachApplyUtf(InterState* istate, Expression str, Expressio args[numParams - 1] = val; - eresult = interpret(fd, istate, &args, pthis); + eresult = interpretFunction(fd, istate, &args, pthis); if (exceptionOrCantInterpret(eresult)) return eresult; assert(eresult.op == TOK.int64); @@ -6950,7 +7070,7 @@ private Expression evaluatePostblit(InterState* istate, Expression e) if (e.op == TOK.structLiteral) { // e.__postblit() - e = interpret(sd.postblit, istate, null, e); + e = interpretFunction(sd.postblit, istate, null, e); if (exceptionOrCantInterpret(e)) return e; return null; @@ -6976,7 +7096,7 @@ private Expression evaluateDtor(InterState* istate, Expression e) else if (e.op == TOK.structLiteral) { // e.__dtor() - e = interpret(sd.dtor, istate, null, e); + e = interpretFunction(sd.dtor, istate, null, e); } else assert(0); @@ -6996,11 +7116,6 @@ private bool hasValue(VarDeclaration vd) return null !is getValue(vd); } -extern (C++) Expression getValue(VarDeclaration vd) -{ - return ctfeStack.getValue(vd); -} - // Don't check for validity private void setValueWithoutChecking(VarDeclaration vd, Expression newval) { diff --git a/dmd/dmangle.d b/dmd/dmangle.d index c107a55c42a..1ba23c11e5c 100644 --- a/dmd/dmangle.d +++ b/dmd/dmangle.d @@ -883,15 +883,16 @@ public: override void visit(IntegerExp e) { - if (cast(sinteger_t)e.value < 0) + const v = e.toInteger(); + if (cast(sinteger_t)v < 0) { buf.writeByte('N'); - buf.print(-e.value); + buf.print(-v); } else { buf.writeByte('i'); - buf.print(e.value); + buf.print(v); } } diff --git a/dmd/dmodule.d b/dmd/dmodule.d index b2abc542ead..cd2225d921e 100644 --- a/dmd/dmodule.d +++ b/dmd/dmodule.d @@ -953,38 +953,6 @@ else if (!Identifier.isValidIdentifier(this.ident.toChars())) error("has non-identifier characters in filename, use module declaration instead"); } - // Add internal used functions in 'object' module members. - if (!parent && ident == Id.object) - { - immutable code_ArrayEq = "bool _ArrayEq(T1, T2)(T1[] a, T2[] b) {\n if (a.length != b.length) return false;\n foreach (size_t i; 0 .. a.length) { if (a[i] != b[i]) return false; }\n return true; }\n"; - immutable code_ArrayPostblit = "void _ArrayPostblit(T)(T[] a) { foreach (ref T e; a) e.__xpostblit(); }\n"; - immutable code_ArrayDtor = "void _ArrayDtor(T)(T[] a) { foreach_reverse (ref T e; a) e.__xdtor(); }\n"; - Identifier arreq = Id._ArrayEq; - for (size_t i = 0; i < members.dim; i++) - { - Dsymbol sx = (*members)[i]; - if (!sx) - continue; - if (arreq && sx.ident == arreq) - arreq = null; - } - if (arreq) - { - scope p = new Parser!ASTCodegen(loc, this, code_ArrayEq, false); - p.nextToken(); - members.append(p.parseDeclDefs(0)); - } - { - scope p = new Parser!ASTCodegen(loc, this, code_ArrayPostblit, false); - p.nextToken(); - members.append(p.parseDeclDefs(0)); - } - { - scope p = new Parser!ASTCodegen(loc, this, code_ArrayDtor, false); - p.nextToken(); - members.append(p.parseDeclDefs(0)); - } - } // Insert module into the symbol table Dsymbol s = this; if (isPackageFile) diff --git a/dmd/doc.d b/dmd/doc.d index b557619be84..360193b3623 100644 --- a/dmd/doc.d +++ b/dmd/doc.d @@ -558,7 +558,8 @@ extern (C++) void escapeDdocString(OutBuffer* buf, size_t start) extern (C++) void escapeStrayParenthesis(Loc loc, OutBuffer* buf, size_t start) { uint par_open = 0; - bool inCode = 0; + char inCode = 0; + bool atLineStart = true; for (size_t u = start; u < buf.offset; u++) { char c = buf.data[u]; @@ -567,6 +568,7 @@ extern (C++) void escapeStrayParenthesis(Loc loc, OutBuffer* buf, size_t start) case '(': if (!inCode) par_open++; + atLineStart = false; break; case ')': if (!inCode) @@ -582,29 +584,41 @@ extern (C++) void escapeStrayParenthesis(Loc loc, OutBuffer* buf, size_t start) else par_open--; } + atLineStart = false; break; + case '\n': + atLineStart = true; version (none) { // For this to work, loc must be set to the beginning of the passed // text which is currently not possible // (loc is set to the Loc of the Dsymbol) - case '\n': loc.linnum++; - break; } + break; + case ' ': + case '\r': + case '\t': + break; case '-': + case '`': // Issue 15465: don't try to escape unbalanced parens inside code // blocks. - int numdash = 0; - while (u < buf.offset && buf.data[u] == '-') + int numdash = 1; + for (++u; u < buf.offset && buf.data[u] == '-'; ++u) + ++numdash; + --u; + if (c == '`' || (atLineStart && numdash >= 3)) { - numdash++; - u++; + if (inCode == c) + inCode = 0; + else if (!inCode) + inCode = c; } - if (numdash >= 3) - inCode = !inCode; + atLineStart = false; break; default: + atLineStart = false; break; } } @@ -2301,6 +2315,7 @@ extern (C++) void highlightText(Scope* sc, Dsymbols* a, OutBuffer* buf, size_t o codebuf.write(buf.peekSlice().ptr + iCodeStart + 1, i - (iCodeStart + 1)); // escape the contents, but do not perform highlighting except for DDOC_PSYMBOL highlightCode(sc, a, &codebuf, 0); + escapeStrayParenthesis(s ? s.loc : Loc.initial, &codebuf, 0); buf.remove(iCodeStart, i - iCodeStart + 1); // also trimming off the current ` immutable pre = "$(DDOC_BACKQUOTED "; i = buf.insert(iCodeStart, pre); @@ -2398,6 +2413,7 @@ extern (C++) void highlightText(Scope* sc, Dsymbols* a, OutBuffer* buf, size_t o ++p; } highlightCode2(sc, a, &codebuf, 0); + escapeStrayParenthesis(s ? s.loc : Loc.initial, &codebuf, 0); buf.remove(iCodeStart, i - iCodeStart); i = buf.insert(iCodeStart, codebuf.peekSlice()); i = buf.insert(i, ")\n"); diff --git a/dmd/dscope.d b/dmd/dscope.d index 1cc009d431f..7db84a9a82b 100644 --- a/dmd/dscope.d +++ b/dmd/dscope.d @@ -272,66 +272,20 @@ version(IN_LLVM) return pop(); } - extern (C++) void mergeCallSuper(const ref Loc loc, CSX cs) - { - // This does a primitive flow analysis to support the restrictions - // regarding when and how constructors can appear. - // It merges the results of two paths. - // The two paths are ctorflow.callSuper and cs; the result is merged into ctorflow.callSuper. - if (cs != ctorflow.callSuper) - { - // Have ALL branches called a constructor? - int aAll = (cs & (CSX.this_ctor | CSX.super_ctor)) != 0; - int bAll = (ctorflow.callSuper & (CSX.this_ctor | CSX.super_ctor)) != 0; - // Have ANY branches called a constructor? - bool aAny = (cs & CSX.any_ctor) != 0; - bool bAny = (ctorflow.callSuper & CSX.any_ctor) != 0; - // Have any branches returned? - bool aRet = (cs & CSX.return_) != 0; - bool bRet = (ctorflow.callSuper & CSX.return_) != 0; - // Have any branches halted? - bool aHalt = (cs & CSX.halt) != 0; - bool bHalt = (ctorflow.callSuper & CSX.halt) != 0; - bool ok = true; - if (aHalt && bHalt) - { - ctorflow.callSuper = CSX.halt; - } - else if ((!aHalt && aRet && !aAny && bAny) || (!bHalt && bRet && !bAny && aAny)) - { - // If one has returned without a constructor call, there must be never - // have been ctor calls in the other. - ok = false; - } - else if (aHalt || aRet && aAll) - { - // If one branch has called a ctor and then exited, anything the - // other branch has done is OK (except returning without a - // ctor call, but we already checked that). - ctorflow.callSuper |= cs & (CSX.any_ctor | CSX.label); - } - else if (bHalt || bRet && bAll) - { - ctorflow.callSuper = cast(CSX)(cs | (ctorflow.callSuper & (CSX.any_ctor | CSX.label))); - } - else - { - // Both branches must have called ctors, or both not. - ok = (aAll == bAll); - // If one returned without a ctor, we must remember that - // (Don't bother if we've already found an error) - if (ok && aRet && !aAny) - ctorflow.callSuper |= CSX.return_; - ctorflow.callSuper |= cs & (CSX.any_ctor | CSX.label); - } - if (!ok) - error(loc, "one path skips constructor"); - } - } - extern (D) void mergeFieldInit(const ref Loc loc, const CSX[] fies) + /******************************* + * Merge results of `ctorflow` into `this`. + * Params: + * loc = for error messages + * ctorflow = flow results to merge in + */ + extern (D) void merge(const ref Loc loc, const ref CtorFlow ctorflow) { - if (ctorflow.fieldinit.length && fies.length) + if (!mergeCallSuper(this.ctorflow.callSuper, ctorflow.callSuper)) + error(loc, "one path skips constructor"); + + const fies = ctorflow.fieldinit; + if (this.ctorflow.fieldinit.length && fies.length) { FuncDeclaration f = func; if (fes) @@ -341,26 +295,14 @@ version(IN_LLVM) foreach (i, v; ad.fields) { bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested()); - if (!mergeFieldInitX(ctorflow.fieldinit[i], fies[i]) && mustInit) + if (!mergeFieldInit(this.ctorflow.fieldinit[i], fies[i]) && mustInit) { - .error(loc, "one path skips field `%s`", v.toChars()); + error(loc, "one path skips field `%s`", v.toChars()); } } } } - /******************************* - * Merge results of `ctorflow` into `this`. - * Params: - * loc = for error messages - * ctorflow = flow results to merge in - */ - extern (D) void merge(const ref Loc loc, const ref CtorFlow ctorflow) - { - mergeCallSuper(loc, ctorflow.callSuper); - mergeFieldInit(loc, ctorflow.fieldinit); - } - extern (C++) Module instantiatingModule() { // TODO: in speculative context, returning 'module' is correct? @@ -395,7 +337,7 @@ version(IN_LLVM) static void printMsg(string txt, Dsymbol s) { - printf("%.*s %s.%s, kind = '%s'\n", cast(int)msg.length, msg.ptr, + printf("%.*s %s.%s, kind = '%s'\n", cast(int)txt.length, txt.ptr, s.parent ? s.parent.toChars() : "", s.toChars(), s.kind()); } } diff --git a/dmd/dsymbol.d b/dmd/dsymbol.d index 850e017f72a..09f940d734a 100644 --- a/dmd/dsymbol.d +++ b/dmd/dsymbol.d @@ -275,6 +275,8 @@ extern (C++) class Dsymbol : RootObject { if (this == o) return true; + if (o.dyncast() != DYNCAST.dsymbol) + return false; Dsymbol s = cast(Dsymbol)o; // Overload sets don't have an ident if (s && ident && s.ident && ident.equals(s.ident)) diff --git a/dmd/dsymbolsem.d b/dmd/dsymbolsem.d index af9fe416f72..618d49a4e22 100644 --- a/dmd/dsymbolsem.d +++ b/dmd/dsymbolsem.d @@ -102,6 +102,7 @@ private extern (C++) FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* stc |= sd.postblits[i].storage_class & STC.disable; } + VarDeclaration[] fieldsToDestroy; auto postblitCalls = new Statements(); // iterate through all the struct fields that are not disabled for (size_t i = 0; i < sd.fields.dim && !(stc & STC.disable); i++) @@ -121,6 +122,84 @@ private extern (C++) FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* continue; assert(!sdv.isUnionDeclaration()); + // 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) + { + // create a list of destructors that need to be called + Expression[] dtorCalls; + foreach(sf; fieldsToDestroy) + { + Expression ex; + tv = sf.type.toBasetype(); + if (tv.ty == Tstruct) + { + // this.v.__xdtor() + + ex = new ThisExp(loc); + ex = new DotVarExp(loc, ex, sf); + + // This is a hack so we can call destructors on const/immutable objects. + ex = new AddrExp(loc, ex); + ex = new CastExp(loc, ex, sf.type.mutableOf().pointerTo()); + ex = new PtrExp(loc, ex); + if (stc & STC.safe) + stc = (stc & ~STC.safe) | STC.trusted; + + auto sfv = (cast(TypeStruct)sf.type.baseElemOf()).sym; + + ex = new DotVarExp(loc, ex, sfv.dtor, false); + ex = new CallExp(loc, ex); + + dtorCalls ~= ex; + } + else + { + // _ArrayDtor((cast(S*)this.v.ptr)[0 .. n]) + + uinteger_t length = 1; + while (tv.ty == Tsarray) + { + length *= (cast(TypeSArray)tv).dim.toUInteger(); + tv = tv.nextOf().toBasetype(); + } + //if (n == 0) + // continue; + + ex = new ThisExp(loc); + ex = new DotVarExp(loc, ex, sf); + + // This is a hack so we can call destructors on const/immutable objects. + ex = new DotIdExp(loc, ex, Id.ptr); + ex = new CastExp(loc, ex, sdv.type.pointerTo()); + if (stc & STC.safe) + stc = (stc & ~STC.safe) | STC.trusted; + + ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t), + new IntegerExp(loc, length, Type.tsize_t)); + // Prevent redundant bounds check + (cast(SliceExp)ex).upperIsInBounds = true; + (cast(SliceExp)ex).lowerIsLessThanUpper = true; + + ex = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayDtor), ex); + + dtorCalls ~= ex; + } + } + fieldsToDestroy = []; + + // aggregate the destructor calls + auto dtors = new Statements(); + foreach_reverse(dc; dtorCalls) + { + dtors.push(new ExpStatement(loc, dc)); + } + + // put destructor calls in a `scope(failure)` block + postblitCalls.push(new OnScopeStatement(loc, TOK.onScopeFailure, new CompoundStatement(loc, dtors))); + } + // perform semantic on the member postblit in order to // be able to aggregate it later on with the rest of the // postblits @@ -184,67 +263,22 @@ private extern (C++) FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* // Prevent redundant bounds check (cast(SliceExp)ex).upperIsInBounds = true; (cast(SliceExp)ex).lowerIsLessThanUpper = true; - ex = new CallExp(loc, new IdentifierExp(loc, Id._ArrayPostblit), ex); + ex = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayPostblit), ex); } postblitCalls.push(new ExpStatement(loc, ex)); // combine in forward order /* https://issues.dlang.org/show_bug.cgi?id=10972 - * When the following field postblit calls fail, + * When subsequent field postblit calls fail, * this field should be destructed for Exception Safety. */ - if (!sdv.dtor) - continue; - sdv.dtor.functionSemantic(); - - tv = structField.type.toBasetype(); - if (tv.ty == Tstruct) - { - // this.v.__xdtor() - - ex = new ThisExp(loc); - ex = new DotVarExp(loc, ex, structField); - - // This is a hack so we can call destructors on const/immutable objects. - ex = new AddrExp(loc, ex); - ex = new CastExp(loc, ex, structField.type.mutableOf().pointerTo()); - ex = new PtrExp(loc, ex); - if (stc & STC.safe) - stc = (stc & ~STC.safe) | STC.trusted; - - ex = new DotVarExp(loc, ex, sdv.dtor, false); - ex = new CallExp(loc, ex); - } - else + if (sdv.dtor) { - // _ArrayDtor((cast(S*)this.v.ptr)[0 .. n]) - - uinteger_t length = 1; - while (tv.ty == Tsarray) - { - length *= (cast(TypeSArray)tv).dim.toUInteger(); - tv = tv.nextOf().toBasetype(); - } - //if (n == 0) - // continue; - - ex = new ThisExp(loc); - ex = new DotVarExp(loc, ex, structField); - - // This is a hack so we can call destructors on const/immutable objects. - ex = new DotIdExp(loc, ex, Id.ptr); - ex = new CastExp(loc, ex, sdv.type.pointerTo()); - if (stc & STC.safe) - stc = (stc & ~STC.safe) | STC.trusted; + sdv.dtor.functionSemantic(); - ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t), - new IntegerExp(loc, length, Type.tsize_t)); - // Prevent redundant bounds check - (cast(SliceExp)ex).upperIsInBounds = true; - (cast(SliceExp)ex).lowerIsLessThanUpper = true; - - ex = new CallExp(loc, new IdentifierExp(loc, Id._ArrayDtor), ex); + // keep a list of fields that need to be destroyed in case + // of a future postblit failure + fieldsToDestroy ~= structField; } - postblitCalls.push(new OnScopeStatement(loc, TOK.onScopeFailure, new ExpStatement(loc, ex))); } void checkShared() @@ -410,7 +444,7 @@ package bool allowsContractWithoutBody(FuncDeclaration funcdecl) InterfaceDeclaration id = parent.isInterfaceDeclaration(); if (!funcdecl.isAbstract() && - (funcdecl.fensure || funcdecl.frequire) && + (funcdecl.fensures || funcdecl.frequires) && !(id && funcdecl.isVirtual())) { auto cd = parent.isClassDeclaration(); @@ -1024,7 +1058,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } } - if (!dsym._init && !fd) + if ((!dsym._init || dsym._init.isVoidInitializer) && !fd) { // If not mutable, initializable by constructor only dsym.storage_class |= STC.ctorinit; @@ -1342,9 +1376,12 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor imp.semanticRun = PASS.semantic; // Load if not already done so + bool loadErrored = false; if (!imp.mod) { + const errors = global.errors; imp.load(sc); + loadErrored = global.errors != errors; if (imp.mod) imp.mod.importAll(null); } @@ -1391,7 +1428,11 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor scopesym.addAccessiblePackage(imp.mod, imp.protection); // d } - imp.mod.dsymbolSemantic(null); + if (!loadErrored) + { + imp.mod.dsymbolSemantic(null); + } + if (imp.mod.needmoduleinfo) { //printf("module4 %s because of %s\n", sc.module.toChars(), mod.toChars()); @@ -3617,6 +3658,18 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } sc = sc.push(); + + if (sc.stc & STC.static_) + { + // Deprecated in 2018-04. + // Change to error in 2019-04. + // @@@DEPRECATED_2019-04@@@. + if (sc.stc & STC.shared_) + deprecation(ctd.loc, "`shared static` has no effect on a constructor inside a `shared static` block. Use `shared static this()`"); + else + deprecation(ctd.loc, "`static` has no effect on a constructor inside a `static` block. Use `static this()`"); + } + sc.stc &= ~STC.static_; // not a static constructor sc.flags |= SCOPE.ctor; @@ -3735,7 +3788,29 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (dd.ident == Id.dtor && dd.semanticRun < PASS.semantic) ad.dtors.push(dd); if (!dd.type) + { dd.type = new TypeFunction(null, Type.tvoid, false, LINK.d, dd.storage_class); + if (ad.classKind == ClassKind.cpp && dd.ident == Id.dtor) + { + if (auto cldec = ad.isClassDeclaration()) + { + assert (cldec.cppDtorVtblIndex == -1); // double-call check already by dd.type + if (cldec.baseClass && cldec.baseClass.cppDtorVtblIndex != -1) + { + // override the base virtual + cldec.cppDtorVtblIndex = cldec.baseClass.cppDtorVtblIndex; + } + else if (!dd.isFinal()) + { + // reserve the dtor slot for the destructor (which we'll create later) + cldec.cppDtorVtblIndex = cast(int)cldec.vtbl.dim; + cldec.vtbl.push(dd); + if (Target.twoDtorInVtable) + cldec.vtbl.push(dd); // deleting destructor uses a second slot + } + } + } + } sc = sc.push(); sc.stc &= ~STC.static_; // not a static destructor @@ -3978,7 +4053,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor //printf("NewDeclaration::semantic()\n"); // `@disable new();` should not be deprecated - if (!nd.isDisabled()) + if (!nd.isDisabled() && !nd.isDeprecated()) { // @@@DEPRECATED_2.084@@@ // Should be changed to an error in 2.084 @@ -4034,7 +4109,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor // @@@DEPRECATED_2.084@@@ // Should be changed to an error in 2.084 - deprecation(deld.loc, "class deallocators have been deprecated, consider moving the deallocation strategy outside of the class"); + if (!deld.isDeprecated()) + deprecation(deld.loc, "class deallocators have been deprecated, consider moving the deallocation strategy outside of the class"); if (deld.semanticRun >= PASS.semanticdone) return; @@ -4076,7 +4152,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor override void visit(StructDeclaration sd) { - //printf("StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", this, toPrettyChars(), sizeok); + //printf("StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok); //static int count; if (++count == 20) assert(0); @@ -4128,6 +4204,9 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor sd.error("structs, unions cannot be `abstract`"); sd.userAttribDecl = sc.userAttribDecl; + + if (sc.linkage == LINK.cpp) + sd.classKind = ClassKind.cpp; } else if (sd.symtab && !scx) return; @@ -4223,6 +4302,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor sd.ctor = sd.searchCtor(); sd.dtor = buildDtor(sd, sc2); + sd.tidtor = buildExternDDtor(sd, sc2); sd.postblit = buildPostBlit(sd, sc2); buildOpAssign(sd, sc2); @@ -4239,7 +4319,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor Module.dprogress++; sd.semanticRun = PASS.semanticdone; - //printf("-StructDeclaration::semantic(this=%p, '%s')\n", this, toChars()); + //printf("-StructDeclaration::semantic(this=%p, '%s')\n", sd, sd.toChars()); sc2.pop(); @@ -4576,7 +4656,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor cldec.baseok = Baseok.done; // If no base class, and this is not an Object, use Object as base class - if (!cldec.baseClass && cldec.ident != Id.Object && !cldec.classKind == ClassKind.cpp) + if (!cldec.baseClass && cldec.ident != Id.Object && cldec.object && !cldec.classKind == ClassKind.cpp) { void badObjectDotD() { @@ -4849,6 +4929,21 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } cldec.dtor = buildDtor(cldec, sc2); + cldec.tidtor = buildExternDDtor(cldec, sc2); + + if (cldec.classKind == ClassKind.cpp && cldec.cppDtorVtblIndex != -1) + { + // now we've built the aggregate destructor, we'll make it virtual and assign it to the reserved vtable slot + cldec.dtor.vtblIndex = cldec.cppDtorVtblIndex; + cldec.vtbl[cldec.cppDtorVtblIndex] = cldec.dtor; + + if (Target.twoDtorInVtable) + { + // TODO: create a C++ compatible deleting destructor (call out to `operator delete`) + // for the moment, we'll call the non-deleting destructor and leak + cldec.vtbl[cldec.cppDtorVtblIndex + 1] = cldec.dtor; + } + } if (auto f = hasIdentityOpAssign(cldec, sc2)) { @@ -5243,7 +5338,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, Expressions* fargs) { - //printf("[%s] TemplateInstance.dsymbolSemantic('%s', this=%p, gag = %d, sc = %p)\n", loc.toChars(), toChars(), this, global.gag, sc); + //printf("[%s] TemplateInstance.dsymbolSemantic('%s', this=%p, gag = %d, sc = %p)\n", tempinst.loc.toChars(), tempinst.toChars(), tempinst, global.gag, sc); version (none) { for (Dsymbol s = tempinst; s; s = s.parent) @@ -5558,11 +5653,10 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, Expressions* } Scope* sc2; sc2 = _scope.push(tempinst); - //printf("enclosing = %d, sc.parent = %s\n", enclosing, sc.parent.toChars()); + //printf("enclosing = %d, sc.parent = %s\n", tempinst.enclosing, sc.parent.toChars()); sc2.parent = tempinst; sc2.tinst = tempinst; sc2.minst = tempinst.minst; - tempinst.tryExpandMembers(sc2); tempinst.semanticRun = PASS.semanticdone; @@ -5654,12 +5748,20 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, Expressions* else if (tempinst.tinst) { bool doSemantic3 = false; - if (sc.func && tempinst.aliasdecl && tempinst.aliasdecl.toAlias().isFuncDeclaration()) + FuncDeclaration fd; + if (tempinst.aliasdecl) + fd = tempinst.aliasdecl.toAlias2().isFuncDeclaration(); + + if (fd) { /* Template function instantiation should run semantic3 immediately * for attribute inference. */ - doSemantic3 = true; + scope fld = fd.isFuncLiteralDeclaration(); + if (fld && fld.tok == TOK.reserved) + doSemantic3 = true; + else if (sc.func) + doSemantic3 = true; } else if (sc.func) { @@ -5800,7 +5902,7 @@ Laftersemantic: // function used to perform semantic on AliasDeclaration void aliasSemantic(AliasDeclaration ds, Scope* sc) { - //printf("AliasDeclaration::semantic() %s\n", toChars()); + //printf("AliasDeclaration::semantic() %s\n", ds.toChars()); if (ds.aliassym) { auto fd = ds.aliassym.isFuncLiteralDeclaration(); diff --git a/dmd/dtemplate.d b/dmd/dtemplate.d index 3eeb47d8434..e2fa843d886 100644 --- a/dmd/dtemplate.d +++ b/dmd/dtemplate.d @@ -59,61 +59,58 @@ enum IDX_NOTFOUND = 0x12345678; * These functions substitute for dynamic_cast. dynamic_cast does not work * on earlier versions of gcc. */ -extern (C++) Expression isExpression(RootObject o) +extern (C++) inout(Expression) isExpression(inout RootObject o) { //return dynamic_cast(o); if (!o || o.dyncast() != DYNCAST.expression) return null; - return cast(Expression)o; + return cast(inout(Expression))o; } -extern (C++) Dsymbol isDsymbol(RootObject o) +extern (C++) inout(Dsymbol) isDsymbol(inout RootObject o) { //return dynamic_cast(o); if (!o || o.dyncast() != DYNCAST.dsymbol) return null; - return cast(Dsymbol)o; + return cast(inout(Dsymbol))o; } -extern (C++) Type isType(RootObject o) +extern (C++) inout(Type) isType(inout RootObject o) { //return dynamic_cast(o); if (!o || o.dyncast() != DYNCAST.type) return null; - return cast(Type)o; + return cast(inout(Type))o; } -extern (C++) Tuple isTuple(RootObject o) +extern (C++) inout(Tuple) isTuple(inout RootObject o) { //return dynamic_cast(o); if (!o || o.dyncast() != DYNCAST.tuple) return null; - return cast(Tuple)o; + return cast(inout(Tuple))o; } -extern (C++) Parameter isParameter(RootObject o) +extern (C++) inout(Parameter) isParameter(inout RootObject o) { //return dynamic_cast(o); if (!o || o.dyncast() != DYNCAST.parameter) return null; - return cast(Parameter)o; + return cast(inout(Parameter))o; } /************************************** * Is this Object an error? */ -extern (C++) bool isError(RootObject o) +extern (C++) bool isError(const RootObject o) { - Type t = isType(o); - if (t) + if (const t = isType(o)) return (t.ty == Terror); - Expression e = isExpression(o); - if (e) + if (const e = isExpression(o)) return (e.op == TOK.error || !e.type || e.type.ty == Terror); - Tuple v = isTuple(o); - if (v) + if (const v = isTuple(o)) return arrayObjectIsError(&v.objects); - Dsymbol s = isDsymbol(o); + const s = isDsymbol(o); assert(s); if (s.errors) return true; @@ -123,11 +120,10 @@ extern (C++) bool isError(RootObject o) /************************************** * Are any of the Objects an error? */ -extern (C++) bool arrayObjectIsError(Objects* args) +extern (C++) bool arrayObjectIsError(const Objects* args) { - for (size_t i = 0; i < args.dim; i++) + foreach (const o; *args) { - RootObject o = (*args)[i]; if (isError(o)) return true; } @@ -137,14 +133,13 @@ extern (C++) bool arrayObjectIsError(Objects* args) /*********************** * Try to get arg as a type. */ -extern (C++) Type getType(RootObject o) +extern (C++) inout(Type) getType(inout RootObject o) { - Type t = isType(o); + inout t = isType(o); if (!t) { - Expression e = isExpression(o); - if (e) - t = e.type; + if (inout e = isExpression(o)) + return e.type; } return t; } @@ -154,8 +149,7 @@ extern (C++) Dsymbol getDsymbol(RootObject oarg) //printf("getDsymbol()\n"); //printf("e %p s %p t %p v %p\n", isExpression(oarg), isDsymbol(oarg), isType(oarg), isTuple(oarg)); Dsymbol sa; - Expression ea = isExpression(oarg); - if (ea) + if (Expression ea = isExpression(oarg)) { // Try to convert Expression to symbol if (ea.op == TOK.variable) @@ -175,8 +169,7 @@ extern (C++) Dsymbol getDsymbol(RootObject oarg) else { // Try to convert Type to symbol - Type ta = isType(oarg); - if (ta) + if (Type ta = isType(oarg)) sa = ta.toDsymbol(null); else sa = isDsymbol(oarg); // if already a symbol @@ -186,16 +179,15 @@ extern (C++) Dsymbol getDsymbol(RootObject oarg) private Expression getValue(ref Dsymbol s) { - Expression e = null; if (s) { - VarDeclaration v = s.isVarDeclaration(); - if (v && v.storage_class & STC.manifest) + if (VarDeclaration v = s.isVarDeclaration()) { - e = v.getConstInitializer(); + if (v.storage_class & STC.manifest) + return v.getConstInitializer(); } } - return e; + return null; } /*********************** @@ -226,9 +218,9 @@ private Expression getExpression(RootObject o) */ private bool match(RootObject o1, RootObject o2) { - enum debugPrint = 0; + enum log = false; - static if (debugPrint) + static if (log) { printf("match() o1 = %p %s (%d), o2 = %p %s (%d)\n", o1, o1.toChars(), o1.dyncast(), o2, o2.toChars(), o2.dyncast()); @@ -248,7 +240,7 @@ private bool match(RootObject o1, RootObject o2) if (!t2) goto Lnomatch; - static if (debugPrint) + static if (log) { printf("\tt1 = %s\n", t1.toChars()); printf("\tt2 = %s\n", t2.toChars()); @@ -264,7 +256,7 @@ private bool match(RootObject o1, RootObject o2) if (!e2) goto Lnomatch; - static if (debugPrint) + static if (log) { printf("\te1 = %s '%s' %s\n", e1.type.toChars(), Token.toChars(e1.op), e1.toChars()); printf("\te2 = %s '%s' %s\n", e2.type.toChars(), Token.toChars(e2.op), e2.toChars()); @@ -285,7 +277,7 @@ private bool match(RootObject o1, RootObject o2) if (!s2) goto Lnomatch; - static if (debugPrint) + static if (log) { printf("\ts1 = %s \n", s1.kind(), s1.toChars()); printf("\ts2 = %s \n", s2.kind(), s2.toChars()); @@ -303,7 +295,7 @@ private bool match(RootObject o1, RootObject o2) if (!u2) goto Lnomatch; - static if (debugPrint) + static if (log) { printf("\tu1 = %s\n", u1.toChars()); printf("\tu2 = %s\n", u2.toChars()); @@ -314,12 +306,12 @@ private bool match(RootObject o1, RootObject o2) goto Lmatch; } Lmatch: - static if (debugPrint) + static if (log) printf("\t. match\n"); return true; Lnomatch: - static if (debugPrint) + static if (log) printf("\t. nomatch\n"); return false; } @@ -327,25 +319,25 @@ Lnomatch: /************************************ * Match an array of them. */ -private int arrayObjectMatch(Objects* oa1, Objects* oa2) +private bool arrayObjectMatch(Objects* oa1, Objects* oa2) { if (oa1 == oa2) - return 1; + return true; if (oa1.dim != oa2.dim) - return 0; + return false; immutable oa1dim = oa1.dim; auto oa1d = (*oa1).data; auto oa2d = (*oa2).data; - for (size_t j = 0; j < oa1dim; j++) + foreach (j; 0 .. oa1dim) { RootObject o1 = oa1d[j]; RootObject o2 = oa2d[j]; if (!match(o1, o2)) { - return 0; + return false; } } - return 1; + return true; } /************************************ @@ -6620,7 +6612,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol Dsymbol s2 = s.getType().toDsymbol(sc); if (!s2) { - error("`%s` is not a template declaration, it is a %s", id.toChars(), s.kind()); + .error(loc, "`%s` is not a valid template instance, because `%s` is not a template declaration but a type (`%s == %s`)", toChars(), id.toChars(), id.toChars(), s.getType.kind()); return false; } s = s2; @@ -7319,7 +7311,9 @@ extern (C++) class TemplateInstance : ScopeDsymbol if (isstatic) { Dsymbol dparent = sa.toParent2(); - if (!enclosing) + if (!dparent) + goto L1; + else if (!enclosing) enclosing = dparent; else if (enclosing != dparent) { diff --git a/dmd/expression.d b/dmd/expression.d index 776705a9dfc..05cf9114c10 100644 --- a/dmd/expression.d +++ b/dmd/expression.d @@ -26,6 +26,7 @@ import dmd.canthrow; import dmd.complex; import dmd.constfold; import dmd.ctfeexpr; +import dmd.ctorflow; import dmd.dcast; import dmd.dclass; import dmd.declaration; @@ -78,6 +79,43 @@ void emplaceExp(T : UnionExp)(T* p, Expression e) memcpy(p, cast(void*)e, e.size); } + +/**************************************** + * Find the first non-comma expression. + * Params: + * e = Expressions connected by commas + * Returns: + * left-most non-comma expression + */ + +inout(Expression) firstComma(inout Expression e) +{ + Expression ex = cast()e; + while (ex.op == TOK.comma) + ex = (cast(CommaExp)ex).e1; + return cast(inout)ex; + +} + + +/**************************************** + * Find the last non-comma expression. + * Params: + * e = Expressions connected by commas + * Returns: + * right-most non-comma expression + */ + +inout(Expression) lastComma(inout Expression e) +{ + Expression ex = cast()e; + while (ex.op == TOK.comma) + ex = (cast(CommaExp)ex).e2; + return cast(inout)ex; + +} + + /************************************************************* * Given var, get the * right `this` pointer if var is in an outer class, but our @@ -490,8 +528,7 @@ extern (C++) bool isNeedThisScope(Scope* sc, Declaration d) */ private bool checkPropertyCall(Expression e) { - while (e.op == TOK.comma) - e = (cast(CommaExp)e).e2; + e = lastComma(e); if (e.op == TOK.call) { @@ -1105,9 +1142,7 @@ extern (C++) TemplateDeclaration getFuncTemplateDecl(Dsymbol s) */ extern (C++) Expression valueNoDtor(Expression e) { - auto ex = e; - while (ex.op == TOK.comma) - ex = (cast(CommaExp)ex).e2; + auto ex = lastComma(e); if (ex.op == TOK.call) { @@ -1731,6 +1766,16 @@ extern (C++) /* IN_LLVM abstract */ class Expression : RootObject return e1; } + static Expression combine(Expression e1, Expression e2, Expression e3) + { + return combine(combine(e1, e2), e3); + } + + static Expression combine(Expression e1, Expression e2, Expression e3, Expression e4) + { + return combine(combine(e1, e2), combine(e3, e4)); + } + /********************************** * If 'e' is a tree of commas, returns the leftmost expression * by stripping off it from the tree. The remained part of the tree @@ -2541,7 +2586,7 @@ extern (C++) /* IN_LLVM abstract */ class Expression : RootObject */ extern (C++) final class IntegerExp : Expression { - dinteger_t value; + private dinteger_t value; extern (D) this(const ref Loc loc, dinteger_t value, Type type) { @@ -2556,7 +2601,7 @@ extern (C++) final class IntegerExp : Expression type = Type.terror; } this.type = type; - setInteger(value); + this.value = normalize(type.toBasetype().ty, value); } extern (D) this(dinteger_t value) @@ -2593,18 +2638,19 @@ extern (C++) final class IntegerExp : Expression override dinteger_t toInteger() { - normalize(); // necessary until we fix all the paints of 'type' - return value; + // normalize() is necessary until we fix all the paints of 'type' + return value = normalize(type.toBasetype().ty, value); } override real_t toReal() { - normalize(); // necessary until we fix all the paints of 'type' - Type t = type.toBasetype(); - if (t.ty == Tuns64) - return real_t(cast(d_uns64)value); - else - return real_t(cast(d_int64)value); + // normalize() is necessary until we fix all the paints of 'type' + const ty = type.toBasetype().ty; + const val = normalize(ty, value); + value = val; + return (ty == Tuns64) + ? real_t(cast(d_uns64)val) + : real_t(cast(d_int64)val); } override real_t toImaginary() @@ -2645,60 +2691,60 @@ extern (C++) final class IntegerExp : Expression void setInteger(dinteger_t value) { - this.value = value; - normalize(); + this.value = normalize(type.toBasetype().ty, value); } - void normalize() + static dinteger_t normalize(TY ty, dinteger_t value) { /* 'Normalize' the value of the integer to be in range of the type */ - switch (type.toBasetype().ty) + dinteger_t result; + switch (ty) { case Tbool: - value = (value != 0); + result = (value != 0); break; case Tint8: - value = cast(d_int8)value; + result = cast(d_int8)value; break; case Tchar: case Tuns8: - value = cast(d_uns8)value; + result = cast(d_uns8)value; break; case Tint16: - value = cast(d_int16)value; + result = cast(d_int16)value; break; case Twchar: case Tuns16: - value = cast(d_uns16)value; + result = cast(d_uns16)value; break; case Tint32: - value = cast(d_int32)value; + result = cast(d_int32)value; break; case Tdchar: case Tuns32: - value = cast(d_uns32)value; + result = cast(d_uns32)value; break; case Tint64: - value = cast(d_int64)value; + result = cast(d_int64)value; break; case Tuns64: - value = cast(d_uns64)value; + result = cast(d_uns64)value; break; case Tpointer: if (Target.ptrsize == 4) - value = cast(d_uns32)value; + result = cast(d_uns32)value; else if (Target.ptrsize == 8) - value = cast(d_uns64)value; + result = cast(d_uns64)value; else assert(0); break; @@ -2706,6 +2752,7 @@ extern (C++) final class IntegerExp : Expression default: break; } + return result; } } @@ -5345,6 +5392,47 @@ extern (C++) final class DotVarExp : UnaExp if (e1.op == TOK.this_) return var.checkModify(loc, sc, e1, flag); + /* https://issues.dlang.org/show_bug.cgi?id=12764 + * If inside a constructor and an expression of type `this.field.var` + * is encountered, where `field` is a struct declaration with + * default construction disabled, we must make sure that + * assigning to `var` does not imply that `field` was initialized + */ + if (sc.func) + { + auto ctd = sc.func.isCtorDeclaration(); + + // if inside a constructor scope and e1 of this DotVarExp + // is a DotVarExp, then check if e1.e1 is a `this` identifier + if (ctd && e1.op == TOK.dotVariable) + { + scope dve = cast(DotVarExp)e1; + if (dve.e1.op == TOK.this_) + { + scope v = dve.var.isVarDeclaration(); + /* if v is a struct member field with no initializer, no default construction + * and v wasn't intialized before + */ + if (v && v.isField() && v.type.ty == Tstruct && !v._init && !v.ctorinit) + { + const sd = (cast(TypeStruct)v.type).sym; + if (sd.noDefaultCtor) + { + /* checkModify will consider that this is an initialization + * of v while it is actually an assignment of a field of v + */ + scope modifyLevel = v.checkModify(loc, sc, dve.e1, flag); + // reflect that assigning a field of v is not initialization of v + v.ctorinit = false; + if (modifyLevel == 2) + return 1; + return modifyLevel; + } + } + } + } + } + //printf("\te1 = %s\n", e1.toChars()); return e1.checkModifiable(sc, flag); } @@ -5359,6 +5447,30 @@ extern (C++) final class DotVarExp : UnaExp override Expression toLvalue(Scope* sc, Expression e) { //printf("DotVarExp::toLvalue(%s)\n", toChars()); + if (e1.op == TOK.this_ && sc.ctorflow.fieldinit.length && !(sc.ctorflow.callSuper & CSX.any_ctor)) + { + if (VarDeclaration vd = var.isVarDeclaration()) + { + auto ad = vd.isMember2(); + if (ad && ad.fields.dim == sc.ctorflow.fieldinit.length) + { + foreach (i, f; ad.fields) + { + if (f == vd) + { + if (!(sc.ctorflow.fieldinit[i] & CSX.this_ctor)) + { + /* If the address of vd is taken, assume it is thereby initialized + * https://issues.dlang.org/show_bug.cgi?id=15869 + */ + modifyFieldVar(loc, sc, vd, e1); + } + break; + } + } + } + } + } return this; } @@ -5689,6 +5801,12 @@ extern (C++) final class AddrExp : UnaExp super(loc, TOK.address, __traits(classInstanceSize, AddrExp), e); } + extern (D) this(const ref Loc loc, Expression e, Type t) + { + this(loc, e); + type = t; + } + override void accept(Visitor v) { v.visit(this); @@ -7099,7 +7217,7 @@ extern (C++) final class CondExp : BinExp { extern (C++) final class DtorVisitor : StoppableVisitor { - alias visit = super.visit; + alias visit = typeof(super).visit; public: Scope* sc; CondExp ce; diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 0c0a64d0d3d..a9e177e3c34 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -1208,7 +1208,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setError(); assert(e.type.deco); - e.normalize(); + e.setInteger(e.getInteger()); result = e; } @@ -1456,8 +1456,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (e.var.checkNestedReference(sc, e.loc)) return setError(); - if (!sc.intypeof) - sc.ctorflow.callSuper |= CSX.this_; result = e; return; @@ -1540,8 +1538,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (e.var.checkNestedReference(sc, e.loc)) return setError(); - if (!sc.intypeof) - sc.ctorflow.callSuper |= CSX.super_; result = e; return; @@ -6613,7 +6609,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ if (exp.op == TOK.assign && exp.e1.checkModifiable(sc) == 2) { - //printf("[%s] change to init - %s\n", loc.toChars(), toChars()); + //printf("[%s] change to init - %s\n", exp.loc.toChars(), exp.toChars()); exp.op = TOK.construct; // https://issues.dlang.org/show_bug.cgi?id=13515 @@ -6661,9 +6657,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // https://issues.dlang.org/show_bug.cgi?id=15661 // Look for the form from last of comma chain. - auto e2y = e2x; - while (e2y.op == TOK.comma) - e2y = (cast(CommaExp)e2y).e2; + auto e2y = lastComma(e2x); CallExp ce = (e2y.op == TOK.call) ? cast(CallExp)e2y : null; DotVarExp dve = (ce && ce.e1.op == TOK.dotVariable) @@ -6787,6 +6781,31 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor * Rewrite as: * e1 = init, e1.ctor(e2) */ + + /* Fix Issue 5153 : https://issues.dlang.org/show_bug.cgi?id=5153 + * Using `new` to initialize a struct object is a common mistake, but + * the error message from the compiler is not very helpful in that + * case. If exp.e2 is a NewExp and the type of new is the same as + * the type as exp.e1 (struct in this case), then we know for sure + * that the user wants to instantiate a struct. This is done to avoid + * issuing an error when the user actually wants to call a constructor + * which receives a class object. + * + * Foo f = new Foo2(0); is a valid expression if Foo has a constructor + * which receives an instance of a Foo2 class + */ + if (exp.e2.op == TOK.new_) + { + auto newExp = cast(NewExp)(exp.e2); + if (newExp.newtype && newExp.newtype == t1) + { + error(exp.loc, "cannot implicitly convert expression `%s` of type `%s` to `%s`", + newExp.toChars(), newExp.type.toChars(), t1.toChars()); + errorSupplemental(exp.loc, "Perhaps remove the `new` keyword?"); + return setError(); + } + } + Expression einit; einit = new BlitExp(exp.loc, e1x, e1x.type.defaultInit(exp.loc)); einit.type = e1x.type; @@ -7004,16 +7023,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (exp.op != TOK.assign) { // If multidimensional static array, treat as one large array - dinteger_t dim = (cast(TypeSArray)t1).dim.toInteger(); - Type t = t1; - while (1) - { - t = t.nextOf().toBasetype(); - if (t.ty != Tsarray) - break; - dim *= (cast(TypeSArray)t).dim.toInteger(); - e1x.type = t.nextOf().sarrayOf(dim); - } + const dim = t1.numberOfElems(exp.loc); + e1x.type = t1.baseElemOf().sarrayOf(dim); } auto sle = new SliceExp(e1x.loc, e1x, null, null); sle.arrayop = true; @@ -7257,7 +7268,30 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (exp.op == TOK.blit) e2x = e2x.castTo(sc, exp.e1.type); else + { e2x = e2x.implicitCastTo(sc, exp.e1.type); + + // Fix Issue 13435: https://issues.dlang.org/show_bug.cgi?id=13435 + + // If the implicit cast has failed and the assign expression is + // the initialization of a struct member field + if (e2x.op == TOK.error && exp.op == TOK.construct && t1.ty == Tstruct) + { + scope sd = (cast(TypeStruct)t1).sym; + Dsymbol opAssign = search_function(sd, Id.assign); + + // and the struct defines an opAssign + if (opAssign) + { + // offer more information about the cause of the problem + errorSupplemental(exp.loc, + "`%s` is the first assignment of `%s` therefore it represents its initialization", + exp.toChars(), exp.e1.toChars()); + errorSupplemental(exp.loc, + "`opAssign` methods are not used for initialization, but for subsequent assignments"); + } + } + } } if (e2x.op == TOK.error) { @@ -8921,7 +8955,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor auto t1 = exp.e1.type; auto t2 = exp.e2.type; if (t1.ty == Tenum && t2.ty == Tenum && !t1.equivalent(t2)) - exp.deprecation("Comparison between different enumeration types `%s` and `%s`; If this behavior is intended consider using `std.conv.asOriginalType`", + exp.error("Comparison between different enumeration types `%s` and `%s`; If this behavior is intended consider using `std.conv.asOriginalType`", t1.toChars(), t2.toChars()); } @@ -9295,14 +9329,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ Expression trySemantic(Expression exp, Scope* sc) { - //printf("+trySemantic(%s)\n", toChars()); + //printf("+trySemantic(%s)\n", exp.toChars()); uint errors = global.startGagging(); Expression e = expressionSemantic(exp, sc); if (global.endGagging(errors)) { e = null; } - //printf("-trySemantic(%s)\n", toChars()); + //printf("-trySemantic(%s)\n", exp.toChars()); return e; } @@ -9428,7 +9462,7 @@ Expression semanticX(DotIdExp exp, Scope* sc) if (exp.e1.op == TOK.variable && exp.e1.type.toBasetype().ty == Tsarray && exp.ident == Id.length) { // bypass checkPurity - return exp.e1.type.dotExp(sc, exp.e1, exp.ident, exp.noderef ? Type.DotExpFlag.noDeref : 0); + return exp.e1.type.dotExp(sc, exp.e1, exp.ident, exp.noderef ? DotExpFlag.noDeref : 0); } if (exp.e1.op == TOK.dot) @@ -9757,13 +9791,13 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag) return null; e = new PtrExp(exp.loc, exp.e1); e = e.expressionSemantic(sc); - return e.type.dotExp(sc, e, exp.ident, flag | (exp.noderef ? Type.DotExpFlag.noDeref : 0)); + return e.type.dotExp(sc, e, exp.ident, flag | (exp.noderef ? DotExpFlag.noDeref : 0)); } else { if (exp.e1.op == TOK.type || exp.e1.op == TOK.template_) flag = 0; - e = exp.e1.type.dotExp(sc, exp.e1, exp.ident, flag | (exp.noderef ? Type.DotExpFlag.noDeref : 0)); + e = exp.e1.type.dotExp(sc, exp.e1, exp.ident, flag | (exp.noderef ? DotExpFlag.noDeref : 0)); if (e) e = e.expressionSemantic(sc); return e; diff --git a/dmd/func.d b/dmd/func.d index 7afb39622b0..52c5d1e5965 100644 --- a/dmd/func.d +++ b/dmd/func.d @@ -68,7 +68,7 @@ enum BUILTIN : int */ extern (C++) final class NrvoWalker : StatementRewriteWalker { - alias visit = super.visit; + alias visit = typeof(super).visit; public: FuncDeclaration fd; Scope* sc; @@ -152,13 +152,48 @@ enum FUNCFLAG : uint compileTimeOnly = 0x100, /// is a compile time only function; no code will be generated for it } +/*********************************************************** + * Tuple of result identifier (possibly null) and statement. + * This is used to store out contracts: out(id){ ensure } + */ +extern (C++) struct Ensure +{ + Identifier id; + Statement ensure; + + Ensure syntaxCopy() + { + return Ensure(id, ensure.syntaxCopy()); + } + + /***************************************** + * Do syntax copy of an array of Ensure's. + */ + static Ensures* arraySyntaxCopy(Ensures* a) + { + Ensures* b = null; + if (a) + { + b = a.copy(); + foreach (i, e; *a) + { + (*b)[i] = e.syntaxCopy(); + } + } + return b; + } + +} + /*********************************************************** */ extern (C++) class FuncDeclaration : Declaration { Types* fthrows; /// Array of Type's of exceptions (not used) - Statement frequire; /// in contract body - Statement fensure; /// out contract body + Statements* frequires; /// in contracts + Ensures* fensures; /// out contracts + Statement frequire; /// lowered in contract + Statement fensure; /// lowered out contract Statement fbody; /// function body FuncDeclarations foverrides; /// functions this function overrides @@ -188,8 +223,7 @@ extern (C++) class FuncDeclaration : Declaration bool emitInstrumentation = true; } - Identifier outId; /// identifier for out statement - VarDeclaration vresult; /// variable corresponding to outId + VarDeclaration vresult; /// result variable for out contracts LabelDsymbol returnLabel; /// where the return goes // used to prevent symbols in different @@ -213,7 +247,7 @@ extern (C++) class FuncDeclaration : Declaration ILS inlineStatusExp = ILS.uninitialized; PINLINE inlining = PINLINE.default_; - CompiledCtfeFunction* ctfeCode; /// Compiled code for interpreter (not actually) + CompiledCtfeFunctionPimpl ctfeCode; /// Local data (i.e. CompileCtfeFunction*) for module dinterpret int inlineNest; /// !=0 if nested inline bool isArrayOp; /// true if array operation bool eh_none; /// true if no exception unwinding is needed @@ -296,9 +330,8 @@ extern (C++) class FuncDeclaration : Declaration { //printf("FuncDeclaration::syntaxCopy('%s')\n", toChars()); FuncDeclaration f = s ? cast(FuncDeclaration)s : new FuncDeclaration(loc, endloc, ident, storage_class, type.syntaxCopy()); - f.outId = outId; - f.frequire = frequire ? frequire.syntaxCopy() : null; - f.fensure = fensure ? fensure.syntaxCopy() : null; + f.frequires = frequires ? Statement.arraySyntaxCopy(frequires) : null; + f.fensures = fensures ? Ensure.arraySyntaxCopy(fensures) : null; f.fbody = fbody ? fbody.syntaxCopy() : null; assert(!fthrows); // deprecated version(IN_LLVM) @@ -690,7 +723,7 @@ else { if (overnext) return overnext.overloadInsert(ad); - if (!ad.aliassym && ad.type.ty != Tident && ad.type.ty != Tinstance) + if (!ad.aliassym && ad.type.ty != Tident && ad.type.ty != Tinstance && ad.type.ty != Ttypeof) { //printf("\tad = '%s'\n", ad.type.toChars()); return false; @@ -1947,6 +1980,18 @@ else return false; } + /**************************************************** + * Check whether result variable can be built. + * Returns: + * `true` if the function has a return type that + * is different from `void`. + */ + extern (D) private bool canBuildResultVar() + { + auto f = cast(TypeFunction)type; + return f && f.nextOf() && f.nextOf().toBasetype().ty != Tvoid; + } + /**************************************************** * Declare result variable lazily. */ @@ -1960,11 +2005,8 @@ else * So, in here it may be a temporary type for vresult, and after * fbody.dsymbolSemantic() running, vresult.type might be modified. */ - vresult = new VarDeclaration(loc, tret, outId ? outId : Id.result, null); - vresult.storage_class |= STC.nodtor; - - if (outId == Id.result) - vresult.storage_class |= STC.temp; + vresult = new VarDeclaration(loc, tret, Id.result, null); + vresult.storage_class |= STC.nodtor | STC.temp; if (!isVirtual()) vresult.storage_class |= STC.const_; vresult.storage_class |= STC.result; @@ -2046,7 +2088,7 @@ version(IN_LLVM) * be completed before code generation occurs. * https://issues.dlang.org/show_bug.cgi?id=3602 */ - if (fdv.frequire && fdv.semanticRun != PASS.semantic3done) + if (fdv.frequires && fdv.semanticRun != PASS.semantic3done) { assert(fdv._scope); Scope* sc = fdv._scope.push(); @@ -2097,7 +2139,7 @@ else */ static bool needsFensure(FuncDeclaration fd) { - if (fd.fensure) + if (fd.fensures) return true; foreach (fdv; fd.foverrides) @@ -2109,14 +2151,67 @@ else } /**************************************************** - * Rewrite contracts as nested functions, then call them. Doing it as nested - * functions means that overriding functions can call them. + * Rewrite contracts as statements. */ final void buildEnsureRequire() { + + if (frequires) + { + /* in { statements1... } + * in { statements2... } + * ... + * becomes: + * in { { statements1... } { statements2... } ... } + */ + assert(frequires.dim); + auto loc = (*frequires)[0].loc; + auto s = new Statements; + foreach (r; *frequires) + { + s.push(new ScopeStatement(r.loc, r, r.loc)); + } + frequire = new CompoundStatement(loc, s); + } + + if (fensures) + { + /* out(id1) { statements1... } + * out(id2) { statements2... } + * ... + * becomes: + * out(__result) { { ref id1 = __result; { statements1... } } + * { ref id2 = __result; { statements2... } } ... } + */ + assert(fensures.dim); + auto loc = (*fensures)[0].ensure.loc; + auto s = new Statements; + foreach (r; *fensures) + { + if (r.id && canBuildResultVar()) + { + auto rloc = r.ensure.loc; + auto resultId = new IdentifierExp(rloc, Id.result); + auto init = new ExpInitializer(rloc, resultId); + auto stc = STC.ref_ | STC.temp | STC.result; + auto decl = new VarDeclaration(rloc, null, r.id, init, stc); + auto sdecl = new ExpStatement(rloc, decl); + s.push(new ScopeStatement(rloc, new CompoundStatement(rloc, sdecl, r.ensure), rloc)); + } + else + { + s.push(r.ensure); + } + } + fensure = new CompoundStatement(loc, s); + } + if (!isVirtual()) return; + /* Rewrite contracts as nested functions, then call them. Doing it as nested + * functions means that overriding functions can call them. + */ TypeFunction f = cast(TypeFunction) type; if (frequire) @@ -2173,9 +2268,6 @@ else fdrequire = fd; } - if (!outId && f.nextOf() && f.nextOf().toBasetype().ty != Tvoid) - outId = Id.result; // provide a default - version(IN_LLVM) { /* We need to set fdensureParams here and not in the block below to @@ -2183,8 +2275,8 @@ else * even if this function doesn't have an out contract. */ fdensureParams = new Expressions(); - if (outId) - fdensureParams.push(new IdentifierExp(loc, outId)); + if (canBuildResultVar()) + fdensureParams.push(new IdentifierExp(loc, Id.result)); if (parameters) { foreach (vd; *parameters) @@ -2210,9 +2302,9 @@ else auto fparams = new Parameters(); } Parameter p = null; - if (outId) + if (canBuildResultVar()) { - p = new Parameter(STC.ref_ | STC.const_, f.nextOf(), outId, null); + p = new Parameter(STC.ref_ | STC.const_, f.nextOf(), Id.result, null); version(IN_LLVM) fparams.insert(0, p); else @@ -2233,8 +2325,8 @@ else else { Expression eresult = null; - if (outId) - eresult = new IdentifierExp(loc, outId); + if (canBuildResultVar()) + eresult = new IdentifierExp(loc, Id.result); Expression e = new CallExp(loc, new VarExp(loc, fd, false), eresult); } Statement s2 = new ExpStatement(loc, e); @@ -2290,7 +2382,7 @@ else //printf("fdv.fensure: %s\n", fdv.fensure.toChars()); // Make the call: __ensure(result) Expression eresult = null; - if (outId) + if (canBuildResultVar()) { version(IN_LLVM) eresult = (*params)[0]; @@ -3256,7 +3348,7 @@ extern (C++) final class FuncLiteralDeclaration : FuncDeclaration extern (C++) final class RetWalker : StatementRewriteWalker { - alias visit = super.visit; + alias visit = typeof(super).visit; public: Scope* sc; Type tret; @@ -3452,8 +3544,9 @@ extern (C++) final class DtorDeclaration : FuncDeclaration override bool isVirtual() const { - // false so that dtor's don't get put into the vtbl[] - return false; + // D dtor's don't get put into the vtbl[] + // this is a hack so that extern(C++) destructors report as virtual, which are manually added to the vtable + return vtblIndex != -1; } override bool addPreInvariant() @@ -3488,12 +3581,12 @@ extern (C++) class StaticCtorDeclaration : FuncDeclaration { extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc) { - super(loc, endloc, Identifier.generateId("_staticCtor"), STC.static_ | stc, null); + super(loc, endloc, Identifier.generateIdWithLoc("_staticCtor", loc), STC.static_ | stc, null); } - extern (D) this(const ref Loc loc, const ref Loc endloc, const(char)* name, StorageClass stc) + extern (D) this(const ref Loc loc, const ref Loc endloc, string name, StorageClass stc) { - super(loc, endloc, Identifier.generateId(name), STC.static_ | stc, null); + super(loc, endloc, Identifier.generateIdWithLoc(name, loc), STC.static_ | stc, null); } override Dsymbol syntaxCopy(Dsymbol s) @@ -3574,12 +3667,12 @@ extern (C++) class StaticDtorDeclaration : FuncDeclaration extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc) { - super(loc, endloc, Identifier.generateId("_staticDtor"), STC.static_ | stc, null); + super(loc, endloc, Identifier.generateIdWithLoc("_staticDtor", loc), STC.static_ | stc, null); } - extern (D) this(const ref Loc loc, const ref Loc endloc, const(char)* name, StorageClass stc) + extern (D) this(const ref Loc loc, const ref Loc endloc, string name, StorageClass stc) { - super(loc, endloc, Identifier.generateId(name), STC.static_ | stc, null); + super(loc, endloc, Identifier.generateIdWithLoc(name, loc), STC.static_ | stc, null); } override Dsymbol syntaxCopy(Dsymbol s) @@ -3707,7 +3800,7 @@ extern (C++) final class UnitTestDeclaration : FuncDeclaration extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, char* codedoc) { - super(loc, endloc, createIdentifier(loc), stc, null); + super(loc, endloc, Identifier.generateIdWithLoc("__unittest", loc), stc, null); this.codedoc = codedoc; } @@ -3718,17 +3811,6 @@ extern (C++) final class UnitTestDeclaration : FuncDeclaration return FuncDeclaration.syntaxCopy(utd); } - /*********************************************************** - * Generate unique unittest function Id so we can have multiple - * instances per module. - */ - private static Identifier createIdentifier(const ref Loc loc) - { - OutBuffer buf; - buf.printf("__unittest_L%u_C%u", loc.linnum, loc.charnum); - return Identifier.idPool(buf.peekSlice()); - } - override inout(AggregateDeclaration) isThis() inout { return null; diff --git a/dmd/hdrgen.d b/dmd/hdrgen.d index 8481992ded3..ea414f79aee 100644 --- a/dmd/hdrgen.d +++ b/dmd/hdrgen.d @@ -926,6 +926,12 @@ public: } t.inuse++; PrePostAppendStrings pas; + extern(C++) int ignoreReturn(void* param, const(char)* name) + { + if (strcmp(name, "return") != 0) + PrePostAppendStrings.fp(param, name); + return 0; + } pas.buf = buf; pas.isCtor = (ident == Id.ctor); pas.isPostfixStyle = false; @@ -936,7 +942,7 @@ public: MODtoBuffer(buf, t.mod); buf.writeByte(' '); } - t.attributesApply(&pas, &PrePostAppendStrings.fp); + t.attributesApply(&pas, &ignoreReturn); if (t.linkage > LINK.d && hgs.ddoc != 1 && !hgs.hdrgen) { linkageToBuffer(buf, t.linkage); @@ -968,6 +974,11 @@ public: buf.writeByte(')'); } parametersToBuffer(t.parameters, t.varargs); + if (t.isreturn) + { + PrePostAppendStrings.fp(&pas, " return"); + pas.buf.offset -= 1; // remove final whitespace + } t.inuse--; } @@ -1914,27 +1925,65 @@ public: hgs.tpltMember = 0; hgs.autoMember = 0; buf.writenl(); + bool requireDo = false; // in{} - if (f.frequire) + if (f.frequires) { - buf.writestring("in"); - buf.writenl(); - f.frequire.accept(this); + foreach (frequire; *f.frequires) + { + buf.writestring("in"); + if (auto es = frequire.isExpStatement()) + { + assert(es.exp && es.exp.op == TOK.assert_); + buf.writestring(" ("); + (cast(AssertExp)es.exp).e1.accept(this); + buf.writeByte(')'); + buf.writenl(); + requireDo = false; + } + else + { + buf.writenl(); + frequire.accept(this); + requireDo = true; + } + } } // out{} - if (f.fensure) + if (f.fensures) { - buf.writestring("out"); - if (f.outId) + foreach (fensure; *f.fensures) { - buf.writeByte('('); - buf.writestring(f.outId.toChars()); - buf.writeByte(')'); + buf.writestring("out"); + if (auto es = fensure.ensure.isExpStatement()) + { + assert(es.exp && es.exp.op == TOK.assert_); + buf.writestring(" ("); + if (fensure.id) + { + buf.writestring(fensure.id.toChars()); + } + buf.writestring("; "); + (cast(AssertExp)es.exp).e1.accept(this); + buf.writeByte(')'); + buf.writenl(); + requireDo = false; + } + else + { + if (fensure.id) + { + buf.writeByte('('); + buf.writestring(fensure.id.toChars()); + buf.writeByte(')'); + } + buf.writenl(); + fensure.ensure.accept(this); + requireDo = true; + } } - buf.writenl(); - f.fensure.accept(this); } - if (f.frequire || f.fensure) + if (requireDo) { buf.writestring("do"); buf.writenl(); @@ -2051,7 +2100,18 @@ public: if (stcToBuffer(buf, d.storage_class)) buf.writeByte(' '); buf.writestring("invariant"); - bodyToBuffer(d); + if(auto es = d.fbody.isExpStatement()) + { + assert(es.exp && es.exp.op == TOK.assert_); + buf.writestring(" ("); + (cast(AssertExp)es.exp).e1.accept(this); + buf.writestring(");"); + buf.writenl(); + } + else + { + bodyToBuffer(d); + } } override void visit(UnitTestDeclaration d) diff --git a/dmd/id.d b/dmd/id.d index c6f4c774cc5..d214e78ed0a 100644 --- a/dmd/id.d +++ b/dmd/id.d @@ -67,6 +67,8 @@ immutable Msgtable[] msgtable = { "__xdtor", "__xdtor" }, { "__fieldDtor", "__fieldDtor" }, { "__aggrDtor", "__aggrDtor" }, + { "cppdtor", "__cppdtor" }, + { "ticppdtor", "__ticppdtor" }, { "postblit", "__postblit" }, { "__xpostblit", "__xpostblit" }, { "__fieldPostblit", "__fieldPostblit" }, @@ -294,9 +296,9 @@ immutable Msgtable[] msgtable = { "monitorexit", "_d_monitorexit" }, { "criticalenter", "_d_criticalenter" }, { "criticalexit", "_d_criticalexit" }, - { "_ArrayEq" }, - { "_ArrayPostblit" }, - { "_ArrayDtor" }, + { "__ArrayEq" }, + { "__ArrayPostblit" }, + { "__ArrayDtor" }, { "_d_delThrowable" }, { "dup" }, diff --git a/dmd/identifier.d b/dmd/identifier.d index 18fdd8b4060..68d8dc294a2 100644 --- a/dmd/identifier.d +++ b/dmd/identifier.d @@ -29,54 +29,54 @@ extern (C++) final class Identifier : RootObject { private: const int value; - const char* string; + const char* name; const size_t len; public: - extern (D) this(const(char)* string, size_t length, int value) nothrow + extern (D) this(const(char)* name, size_t length, int value) nothrow { - //printf("Identifier('%s', %d)\n", string, value); - this.string = string; + //printf("Identifier('%s', %d)\n", name, value); + this.name = name; this.value = value; this.len = length; } - extern (D) this(const(char)* string) nothrow + extern (D) this(const(char)* name) nothrow { - //printf("Identifier('%s', %d)\n", string, value); - this(string, strlen(string), TOK.identifier); + //printf("Identifier('%s', %d)\n", name, value); + this(name, strlen(name), TOK.identifier); } - static Identifier create(const(char)* string) nothrow + static Identifier create(const(char)* name) nothrow { - return new Identifier(string); + return new Identifier(name); } override bool equals(RootObject o) const { - return this == o || strncmp(string, o.toChars(), len + 1) == 0; + return this == o || strncmp(name, o.toChars(), len + 1) == 0; } override int compare(RootObject o) const { - return strncmp(string, o.toChars(), len + 1); + return strncmp(name, o.toChars(), len + 1); } nothrow: override void print() const { - fprintf(stderr, "%s", string); + fprintf(stderr, "%s", name); } override const(char)* toChars() const pure { - return string; + return name; } extern (D) final const(char)[] toString() const pure { - return string[0 .. len]; + return name[0 .. len]; } final int getValue() const pure @@ -138,6 +138,40 @@ nothrow: return idPool(buf.peekSlice()); } + /*************************************** + * Generate deterministic named identifier based on a source location, + * such that the name is consistent across multiple compilations. + * A new unique name is generated. If the prefix+location is already in + * the stringtable, an extra suffix is added (starting the count at "_1"). + * + * Params: + * prefix = first part of the identifier name. + * loc = source location to use in the identifier name. + * Returns: + * Identifier (inside Identifier.idPool) with deterministic name based + * on the source location. + */ + extern (D) static Identifier generateIdWithLoc(string prefix, const ref Loc loc) + { + OutBuffer buf; + buf.writestring(prefix); + buf.writestring("_L"); + buf.print(loc.linnum); + buf.writestring("_C"); + buf.print(loc.charnum); + auto basesize = buf.peekSlice().length; + uint counter = 1; + while (stringtable.lookup(buf.peekSlice().ptr, buf.peekSlice().length) !is null) + { + // Strip the extra suffix + buf.setsize(basesize); + // Add new suffix with increased counter + buf.writestring("_"); + buf.print(counter++); + } + return idPool(buf.peekSlice()); + } + /******************************************** * Create an identifier in the string table. */ diff --git a/dmd/initsem.d b/dmd/initsem.d index 7917f55118d..4062c004e4c 100644 --- a/dmd/initsem.d +++ b/dmd/initsem.d @@ -155,10 +155,11 @@ private extern(C++) final class InitializerSemanticVisitor : Visitor if (!s) { s = sd.search_correct(id); + Loc initLoc = i.value[j].loc; if (s) - error(i.loc, "`%s` is not a member of `%s`, did you mean %s `%s`?", id.toChars(), sd.toChars(), s.kind(), s.toChars()); + error(initLoc, "`%s` is not a member of `%s`, did you mean %s `%s`?", id.toChars(), sd.toChars(), s.kind(), s.toChars()); else - error(i.loc, "`%s` is not a member of `%s`", id.toChars(), sd.toChars()); + error(initLoc, "`%s` is not a member of `%s`", id.toChars(), sd.toChars()); result = new ErrorInitializer(); return; } diff --git a/dmd/inline.d b/dmd/inline.d index d8bd776efc9..bd1c73323ee 100644 --- a/dmd/inline.d +++ b/dmd/inline.d @@ -2040,8 +2040,7 @@ private void expandInline(Loc callLoc, FuncDeclaration fd, FuncDeclaration paren */ private bool isConstruction(Expression e) { - while (e.op == TOK.comma) - e = (cast(CommaExp)e).e2; + e = lastComma(e); if (e.op == TOK.structLiteral) { diff --git a/dmd/inlinecost.d b/dmd/inlinecost.d index b748e8cbf29..56e30c7e6dd 100644 --- a/dmd/inlinecost.d +++ b/dmd/inlinecost.d @@ -287,7 +287,7 @@ public: { extern (C++) final class LambdaInlineCost : StoppableVisitor { - alias visit = super.visit; + alias visit = typeof(super).visit; InlineCostVisitor icv; public: diff --git a/dmd/lambdacomp.d b/dmd/lambdacomp.d index 61c75205b07..f890f8c33c3 100644 --- a/dmd/lambdacomp.d +++ b/dmd/lambdacomp.d @@ -341,9 +341,7 @@ public: if (buf.offset == 0) return; - exp.normalize(); - auto val = exp.value; - buf.print(val); + buf.print(exp.toInteger()); buf.writeByte('_'); } diff --git a/dmd/mars.d b/dmd/mars.d index 4ad81c4fdd3..031782beae6 100644 --- a/dmd/mars.d +++ b/dmd/mars.d @@ -88,6 +88,25 @@ private void logo() printf("DMD%llu D Compiler %s\n%s %s\n", cast(ulong)size_t.sizeof * 8, global._version, global.copyright, global.written); } +/** +Print DMD's logo with more debug information and error-reporting pointers. + +Params: + stream = output stream to print the information on +*/ +extern(C) void printInternalFailure(FILE* stream) +{ + fputs(("---\n" ~ + "ERROR: This is a compiler bug.\n" ~ + "Please report it via https://issues.dlang.org/enter_bug.cgi\n" ~ + "with, preferably, a reduced, reproducible example and the information below.\n" ~ + "DustMite (https://github.com/CyberShadow/DustMite/wiki) can help with the reduction.\n" ~ + "---\n").ptr, stream); + stream.fprintf("DMD %s\n", global._version); + stream.printPredefinedVersions; + stream.printGlobalConfigs(); + fputs("---\n".ptr, stream); +} /** * Print DMD's usage message on stdout @@ -558,51 +577,16 @@ extern (C++) int mars_mainBody(ref Strings files, ref Strings libmodules) Objc._init(); builtin_init(); - version (IN_LLVM) - { - // LDC prints binary/version/config before entering this function. - // DMD prints the predefined versions as part of addDefaultVersionIdentifiers(). - // Let's do it here after initialization, as e.g. Objc.init() may add `D_ObjectiveC`. - printPredefinedVersions(); - } - else - { - printPredefinedVersions(); - if (global.params.verbose) { - message("binary %s", global.params.argv0); - message("version %s", global._version); - message("config %s", global.inifilename ? global.inifilename : "(none)"); - // Print DFLAGS environment variable + stdout.printPredefinedVersions(); + version (IN_LLVM) { - Strings dflags; - getenv_setargv(readFromEnv(&environment, "DFLAGS"), &dflags); - OutBuffer buf; - foreach (flag; dflags.asDArray) - { - bool needsQuoting; - for (auto flagp = flag; flagp; flagp++) - { - auto c = flagp[0]; - if (!(isalnum(c) || c == '_')) - { - needsQuoting = true; - break; - } - } - - if (flag.strchr(' ')) - buf.printf("'%s' ", flag); - else - buf.printf("%s ", flag); - } - - auto res = buf.peekSlice() ? buf.peekSlice()[0 .. $ - 1] : "(none)"; - message("DFLAGS %.*s", res.length, res.ptr); + // LDC prints binary/version/config before entering this function. } + else + stdout.printGlobalConfigs(); } - } //printf("%d source files\n",files.dim); // Build import search path @@ -1317,6 +1301,8 @@ int main() dmd_coverSetMerge(true); } + scope(failure) stderr.printInternalFailure; + auto args = Runtime.cArgs(); return tryMain(args.argc, cast(const(char)**)args.argv); } @@ -1631,9 +1617,9 @@ void addDefaultVersionIdentifiers() } // !IN_LLVM -private void printPredefinedVersions() +private void printPredefinedVersions(FILE* stream) { - if (global.params.verbose && global.versionids) + if (global.versionids) { OutBuffer buf; foreach (const str; *global.versionids) @@ -1641,10 +1627,48 @@ private void printPredefinedVersions() buf.writeByte(' '); buf.writestring(str.toChars()); } - message("predefs %s", buf.peekString()); + stream.fprintf("predefs %s", buf.peekString()); } } +version (IN_LLVM) {} else +{ + +extern(C) void printGlobalConfigs(FILE* stream) +{ + stream.fprintf("binary %s\n", global.params.argv0); + stream.fprintf("version %s\n", global._version); + stream.fprintf("config %s\n", global.inifilename ? global.inifilename : "(none)"); + // Print DFLAGS environment variable + { + StringTable environment; + environment._init(0); + Strings dflags; + getenv_setargv(readFromEnv(&environment, "DFLAGS"), &dflags); + environment.reset(1); + OutBuffer buf; + foreach (flag; dflags[]) + { + bool needsQuoting; + foreach (c; flag[0 .. strlen(flag)]) + { + if (!(isalnum(c) || c == '_')) + { + needsQuoting = true; + break; + } + } + + if (flag.strchr(' ')) + buf.printf("'%s' ", flag); + else + buf.printf("%s ", flag); + } + + auto res = buf.peekSlice() ? buf.peekSlice()[0 .. $ - 1] : "(none)"; + stream.fprintf("DFLAGS %.*s\n", res.length, res.ptr); + } +} /**************************************** * Determine the instruction set to be used. @@ -1654,7 +1678,6 @@ private void printPredefinedVersions() * value to generate code for */ -version (IN_LLVM) {} else private CPU setTargetCPU(CPU cpu) { // Determine base line for target @@ -1710,7 +1733,6 @@ private CPU setTargetCPU(CPU cpu) * true if errors in command line */ -version (IN_LLVM) {} else private bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param params, ref Strings files) { bool errors; @@ -2459,6 +2481,8 @@ private bool parseCommandLine(const ref Strings arguments, const size_t argc, re return errors; } +} // !IN_LLVM + // IN_LLVM: `private` replaced by `extern(C++)` extern(C++) __gshared bool includeImports = false; diff --git a/dmd/mtype.d b/dmd/mtype.d index 835070de202..3733559ecf3 100644 --- a/dmd/mtype.d +++ b/dmd/mtype.d @@ -401,6 +401,15 @@ enum MODFlags : int alias MOD = ubyte; +/**************** + * dotExp() bit flags + */ +enum DotExpFlag +{ + 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 +} + /*********************************************************** */ extern (C++) abstract class Type : RootObject @@ -2411,15 +2420,6 @@ extern (C++) abstract class Type : RootObject return null; } - /**************** - * dotExp() bit flags - */ - enum DotExpFlag - { - 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 - } - /************************************ * Return alignment to use for this type. */ @@ -2618,6 +2618,33 @@ extern (C++) abstract class Type : RootObject return t; } + /******************************************* + * Compute number of elements for a (possibly multidimensional) static array, + * or 1 for other types. + * Params: + * loc = for error message + * Returns: + * number of elements, uint.max on overflow + */ + final uint numberOfElems(const ref Loc loc) + { + //printf("Type::numberOfElems()\n"); + uinteger_t n = 1; + Type tb = this; + while ((tb = tb.toBasetype()).ty == Tsarray) + { + bool overflow = false; + n = mulu(n, (cast(TypeSArray)tb).dim.toUInteger(), overflow); + if (overflow || n >= uint.max) + { + error(loc, "static array `%s` size overflowed to %llu", toChars(), cast(ulong)n); + return uint.max; + } + tb = (cast(TypeSArray)tb).next; + } + return cast(uint)n; + } + /**************************************** * Return the mask that an integral type will * fit into. @@ -3722,23 +3749,17 @@ extern (C++) final class TypeSArray : TypeArray override d_uns64 size(const ref Loc loc) { //printf("TypeSArray::size()\n"); - dinteger_t sz; - if (!dim) - return Type.size(loc); - sz = dim.toInteger(); + const n = numberOfElems(loc); + const elemsize = baseElemOf().size(loc); + bool overflow = false; + const sz = mulu(n, elemsize, overflow); + if (overflow || sz >= uint.max) { - bool overflow = false; - sz = mulu(next.size(), sz, overflow); - if (overflow) - goto Loverflow; + if (elemsize != SIZE_INVALID && n != uint.max) + error(loc, "static array `%s` size overflowed to %lld", toChars(), cast(long)sz); + return SIZE_INVALID; } - if (sz > uint.max) - goto Loverflow; return sz; - - Loverflow: - error(loc, "static array `%s` size overflowed to %lld", toChars(), cast(long)sz); - return SIZE_INVALID; } override uint alignsize() @@ -4817,7 +4838,7 @@ extern (C++) final class TypeFunction : TypeNext } // arguments get specially formatted - private const(char)* getParamError(const(char)* format, Expression arg, Parameter par) + private const(char)* getParamError(Expression arg, Parameter par) { if (global.gag && !global.params.showGaggedErrors) return null; @@ -4826,12 +4847,12 @@ extern (C++) final class TypeFunction : TypeNext bool qual = !arg.type.equals(par.type) && strcmp(at, par.type.toChars()) == 0; if (qual) at = arg.type.toPrettyChars(true); - OutBuffer as; - as.printf("`%s` of type `%s`", arg.toChars(), at); - OutBuffer ps; - ps.printf("`%s`", parameterToChars(par, this, qual)); OutBuffer buf; - buf.printf(format, as.peekString(), ps.peekString()); + // only mention rvalue if it's relevant + const rv = !arg.isLvalue() && par.storageClass & (STC.ref_ | STC.out_); + buf.printf("cannot pass %sargument `%s` of type `%s` to parameter `%s`", + rv ? "rvalue ".ptr : "".ptr, arg.toChars(), at, + parameterToChars(par, this, qual)); return buf.extractString(); } @@ -4974,7 +4995,7 @@ extern (C++) final class TypeFunction : TypeNext { if (p.storageClass & STC.out_) { - if (pMessage) *pMessage = getParamError("cannot pass rvalue argument %s to parameter %s", arg, p); + if (pMessage) *pMessage = getParamError(arg, p); goto Nomatch; } @@ -4999,7 +5020,7 @@ extern (C++) final class TypeFunction : TypeNext } else { - if (pMessage) *pMessage = getParamError("cannot pass rvalue argument %s to parameter %s", arg, p); + if (pMessage) *pMessage = getParamError(arg, p); goto Nomatch; } } @@ -5026,9 +5047,7 @@ extern (C++) final class TypeFunction : TypeNext */ if (!ta.constConv(tp)) { - if (pMessage) *pMessage = getParamError( - arg.isLvalue() ? "cannot pass argument %s to parameter %s" : - "cannot pass rvalue argument %s to parameter %s", arg, p); + if (pMessage) *pMessage = getParamError(arg, p); goto Nomatch; } } @@ -5088,7 +5107,7 @@ extern (C++) final class TypeFunction : TypeNext if (m == MATCH.nomatch) { - if (pMessage) *pMessage = getParamError("cannot pass argument %s to parameter %s", arg, p); + if (pMessage) *pMessage = getParamError(arg, p); goto Nomatch; } if (m < match) @@ -5106,7 +5125,7 @@ extern (C++) final class TypeFunction : TypeNext } } if (pMessage && u < nargs) - *pMessage = getParamError("cannot pass argument %s to parameter %s", (*args)[u], p); + *pMessage = getParamError((*args)[u], p); goto Nomatch; } if (m < match) diff --git a/dmd/nogc.d b/dmd/nogc.d index 503bc1f1490..2bc8bdae41a 100644 --- a/dmd/nogc.d +++ b/dmd/nogc.d @@ -29,7 +29,7 @@ import dmd.visitor; */ extern (C++) final class NOGCVisitor : StoppableVisitor { - alias visit = super.visit; + alias visit = typeof(super).visit; public: FuncDeclaration f; bool err; diff --git a/dmd/opover.d b/dmd/opover.d index 9a19538a82e..b37dcd20184 100644 --- a/dmd/opover.d +++ b/dmd/opover.d @@ -1104,9 +1104,9 @@ extern (C++) Expression op_overload(Expression e, Scope* sc) if (needsDirectEq() && !(t1.ty == Tarray && t2.ty == Tarray)) { /* Rewrite as: - * _ArrayEq(e1, e2) + * __ArrayEq(e1, e2) */ - Expression eeq = new IdentifierExp(e.loc, Id._ArrayEq); + Expression eeq = new IdentifierExp(e.loc, Id.__ArrayEq); result = new CallExp(e.loc, eeq, e.e1, e.e2); if (e.op == TOK.notEqual) result = new NotExp(e.loc, result); diff --git a/dmd/optimize.d b/dmd/optimize.d index 1e21f39642d..f88d4d00864 100644 --- a/dmd/optimize.d +++ b/dmd/optimize.d @@ -426,8 +426,7 @@ extern (C++) Expression Expression_optimize(Expression e, int result, bool keepL if (e.e1.op == TOK.comma) { CommaExp ce = cast(CommaExp)e.e1; - auto ae = new AddrExp(e.loc, ce.e2); - ae.type = e.type; + auto ae = new AddrExp(e.loc, ce.e2, e.type); ret = new CommaExp(ce.loc, ce.e1, ae); ret.type = e.type; return; diff --git a/dmd/parse.d b/dmd/parse.d index 69a1d8aeb25..86282dec27f 100644 --- a/dmd/parse.d +++ b/dmd/parse.d @@ -533,10 +533,11 @@ final class Parser(AST) : Lexer case TOK.invariant_: { Token* t = peek(&token); - if (t.value == TOK.leftParentheses && peek(t).value == TOK.rightParentheses || t.value == TOK.leftCurly) + if (t.value == TOK.leftParentheses || t.value == TOK.leftCurly) { - // invariant {} - // invariant() {} + // invariant { statements... } + // invariant() { statements... } + // invariant (expression); s = parseInvariant(pAttrs); } else @@ -1258,19 +1259,14 @@ final class Parser(AST) : Lexer /********************************************* * Give error on redundant/conflicting storage class. - * - * TODO: remove deprecation in 2.068 and keep only error */ - StorageClass appendStorageClass(StorageClass storageClass, StorageClass stc, bool deprec = false) + StorageClass appendStorageClass(StorageClass storageClass, StorageClass stc) { if ((storageClass & stc) || (storageClass & AST.STC.in_ && stc & (AST.STC.const_ | AST.STC.scope_)) || (stc & AST.STC.in_ && storageClass & (AST.STC.const_ | AST.STC.scope_))) { OutBuffer buf; AST.stcToBuffer(&buf, stc); - if (deprec) - deprecation("redundant attribute `%s`", buf.peekString()); - else - error("redundant attribute `%s`", buf.peekString()); + error("redundant attribute `%s`", buf.peekString()); return storageClass | stc; } @@ -1432,7 +1428,7 @@ final class Parser(AST) : Lexer default: return storageClass; } - storageClass = appendStorageClass(storageClass, stc, true); + storageClass = appendStorageClass(storageClass, stc); nextToken(); } } @@ -2616,7 +2612,9 @@ final class Parser(AST) : Lexer /***************************************** * Parse an invariant definition: - * invariant() { body } + * invariant { statements... } + * invariant() { statements... } + * invariant (expression); * Current token is 'invariant'. */ AST.Dsymbol parseInvariant(PrefixAttributes!AST* pAttrs) @@ -2625,10 +2623,33 @@ final class Parser(AST) : Lexer StorageClass stc = getStorageClass!AST(pAttrs); nextToken(); - if (token.value == TOK.leftParentheses) // optional () + if (token.value == TOK.leftParentheses) // optional () or invariant (expression); { nextToken(); - check(TOK.rightParentheses); + if (token.value != TOK.rightParentheses) // invariant (expression); + { + AST.Expression e = parseAssignExp(), msg = null; + if (token.value == TOK.comma) + { + nextToken(); + if (token.value != TOK.rightParentheses) + { + msg = parseAssignExp(); + if (token.value == TOK.comma) + nextToken(); + } + } + check(TOK.rightParentheses); + check(TOK.semicolon); + e = new AST.AssertExp(loc, e, msg); + auto fbody = new AST.ExpStatement(loc, e); + auto f = new AST.InvariantDeclaration(loc, token.loc, stc, null, fbody); + return f; + } + else + { + nextToken(); + } } auto fbody = parseStatement(ParseStatementFlags.curly); @@ -4661,11 +4682,12 @@ final class Parser(AST) : Lexer // The following is irrelevant, as it is overridden by sc.linkage in // TypeFunction::semantic linkage = LINK.d; // nested functions have D linkage + bool requireDo = false; L1: switch (token.value) { case TOK.leftCurly: - if (f.frequire || f.fensure) + if (requireDo) error("missing `do { ... }` after `in` or `out`"); f.fbody = parseStatement(ParseStatementFlags.semi); f.endloc = endloc; @@ -4715,27 +4737,86 @@ final class Parser(AST) : Lexer } case TOK.in_: + // in { statements... } + // in (expression) + auto loc = token.loc; nextToken(); - if (f.frequire) - error("redundant `in` statement"); - f.frequire = parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_); + if (!f.frequires) + { + f.frequires = new AST.Statements; + } + if (token.value == TOK.leftParentheses) + { + nextToken(); + AST.Expression e = parseAssignExp(), msg = null; + if (token.value == TOK.comma) + { + nextToken(); + if (token.value != TOK.rightParentheses) + { + msg = parseAssignExp(); + if (token.value == TOK.comma) + nextToken(); + } + } + check(TOK.rightParentheses); + e = new AST.AssertExp(loc, e, msg); + f.frequires.push(new AST.ExpStatement(loc, e)); + requireDo = false; + } + else + { + f.frequires.push(parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_)); + requireDo = true; + } goto L1; case TOK.out_: - // parse: out (identifier) { statement } + // out { statements... } + // out (; expression) + // out (identifier) { statements... } + // out (identifier; expression) + auto loc = token.loc; nextToken(); + if (!f.fensures) + { + f.fensures = new AST.Ensures; + } + Identifier id = null; if (token.value != TOK.leftCurly) { check(TOK.leftParentheses); - if (token.value != TOK.identifier) - error("`(identifier)` following `out` expected, not `%s`", token.toChars()); - f.outId = token.ident; - nextToken(); + if (token.value != TOK.identifier && token.value != TOK.semicolon) + error("`(identifier) { ... }` or `(identifier; expression)` following `out` expected, not `%s`", token.toChars()); + if (token.value != TOK.semicolon) + { + id = token.ident; + nextToken(); + } + if (token.value == TOK.semicolon) + { + nextToken(); + AST.Expression e = parseAssignExp(), msg = null; + if (token.value == TOK.comma) + { + nextToken(); + if (token.value != TOK.rightParentheses) + { + msg = parseAssignExp(); + if (token.value == TOK.comma) + nextToken(); + } + } + check(TOK.rightParentheses); + e = new AST.AssertExp(loc, e, msg); + f.fensures.push(AST.Ensure(id, new AST.ExpStatement(loc, e))); + requireDo = false; + goto L1; + } check(TOK.rightParentheses); } - if (f.fensure) - error("redundant `out` statement"); - f.fensure = parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_); + f.fensures.push(AST.Ensure(id, parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_))); + requireDo = true; goto L1; case TOK.semicolon: @@ -4743,8 +4824,8 @@ final class Parser(AST) : Lexer { // https://issues.dlang.org/show_bug.cgi?id=15799 // Semicolon becomes a part of function declaration - // only when neither of contracts exists. - if (!f.frequire && !f.fensure) + // only when 'do' is not required + if (!requireDo) nextToken(); break; } @@ -4753,10 +4834,10 @@ final class Parser(AST) : Lexer default: if (literal) { - const(char)* sbody = (f.frequire || f.fensure) ? "do " : ""; + const(char)* sbody = requireDo ? "do " : ""; error("missing `%s{ ... }` for function literal", sbody); } - else if (!f.frequire && !f.fensure) // allow these even with no body + else if (!requireDo) // allow contracts even with no body { TOK t = token.value; if (t == TOK.const_ || t == TOK.immutable_ || t == TOK.inout_ || t == TOK.return_ || @@ -5518,6 +5599,11 @@ final class Parser(AST) : Lexer s = null; // don't propagate parsing errors break; } + + case TOK.else_: + error("found `else` without a corresponding `if`, `version` or `debug` statement"); + goto Lerror; + case TOK.scope_: if (peek(&token).value != TOK.leftParentheses) goto Ldeclaration; // scope used as storage class @@ -5990,10 +6076,28 @@ final class Parser(AST) : Lexer } case TOK.import_: { - AST.Dsymbols* imports = parseImport(); - s = new AST.ImportStatement(loc, imports); - if (flags & ParseStatementFlags.scope_) - s = new AST.ScopeStatement(loc, s, token.loc); + /* https://issues.dlang.org/show_bug.cgi?id=16088 + * + * At this point it can either be an + * https://dlang.org/spec/grammar.html#ImportExpression + * or an + * https://dlang.org/spec/grammar.html#ImportDeclaration. + * See if the next token after `import` is a `(`; if so, + * then it is an import expression. + */ + if (peekNext() == TOK.leftParentheses) + { + AST.Expression e = parseExpression(); + check(TOK.semicolon); + s = new AST.ExpStatement(loc, e); + } + else + { + AST.Dsymbols* imports = parseImport(); + s = new AST.ImportStatement(loc, imports); + if (flags & ParseStatementFlags.scope_) + s = new AST.ScopeStatement(loc, s, token.loc); + } break; } case TOK.template_: @@ -7375,7 +7479,7 @@ final class Parser(AST) : Lexer postfix = token.postfix; } - deprecation("Implicit string concatenation is deprecated, use %s ~ %s instead", + error("Implicit string concatenation is deprecated, use %s ~ %s instead", prev.toChars(), token.toChars()); const len1 = len; @@ -8385,100 +8489,97 @@ final class Parser(AST) : Lexer AST.Expression parseAssignExp() { auto e = parseCondExp(); - while (1) + const loc = token.loc; + switch (token.value) { - const loc = token.loc; - switch (token.value) - { - case TOK.assign: - nextToken(); - auto e2 = parseAssignExp(); - e = new AST.AssignExp(loc, e, e2); - continue; + case TOK.assign: + nextToken(); + auto e2 = parseAssignExp(); + e = new AST.AssignExp(loc, e, e2); + break; - case TOK.addAssign: - nextToken(); - auto e2 = parseAssignExp(); - e = new AST.AddAssignExp(loc, e, e2); - continue; + case TOK.addAssign: + nextToken(); + auto e2 = parseAssignExp(); + e = new AST.AddAssignExp(loc, e, e2); + break; - case TOK.minAssign: - nextToken(); - auto e2 = parseAssignExp(); - e = new AST.MinAssignExp(loc, e, e2); - continue; + case TOK.minAssign: + nextToken(); + auto e2 = parseAssignExp(); + e = new AST.MinAssignExp(loc, e, e2); + break; - case TOK.mulAssign: - nextToken(); - auto e2 = parseAssignExp(); - e = new AST.MulAssignExp(loc, e, e2); - continue; + case TOK.mulAssign: + nextToken(); + auto e2 = parseAssignExp(); + e = new AST.MulAssignExp(loc, e, e2); + break; - case TOK.divAssign: - nextToken(); - auto e2 = parseAssignExp(); - e = new AST.DivAssignExp(loc, e, e2); - continue; + case TOK.divAssign: + nextToken(); + auto e2 = parseAssignExp(); + e = new AST.DivAssignExp(loc, e, e2); + break; - case TOK.modAssign: - nextToken(); - auto e2 = parseAssignExp(); - e = new AST.ModAssignExp(loc, e, e2); - continue; + case TOK.modAssign: + nextToken(); + auto e2 = parseAssignExp(); + e = new AST.ModAssignExp(loc, e, e2); + break; - case TOK.powAssign: - nextToken(); - auto e2 = parseAssignExp(); - e = new AST.PowAssignExp(loc, e, e2); - continue; + case TOK.powAssign: + nextToken(); + auto e2 = parseAssignExp(); + e = new AST.PowAssignExp(loc, e, e2); + break; - case TOK.andAssign: - nextToken(); - auto e2 = parseAssignExp(); - e = new AST.AndAssignExp(loc, e, e2); - continue; + case TOK.andAssign: + nextToken(); + auto e2 = parseAssignExp(); + e = new AST.AndAssignExp(loc, e, e2); + break; - case TOK.orAssign: - nextToken(); - auto e2 = parseAssignExp(); - e = new AST.OrAssignExp(loc, e, e2); - continue; + case TOK.orAssign: + nextToken(); + auto e2 = parseAssignExp(); + e = new AST.OrAssignExp(loc, e, e2); + break; - case TOK.xorAssign: - nextToken(); - auto e2 = parseAssignExp(); - e = new AST.XorAssignExp(loc, e, e2); - continue; + case TOK.xorAssign: + nextToken(); + auto e2 = parseAssignExp(); + e = new AST.XorAssignExp(loc, e, e2); + break; - case TOK.leftShiftAssign: - nextToken(); - auto e2 = parseAssignExp(); - e = new AST.ShlAssignExp(loc, e, e2); - continue; + case TOK.leftShiftAssign: + nextToken(); + auto e2 = parseAssignExp(); + e = new AST.ShlAssignExp(loc, e, e2); + break; - case TOK.rightShiftAssign: - nextToken(); - auto e2 = parseAssignExp(); - e = new AST.ShrAssignExp(loc, e, e2); - continue; + case TOK.rightShiftAssign: + nextToken(); + auto e2 = parseAssignExp(); + e = new AST.ShrAssignExp(loc, e, e2); + break; - case TOK.unsignedRightShiftAssign: - nextToken(); - auto e2 = parseAssignExp(); - e = new AST.UshrAssignExp(loc, e, e2); - continue; + case TOK.unsignedRightShiftAssign: + nextToken(); + auto e2 = parseAssignExp(); + e = new AST.UshrAssignExp(loc, e, e2); + break; - case TOK.concatenateAssign: - nextToken(); - auto e2 = parseAssignExp(); - e = new AST.CatAssignExp(loc, e, e2); - continue; + case TOK.concatenateAssign: + nextToken(); + auto e2 = parseAssignExp(); + e = new AST.CatAssignExp(loc, e, e2); + break; - default: - break; - } + default: break; } + return e; } diff --git a/dmd/root/ctfloat.d b/dmd/root/ctfloat.d index f936e33239b..d30f62d8ac2 100644 --- a/dmd/root/ctfloat.d +++ b/dmd/root/ctfloat.d @@ -35,12 +35,8 @@ private // IN_LLVM replaced: version(CRuntime_Microsoft) extern (C++) version(none) extern (C++) { - static if (is(real_t == real)) - struct longdouble { real_t r; } - else - import dmd.root.longdouble : longdouble; - size_t ld_sprint(char* str, int fmt, longdouble x); - longdouble strtold_dm(const(char)* p, char** endp); + public import dmd.root.longdouble : longdouble_soft, ld_sprint; + longdouble_soft strtold_dm(const(char)* p, char** endp); } } @@ -176,9 +172,9 @@ extern (C++) struct CTFloat // doesn't match with the C++ header. // add a wrapper just for isSNaN as this is the only function called from C++ version(CRuntime_Microsoft) static if (is(real_t == real)) - static bool isSNaN(longdouble ld) + static bool isSNaN(longdouble_soft ld) { - return isSNaN(ld.r); + return isSNaN(cast(real)ld); } } @@ -204,10 +200,7 @@ else } version(CRuntime_Microsoft) { - version(LDC) - auto r = strtold_dm(literal, null); - else - auto r = strtold_dm(literal, null).r; + auto r = cast(real_t) strtold_dm(literal, null); } else auto r = strtold(literal, null); @@ -223,7 +216,7 @@ else // IN_LLVM replaced: version(CRuntime_Microsoft) version(none) { - return cast(int)ld_sprint(str, fmt, longdouble(x)); + return cast(int)ld_sprint(str, fmt, longdouble_soft(x)); } else { diff --git a/dmd/root/filename.d b/dmd/root/filename.d index 4a0893ed21d..bdc3449cc73 100644 --- a/dmd/root/filename.d +++ b/dmd/root/filename.d @@ -728,8 +728,8 @@ nothrow: // Actually get the full path name const fullPathLengthNoTerminator = GetFullPathNameW(&wname[0], cast(uint)fullPath.length, &fullPath[0], null /*filePart*/); // Unfortunately, when the buffer is large enough the return value is the number of characters - // _not_ counting the null terminator, so fullPathLength2 should be smaller - assert(fullPathLength == fullPathLengthNoTerminator + 1); + // _not_ counting the null terminator, so fullPathLengthNoTerminator should be smaller + assert(fullPathLength > fullPathLengthNoTerminator); // Find out size of the converted string const retLength = WideCharToMultiByte(0 /*codepage*/, 0 /*flags*/, &fullPath[0], fullPathLength, null, 0, null, null); diff --git a/dmd/root/longdouble.d b/dmd/root/longdouble.d new file mode 100644 index 00000000000..234de1affbb --- /dev/null +++ b/dmd/root/longdouble.d @@ -0,0 +1,843 @@ +/* Copyright (c) 1999-2017 by Digital Mars + * All Rights Reserved, written by Rainer Schuetze + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + * https://github.com/dlang/dmd/blob/master/src/root/longdouble.d + */ + +// 80-bit floating point value implementation if the C/D compiler does not support them natively + +module dmd.root.longdouble; + +static if (real.sizeof > 8) + alias longdouble = real; +else + alias longdouble = longdouble_soft; + +// longdouble_soft needed when building the backend with +// Visual C or the frontend with LDC on Windows +version(CRuntime_Microsoft): +extern(C++): +nothrow: +@nogc: + +version(D_InlineAsm_X86_64) + version = AsmX86; +else version(D_InlineAsm_X86) + version = AsmX86; +else + static assert(false, "longdouble_soft not supported on this platform"); + +bool initFPU() +{ + version(D_InlineAsm_X86_64) + { + // set precision to 64-bit mantissa and rounding control to nearest + asm nothrow @nogc @trusted + { + push RAX; // add space on stack + fstcw word ptr [RSP]; + movzx EAX,word ptr [RSP]; // also return old CW in EAX + and EAX, ~0xF00; // mask for PC and RC + or EAX, 0x300; + mov dword ptr [RSP],EAX; + fldcw word ptr [RSP]; + pop RAX; + } + } + else version(D_InlineAsm_X86) + { + // set precision to 64-bit mantissa and rounding control to nearest + asm nothrow @nogc @trusted + { + push EAX; // add space on stack + fstcw word ptr [ESP]; + movzx EAX,word ptr [ESP]; // also return old CW in EAX + and EAX, ~0xF00; // mask for PC and RC + or EAX, 0x300; + mov dword ptr [ESP],EAX; + fldcw word ptr [ESP]; + pop EAX; + } + } + + return true; +} + +shared static this() +{ + initFPU(); +} + +void ld_clearfpu() +{ + version(AsmX86) + { + asm nothrow @nogc @trusted + { + fclex; + } + } +} + +pure: +@safe: + +align(2) struct longdouble_soft +{ +nothrow @nogc pure: + ulong mantissa = 0xC000000000000001UL; // default to snan + ushort exp_sign = 0x7fff; // sign is highest bit + + this(ulong m, ushort es) { mantissa = m; exp_sign = es; } + this(longdouble_soft ld) { mantissa = ld.mantissa; exp_sign = ld.exp_sign; } + this(int i) { ld_set(&this, i); } + this(uint i) { ld_set(&this, i); } + this(long i) { ld_setll(&this, i); } + this(ulong i) { ld_setull(&this, i); } + this(float f) { ld_set(&this, f); } + this(double d) + { + // allow zero initialization at compile time + if (__ctfe && d == 0) + { + mantissa = 0; + exp_sign = 0; + } + else + ld_set(&this, d); + } + this(real r) + { + static if (real.sizeof > 8) + *cast(real*)&this = r; + else + this(cast(double)r); + } + + ushort exponent() const { return exp_sign & 0x7fff; } + bool sign() const { return (exp_sign & 0x8000) != 0; } + + extern(D) + { + ref longdouble_soft opAssign(longdouble_soft ld) { mantissa = ld.mantissa; exp_sign = ld.exp_sign; return this; } + ref longdouble_soft opAssign(T)(T rhs) { this = longdouble_soft(rhs); return this; } + + longdouble_soft opNeg() const { return longdouble_soft(mantissa, exp_sign ^ 0x8000); } + + bool opEquals(T)(T rhs) const { return this.ld_cmpe(longdouble_soft(rhs)); } + int opCmp(T)(T rhs) const { return this.ld_cmp(longdouble_soft(rhs)); } + longdouble_soft opAdd(T)(T rhs) const { return this.ld_add(longdouble_soft(rhs)); } + longdouble_soft opSub(T)(T rhs) const { return this.ld_sub(longdouble_soft(rhs)); } + longdouble_soft opMul(T)(T rhs) const { return this.ld_mul(longdouble_soft(rhs)); } + longdouble_soft opDiv(T)(T rhs) const { return this.ld_div(longdouble_soft(rhs)); } + longdouble_soft opMod(T)(T rhs) const { return this.ld_mod(longdouble_soft(rhs)); } + longdouble_soft opAdd_r(T)(T rhs) const { return longdouble_soft(rhs).ld_add(this); } + longdouble_soft opSub_r(T)(T rhs) const { return longdouble_soft(rhs).ld_sub(this); } + longdouble_soft opMul_r(T)(T rhs) const { return longdouble_soft(rhs).ld_mul(this); } + longdouble_soft opMod_r(T)(T rhs) const { return longdouble_soft(rhs).ld_mod(this); } + + ref longdouble_soft opOpAssign(string op)(longdouble_soft rhs) + { + mixin("this = this " ~ op ~ " rhs;"); + return this; + } + + T opCast(T)() const @trusted + { + static if (is(T == bool)) return mantissa != 0 || (exp_sign & 0x7fff) != 0; + else static if (is(T == byte)) return cast(T)ld_read(&this); + else static if (is(T == ubyte)) return cast(T)ld_read(&this); + else static if (is(T == short)) return cast(T)ld_read(&this); + else static if (is(T == ushort)) return cast(T)ld_read(&this); + else static if (is(T == int)) return cast(T)ld_read(&this); + else static if (is(T == uint)) return cast(T)ld_read(&this); + else static if (is(T == float)) return cast(T)ld_read(&this); + else static if (is(T == double)) return cast(T)ld_read(&this); + else static if (is(T == long)) return ld_readll(&this); + else static if (is(T == ulong)) return ld_readull(&this); + else static if (is(T == real)) + { + // convert to front end real if built with dmd + if (real.sizeof > 8) + return *cast(real*)&this; + else + return ld_read(&this); + } + else static assert(false, "usupported type"); + } + } + + static longdouble_soft nan() { return longdouble_soft(0xC000000000000000UL, 0x7fff); } + static longdouble_soft infinity() { return longdouble_soft(0x8000000000000000UL, 0x7fff); } + static longdouble_soft zero() { return longdouble_soft(0, 0); } + static longdouble_soft max() { return longdouble_soft(0xffffffffffffffffUL, 0x7ffe); } + static longdouble_soft min_normal() { return longdouble_soft(0x8000000000000000UL, 1); } + static longdouble_soft epsilon() { return longdouble_soft(0x8000000000000000UL, 0x3fff - 63); } + + static uint dig() { return 18; } + static uint mant_dig() { return 64; } + static uint max_exp() { return 16384; } + static uint min_exp() { return -16381; } + static uint max_10_exp() { return 4932; } + static uint min_10_exp() { return -4932; } +}; + +version(LDC) +{ + enum fld_eax(string arg) = "jmp L_" ~ arg ~ "+2; L_" ~ arg ~ ": mov DX, 0x28db;"; // jump to 0xdb,0x28 (fld real ptr[EAX]) + enum fstp_eax = "jmp L_stp+2; L_stp: mov DX, 0x38db;"; // jump to 0xdb,0x38 (fstp real ptr[EAX]) +} +else version(D_InlineAsm_X86_64) +{ + enum fld_eax(string arg) = "fld real ptr [RAX];"; + enum fstp_eax = "fstp real ptr [RAX];"; +} +else version(D_InlineAsm_X86) +{ + enum fld_eax(string arg) = "fld real ptr [EAX];"; + enum fstp_eax = "fstp real ptr [EAX];"; +} + +version(D_InlineAsm_X86_64) +{ + // longdouble_soft passed by reference + extern(D): + private: + string fld_arg(string arg)() + { + return "asm nothrow @nogc pure @trusted { mov RAX, " ~ arg ~ "; " ~ fld_eax!arg ~ " }"; + } + string fstp_arg(string arg)() + { + return "asm nothrow @nogc pure @trusted { mov RAX, " ~ arg ~ "; " ~ fstp_eax ~ " }"; + } + alias fld_parg = fld_arg; + alias fstp_parg = fstp_arg; + string fld_local(string arg)() + { + return "asm nothrow @nogc pure @trusted { lea RAX, " ~ arg ~ "; " ~ fld_eax!arg ~ " }"; + } +} +else version(D_InlineAsm_X86) +{ + // longdouble_soft passed by value + extern(D): + private: + string fld_arg(string arg)() + { + return "asm nothrow @nogc pure @trusted { lea EAX, " ~ arg ~ "; " ~ fld_eax!arg ~ " }"; + } + string fstp_arg(string arg)() + { + return "asm nothrow @nogc pure @trusted { lea EAX, " ~ arg ~ "; " ~ fstp_eax ~ " }"; + } + string fld_parg(string arg)() + { + return "asm nothrow @nogc pure @trusted { mov EAX, " ~ arg ~ "; " ~ fld_eax!arg ~ " }"; + } + string fstp_parg(string arg)() + { + return "asm nothrow @nogc pure @trusted { mov EAX, " ~ arg ~ "; " ~ fstp_eax ~ " }"; + } + alias fld_local = fld_arg; +} + +double ld_read(const longdouble_soft* pthis) +{ + double res; + version(AsmX86) + { + mixin(fld_parg!("pthis")); + asm nothrow @nogc pure @trusted + { + fstp res; + } + } + return res; +} + +long ld_readll(const longdouble_soft* pthis) +{ + return ld_readull(pthis); +} + +ulong ld_readull(const longdouble_soft* pthis) +{ + // somehow the FPU does not respect the CHOP mode of the rounding control + // in 64-bit mode + // so we roll our own conversion (it also allows the usual C wrap-around + // instead of the "invalid value" created by the FPU) + int expo = pthis.exponent - 0x3fff; + ulong u; + if(expo < 0 || expo > 127) + return 0; + if(expo < 64) + u = pthis.mantissa >> (63 - expo); + else + u = pthis.mantissa << (expo - 63); + if(pthis.sign) + u = ~u + 1; + return u; +} + +int ld_statusfpu() +{ + int res = 0; + version(AsmX86) + { + asm nothrow @nogc pure @trusted + { + fstsw word ptr [res]; + } + } + return res; +} + +void ld_set(longdouble_soft* pthis, double d) +{ + version(AsmX86) + { + asm nothrow @nogc pure @trusted + { + fld d; + } + mixin(fstp_parg!("pthis")); + } +} + +void ld_setll(longdouble_soft* pthis, long d) +{ + version(AsmX86) + { + asm nothrow @nogc pure @trusted + { + fild qword ptr d; + } + mixin(fstp_parg!("pthis")); + } +} + +void ld_setull(longdouble_soft* pthis, ulong d) +{ + d ^= (1L << 63); + version(AsmX86) + { + mixin(fld_local!("twoPow63")); + asm nothrow @nogc pure @trusted + { + fild qword ptr d; + faddp; + } + mixin(fstp_parg!("pthis")); + } +} + +// using an argument as result to avoid RVO, see https://issues.dlang.org/show_bug.cgi?id=18758 +longdouble_soft ldexpl(longdouble_soft ld, int exp) +{ + version(AsmX86) + { + asm nothrow @nogc pure @trusted + { + fild dword ptr exp; + } + mixin(fld_arg!("ld")); + asm nothrow @nogc pure @trusted + { + fscale; // ST(0) = ST(0) * (2**ST(1)) + fstp ST(1); + } + mixin(fstp_arg!("ld")); + } + return ld; +} + +/////////////////////////////////////////////////////////////////////// +longdouble_soft ld_add(longdouble_soft ld1, longdouble_soft ld2) +{ + version(AsmX86) + { + mixin(fld_arg!("ld1")); + mixin(fld_arg!("ld2")); + asm nothrow @nogc pure @trusted + { + fadd; + } + mixin(fstp_arg!("ld1")); + } + return ld1; +} + +longdouble_soft ld_sub(longdouble_soft ld1, longdouble_soft ld2) +{ + version(AsmX86) + { + mixin(fld_arg!("ld1")); + mixin(fld_arg!("ld2")); + asm nothrow @nogc pure @trusted + { + fsub; + } + mixin(fstp_arg!("ld1")); + } + return ld1; +} + +longdouble_soft ld_mul(longdouble_soft ld1, longdouble_soft ld2) +{ + version(AsmX86) + { + mixin(fld_arg!("ld1")); + mixin(fld_arg!("ld2")); + asm nothrow @nogc pure @trusted + { + fmul; + } + mixin(fstp_arg!("ld1")); + } + return ld1; +} + +longdouble_soft ld_div(longdouble_soft ld1, longdouble_soft ld2) +{ + version(AsmX86) + { + mixin(fld_arg!("ld1")); + mixin(fld_arg!("ld2")); + asm nothrow @nogc pure @trusted + { + fdiv; + } + mixin(fstp_arg!("ld1")); + } + return ld1; +} + +bool ld_cmpb(longdouble_soft x, longdouble_soft y) +{ + short sw; + bool res; + version(AsmX86) + { + mixin(fld_arg!("y")); + mixin(fld_arg!("x")); + asm nothrow @nogc pure @trusted + { + fucomip ST(1); + setb AL; + setnp AH; + and AL,AH; + mov res,AL; + fstp ST(0); + } + } + return res; +} + +bool ld_cmpbe(longdouble_soft x, longdouble_soft y) +{ + short sw; + bool res; + version(AsmX86) + { + mixin(fld_arg!("y")); + mixin(fld_arg!("x")); + asm nothrow @nogc pure @trusted + { + fucomip ST(1); + setbe AL; + setnp AH; + and AL,AH; + mov res,AL; + fstp ST(0); + } + } + return res; +} + +bool ld_cmpa(longdouble_soft x, longdouble_soft y) +{ + short sw; + bool res; + version(AsmX86) + { + mixin(fld_arg!("y")); + mixin(fld_arg!("x")); + asm nothrow @nogc pure @trusted + { + fucomip ST(1); + seta AL; + setnp AH; + and AL,AH; + mov res,AL; + fstp ST(0); + } + } + return res; +} + +bool ld_cmpae(longdouble_soft x, longdouble_soft y) +{ + short sw; + bool res; + version(AsmX86) + { + mixin(fld_arg!("y")); + mixin(fld_arg!("x")); + asm nothrow @nogc pure @trusted + { + fucomip ST(1); + setae AL; + setnp AH; + and AL,AH; + mov res,AL; + fstp ST(0); + } + } + return res; +} + +bool ld_cmpe(longdouble_soft x, longdouble_soft y) +{ + short sw; + bool res; + version(AsmX86) + { + mixin(fld_arg!("y")); + mixin(fld_arg!("x")); + asm nothrow @nogc pure @trusted + { + fucomip ST(1); + sete AL; + setnp AH; + and AL,AH; + mov res,AL; + fstp ST(0); + } + } + return res; +} + +bool ld_cmpne(longdouble_soft x, longdouble_soft y) +{ + short sw; + bool res; + version(AsmX86) + { + mixin(fld_arg!("y")); + mixin(fld_arg!("x")); + asm nothrow @nogc pure @trusted + { + fucomip ST(1); + setne AL; + setp AH; + or AL,AH; + mov res,AL; + fstp ST(0); + } + } + return res; +} + +int ld_cmp(longdouble_soft x, longdouble_soft y) +{ + // return -1 if x < y, 0 if x == y or unordered, 1 if x > y + short sw; + int res; + version(AsmX86) + { + mixin(fld_arg!("y")); + mixin(fld_arg!("x")); + asm nothrow @nogc pure @trusted + { + fucomip ST(1); + seta AL; + setb AH; + setp DL; + or AL, DL; + or AH, DL; + sub AL, AH; + movsx EAX, AL; + fstp ST(0); + mov res, EAX; + } + } +} + + +int _isnan(longdouble_soft ld) +{ + return (ld.exponent == 0x7fff && ld.mantissa != 0 && ld.mantissa != (1L << 63)); // exclude pseudo-infinity and infinity, but not FP Indefinite +} + +longdouble_soft fabsl(longdouble_soft ld) +{ + ld.exp_sign = ld.exponent; + return ld; +} + +longdouble_soft sqrtl(longdouble_soft ld) +{ + version(AsmX86) + { + mixin(fld_arg!("ld")); + asm nothrow @nogc pure @trusted + { + fsqrt; + } + mixin(fstp_arg!("ld")); + } + return ld; +} + +longdouble_soft sqrt(longdouble_soft ld) { return sqrtl(ld); } + +longdouble_soft sinl (longdouble_soft ld) +{ + version(AsmX86) + { + mixin(fld_arg!("ld")); + asm nothrow @nogc pure @trusted + { + fsin; // exact for |x|<=PI/4 + } + mixin(fstp_arg!("ld")); + } + return ld; +} +longdouble_soft cosl (longdouble_soft ld) +{ + version(AsmX86) + { + mixin(fld_arg!("ld")); + asm nothrow @nogc pure @trusted + { + fcos; // exact for |x|<=PI/4 + } + mixin(fstp_arg!("ld")); + } + return ld; +} +longdouble_soft tanl (longdouble_soft ld) +{ + version(AsmX86) + { + mixin(fld_arg!("ld")); + asm nothrow @nogc pure @trusted + { + fptan; + fstp ST(0); // always 1 + } + mixin(fstp_arg!("ld")); + } + return ld; +} + +longdouble_soft fmodl(longdouble_soft x, longdouble_soft y) +{ + return ld_mod(x, y); +} + +longdouble_soft ld_mod(longdouble_soft x, longdouble_soft y) +{ + short sw; + version(AsmX86) + { + mixin(fld_arg!("y")); + mixin(fld_arg!("x")); + asm nothrow @nogc pure @trusted + { + FM1: // We don't use fprem1 because for some inexplicable + // reason we get -5 when we do _modulo(15, 10) + fprem; // ST = ST % ST1 + fstsw word ptr sw; + fwait; + mov AH,byte ptr sw+1; // get msb of status word in AH + sahf; // transfer to flags + jp FM1; // continue till ST < ST1 + fstp ST(1); // leave remainder on stack + } + mixin(fstp_arg!("x")); + } + return x; +} + +////////////////////////////////////////////////////////////// + +__gshared longdouble_soft ld_qnan = longdouble_soft(0xC000000000000000UL, 0x7fff); +__gshared longdouble_soft ld_snan = longdouble_soft(0xC000000000000001UL, 0x7fff); +__gshared longdouble_soft ld_inf = longdouble_soft(0x8000000000000000UL, 0x7fff); + +__gshared longdouble_soft ld_zero = longdouble_soft(0, 0); +__gshared longdouble_soft ld_one = longdouble_soft(0x8000000000000000UL, 0x3fff); +__gshared longdouble_soft ld_pi = longdouble_soft(0xc90fdaa22168c235UL, 0x4000); +__gshared longdouble_soft ld_log2t = longdouble_soft(0xd49a784bcd1b8afeUL, 0x4000); +__gshared longdouble_soft ld_log2e = longdouble_soft(0xb8aa3b295c17f0bcUL, 0x3fff); +__gshared longdouble_soft ld_log2 = longdouble_soft(0x9a209a84fbcff799UL, 0x3ffd); +__gshared longdouble_soft ld_ln2 = longdouble_soft(0xb17217f7d1cf79acUL, 0x3ffe); + +__gshared longdouble_soft ld_pi2 = longdouble_soft(0xc90fdaa22168c235UL, 0x4001); +__gshared longdouble_soft ld_piOver2 = longdouble_soft(0xc90fdaa22168c235UL, 0x3fff); +__gshared longdouble_soft ld_piOver4 = longdouble_soft(0xc90fdaa22168c235UL, 0x3ffe); + +__gshared longdouble_soft twoPow63 = longdouble_soft(1UL << 63, 0x3fff + 63); + +////////////////////////////////////////////////////////////// + +enum LD_TYPE_OTHER = 0; +enum LD_TYPE_ZERO = 1; +enum LD_TYPE_INFINITE = 2; +enum LD_TYPE_SNAN = 3; +enum LD_TYPE_QNAN = 4; + +int ld_type(longdouble_soft x) +{ + if(x.exponent == 0) + return x.mantissa == 0 ? LD_TYPE_ZERO : LD_TYPE_OTHER; // dnormal if not zero + if(x.exponent != 0x7fff) + return LD_TYPE_OTHER; + if(x.mantissa == 0) + return LD_TYPE_INFINITE; + if(x.mantissa & (1L << 63)) + return LD_TYPE_QNAN; + return LD_TYPE_SNAN; +} + +// consider sprintf pure +private extern(C) int sprintf(scope char* s, scope const char* format, ...) pure @nogc nothrow; + +size_t ld_sprint(char* str, int fmt, longdouble_soft x) @system +{ + // ensure dmc compatible strings for nan and inf + switch(ld_type(x)) + { + case LD_TYPE_QNAN: + case LD_TYPE_SNAN: + return sprintf(str, "nan"); + case LD_TYPE_INFINITE: + return sprintf(str, x.sign ? "-inf" : "inf"); + default: + break; + } + + // fmt is 'a','A','f' or 'g' + if(fmt != 'a' && fmt != 'A') + { + if (longdouble_soft(ld_readull(&x)) == x) + { // ((1.5 -> 1 -> 1.0) == 1.5) is false + // ((1.0 -> 1 -> 1.0) == 1.0) is true + // see http://en.cppreference.com/w/cpp/io/c/fprintf + char[5] format = ['%', '#', 'L', cast(char)fmt, 0]; + return sprintf(str, format.ptr, ld_read(&x)); + } + char[3] format = ['%', cast(char)fmt, 0]; + return sprintf(str, format.ptr, ld_read(&x)); + } + + ushort exp = x.exponent; + ulong mantissa = x.mantissa; + + if(ld_type(x) == LD_TYPE_ZERO) + return sprintf(str, fmt == 'a' ? "0x0.0L" : "0X0.0L"); + + size_t len = 0; + if(x.sign) + str[len++] = '-'; + str[len++] = '0'; + str[len++] = cast(char)('X' + fmt - 'A'); + str[len++] = mantissa & (1L << 63) ? '1' : '0'; + str[len++] = '.'; + mantissa = mantissa << 1; + while(mantissa) + { + int dig = (mantissa >> 60) & 0xf; + dig += dig < 10 ? '0' : fmt - 10; + str[len++] = cast(char)dig; + mantissa = mantissa << 4; + } + str[len++] = cast(char)('P' + fmt - 'A'); + if(exp < 0x3fff) + { + str[len++] = '-'; + exp = cast(ushort)(0x3fff - exp); + } + else + { + str[len++] = '+'; + exp = cast(ushort)(exp - 0x3fff); + } + size_t exppos = len; + for(int i = 12; i >= 0; i -= 4) + { + int dig = (exp >> i) & 0xf; + if(dig != 0 || len > exppos || i == 0) + str[len++] = cast(char)(dig + (dig < 10 ? '0' : fmt - 10)); + } + str[len] = 0; + return len; +} + +////////////////////////////////////////////////////////////// + +unittest +{ + import core.stdc.string; + import core.stdc.stdio; + + char[32] buffer; + ld_sprint(buffer.ptr, 'a', ld_pi); + assert(strcmp(buffer.ptr, "0x1.921fb54442d1846ap+1") == 0); + + ld_sprint(buffer.ptr, 'g', longdouble_soft(2.0)); + assert(strcmp(buffer.ptr, "2.00000") == 0); + + ld_sprint(buffer.ptr, 'g', longdouble_soft(1234567.89)); + assert(strcmp(buffer.ptr, "1.23457e+06") == 0); + + longdouble_soft ldb = longdouble_soft(0.4); + long b = cast(long)ldb; + assert(b == 0); + + b = cast(long)longdouble_soft(0.9); + assert(b == 0); + + long x = 0x12345678abcdef78L; + longdouble_soft ldx = longdouble_soft(x); + assert(ldx > ld_zero); + long y = cast(long)ldx; + assert(x == y); + + x = -0x12345678abcdef78L; + ldx = longdouble_soft(x); + assert(ldx < ld_zero); + y = cast(long)ldx; + assert(x == y); + + ulong u = 0x12345678abcdef78L; + longdouble_soft ldu = longdouble_soft(u); + assert(ldu > ld_zero); + ulong v = cast(ulong)ldu; + assert(u == v); + + u = 0xf234567812345678UL; + ldu = longdouble_soft(u); + assert(ldu > ld_zero); + v = cast(ulong)ldu; + assert(u == v); + + u = 0xf2345678; + ldu = longdouble_soft(u); + ldu = ldu * ldu; + ldu = sqrt(ldu); + v = cast(ulong)ldu; + assert(u == v); + + u = 0x123456789A; + ldu = longdouble_soft(u); + ldu = ldu * longdouble_soft(1L << 23); + v = cast(ulong)ldu; + u = u * (1L << 23); + assert(u == v); +} diff --git a/dmd/root/longdouble.h b/dmd/root/longdouble.h index e783ec0aa21..eb82617c7cd 100644 --- a/dmd/root/longdouble.h +++ b/dmd/root/longdouble.h @@ -54,44 +54,51 @@ inline size_t ld_sprint(char* str, int fmt, longdouble x) #include #include -struct longdouble; - -extern "C" -{ - // implemented in ldfpu.asm for _WIN64 - double ld_read(const longdouble* ld); - long long ld_readll(const longdouble* ld); - unsigned long long ld_readull(const longdouble* ld); - void ld_set(longdouble* ld, double d); - void ld_setll(longdouble* ld, long long d); - void ld_setull(longdouble* ld, unsigned long long d); - int ld_statusfpu(); - void ld_clearfpu(); - int ld_initfpu(int bits, int mask); - - void ld_expl(longdouble* ld, int exp); - bool ld_cmpb(longdouble ld1, longdouble ld2); - bool ld_cmpbe(longdouble ld1, longdouble ld2); - bool ld_cmpa(longdouble ld1, longdouble ld2); - bool ld_cmpae(longdouble ld1, longdouble ld2); - bool ld_cmpe(longdouble ld1, longdouble ld2); - bool ld_cmpne(longdouble ld1, longdouble ld2); - int ld_cmp(longdouble x, longdouble y); -} +struct longdouble_soft; + +// implemented in longdouble.d +double ld_read(const longdouble_soft* const ld); +long long ld_readll(const longdouble_soft* const ld); +unsigned long long ld_readull(const longdouble_soft* const ld); +void ld_set(longdouble_soft* ld, double d); +void ld_setll(longdouble_soft* ld, long long d); +void ld_setull(longdouble_soft* ld, unsigned long long d); +int ld_statusfpu(); +void ld_clearfpu(); +int ld_initfpu(int bits, int mask); + +void ld_expl(longdouble_soft* ld, int exp); +bool ld_cmpb(longdouble_soft ld1, longdouble_soft ld2); +bool ld_cmpbe(longdouble_soft ld1, longdouble_soft ld2); +bool ld_cmpa(longdouble_soft ld1, longdouble_soft ld2); +bool ld_cmpae(longdouble_soft ld1, longdouble_soft ld2); +bool ld_cmpe(longdouble_soft ld1, longdouble_soft ld2); +bool ld_cmpne(longdouble_soft ld1, longdouble_soft ld2); +int ld_cmp(longdouble_soft x, longdouble_soft y); + +longdouble_soft ld_add(longdouble_soft ld1, longdouble_soft ld2); +longdouble_soft ld_sub(longdouble_soft ld1, longdouble_soft ld2); +longdouble_soft ld_mul(longdouble_soft ld1, longdouble_soft ld2); +longdouble_soft ld_div(longdouble_soft ld1, longdouble_soft ld2); +longdouble_soft ld_mod(longdouble_soft ld1, longdouble_soft ld2); +longdouble_soft ld_sqrt(longdouble_soft ld1); +longdouble_soft ld_sin(longdouble_soft ld1); +longdouble_soft ld_cos(longdouble_soft ld1); +longdouble_soft ld_tan(longdouble_soft ld1); #pragma pack(push, 1) -struct longdouble +struct longdouble_soft { unsigned long long mantissa; unsigned short exponent:15; // bias 0x3fff unsigned short sign:1; // no constructor to be able to use this class in a union - // use ldouble() to explicitly create a longdouble value + // use ldouble() to explicitly create a longdouble_soft value - template longdouble& operator=(T x) { set(x); return *this; } + template longdouble_soft& operator=(T x) { set(x); return *this; } - void set(longdouble ld) { mantissa = ld.mantissa; exponent = ld.exponent; sign = ld.sign; } + void set(longdouble_soft ld) { mantissa = ld.mantissa; exponent = ld.exponent; sign = ld.sign; } // we need to list all basic types to avoid ambiguities void set(float d) { ld_set(this, d); } @@ -129,17 +136,11 @@ struct longdouble }; #pragma pack(pop) -// static_assert(sizeof(longdouble) == 10, "bad sizeof longdouble"); - -// some optimizations are avoided by adding volatile to the longdouble -// type, but this introduces bad ambiguities when using the class implementation above -// as we are going through asm these optimizations won't kick in anyway, so "volatile" -// is not required. -typedef longdouble volatile_longdouble; +// static_assert(sizeof(longdouble_soft) == 10, "bad sizeof longdouble_soft"); -inline longdouble ldouble(unsigned long long mantissa, int exp, int sign = 0) +inline longdouble_soft ldouble(unsigned long long mantissa, int exp, int sign = 0) { - longdouble d; + longdouble_soft d; d.mantissa = mantissa; d.exponent = exp; d.sign = sign; @@ -154,67 +155,67 @@ inline longdouble ldouble(unsigned long long mantissa, int exp, int sign = 0) #define LDOUBLE_INLINE inline #endif -template LDOUBLE_INLINE longdouble ldouble(T x) { longdouble d; d.set(x); return d; } +template LDOUBLE_INLINE longdouble_soft ldouble(T x) { longdouble_soft d; d.set(x); return d; } #undef LDOUBLE_INLINE -longdouble operator+(longdouble ld1, longdouble ld2); -longdouble operator-(longdouble ld1, longdouble ld2); -longdouble operator*(longdouble ld1, longdouble ld2); -longdouble operator/(longdouble ld1, longdouble ld2); - -inline bool operator< (longdouble ld1, longdouble ld2) { return ld_cmpb(ld1, ld2); } -inline bool operator<=(longdouble ld1, longdouble ld2) { return ld_cmpbe(ld1, ld2); } -inline bool operator> (longdouble ld1, longdouble ld2) { return ld_cmpa(ld1, ld2); } -inline bool operator>=(longdouble ld1, longdouble ld2) { return ld_cmpae(ld1, ld2); } -inline bool operator==(longdouble ld1, longdouble ld2) { return ld_cmpe(ld1, ld2); } -inline bool operator!=(longdouble ld1, longdouble ld2) { return ld_cmpne(ld1, ld2); } - -inline longdouble operator-(longdouble ld1) { ld1.sign ^= 1; return ld1; } -inline longdouble operator+(longdouble ld1) { return ld1; } - -template inline longdouble operator+(longdouble ld, T x) { return ld + ldouble(x); } -template inline longdouble operator-(longdouble ld, T x) { return ld - ldouble(x); } -template inline longdouble operator*(longdouble ld, T x) { return ld * ldouble(x); } -template inline longdouble operator/(longdouble ld, T x) { return ld / ldouble(x); } - -template inline longdouble operator+(T x, longdouble ld) { return ldouble(x) + ld; } -template inline longdouble operator-(T x, longdouble ld) { return ldouble(x) - ld; } -template inline longdouble operator*(T x, longdouble ld) { return ldouble(x) * ld; } -template inline longdouble operator/(T x, longdouble ld) { return ldouble(x) / ld; } - -template inline longdouble& operator+=(longdouble& ld, T x) { return ld = ld + x; } -template inline longdouble& operator-=(longdouble& ld, T x) { return ld = ld - x; } -template inline longdouble& operator*=(longdouble& ld, T x) { return ld = ld * x; } -template inline longdouble& operator/=(longdouble& ld, T x) { return ld = ld / x; } - -template inline bool operator< (longdouble ld, T x) { return ld < ldouble(x); } -template inline bool operator<=(longdouble ld, T x) { return ld <= ldouble(x); } -template inline bool operator> (longdouble ld, T x) { return ld > ldouble(x); } -template inline bool operator>=(longdouble ld, T x) { return ld >= ldouble(x); } -template inline bool operator==(longdouble ld, T x) { return ld == ldouble(x); } -template inline bool operator!=(longdouble ld, T x) { return ld != ldouble(x); } - -template inline bool operator< (T x, longdouble ld) { return ldouble(x) < ld; } -template inline bool operator<=(T x, longdouble ld) { return ldouble(x) <= ld; } -template inline bool operator> (T x, longdouble ld) { return ldouble(x) > ld; } -template inline bool operator>=(T x, longdouble ld) { return ldouble(x) >= ld; } -template inline bool operator==(T x, longdouble ld) { return ldouble(x) == ld; } -template inline bool operator!=(T x, longdouble ld) { return ldouble(x) != ld; } - -int _isnan(longdouble ld); - -longdouble fabsl(longdouble ld); -longdouble sqrtl(longdouble ld); -longdouble sinl (longdouble ld); -longdouble cosl (longdouble ld); -longdouble tanl (longdouble ld); - -longdouble fmodl(longdouble x, longdouble y); -longdouble ldexpl(longdouble ldval, int exp); // see strtold - -inline longdouble fabs (longdouble ld) { return fabsl(ld); } -inline longdouble sqrt (longdouble ld) { return sqrtl(ld); } +inline longdouble_soft operator+(longdouble_soft ld1, longdouble_soft ld2) { return ld_add(ld1, ld2); } +inline longdouble_soft operator-(longdouble_soft ld1, longdouble_soft ld2) { return ld_sub(ld1, ld2); } +inline longdouble_soft operator*(longdouble_soft ld1, longdouble_soft ld2) { return ld_mul(ld1, ld2); } +inline longdouble_soft operator/(longdouble_soft ld1, longdouble_soft ld2) { return ld_div(ld1, ld2); } + +inline bool operator< (longdouble_soft ld1, longdouble_soft ld2) { return ld_cmpb(ld1, ld2); } +inline bool operator<=(longdouble_soft ld1, longdouble_soft ld2) { return ld_cmpbe(ld1, ld2); } +inline bool operator> (longdouble_soft ld1, longdouble_soft ld2) { return ld_cmpa(ld1, ld2); } +inline bool operator>=(longdouble_soft ld1, longdouble_soft ld2) { return ld_cmpae(ld1, ld2); } +inline bool operator==(longdouble_soft ld1, longdouble_soft ld2) { return ld_cmpe(ld1, ld2); } +inline bool operator!=(longdouble_soft ld1, longdouble_soft ld2) { return ld_cmpne(ld1, ld2); } + +inline longdouble_soft operator-(longdouble_soft ld1) { ld1.sign ^= 1; return ld1; } +inline longdouble_soft operator+(longdouble_soft ld1) { return ld1; } + +template inline longdouble_soft operator+(longdouble_soft ld, T x) { return ld + ldouble(x); } +template inline longdouble_soft operator-(longdouble_soft ld, T x) { return ld - ldouble(x); } +template inline longdouble_soft operator*(longdouble_soft ld, T x) { return ld * ldouble(x); } +template inline longdouble_soft operator/(longdouble_soft ld, T x) { return ld / ldouble(x); } + +template inline longdouble_soft operator+(T x, longdouble_soft ld) { return ldouble(x) + ld; } +template inline longdouble_soft operator-(T x, longdouble_soft ld) { return ldouble(x) - ld; } +template inline longdouble_soft operator*(T x, longdouble_soft ld) { return ldouble(x) * ld; } +template inline longdouble_soft operator/(T x, longdouble_soft ld) { return ldouble(x) / ld; } + +template inline longdouble_soft& operator+=(longdouble_soft& ld, T x) { return ld = ld + x; } +template inline longdouble_soft& operator-=(longdouble_soft& ld, T x) { return ld = ld - x; } +template inline longdouble_soft& operator*=(longdouble_soft& ld, T x) { return ld = ld * x; } +template inline longdouble_soft& operator/=(longdouble_soft& ld, T x) { return ld = ld / x; } + +template inline bool operator< (longdouble_soft ld, T x) { return ld < ldouble(x); } +template inline bool operator<=(longdouble_soft ld, T x) { return ld <= ldouble(x); } +template inline bool operator> (longdouble_soft ld, T x) { return ld > ldouble(x); } +template inline bool operator>=(longdouble_soft ld, T x) { return ld >= ldouble(x); } +template inline bool operator==(longdouble_soft ld, T x) { return ld == ldouble(x); } +template inline bool operator!=(longdouble_soft ld, T x) { return ld != ldouble(x); } + +template inline bool operator< (T x, longdouble_soft ld) { return ldouble(x) < ld; } +template inline bool operator<=(T x, longdouble_soft ld) { return ldouble(x) <= ld; } +template inline bool operator> (T x, longdouble_soft ld) { return ldouble(x) > ld; } +template inline bool operator>=(T x, longdouble_soft ld) { return ldouble(x) >= ld; } +template inline bool operator==(T x, longdouble_soft ld) { return ldouble(x) == ld; } +template inline bool operator!=(T x, longdouble_soft ld) { return ldouble(x) != ld; } + +int _isnan(longdouble_soft ld); + +longdouble_soft fabsl(longdouble_soft ld); +longdouble_soft sqrtl(longdouble_soft ld); +longdouble_soft sinl (longdouble_soft ld); +longdouble_soft cosl (longdouble_soft ld); +longdouble_soft tanl (longdouble_soft ld); + +longdouble_soft fmodl(longdouble_soft x, longdouble_soft y); +longdouble_soft ldexpl(longdouble_soft ldval, int exp); // see strtold + +inline longdouble_soft fabs (longdouble_soft ld) { return fabsl(ld); } +inline longdouble_soft sqrt (longdouble_soft ld) { return sqrtl(ld); } #undef LDBL_DIG #undef LDBL_MAX @@ -236,19 +237,28 @@ inline longdouble sqrt (longdouble ld) { return sqrtl(ld); } #define LDBL_MAX_10_EXP 4932 #define LDBL_MIN_10_EXP (-4932) -extern longdouble ld_zero; -extern longdouble ld_one; -extern longdouble ld_pi; -extern longdouble ld_log2t; -extern longdouble ld_log2e; -extern longdouble ld_log2; -extern longdouble ld_ln2; +extern longdouble_soft ld_zero; +extern longdouble_soft ld_one; +extern longdouble_soft ld_pi; +extern longdouble_soft ld_log2t; +extern longdouble_soft ld_log2e; +extern longdouble_soft ld_log2; +extern longdouble_soft ld_ln2; + +extern longdouble_soft ld_inf; +extern longdouble_soft ld_qnan; +extern longdouble_soft ld_snan; + +size_t ld_sprint(char* str, int fmt, longdouble_soft x); -extern longdouble ld_inf; -extern longdouble ld_qnan; -extern longdouble ld_snan; +////////////////////////////////////////////// +typedef longdouble_soft longdouble; -size_t ld_sprint(char* str, int fmt, longdouble x); +// some optimizations are avoided by adding volatile to the longdouble_soft +// type, but this introduces bad ambiguities when using the class implementation above +// as we are going through asm these optimizations won't kick in anyway, so "volatile" +// is not required. +typedef longdouble_soft volatile_longdouble; #endif // !_MSC_VER diff --git a/dmd/root/stringtable.d b/dmd/root/stringtable.d index fe19c0accc2..0452e791f3e 100644 --- a/dmd/root/stringtable.d +++ b/dmd/root/stringtable.d @@ -103,7 +103,7 @@ public: pools = null; } - extern (C++) StringValue* lookup(const(char)* s, size_t length) nothrow pure + extern (C++) inout(StringValue)* lookup(const(char)* s, size_t length) inout nothrow pure { const(hash_t) hash = calcHash(s, length); const(size_t) i = findSlot(hash, s, length); @@ -194,16 +194,16 @@ nothrow: return vptr; } - StringValue* getValue(uint vptr) pure + inout(StringValue)* getValue(uint vptr) inout pure { if (!vptr) return null; const(size_t) idx = (vptr >> POOL_BITS) - 1; const(size_t) off = vptr & POOL_SIZE - 1; - return cast(StringValue*)&pools[idx][off]; + return cast(inout(StringValue)*)&pools[idx][off]; } - size_t findSlot(hash_t hash, const(char)* s, size_t length) pure + size_t findSlot(hash_t hash, const(char)* s, size_t length) const pure { // quadratic probing using triangular numbers // http://stackoverflow.com/questions/2348187/moving-from-linear-probing-to-quadratic-probing-hash-collisons/2349774#2349774 diff --git a/dmd/sapply.d b/dmd/sapply.d index 1bccc0bc6fb..8cc7e90100f 100644 --- a/dmd/sapply.d +++ b/dmd/sapply.d @@ -27,7 +27,7 @@ import dmd.visitor; */ extern (C++) final class PostorderStatementVisitor : StoppableVisitor { - alias visit = super.visit; + alias visit = typeof(super).visit; public: StoppableVisitor v; diff --git a/dmd/semantic3.d b/dmd/semantic3.d index d760d3cc525..3ed5933ba61 100644 --- a/dmd/semantic3.d +++ b/dmd/semantic3.d @@ -281,12 +281,12 @@ private extern(C++) final class Semantic3Visitor : Visitor uint oldErrors = global.errors; - if (funcdecl.frequire) + if (funcdecl.frequires) { for (size_t i = 0; i < funcdecl.foverrides.dim; i++) { FuncDeclaration fdv = funcdecl.foverrides[i]; - if (fdv.fbody && !fdv.frequire) + if (fdv.fbody && !fdv.frequires) { funcdecl.error("cannot have an in contract when overridden function `%s` does not have an in contract", fdv.toPrettyChars()); break; @@ -297,7 +297,7 @@ private extern(C++) final class Semantic3Visitor : Visitor // Remember whether we need to generate an 'out' contract. immutable bool needEnsure = FuncDeclaration.needsFensure(funcdecl); - if (funcdecl.fbody || funcdecl.frequire || needEnsure) + if (funcdecl.fbody || funcdecl.frequires || needEnsure) { /* Symbol table into which we place parameters and nested functions, * solely to diagnose name collisions. @@ -913,7 +913,7 @@ private extern(C++) final class Semantic3Visitor : Visitor } funcdecl.frequire = funcdecl.mergeFrequire(funcdecl.frequire); - funcdecl.fensure = funcdecl.mergeFensure(funcdecl.fensure, funcdecl.outId); + funcdecl.fensure = funcdecl.mergeFensure(funcdecl.fensure, Id.result); Statement freq = funcdecl.frequire; Statement fens = funcdecl.fensure; @@ -949,8 +949,17 @@ private extern(C++) final class Semantic3Visitor : Visitor { /* fensure is composed of the [out] contracts */ - if (f.next.ty == Tvoid && funcdecl.outId) - funcdecl.error("`void` functions have no result"); + if (f.next.ty == Tvoid && funcdecl.fensures) + { + foreach (e; *funcdecl.fensures) + { + if (e.id) + { + funcdecl.error(e.ensure.loc, "`void` functions have no result"); + //fens = null; + } + } + } sc2 = scout; //push sc2.flags = (sc2.flags & ~SCOPE.contract) | SCOPE.ensure; @@ -1169,7 +1178,7 @@ else } } - if (funcdecl.naked && (funcdecl.fensure || funcdecl.frequire)) + if (funcdecl.naked && (funcdecl.fensures || funcdecl.frequires)) funcdecl.error("naked assembly functions with contracts are not supported"); sc2.ctorflow.callSuper = CSX.none; diff --git a/dmd/sideeffect.d b/dmd/sideeffect.d index 27b7e9a26f0..5fd061ef44d 100644 --- a/dmd/sideeffect.d +++ b/dmd/sideeffect.d @@ -35,7 +35,7 @@ extern (C++) bool isTrivialExp(Expression e) { extern (C++) final class IsTrivialExp : StoppableVisitor { - alias visit = super.visit; + alias visit = typeof(super).visit; public: extern (D) this() { @@ -68,7 +68,7 @@ extern (C++) bool hasSideEffect(Expression e) { extern (C++) final class LambdaHasSideEffect : StoppableVisitor { - alias visit = super.visit; + alias visit = typeof(super).visit; public: extern (D) this() { @@ -329,10 +329,8 @@ extern (C++) bool discardValue(Expression e) * no side effect). * See https://issues.dlang.org/show_bug.cgi?id=4231 for discussion */ - CommaExp firstComma = ce; - while (firstComma.e1.op == TOK.comma) - firstComma = cast(CommaExp)firstComma.e1; - if (firstComma.e1.op == TOK.declaration && ce.e2.op == TOK.variable && (cast(DeclarationExp)firstComma.e1).declaration == (cast(VarExp)ce.e2).var) + auto fc = firstComma(ce); + if (fc.op == TOK.declaration && ce.e2.op == TOK.variable && (cast(DeclarationExp)fc).declaration == (cast(VarExp)ce.e2).var) { return false; } diff --git a/dmd/statement.d b/dmd/statement.d index 838b6ecede2..2fee09ce69a 100644 --- a/dmd/statement.d +++ b/dmd/statement.d @@ -86,6 +86,23 @@ extern (C++) abstract class Statement : RootObject assert(0); } + /************************************* + * Do syntax copy of an array of Statement's. + */ + static Statements* arraySyntaxCopy(Statements* a) + { + Statements* b = null; + if (a) + { + b = a.copy(); + foreach (i, s; *a) + { + (*b)[i] = s ? s.syntaxCopy() : null; + } + } + return b; + } + override final void print() { fprintf(stderr, "%s\n", toChars()); @@ -160,7 +177,7 @@ extern (C++) abstract class Statement : RootObject { extern (C++) final class UsesEH : StoppableVisitor { - alias visit = super.visit; + alias visit = typeof(super).visit; public: override void visit(Statement s) { @@ -199,7 +216,7 @@ extern (C++) abstract class Statement : RootObject { extern (C++) final class ComeFrom : StoppableVisitor { - alias visit = super.visit; + alias visit = typeof(super).visit; public: override void visit(Statement s) { @@ -238,7 +255,7 @@ extern (C++) abstract class Statement : RootObject { extern (C++) final class HasCode : StoppableVisitor { - alias visit = super.visit; + alias visit = typeof(super).visit; public: override void visit(Statement s) { @@ -868,13 +885,7 @@ extern (C++) class CompoundStatement : Statement override Statement syntaxCopy() { - auto a = new Statements(); - a.setDim(statements.dim); - foreach (i, s; *statements) - { - (*a)[i] = s ? s.syntaxCopy() : null; - } - return new CompoundStatement(loc, a); + return new CompoundStatement(loc, Statement.arraySyntaxCopy(statements)); } override Statements* flatten(Scope* sc) @@ -1612,44 +1623,31 @@ version(IN_LLVM) return true; } + /************************************ + * Returns: + * true if error + */ final bool checkLabel() { /* - * Checks the scope of a label for existing variable declaration. - * Params: - * vd = variable declaration to check - * Returns: `true` if the variables declared in this label would be skipped. - */ + * Checks the scope of a label for existing variable declaration. + * Params: + * vd = last variable declared before this case/default label + * Returns: `true` if the variables declared in this label would be skipped. + */ bool checkVar(VarDeclaration vd) { - if (!vd || vd.isDataseg() || (vd.storage_class & STC.manifest)) - return false; - - VarDeclaration last = lastVar; - while (last && last != vd) - last = last.lastVar; - if (last == vd) - { - // All good, the label's scope has no variables - return false; - } - else if (vd.storage_class & STC.temp) - { - // Lifetime ends at end of expression, so no issue with skipping the statement - return false; - } - else if (vd.ident == Id.withSym) + for (auto v = vd; v && v != lastVar; v = v.lastVar) { - error("`switch` skips declaration of `with` temporary at %s", vd.loc.toChars()); - return true; - } - else - { - if (!vd._init.isVoidInitializer) - error("`switch` skips declaration of variable `%s` at %s", vd.toPrettyChars(), vd.loc.toChars()); - + if (v.isDataseg() || (v.storage_class & (STC.manifest | STC.temp)) || v._init.isVoidInitializer()) + continue; + if (vd.ident == Id.withSym) + error("`switch` skips declaration of `with` temporary at %s", v.loc.toChars()); + else + error("`switch` skips declaration of variable `%s` at %s", v.toPrettyChars(), v.loc.toChars()); return true; } + return false; } enum error = true; @@ -2090,11 +2088,14 @@ extern (C++) final class TryFinallyStatement : Statement Statement _body; Statement finalbody; + bool bodyFallsThru; // true if _body falls through to finally + extern (D) this(const ref Loc loc, Statement _body, Statement finalbody) { super(loc); this._body = _body; this.finalbody = finalbody; + this.bodyFallsThru = true; // assume true until statementSemantic() } static TryFinallyStatement create(Loc loc, Statement _body, Statement finalbody) diff --git a/dmd/statement.h b/dmd/statement.h index f5190776306..f1999f539ad 100644 --- a/dmd/statement.h +++ b/dmd/statement.h @@ -627,6 +627,8 @@ class TryFinallyStatement : public Statement Statement *_body; Statement *finalbody; + bool bodyFallsThru; // true if _body falls through to finally + static TryFinallyStatement *create(Loc loc, Statement *body, Statement *finalbody); Statement *syntaxCopy(); bool hasBreak(); diff --git a/dmd/statementsem.d b/dmd/statementsem.d index 8fa04ecfff6..0153b22b8d1 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -110,9 +110,7 @@ private LabelStatement checkLabeledLoop(Scope* sc, Statement statement) */ private Expression checkAssignmentAsCondition(Expression e) { - auto ec = e; - while (ec.op == TOK.comma) - ec = (cast(CommaExp)ec).e2; + auto ec = lastComma(e); if (ec.op == TOK.assign) { ec.error("assignment cannot be used as a condition, perhaps `==` was meant?"); @@ -3935,6 +3933,7 @@ version(IN_LLVM) result = new CompoundStatement(tfs.loc, tfs._body, tfs.finalbody); return; } + tfs.bodyFallsThru = (blockexit & BE.fallthru) != 0; result = tfs; } @@ -4202,8 +4201,9 @@ void catchSemantic(Catch c, Scope* sc) if (!c.type) { - deprecation(c.loc, "`catch` statement without an exception " ~ - "specification is deprecated; use `catch(Throwable)` for old behavior"); + error(c.loc, "`catch` statement without an exception specification is deprecated"); + errorSupplemental(c.loc, "use `catch(Throwable)` for old behavior"); + c.errors = true; // reference .object.Throwable c.type = getThrowable(); diff --git a/dmd/target.d b/dmd/target.d index 94e008be11c..23d640671d1 100644 --- a/dmd/target.d +++ b/dmd/target.d @@ -62,6 +62,7 @@ struct Target bool cppExceptions; /// set if catching C++ exceptions is supported char int64Mangle; /// mangling character for C++ int64_t char uint64Mangle; /// mangling character for C++ uint64_t + bool twoDtorInVtable; /// target C++ ABI puts deleting and non-deleting destructor into vtable } version(IN_LLVM) @@ -203,6 +204,7 @@ struct Target realpad = 2; realalignsize = 4; c_longsize = 4; + twoDtorInVtable = true; } else if (global.params.isOSX) { @@ -210,6 +212,7 @@ struct Target realpad = 6; realalignsize = 16; c_longsize = 4; + twoDtorInVtable = true; } else if (global.params.isWindows) { @@ -217,6 +220,7 @@ struct Target realpad = 0; realalignsize = 2; reverseCppOverloads = true; + twoDtorInVtable = false; c_longsize = 4; if (ptrsize == 4) { diff --git a/dmd/target.h b/dmd/target.h index b3b2b673cba..965f268bb61 100644 --- a/dmd/target.h +++ b/dmd/target.h @@ -46,6 +46,7 @@ struct Target static bool cppExceptions; // set if catching C++ exceptions is supported static char int64Mangle; // mangling character for C++ int64_t static char uint64Mangle; // mangling character for C++ uint64_t + static bool twoDtorInVtable; // target C++ ABI puts deleting and non-deleting destructor into vtable #if IN_LLVM struct FPTypeProperties diff --git a/dmd/template.h b/dmd/template.h index d968e924b06..4b8cfa2c37a 100644 --- a/dmd/template.h +++ b/dmd/template.h @@ -365,7 +365,7 @@ Type *isType(RootObject *o); Tuple *isTuple(RootObject *o); Parameter *isParameter(RootObject *o); bool arrayObjectIsError(Objects *args); -bool isError(RootObject *o); +bool isError(const RootObject * const o); Type *getType(RootObject *o); Dsymbol *getDsymbol(RootObject *o); diff --git a/dmd/tokens.d b/dmd/tokens.d index c80b114a809..c380f975ea3 100644 --- a/dmd/tokens.d +++ b/dmd/tokens.d @@ -564,7 +564,7 @@ extern (C++) struct Token TOK.shared_: "shared", TOK.immutable_: "immutable", - TOK.endOfFile: "EOF", + TOK.endOfFile: "End of File", TOK.leftCurly: "{", TOK.rightCurly: "}", TOK.leftParentheses: "(", diff --git a/dmd/traits.d b/dmd/traits.d index 6028203a6e1..01d86012a1c 100644 --- a/dmd/traits.d +++ b/dmd/traits.d @@ -825,7 +825,7 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) e.ident == Id.getVirtualMethods || e.ident == Id.getVirtualFunctions) { - if (dim != 2) + if (dim != 2 && !(dim == 3 && e.ident == Id.getOverloads)) return dimError(2); auto o = (*e.args)[0]; @@ -837,6 +837,19 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) } ex = ex.ctfeInterpret(); + bool includeTemplates = false; + if (dim == 3 && e.ident == Id.getOverloads) + { + auto b = isExpression((*e.args)[2]); + b = b.ctfeInterpret(); + if (!b.type.equals(Type.tbool)) + { + e.error("`bool` expected as third argument of `__traits(getOverloads)`, not `%s` of type `%s`", b.toChars(), b.type.toChars()); + return new ErrorExp(); + } + includeTemplates = b.isBool(true); + } + StringExp se = ex.toStringExp(); if (!se || se.len == 0) { @@ -909,7 +922,7 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) /* Create tuple of functions of ex */ auto exps = new Expressions(); - FuncDeclaration f; + Dsymbol f; if (ex.op == TOK.variable) { VarExp ve = cast(VarExp)ex; @@ -925,9 +938,49 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) else ex = dve.e1; } + else if (ex.op == TOK.template_) + { + VarExp ve = cast(VarExp)ex; + auto td = ve.var.isTemplateDeclaration(); + f = td; + if (td && td.funcroot) + f = td.funcroot; + ex = null; + } + + bool[string] funcTypeHash; + + /* Compute the function signature and insert it in the + * hashtable, if not present. This is needed so that + * traits(getOverlods, F3, "visit") does not count `int visit(int)` + * twice in the following example: + * + * ============================================= + * interface F1 { int visit(int);} + * interface F2 { int visit(int); void visit(); } + * interface F3 : F2, F1 {} + *============================================== + */ + void insertInterfaceInheritedFunction(FuncDeclaration fd, Expression e) + { + auto funcType = fd.type.toChars(); + auto len = strlen(funcType); + string signature = funcType[0 .. len].idup; + //printf("%s - %s\n", fd.toChars, signature); + if (signature !in funcTypeHash) + { + funcTypeHash[signature] = true; + exps.push(e); + } + } - overloadApply(f, (Dsymbol s) + int dg(Dsymbol s) { + if (includeTemplates) + { + exps.push(new DsymbolExp(Loc.initial, s, false)); + return 0; + } auto fd = s.isFuncDeclaration(); if (!fd) return 0; @@ -942,9 +995,32 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) auto e = ex ? new DotVarExp(Loc.initial, ex, fa, false) : new DsymbolExp(Loc.initial, fa, false); - exps.push(e); + // if the parent is an interface declaration + // we must check for functions with the same signature + // in different inherited interfaces + if (sym.isInterfaceDeclaration()) + insertInterfaceInheritedFunction(fd, e); + else + exps.push(e); return 0; - }); + } + + InterfaceDeclaration ifd = null; + if (sym) + ifd = sym.isInterfaceDeclaration(); + // If the symbol passed as a parameter is an + // interface that inherits other interfaces + if (ifd && ifd.interfaces) + { + // check the overloads of each inherited interface individually + foreach (bc; ifd.interfaces) + { + if (auto fd = bc.sym.search(e.loc, f.ident)) + overloadApply(fd, &dg); + } + } + else + overloadApply(f, &dg); auto tup = new TupleExp(e.loc, exps); return tup.expressionSemantic(scx); @@ -1236,12 +1312,26 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) { auto s = getDsymbol(o); Declaration d; - if (!s || (d = s.isDeclaration()) is null) + AggregateDeclaration agg; + if (!s || ((d = s.isDeclaration()) is null && (agg = s.isAggregateDeclaration()) is null)) { e.error("argument to `__traits(getLinkage, %s)` is not a declaration", o.toChars()); return new ErrorExp(); } - link = d.linkage; + if (d !is null) + link = d.linkage; + else final switch (agg.classKind) + { + case ClassKind.d: + link = LINK.d; + break; + case ClassKind.cpp: + link = LINK.cpp; + break; + case ClassKind.objc: + link = LINK.objc; + break; + } } auto linkage = linkageToChars(link); auto se = new StringExp(e.loc, cast(char*)linkage); diff --git a/dmd/transitivevisitor.d b/dmd/transitivevisitor.d index c16b117e772..cf89127e9e8 100644 --- a/dmd/transitivevisitor.d +++ b/dmd/transitivevisitor.d @@ -573,13 +573,24 @@ package mixin template ParseVisitMethods(AST) private void visitFuncBody(AST.FuncDeclaration f) { //printf("Visiting funcBody\n"); - if (!f.fbody) - return; - if (f.frequire) - f.frequire.accept(this); - if (f.fensure) - f.fensure.accept(this); - f.fbody.accept(this); + if (f.frequires) + { + foreach (frequire; *f.frequires) + { + frequire.accept(this); + } + } + if (f.fensures) + { + foreach (fensure; *f.fensures) + { + fensure.ensure.accept(this); + } + } + if (f.fbody) + { + f.fbody.accept(this); + } } private void visitBaseClasses(AST.ClassDeclaration d) diff --git a/dmd/typesem.d b/dmd/typesem.d index 35e8caa8b36..275bf1c8768 100644 --- a/dmd/typesem.d +++ b/dmd/typesem.d @@ -14,6 +14,7 @@ module dmd.typesem; import core.checkedint; import core.stdc.string; +import core.stdc.stdio; import dmd.access; import dmd.aggregate; @@ -56,35 +57,58 @@ import dmd.tokens; import dmd.typesem; /************************************ - * Strip all parameter's idenfiers and their default arguments for merging types. - * If some of parameter types or return type are function pointer, delegate, or - * the types which contains either, then strip also from them. + * Transitively search a type for all function types. + * If any function types with parameters are found that have parameter identifiers + * or default arguments, remove those and create a new type stripped of those. + * This is used to determine the "canonical" version of a type which is useful for + * comparisons. + * Params: + * t = type to scan + * Returns: + * `t` if no parameter identifiers or default arguments found, otherwise a new type that is + * the same as t but with no parameter identifiers or default arguments. */ private Type stripDefaultArgs(Type t) { static Parameters* stripParams(Parameters* parameters) { - Parameters* params = parameters; - if (params && params.dim > 0) + static Parameter stripParameter(Parameter p) + { + Type t = stripDefaultArgs(p.type); + return (t != p.type || p.defaultArg || p.ident) + ? new Parameter(p.storageClass, t, null, null) + : null; + } + + if (parameters) { - foreach (i; 0 .. params.dim) + foreach (i, p; *parameters) { - Parameter p = (*params)[i]; - Type ta = stripDefaultArgs(p.type); - if (ta != p.type || p.defaultArg || p.ident) + Parameter ps = stripParameter(p); + if (ps) { - if (params == parameters) + // Replace params with a copy we can modify + Parameters* nparams = new Parameters(); + nparams.setDim(parameters.dim); + + foreach (j, ref np; *nparams) { - params = new Parameters(); - params.setDim(parameters.dim); - foreach (j; 0 .. params.dim) - (*params)[j] = (*parameters)[j]; + Parameter pj = (*parameters)[j]; + if (j < i) + np = pj; + else if (j == i) + np = ps; + else + { + Parameter nps = stripParameter(pj); + np = nps ? nps : pj; + } } - (*params)[i] = new Parameter(p.storageClass, ta, null, null); + return nparams; } } } - return params; + return parameters; } if (t is null) @@ -96,39 +120,38 @@ private Type stripDefaultArgs(Type t) Type tret = stripDefaultArgs(tf.next); Parameters* params = stripParams(tf.parameters); if (tret == tf.next && params == tf.parameters) - goto Lnot; - tf = cast(TypeFunction)tf.copy(); - tf.parameters = params; - tf.next = tret; - //printf("strip %s\n <- %s\n", tf.toChars(), t.toChars()); - t = tf; + return t; + TypeFunction tr = cast(TypeFunction)tf.copy(); + tr.parameters = params; + tr.next = tret; + //printf("strip %s\n <- %s\n", tr.toChars(), t.toChars()); + return tr; } else if (t.ty == Ttuple) { TypeTuple tt = cast(TypeTuple)t; Parameters* args = stripParams(tt.arguments); if (args == tt.arguments) - goto Lnot; - t = t.copy(); - (cast(TypeTuple)t).arguments = args; + return t; + TypeTuple tr = cast(TypeTuple)t.copy(); + tr.arguments = args; + return tr; } else if (t.ty == Tenum) { // TypeEnum::nextOf() may be != NULL, but it's not necessary here. - goto Lnot; + return t; } else { Type tn = t.nextOf(); Type n = stripDefaultArgs(tn); if (n == tn) - goto Lnot; - t = t.copy(); - (cast(TypeNext)t).next = n; + return t; + TypeNext tr = cast(TypeNext)t.copy(); + tr.next = n; + return tr; } - //printf("strip %s\n", t.toChars()); -Lnot: - return t; } /****************************************** @@ -218,12 +241,11 @@ extern (C++) Expression typeToExpression(Type t) extern (C++) Expression typeToExpressionHelper(TypeQualified t, Expression e, size_t i = 0) { //printf("toExpressionHelper(e = %s %s)\n", Token.toChars(e.op), e.toChars()); - for (; i < t.idents.dim; i++) + foreach (id; t.idents[i .. t.idents.dim]) { - RootObject id = t.idents[i]; //printf("\t[%d] e: '%s', id: '%s'\n", i, e.toChars(), id.toChars()); - switch (id.dyncast()) + final switch (id.dyncast()) { // ... '. ident' case DYNCAST.identifier: @@ -247,7 +269,11 @@ extern (C++) Expression typeToExpressionHelper(TypeQualified t, Expression e, si e = new ArrayExp(t.loc, e, cast(Expression)id); break; - default: + case DYNCAST.object: + case DYNCAST.tuple: + case DYNCAST.parameter: + case DYNCAST.statement: + case DYNCAST.condition: assert(0); } } @@ -267,13 +293,17 @@ private extern (C++) final class TypeSemanticVisitor : Visitor this.sc = sc; } + void error() + { + result = Type.terror; + } + override void visit(Type t) { if (t.ty == Tint128 || t.ty == Tuns128) { t.error(loc, "`cent` and `ucent` types not implemented"); - result = Type.terror; - return; + return error(); } result = t.merge(); @@ -284,48 +314,41 @@ private extern (C++) final class TypeSemanticVisitor : Visitor uint errors = global.errors; mtype.basetype = mtype.basetype.typeSemantic(loc, sc); if (errors != global.errors) - { - result = Type.terror; - return; - } + return error(); mtype.basetype = mtype.basetype.toBasetype().mutableOf(); if (mtype.basetype.ty != Tsarray) { mtype.error(loc, "T in __vector(T) must be a static array, not `%s`", mtype.basetype.toChars()); - result = Type.terror; - return; + return error(); } TypeSArray t = cast(TypeSArray)mtype.basetype; - int sz = cast(int)t.size(loc); - switch (Target.isVectorTypeSupported(sz, t.nextOf())) + const sz = cast(int)t.size(loc); + final switch (Target.isVectorTypeSupported(sz, t.nextOf())) { case 0: // valid break; + case 1: // no support at all mtype.error(loc, "SIMD vector types not supported on this platform"); - result = Type.terror; - return; + return error(); + case 2: // invalid base type mtype.error(loc, "vector type `%s` is not supported on this platform", mtype.toChars()); - result = Type.terror; - return; + return error(); + case 3: // invalid size if (sz == 32) - { deprecation(loc, "%d byte vector types are only supported with -mcpu=avx", sz, mtype.toChars()); - result = merge(mtype); - return; - } else + { mtype.error(loc, "%d byte vector type `%s` is not supported on this platform", sz, mtype.toChars()); - result = Type.terror; - return; - default: - assert(0); + return error(); + } + break; } result = merge(mtype); } @@ -333,12 +356,6 @@ private extern (C++) final class TypeSemanticVisitor : Visitor override void visit(TypeSArray mtype) { //printf("TypeSArray::semantic() %s\n", toChars()); - - static Type errorReturn() - { - return Type.terror; - } - Type t; Expression e; Dsymbol s; @@ -349,92 +366,70 @@ private extern (C++) final class TypeSemanticVisitor : Visitor mtype.dim = semanticLength(sc, tup, mtype.dim); mtype.dim = mtype.dim.ctfeInterpret(); if (mtype.dim.op == TOK.error) - { - result = errorReturn(); - return; - } + return error(); + uinteger_t d = mtype.dim.toUInteger(); if (d >= tup.objects.dim) { mtype.error(loc, "tuple index %llu exceeds %llu", cast(ulong)d, cast(ulong)tup.objects.dim); - result = errorReturn(); - return; + return error(); } RootObject o = (*tup.objects)[cast(size_t)d]; if (o.dyncast() != DYNCAST.type) { mtype.error(loc, "`%s` is not a type", mtype.toChars()); - result = errorReturn(); - return; + return error(); } - t = (cast(Type)o).addMod(mtype.mod); - result = t; + result = (cast(Type)o).addMod(mtype.mod); return; } Type tn = mtype.next.typeSemantic(loc, sc); if (tn.ty == Terror) - { - result = errorReturn(); - return; - } + return error(); Type tbn = tn.toBasetype(); if (mtype.dim) { - uint errors = global.errors; + auto errors = global.errors; mtype.dim = semanticLength(sc, tbn, mtype.dim); if (errors != global.errors) - { - result = errorReturn(); - return; - } + return error(); mtype.dim = mtype.dim.optimize(WANTvalue); mtype.dim = mtype.dim.ctfeInterpret(); if (mtype.dim.op == TOK.error) - { - result = errorReturn(); - return; - } + return error(); + errors = global.errors; dinteger_t d1 = mtype.dim.toInteger(); if (errors != global.errors) - { - result = errorReturn(); - return; - } + return error(); mtype.dim = mtype.dim.implicitCastTo(sc, Type.tsize_t); mtype.dim = mtype.dim.optimize(WANTvalue); if (mtype.dim.op == TOK.error) - { - result = errorReturn(); - return; - } + return error(); + errors = global.errors; dinteger_t d2 = mtype.dim.toInteger(); if (errors != global.errors) - { - result = errorReturn(); - return; - } + return error(); if (mtype.dim.op == TOK.error) - { - result = errorReturn(); - return; - } + return error(); - if (d1 != d2) + void overflowError() { - Loverflow: mtype.error(loc, "`%s` size %llu * %llu exceeds 0x%llx size limit for static array", mtype.toChars(), cast(ulong)tbn.size(loc), cast(ulong)d1, Target.maxStaticDataSize); - result = errorReturn(); - return; + return error(); } + + if (d1 != d2) + return overflowError(); + Type tbx = tbn.baseElemOf(); if (tbx.ty == Tstruct && !(cast(TypeStruct)tbx).sym.members || tbx.ty == Tenum && !(cast(TypeEnum)tbx).sym.members) { @@ -449,7 +444,7 @@ private extern (C++) final class TypeSemanticVisitor : Visitor */ bool overflow = false; if (mulu(tbn.size(loc), d2, overflow) >= Target.maxStaticDataSize || overflow) - goto Loverflow; + return overflowError(); } } switch (tbn.ty) @@ -463,26 +458,25 @@ private extern (C++) final class TypeSemanticVisitor : Visitor if (d >= tt.arguments.dim) { mtype.error(loc, "tuple index %llu exceeds %llu", cast(ulong)d, cast(ulong)tt.arguments.dim); - result = errorReturn(); - return; + return error(); } Type telem = (*tt.arguments)[cast(size_t)d].type; result = telem.addMod(mtype.mod); return; } + case Tfunction: case Tnone: mtype.error(loc, "cannot have array of `%s`", tbn.toChars()); - result = errorReturn(); - return; + return error(); + default: break; } if (tbn.isscope()) { mtype.error(loc, "cannot have array of scope `%s`", tbn.toChars()); - result = errorReturn(); - return; + return error(); } /* Ensure things like const(immutable(T)[3]) become immutable(T[3]) @@ -490,9 +484,7 @@ private extern (C++) final class TypeSemanticVisitor : Visitor */ mtype.next = tn; mtype.transitive(); - t = mtype.addMod(tn.mod); - - result = t.merge(); + result = mtype.addMod(tn.mod).merge(); } override void visit(TypeDArray mtype) @@ -504,22 +496,22 @@ private extern (C++) final class TypeSemanticVisitor : Visitor case Ttuple: result = tbn; return; + case Tfunction: case Tnone: mtype.error(loc, "cannot have array of `%s`", tbn.toChars()); - result = Type.terror; - return; + return error(); + case Terror: - result = Type.terror; - return; + return error(); + default: break; } if (tn.isscope()) { mtype.error(loc, "cannot have array of scope `%s`", tn.toChars()); - result = Type.terror; - return; + return error(); } mtype.next = tn; mtype.transitive(); @@ -528,7 +520,7 @@ private extern (C++) final class TypeSemanticVisitor : Visitor override void visit(TypeAArray mtype) { - //printf("TypeAArray::semantic() %s index.ty = %d\n", toChars(), index.ty); + //printf("TypeAArray::semantic() %s index.ty = %d\n", mtype.toChars(), mtype.index.ty); if (mtype.deco) { result = mtype; @@ -561,8 +553,7 @@ private extern (C++) final class TypeSemanticVisitor : Visitor else { mtype.index.error(loc, "index is not a type or an expression"); - result = Type.terror; - return; + return error(); } } else @@ -595,8 +586,8 @@ private extern (C++) final class TypeSemanticVisitor : Visitor mtype.error(loc, "cannot have associative array key of `%s`", mtype.index.toBasetype().toChars()); goto case Terror; case Terror: - result = Type.terror; - return; + return error(); + default: break; } @@ -641,8 +632,7 @@ private extern (C++) final class TypeSemanticVisitor : Visitor { mtype.error(loc, "%sAA key type `%s` does not support const equality", s, sd.toChars()); } - result = Type.terror; - return; + return error(); } else if (!sd.xhash) { @@ -654,8 +644,7 @@ private extern (C++) final class TypeSemanticVisitor : Visitor { mtype.error(loc, "%sAA key type `%s` supports const equality but doesn't support const hashing", s, sd.toChars()); } - result = Type.terror; - return; + return error(); } else { @@ -717,16 +706,14 @@ private extern (C++) final class TypeSemanticVisitor : Visitor mtype.error(loc, "cannot have associative array of `%s`", mtype.next.toChars()); goto case Terror; case Terror: - result = Type.terror; - return; + return error(); default: break; } if (mtype.next.isscope()) { mtype.error(loc, "cannot have array of scope `%s`", mtype.next.toChars()); - result = Type.terror; - return; + return error(); } result = merge(mtype); } @@ -746,8 +733,7 @@ private extern (C++) final class TypeSemanticVisitor : Visitor mtype.error(loc, "cannot have pointer to `%s`", n.toChars()); goto case Terror; case Terror: - result = Type.terror; - return; + return error(); default: break; } @@ -783,8 +769,8 @@ private extern (C++) final class TypeSemanticVisitor : Visitor { //printf("TypeReference::semantic()\n"); Type n = mtype.next.typeSemantic(loc, sc); - if (n !=mtype. next) - mtype. deco = null; + if (n != mtype.next) + mtype.deco = null; mtype.next = n; mtype.transitive(); result = merge(mtype); @@ -889,7 +875,7 @@ private extern (C++) final class TypeSemanticVisitor : Visitor if (tf.isreturn && !tf.isref && !tf.next.hasPointers()) { - mtype.error(loc, "function type `%s` has `return` but does not return any indirections", tf.toChars()); + tf.isreturn = false; } } @@ -978,9 +964,7 @@ private extern (C++) final class TypeSemanticVisitor : Visitor } else if (tf.next && !tf.next.hasPointers()) { - mtype.error(loc, "parameter `%s` is `return` but function does not return any indirections", - fparam.ident ? fparam.ident.toChars() : ""); - errors = true; + fparam.storageClass &= ~STC.return_; // https://issues.dlang.org/show_bug.cgi?id=18963 } } } @@ -1173,10 +1157,7 @@ private extern (C++) final class TypeSemanticVisitor : Visitor } if (errors) - { - result = Type.terror; - return; - } + return error(); if (tf.next) tf.deco = tf.merge().deco; @@ -1199,10 +1180,7 @@ private extern (C++) final class TypeSemanticVisitor : Visitor } mtype.next = mtype.next.typeSemantic(loc, sc); if (mtype.next.ty != Tfunction) - { - result = Type.terror; - return; - } + return error(); /* In order to deal with https://issues.dlang.org/show_bug.cgi?id=4028 * perhaps default arguments should @@ -1234,7 +1212,7 @@ private extern (C++) final class TypeSemanticVisitor : Visitor if (t) { //printf("\tit's a type %d, %s, %s\n", t.ty, t.toChars(), t.deco); - t = t.addMod(mtype.mod); + result = t.addMod(mtype.mod); } else { @@ -1251,10 +1229,8 @@ private extern (C++) final class TypeSemanticVisitor : Visitor } else mtype.error(loc, "`%s` is used as a type", mtype.toChars()); - t = Type.terror; + return error(); } - //t.print(); - result = t; } override void visit(TypeInstance mtype) @@ -1265,7 +1241,7 @@ private extern (C++) final class TypeSemanticVisitor : Visitor //printf("TypeInstance::semantic(%p, %s)\n", this, toChars()); { - uint errors = global.errors; + const errors = global.errors; mtype.resolve(loc, sc, &e, &t, &s); // if we had an error evaluating the symbol, suppress further errors if (!t && errors != global.errors) @@ -1285,7 +1261,7 @@ private extern (C++) final class TypeSemanticVisitor : Visitor } else mtype.error(loc, "`%s` is used as a type", mtype.toChars()); - t = Type.terror; + return error(); } result = t; } @@ -1302,7 +1278,7 @@ private extern (C++) final class TypeSemanticVisitor : Visitor if (!t) { mtype.error(loc, "`%s` is used as a type", mtype.toChars()); - t = Type.terror; + return error(); } result = t; } @@ -1319,7 +1295,7 @@ private extern (C++) final class TypeSemanticVisitor : Visitor if (!t) { mtype.error(loc, "`%s` is used as a type", mtype.toChars()); - t = Type.terror; + return error(); } result = t; } @@ -1347,10 +1323,8 @@ private extern (C++) final class TypeSemanticVisitor : Visitor assert(mtype.sym.parent); if (mtype.sym.type.ty == Terror) - { - result = Type.terror; - return; - } + return error(); + if (sc) mtype.cppmangle = sc.cppmangle; result = merge(mtype); @@ -1359,12 +1333,7 @@ private extern (C++) final class TypeSemanticVisitor : Visitor override void visit(TypeEnum mtype) { //printf("TypeEnum::semantic() %s\n", toChars()); - if (mtype.deco) - { - result = mtype; - return; - } - result = merge(mtype); + result = mtype.deco ? mtype : merge(mtype); } override void visit(TypeClass mtype) @@ -1390,10 +1359,8 @@ private extern (C++) final class TypeSemanticVisitor : Visitor assert(mtype.sym.parent); if (mtype.sym.type.ty == Terror) - { - result = Type.terror; - return; - } + return error(); + if (sc) mtype.cppmangle = sc.cppmangle; result = merge(mtype); @@ -1422,8 +1389,7 @@ private extern (C++) final class TypeSemanticVisitor : Visitor if (tbn.ty != Ttuple) { mtype.error(loc, "can only slice tuple types, not `%s`", tbn.toChars()); - result = Type.terror; - return; + return error(); } TypeTuple tt = cast(TypeTuple)tbn; @@ -1432,10 +1398,7 @@ private extern (C++) final class TypeSemanticVisitor : Visitor mtype.lwr = mtype.lwr.ctfeInterpret(); mtype.upr = mtype.upr.ctfeInterpret(); if (mtype.lwr.op == TOK.error || mtype.upr.op == TOK.error) - { - result = Type.terror; - return; - } + return error(); uinteger_t i1 = mtype.lwr.toUInteger(); uinteger_t i2 = mtype.upr.toUInteger(); @@ -1443,8 +1406,7 @@ private extern (C++) final class TypeSemanticVisitor : Visitor { mtype.error(loc, "slice `[%llu..%llu]` is out of range of `[0..%llu]`", cast(ulong)i1, cast(ulong)i2, cast(ulong)tt.arguments.dim); - result = Type.terror; - return; + return error(); } mtype.next = tn; @@ -1452,9 +1414,8 @@ private extern (C++) final class TypeSemanticVisitor : Visitor auto args = new Parameters(); args.reserve(cast(size_t)(i2 - i1)); - for (size_t i = cast(size_t)i1; i < cast(size_t)i2; i++) + foreach (arg; (*tt.arguments)[cast(size_t)i1 .. cast(size_t)i2]) { - Parameter arg = (*tt.arguments)[i]; args.push(arg); } Type t = new TypeTuple(args); @@ -1464,26 +1425,40 @@ private extern (C++) final class TypeSemanticVisitor : Visitor } /************************************ + * If an identical type to `type` is in `type.stringtable`, return + * the latter one. Otherwise, add it to `type.stringtable`. + * Some types don't get merged and are returned as-is. + * Params: + * type = Type to check against existing types + * Returns: + * the type that was merged */ // LLVM: added `extern(C++)` extern(C++) Type merge(Type type) { - if (type.ty == Terror) - return type; - if (type.ty == Ttypeof) - return type; - if (type.ty == Tident) - return type; - if (type.ty == Tinstance) - return type; - if (type.ty == Taarray && !(cast(TypeAArray)type).index.merge().deco) - return type; - if (type.ty != Tenum && type.nextOf() && !type.nextOf().deco) - return type; + switch (type.ty) + { + case Terror: + case Ttypeof: + case Tident: + case Tinstance: + return type; + + case Tenum: + break; + + case Taarray: + if (!(cast(TypeAArray)type).index.merge().deco) + return type; + goto default; + + default: + if (type.nextOf() && !type.nextOf().deco) + return type; + break; + } //printf("merge(%s)\n", toChars()); - Type t = type; - assert(t); if (!type.deco) { OutBuffer buf; @@ -1494,7 +1469,7 @@ extern(C++) Type merge(Type type) StringValue* sv = type.stringtable.update(cast(char*)buf.data, buf.offset); if (sv.ptrvalue) { - t = cast(Type)sv.ptrvalue; + Type t = cast(Type)sv.ptrvalue; debug { import core.stdc.stdio; @@ -1503,15 +1478,18 @@ extern(C++) Type merge(Type type) } assert(t.deco); //printf("old value, deco = '%s' %p\n", t.deco, t.deco); + return t; } else { - sv.ptrvalue = cast(char*)(t = stripDefaultArgs(t)); + Type t = stripDefaultArgs(type); + sv.ptrvalue = cast(char*)t; type.deco = t.deco = cast(char*)sv.toDchars(); //printf("new value, deco = '%s' %p\n", t.deco, t.deco); + return t; } } - return t; + return type; } /*************************************** @@ -1532,7 +1510,7 @@ extern(C++) Expression getProperty(Type t, const ref Loc loc, Identifier ident, private extern (C++) final class GetPropertyVisitor : Visitor { - alias visit = super.visit; + alias visit = typeof(super).visit; Loc loc; Identifier ident; int flag; @@ -2078,7 +2056,7 @@ extern(C++) void resolve(Type mt, const ref Loc loc, Scope* sc, Expression* pe, private extern(C++) final class ResolveVisitor : Visitor { - alias visit = super.visit; + alias visit = typeof(super).visit; Loc loc; Scope* sc; Expression* pe; @@ -2260,6 +2238,16 @@ private extern(C++) final class ResolveVisitor : Visitor //printf("TypeIdentifier::resolve(sc = %p, idents = '%s')\n", sc, mt.toChars()); if ((mt.ident.equals(Id._super) || mt.ident.equals(Id.This)) && !hasThis(sc)) { + // @@@DEPRECATED_v2.086@@@. + if (mt.ident.equals(Id._super)) + { + deprecation(mt.loc, "Using `super` as a type is deprecated. Use `typeof(super)` instead"); + } + // @@@DEPRECATED_v2.086@@@. + if (mt.ident.equals(Id.This)) + { + deprecation(mt.loc, "Using `this` as a type is deprecated. Use `typeof(this)` instead"); + } AggregateDeclaration ad = sc.getStructClassScope(); if (ad) { @@ -2343,6 +2331,12 @@ private extern(C++) final class ResolveVisitor : Visitor //printf("TypeTypeof::resolve(this = %p, sc = %p, idents = '%s')\n", mt, sc, mt.toChars()); //static int nest; if (++nest == 50) *(char*)0=0; + if (sc is null) + { + *pt = Type.terror; + error(loc, "Invalid scope."); + return; + } if (mt.inuse) { mt.inuse = 2; @@ -2362,7 +2356,7 @@ private extern(C++) final class ResolveVisitor : Visitor * } * void main() { * alias X = typeof(S!int()); - * assert(typeid(X).xtoString(null) == "x"); + * assert(typeid(X).toString() == "x"); * } */ Scope* sc2 = sc.push(); @@ -2571,7 +2565,7 @@ extern(C++) Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident private extern(C++) final class DotExpVisitor : Visitor { - alias visit = super.visit; + alias visit = typeof(super).visit; Scope *sc; Expression e; Identifier ident; @@ -2646,7 +2640,7 @@ private extern(C++) final class DotExpVisitor : Visitor e = new StringExp(e.loc, cast(char*)s); } else - e = mt.getProperty(e.loc, ident, flag & mt.DotExpFlag.gag); + e = mt.getProperty(e.loc, ident, flag & DotExpFlag.gag); Lreturn: if (e) @@ -2851,7 +2845,7 @@ else result = new ErrorExp(); return; } - else if (!(flag & mt.DotExpFlag.noDeref) && sc.func && !sc.intypeof && sc.func.setUnsafe()) + else if (!(flag & DotExpFlag.noDeref) && sc.func && !sc.intypeof && sc.func.setUnsafe()) { e.error("`%s.ptr` cannot be used in `@safe` code, use `&%s[0]` instead", e.toChars(), e.toChars()); result = new ErrorExp(); @@ -2906,7 +2900,7 @@ else } else if (ident == Id.ptr) { - if (!(flag & mt.DotExpFlag.noDeref) && sc.func && !sc.intypeof && sc.func.setUnsafe()) + if (!(flag & DotExpFlag.noDeref) && sc.func && !sc.intypeof && sc.func.setUnsafe()) { e.error("`%s.ptr` cannot be used in `@safe` code, use `&%s[0]` instead", e.toChars(), e.toChars()); result = new ErrorExp(); @@ -2978,7 +2972,7 @@ else } else if (ident == Id.funcptr) { - if (!(flag & mt.DotExpFlag.noDeref) && sc.func && !sc.intypeof && sc.func.setUnsafe()) + if (!(flag & DotExpFlag.noDeref) && sc.func && !sc.intypeof && sc.func.setUnsafe()) { e.error("`%s.funcptr` cannot be used in `@safe` code", e.toChars()); result = new ErrorExp(); @@ -3005,6 +2999,8 @@ else { //printf("Type.noMember(e: %s ident: %s flag: %d)\n", e.toChars(), ident.toChars(), flag); + bool gagError = flag & 1; + static __gshared int nest; // https://issues.dlang.org/show_bug.cgi?id=17380 static Expression returnExp(Expression e) @@ -3016,7 +3012,7 @@ else if (++nest > 500) { .error(e.loc, "cannot resolve identifier `%s`", ident.toChars()); - return returnExp(flag & 1 ? null : new ErrorExp()); + return returnExp(gagError ? null : new ErrorExp()); } @@ -3070,13 +3066,12 @@ else auto dti = new DotTemplateInstanceExp(e.loc, e, Id.opDispatch, tiargs); dti.ti.tempdecl = td; /* opDispatch, which doesn't need IFTI, may occur instantiate error. - * It should be gagged if flag & 1. * e.g. * template opDispatch(name) if (isValid!name) { ... } */ - uint errors = flag & 1 ? global.startGagging() : 0; + uint errors = gagError ? global.startGagging() : 0; e = dti.semanticY(sc, 0); - if (flag & 1 && global.endGagging(errors)) + if (gagError && global.endGagging(errors)) e = null; return returnExp(e); } @@ -3088,9 +3083,28 @@ else /* Rewrite e.ident as: * e.aliasthis.ident */ - e = resolveAliasThis(sc, e); - auto die = new DotIdExp(e.loc, e, ident); - return returnExp(die.semanticY(sc, flag & 1)); + auto alias_e = resolveAliasThis(sc, e, gagError); + + if (!alias_e) + return returnExp(null); + + auto die = new DotIdExp(e.loc, alias_e, ident); + + auto errors = gagError ? 0 : global.startGagging(); + auto exp = die.semanticY(sc, gagError); + if (!gagError) + { + global.endGagging(errors); + if (exp && exp.op == TOK.error) + exp = null; + } + + if (exp && gagError) + // now that we know that the alias this leads somewhere useful, + // go back and print deprecations/warnings that we skipped earlier due to the gag + resolveAliasThis(sc, e, false); + + return returnExp(exp); } } visit(cast(Type)mt); @@ -3906,7 +3920,7 @@ else if (auto fd = d.isFuncDeclaration()) { import dmd.access : mostVisibleOverload; - d = cast(Declaration)mostVisibleOverload(fd); + d = cast(Declaration)mostVisibleOverload(fd, sc._module); } checkAccess(e.loc, sc, e, d); diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 90c654e0ef2..fcc2407f2d1 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -218,25 +218,6 @@ if(PHOBOS2_DIR) file(GLOB_RECURSE PHOBOS2_D ${PHOBOS2_DIR}/*.d) # remove top-level modules index.d and unittest.d list(REMOVE_ITEM PHOBOS2_D ${PHOBOS2_DIR}/index.d ${PHOBOS2_DIR}/unittest.d) - # only include std/c/ modules matching the platform - file(GLOB_RECURSE PHOBOS2_D_FREEBSD ${PHOBOS2_DIR}/std/c/freebsd/*.d) - file(GLOB_RECURSE PHOBOS2_D_LINUX ${PHOBOS2_DIR}/std/c/linux/*.d) - file(GLOB_RECURSE PHOBOS2_D_OSX ${PHOBOS2_DIR}/std/c/osx/*.d) - file(GLOB_RECURSE PHOBOS2_D_WINDOWS ${PHOBOS2_DIR}/std/c/windows/*.d) - list(REMOVE_ITEM PHOBOS2_D - ${PHOBOS2_D_FREEBSD} ${PHOBOS2_D_LINUX} ${PHOBOS2_D_OSX} ${PHOBOS2_D_WINDOWS} - ) - if("${TARGET_SYSTEM}" MATCHES "UNIX") - if("${TARGET_SYSTEM}" MATCHES "APPLE") - list(APPEND PHOBOS2_D ${PHOBOS2_D_OSX}) - elseif("${TARGET_SYSTEM}" MATCHES "FreeBSD") - list(APPEND PHOBOS2_D ${PHOBOS2_D_FREEBSD}) - elseif("${TARGET_SYSTEM}" MATCHES "Linux") - list(APPEND PHOBOS2_D ${PHOBOS2_D_LINUX}) - endif() - elseif("${TARGET_SYSTEM}" MATCHES "Windows") - list(APPEND PHOBOS2_D ${PHOBOS2_D_WINDOWS}) - endif() # only include std/windows/ modules on Windows if(NOT "${TARGET_SYSTEM}" MATCHES "Windows") file(GLOB_RECURSE PHOBOS2_D_WINDOWS ${PHOBOS2_DIR}/std/windows/*.d) diff --git a/runtime/druntime b/runtime/druntime index 41e9dece3fc..959f4714fa2 160000 --- a/runtime/druntime +++ b/runtime/druntime @@ -1 +1 @@ -Subproject commit 41e9dece3fc6c2bc966cfa327ae8160f67a4c569 +Subproject commit 959f4714fa222ad1390f2d3cc04e465f26078842 diff --git a/runtime/phobos b/runtime/phobos index 154c05b84af..4f26e50a667 160000 --- a/runtime/phobos +++ b/runtime/phobos @@ -1 +1 @@ -Subproject commit 154c05b84af61543f4e5f63db9a69c4594c8c627 +Subproject commit 4f26e50a66709fed4674daf470af77e7c03134ae diff --git a/tests/d2/dmd-testsuite b/tests/d2/dmd-testsuite index e1e05efb40f..8d9193ec951 160000 --- a/tests/d2/dmd-testsuite +++ b/tests/d2/dmd-testsuite @@ -1 +1 @@ -Subproject commit e1e05efb40fa122cae53faa6d8dff25b098c9203 +Subproject commit 8d9193ec95191e0539a7fa58dcc70e4d3ca8c303 diff --git a/tests/d2/src/osmodel.mak b/tests/d2/src/osmodel.mak index dfbcdc3a3b9..6b4681f32bd 100644 --- a/tests/d2/src/osmodel.mak +++ b/tests/d2/src/osmodel.mak @@ -2,7 +2,7 @@ # # Detects and sets the macros: # -# OS = one of {osx,linux,freebsd,openbsd,netbsd,solaris} +# OS = one of {osx,linux,freebsd,openbsd,netbsd,dragonflybsd,solaris} # MODEL = one of { 32, 64 } # MODEL_FLAG = one of { -m32, -m64 } # From 90a1c6b1cc879d8882c426a38e02e29de1234937 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Tue, 19 Jun 2018 01:10:02 +0200 Subject: [PATCH 02/36] druntime: Fix rt.backtrace merge regressions --- runtime/druntime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/druntime b/runtime/druntime index 959f4714fa2..a382ade8d41 160000 --- a/runtime/druntime +++ b/runtime/druntime @@ -1 +1 @@ -Subproject commit 959f4714fa222ad1390f2d3cc04e465f26078842 +Subproject commit a382ade8d41fe65236fe5c5c51bdec37af4c4516 From acf7de3ce678c0b8addf0f9bb5b1d50f14ed8906 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Tue, 19 Jun 2018 01:55:17 +0200 Subject: [PATCH 03/36] Initialize new Target::twoDtorInVtable appropriately --- gen/target.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gen/target.cpp b/gen/target.cpp index 06463033f46..b91a408ab89 100644 --- a/gen/target.cpp +++ b/gen/target.cpp @@ -49,6 +49,8 @@ void Target::_init() { int64Mangle = 'l'; // C++ long uint64Mangle = 'm'; // C++ unsigned long + twoDtorInVtable = !triple.isWindowsMSVCEnvironment(); + // {Float,Double,Real}Properties have been initialized with the D host // compiler's properties. // Now finalize RealProperties for the target's `real` type. From a2badc2cb33cf3ffb182b8555bc9ab26fa418bd0 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Tue, 19 Jun 2018 23:28:24 +0200 Subject: [PATCH 04/36] Fix trivial test regressions (catch statements without type etc.) --- tests/codegen/attr_fastmath.d | 2 +- tests/codegen/inlining_stdlib.d | 4 ++-- tests/d2/dmd-testsuite | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/codegen/attr_fastmath.d b/tests/codegen/attr_fastmath.d index 210ccd40e22..fbf24da6c40 100644 --- a/tests/codegen/attr_fastmath.d +++ b/tests/codegen/attr_fastmath.d @@ -38,7 +38,7 @@ extern (C) double fast(double a, double b) // CHECK: fmul fast c += a * b; } - catch + catch (Throwable) { // CHECK: fmul fast return a * b; diff --git a/tests/codegen/inlining_stdlib.d b/tests/codegen/inlining_stdlib.d index 3df5788608d..4ad29bab01f 100644 --- a/tests/codegen/inlining_stdlib.d +++ b/tests/codegen/inlining_stdlib.d @@ -25,11 +25,11 @@ char[] ggg(char* str) { // std.string.fromStringz() is inlined when optimizing import std.string; - // OPT0: call {{.*}} @{{.*}}std6string11fromStringz + // OPT0: call {{.*}} @{{.*}}D3std6string__T11fromStringz // OPT3: call {{.*}}strlen return fromStringz(str); // OPT0: ret // OPT3: ret } -// OPT0: declare {{.*}}std6string11fromStringz +// OPT0: declare {{.*}}D3std6string__T11fromStringz // OPT3: declare {{.*}}strlen diff --git a/tests/d2/dmd-testsuite b/tests/d2/dmd-testsuite index 8d9193ec951..cf5d53b1139 160000 --- a/tests/d2/dmd-testsuite +++ b/tests/d2/dmd-testsuite @@ -1 +1 @@ -Subproject commit 8d9193ec95191e0539a7fa58dcc70e4d3ca8c303 +Subproject commit cf5d53b1139de8615750c2512b1bb69031576895 From 2b76e23bfebba6556030be4a856f6df9db13b6b1 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Wed, 20 Jun 2018 00:59:51 +0200 Subject: [PATCH 05/36] tests/codegen/inlining_stdlib.d: Find another Phobos function to be inlined As `std.string.fromStringz()` is now a template. Use `std.math.nextDown(double)` instead. --- tests/codegen/inlining_stdlib.d | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/codegen/inlining_stdlib.d b/tests/codegen/inlining_stdlib.d index 4ad29bab01f..b9319226190 100644 --- a/tests/codegen/inlining_stdlib.d +++ b/tests/codegen/inlining_stdlib.d @@ -21,15 +21,15 @@ int foo(size_t i) // OPT0-LABEL: define{{.*}} @ggg( // OPT3-LABEL: define{{.*}} @ggg( -char[] ggg(char* str) +double ggg(double r) { - // std.string.fromStringz() is inlined when optimizing - import std.string; - // OPT0: call {{.*}} @{{.*}}D3std6string__T11fromStringz - // OPT3: call {{.*}}strlen - return fromStringz(str); + // std.math.nextDown() is inlined when optimizing + import std.math; + // OPT0: call {{.*}} @{{.*}}D3std4math8nextDown + // OPT3: call {{.*}} @{{.*}}D3std4math6nextUp + return nextDown(r); // OPT0: ret // OPT3: ret } -// OPT0: declare {{.*}}D3std6string__T11fromStringz -// OPT3: declare {{.*}}strlen +// OPT0: declare {{.*}}D3std4math8nextDown +// OPT3: declare {{.*}}D3std4math6nextUp From 6dfaebb33ed7e3aa47b95938b57df7071d6f171f Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Wed, 20 Jun 2018 21:31:52 +0200 Subject: [PATCH 06/36] Adapt to new special-ref result variables in out contracts The AST now features special-ref result variables (storage classes: ref, temp, result) after rewriting out contracts; from dmd/func.d: /* out(id1) { statements1... } * out(id2) { statements2... } * ... * becomes: * out(__result) { { ref id1 = __result; { statements1... } } * { ref id2 = __result; { statements2... } } ... } */ We are talking about the `id1` and `id2` variables here. There's an existing assertion that we don't set a special-ref variable's lvalue (T**) to the sret pointer (T*) which was already triggered when compiling Phobos without unittests. --- gen/llvmhelpers.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index 119232f41fc..f487c090633 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -966,9 +966,10 @@ void DtoVarDeclaration(VarDeclaration *vd) { if (isIrLocalCreated(vd)) { // Nothing to do if it has already been allocated. - } else if (gIR->func()->sretArg && ((gIR->func()->decl->nrvo_can && - gIR->func()->decl->nrvo_var == vd) || - vd->isResult())) { + } else if (gIR->func()->sretArg && + ((gIR->func()->decl->nrvo_can && + gIR->func()->decl->nrvo_var == vd) || + (vd->isResult() && !isSpecialRefVar(vd)))) { // Named Return Value Optimization (NRVO): // T f() { // T ret; // &ret == hidden pointer From 34c6ae5cbad9720c70c83a668c66022bdd7617b6 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Thu, 21 Jun 2018 03:18:35 +0200 Subject: [PATCH 07/36] Fixup dmd/root/longdouble.d for LDC Use more robust LLVM inline assembly, which works around a `mov` issue in DMD-style inline asm (at least on Win64) - additional dereferencing when moving a `longdouble_soft` parameter (passed indirectly by value in a GP register) to RAX instead of just copying the address. --- dmd/root/longdouble.d | 108 +++++++++++++++++++++++------------------ tests/d2/dmd-testsuite | 2 +- 2 files changed, 61 insertions(+), 49 deletions(-) diff --git a/dmd/root/longdouble.d b/dmd/root/longdouble.d index 234de1affbb..87c7e346d23 100644 --- a/dmd/root/longdouble.d +++ b/dmd/root/longdouble.d @@ -82,9 +82,9 @@ void ld_clearfpu() } pure: -@safe: +@trusted: // LDC: LLVM __asm is @system AND requires taking the address of variables -align(2) struct longdouble_soft +struct longdouble_soft { nothrow @nogc pure: ulong mantissa = 0xC000000000000001UL; // default to snan @@ -186,63 +186,73 @@ nothrow @nogc pure: version(LDC) { - enum fld_eax(string arg) = "jmp L_" ~ arg ~ "+2; L_" ~ arg ~ ": mov DX, 0x28db;"; // jump to 0xdb,0x28 (fld real ptr[EAX]) - enum fstp_eax = "jmp L_stp+2; L_stp: mov DX, 0x38db;"; // jump to 0xdb,0x38 (fstp real ptr[EAX]) -} -else version(D_InlineAsm_X86_64) -{ - enum fld_eax(string arg) = "fld real ptr [RAX];"; - enum fstp_eax = "fstp real ptr [RAX];"; -} -else version(D_InlineAsm_X86) -{ - enum fld_eax(string arg) = "fld real ptr [EAX];"; - enum fstp_eax = "fstp real ptr [EAX];"; -} + import ldc.llvmasm; -version(D_InlineAsm_X86_64) -{ - // longdouble_soft passed by reference extern(D): private: - string fld_arg(string arg)() - { - return "asm nothrow @nogc pure @trusted { mov RAX, " ~ arg ~ "; " ~ fld_eax!arg ~ " }"; - } - string fstp_arg(string arg)() - { - return "asm nothrow @nogc pure @trusted { mov RAX, " ~ arg ~ "; " ~ fstp_eax ~ " }"; - } - alias fld_parg = fld_arg; - alias fstp_parg = fstp_arg; - string fld_local(string arg)() - { - return "asm nothrow @nogc pure @trusted { lea RAX, " ~ arg ~ "; " ~ fld_eax!arg ~ " }"; - } + string fld_arg (string arg)() { return `__asm("fldt $0", "*m,~{st}", &` ~ arg ~ `);`; } + string fstp_arg (string arg)() { return `__asm("fstpt $0", "*m,~{st}", &` ~ arg ~ `);`; } + string fld_parg (string arg)() { return `__asm("fldt $0", "*m,~{st}", ` ~ arg ~ `);`; } + string fstp_parg(string arg)() { return `__asm("fstpt $0", "*m,~{st}", ` ~ arg ~ `);`; } + alias fld_local = fld_arg; } -else version(D_InlineAsm_X86) +else { - // longdouble_soft passed by value - extern(D): - private: - string fld_arg(string arg)() + version(D_InlineAsm_X86_64) { - return "asm nothrow @nogc pure @trusted { lea EAX, " ~ arg ~ "; " ~ fld_eax!arg ~ " }"; + enum fld_eax(string arg) = "fld real ptr [RAX];"; + enum fstp_eax = "fstp real ptr [RAX];"; } - string fstp_arg(string arg)() + else version(D_InlineAsm_X86) { - return "asm nothrow @nogc pure @trusted { lea EAX, " ~ arg ~ "; " ~ fstp_eax ~ " }"; + enum fld_eax(string arg) = "fld real ptr [EAX];"; + enum fstp_eax = "fstp real ptr [EAX];"; } - string fld_parg(string arg)() + + version(D_InlineAsm_X86_64) { - return "asm nothrow @nogc pure @trusted { mov EAX, " ~ arg ~ "; " ~ fld_eax!arg ~ " }"; + // longdouble_soft passed by reference + extern(D): + private: + string fld_arg(string arg)() + { + return "asm nothrow @nogc pure @trusted { mov RAX, " ~ arg ~ "; " ~ fld_eax!arg ~ " }"; + } + string fstp_arg(string arg)() + { + return "asm nothrow @nogc pure @trusted { mov RAX, " ~ arg ~ "; " ~ fstp_eax ~ " }"; + } + alias fld_parg = fld_arg; + alias fstp_parg = fstp_arg; + string fld_local(string arg)() + { + return "asm nothrow @nogc pure @trusted { lea RAX, " ~ arg ~ "; " ~ fld_eax!arg ~ " }"; + } } - string fstp_parg(string arg)() + else version(D_InlineAsm_X86) { - return "asm nothrow @nogc pure @trusted { mov EAX, " ~ arg ~ "; " ~ fstp_eax ~ " }"; + // longdouble_soft passed by value + extern(D): + private: + string fld_arg(string arg)() + { + return "asm nothrow @nogc pure @trusted { lea EAX, " ~ arg ~ "; " ~ fld_eax!arg ~ " }"; + } + string fstp_arg(string arg)() + { + return "asm nothrow @nogc pure @trusted { lea EAX, " ~ arg ~ "; " ~ fstp_eax ~ " }"; + } + string fld_parg(string arg)() + { + return "asm nothrow @nogc pure @trusted { mov EAX, " ~ arg ~ "; " ~ fld_eax!arg ~ " }"; + } + string fstp_parg(string arg)() + { + return "asm nothrow @nogc pure @trusted { mov EAX, " ~ arg ~ "; " ~ fstp_eax ~ " }"; + } + alias fld_local = fld_arg; } - alias fld_local = fld_arg; -} +} // !LDC double ld_read(const longdouble_soft* pthis) { @@ -324,7 +334,7 @@ void ld_setull(longdouble_soft* pthis, ulong d) d ^= (1L << 63); version(AsmX86) { - mixin(fld_local!("twoPow63")); + mixin(fld_local!("_twoPow63")); asm nothrow @nogc pure @trusted { fild qword ptr d; @@ -682,7 +692,9 @@ __gshared longdouble_soft ld_pi2 = longdouble_soft(0xc90fdaa22168c235UL, 0x4 __gshared longdouble_soft ld_piOver2 = longdouble_soft(0xc90fdaa22168c235UL, 0x3fff); __gshared longdouble_soft ld_piOver4 = longdouble_soft(0xc90fdaa22168c235UL, 0x3ffe); -__gshared longdouble_soft twoPow63 = longdouble_soft(1UL << 63, 0x3fff + 63); +// accessible by pure ld_setull(): +private extern(D) __gshared immutable longdouble_soft _twoPow63 = longdouble_soft(1UL << 63, 0x3fff + 63); +__gshared longdouble_soft twoPow63 = _twoPow63; ////////////////////////////////////////////////////////////// diff --git a/tests/d2/dmd-testsuite b/tests/d2/dmd-testsuite index cf5d53b1139..732dfe13760 160000 --- a/tests/d2/dmd-testsuite +++ b/tests/d2/dmd-testsuite @@ -1 +1 @@ -Subproject commit cf5d53b1139de8615750c2512b1bb69031576895 +Subproject commit 732dfe137606c398786b17490cf087f7c6620ad6 From 306bdb7dfad2f26898e4126699236426bfb01a8f Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Thu, 21 Jun 2018 03:31:22 +0200 Subject: [PATCH 08/36] Allow all linkages for implicit ABI returns of naked functions And revise the Win64 ABI return rules in this function. Required to make dmd/root/longdouble.d (mostly extern(C++)) compile. --- gen/naked.cpp | 73 ++++++++++++++++++++++++++------------------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/gen/naked.cpp b/gen/naked.cpp index 954f842b803..72ea34da4d8 100644 --- a/gen/naked.cpp +++ b/gen/naked.cpp @@ -275,14 +275,11 @@ void emitABIReturnAsmStmt(IRAsmBlock *asmblock, Loc &loc, // FIXME: This should probably be handled by the TargetABI somehow. // It should be able to do this for a greater variety of types. - // x86 - if (global.params.targetTriple->getArch() == llvm::Triple::x86) { - const LINK l = fdecl->linkage; - (void)l; - assert((l == LINKd || l == LINKc || l == LINKwindows) && - "invalid linkage for asm implicit return"); + const auto &triple = *global.params.targetTriple; + Type *const rt = fdecl->type->nextOf()->toBasetype(); - Type *rt = fdecl->type->nextOf()->toBasetype(); + // x86 + if (triple.getArch() == llvm::Triple::x86) { if (rt->isintegral() || rt->ty == Tpointer || rt->ty == Tclass || rt->ty == Taarray) { if (rt->size() == 8) { @@ -297,11 +294,11 @@ void emitABIReturnAsmStmt(IRAsmBlock *asmblock, Loc &loc, as->out_c = "={st},={st(1)},"; asmblock->retn = 2; } else if (rt->ty == Tcomplex32) { - // extern(C) cfloat is return as i64 + // non-extern(D) cfloat is returned as i64 as->out_c = "=A,"; asmblock->retty = LLType::getInt64Ty(gIR->context()); } else { - // cdouble and creal extern(C) are returned in pointer + // non-extern(D) cdouble and creal are returned via sret // don't add anything! asmblock->retty = LLType::getVoidTy(gIR->context()); asmblock->retn = 0; @@ -346,40 +343,43 @@ void emitABIReturnAsmStmt(IRAsmBlock *asmblock, Loc &loc, } // x86_64 - else if (global.params.targetTriple->getArch() == llvm::Triple::x86_64) { - LINK l = fdecl->linkage; - /* TODO: Check if this works with extern(Windows), completely untested. - * In particular, returning cdouble may not work with - * extern(Windows) since according to X86CallingConv.td it - * doesn't allow XMM1 to be used. - * (So is extern(C), but that should be fine as the calling convention - * is identical to that of extern(D)) - */ - assert((l == LINKd || l == LINKc || l == LINKwindows) && - "invalid linkage for asm implicit return"); - - Type *rt = fdecl->type->nextOf()->toBasetype(); + else if (triple.getArch() == llvm::Triple::x86_64) { if (rt->isintegral() || rt->ty == Tpointer || rt->ty == Tclass || rt->ty == Taarray) { as->out_c = "={ax},"; } else if (rt->isfloating()) { - if (rt == Type::tcomplex80) { + const bool isWin64 = triple.isOSWindows(); + + if (rt == Type::tcomplex80 && !isWin64) { // On x87 stack, re=st, im=st(1) as->out_c = "={st},={st(1)},"; asmblock->retn = 2; - } else if (rt == Type::tfloat80 || rt == Type::timaginary80) { + } else if ((rt == Type::tfloat80 || rt == Type::timaginary80) && + !triple.isWindowsMSVCEnvironment()) { // On x87 stack as->out_c = "={st},"; - } else if (l != LINKd && rt == Type::tcomplex32) { - // LLVM and GCC disagree on how to return {float, float}. - // For compatibility, use the GCC/LLVM-GCC way for extern(C/Windows) - // extern(C) cfloat -> %xmm0 (extract two floats) - as->out_c = "={xmm0},"; - asmblock->retty = LLType::getDoubleTy(gIR->context()); + } else if (rt == Type::tcomplex32) { + if (isWin64) { + // cfloat on Win64 -> %rax + as->out_c = "={ax},"; + asmblock->retty = LLType::getInt64Ty(gIR->context()); + } else { + // cfloat on Posix -> %xmm0 (extract two floats) + as->out_c = "={xmm0},"; + asmblock->retty = LLType::getDoubleTy(gIR->context()); + } } else if (rt->iscomplex()) { - // cdouble and extern(D) cfloat -> re=%xmm0, im=%xmm1 - as->out_c = "={xmm0},={xmm1},"; - asmblock->retn = 2; + if (isWin64) { + // Win64: cdouble and creal are returned via sret + // don't add anything! + asmblock->retty = LLType::getVoidTy(gIR->context()); + asmblock->retn = 0; + return; + } else { + // cdouble on Posix -> re=%xmm0, im=%xmm1 + as->out_c = "={xmm0},={xmm1},"; + asmblock->retn = 2; + } } else { // Plain float/double/ifloat/idouble as->out_c = "={xmm0},"; @@ -396,9 +396,10 @@ void emitABIReturnAsmStmt(IRAsmBlock *asmblock, Loc &loc, // unsupported else { - error(loc, "this target (%s) does not implement inline asm falling off the " - "end of the function", - global.params.targetTriple->str().c_str()); + error(loc, + "this target (%s) does not implement inline asm falling off the end " + "of the function", + triple.str().c_str()); fatal(); } From 218fe5299ed667c5fced64b3da744464e6fa8cad Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Thu, 21 Jun 2018 20:06:49 +0200 Subject: [PATCH 09/36] Phobos: Fix std.math.logb() for MSVC --- runtime/phobos | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/phobos b/runtime/phobos index 4f26e50a667..1fd82281390 160000 --- a/runtime/phobos +++ b/runtime/phobos @@ -1 +1 @@ -Subproject commit 4f26e50a66709fed4674daf470af77e7c03134ae +Subproject commit 1fd822813909fb3506342d187b49de4641aadd50 From 3aedadc8e4d334182ff97d4eeac0921e21dfa76e Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Thu, 21 Jun 2018 20:13:55 +0200 Subject: [PATCH 10/36] druntime: Cherry-pick upstream typo fix --- runtime/druntime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/druntime b/runtime/druntime index a382ade8d41..1538618ddb7 160000 --- a/runtime/druntime +++ b/runtime/druntime @@ -1 +1 @@ -Subproject commit a382ade8d41fe65236fe5c5c51bdec37af4c4516 +Subproject commit 1538618ddb7ab0c75edfb692e58ac6f46c9d32b3 From 6f7e0d4398d0f8120f1026d43b5a4349bddfe04c Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Thu, 21 Jun 2018 20:14:14 +0200 Subject: [PATCH 11/36] dmd-testsuite: Disable runnable/minimal2.d for now --- tests/d2/dmd-testsuite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/d2/dmd-testsuite b/tests/d2/dmd-testsuite index 732dfe13760..96acc38b0c9 160000 --- a/tests/d2/dmd-testsuite +++ b/tests/d2/dmd-testsuite @@ -1 +1 @@ -Subproject commit 732dfe137606c398786b17490cf087f7c6620ad6 +Subproject commit 96acc38b0c9f7488ebcd289b17468ce9413b9fd6 From 37e0fbcfaeb94fcab66f11aea3f5ec342365eeed Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Thu, 21 Jun 2018 21:00:34 +0200 Subject: [PATCH 12/36] dmd/root/longdouble.d: Fix unittest compile errors Move the unittest block before the module-level `pure:`, as a pure unittest function cannot access mutable globals. --- dmd/root/longdouble.d | 141 ++++++++++++++++++++++-------------------- 1 file changed, 74 insertions(+), 67 deletions(-) diff --git a/dmd/root/longdouble.d b/dmd/root/longdouble.d index 87c7e346d23..bab090da60c 100644 --- a/dmd/root/longdouble.d +++ b/dmd/root/longdouble.d @@ -81,6 +81,71 @@ void ld_clearfpu() } } +////////////////////////////////////////////////////////////// + +unittest // impure due to accessing mutable globals (ld_pi etc.) +{ + import core.stdc.string; + import core.stdc.stdio; + + char[32] buffer; + ld_sprint(buffer.ptr, 'a', ld_pi); + assert(strcmp(buffer.ptr, "0x1.921fb54442d1846ap+1") == 0); + + ld_sprint(buffer.ptr, 'g', longdouble_soft(2.0)); + assert(strcmp(buffer.ptr, "2.00000") == 0); + + ld_sprint(buffer.ptr, 'g', longdouble_soft(1234567.89)); + assert(strcmp(buffer.ptr, "1.23457e+06") == 0); + + longdouble_soft ldb = longdouble_soft(0.4); + long b = cast(long)ldb; + assert(b == 0); + + b = cast(long)longdouble_soft(0.9); + assert(b == 0); + + long x = 0x12345678abcdef78L; + longdouble_soft ldx = longdouble_soft(x); + assert(ldx > ld_zero); + long y = cast(long)ldx; + assert(x == y); + + x = -0x12345678abcdef78L; + ldx = longdouble_soft(x); + assert(ldx < ld_zero); + y = cast(long)ldx; + assert(x == y); + + ulong u = 0x12345678abcdef78L; + longdouble_soft ldu = longdouble_soft(u); + assert(ldu > ld_zero); + ulong v = cast(ulong)ldu; + assert(u == v); + + u = 0xf234567812345678UL; + ldu = longdouble_soft(u); + assert(ldu > ld_zero); + v = cast(ulong)ldu; + assert(u == v); + + u = 0xf2345678; + ldu = longdouble_soft(u); + ldu = ldu * ldu; + ldu = sqrt(ldu); + v = cast(ulong)ldu; + assert(u == v); + + u = 0x123456789A; + ldu = longdouble_soft(u); + ldu = ldu * longdouble_soft(1L << 23); + v = cast(ulong)ldu; + u = u * (1L << 23); + assert(u == v); +} + +////////////////////////////////////////////////////////////// + pure: @trusted: // LDC: LLVM __asm is @system AND requires taking the address of variables @@ -334,7 +399,14 @@ void ld_setull(longdouble_soft* pthis, ulong d) d ^= (1L << 63); version(AsmX86) { - mixin(fld_local!("_twoPow63")); + version (LDC) + { + // cannot access mutable global in pure function + static __gshared immutable _twoPow63 = longdouble_soft(1UL << 63, 0x3fff + 63); + mixin(fld_local!("_twoPow63")); + } + else + mixin(fld_local!("twoPow63")); asm nothrow @nogc pure @trusted { fild qword ptr d; @@ -692,9 +764,7 @@ __gshared longdouble_soft ld_pi2 = longdouble_soft(0xc90fdaa22168c235UL, 0x4 __gshared longdouble_soft ld_piOver2 = longdouble_soft(0xc90fdaa22168c235UL, 0x3fff); __gshared longdouble_soft ld_piOver4 = longdouble_soft(0xc90fdaa22168c235UL, 0x3ffe); -// accessible by pure ld_setull(): -private extern(D) __gshared immutable longdouble_soft _twoPow63 = longdouble_soft(1UL << 63, 0x3fff + 63); -__gshared longdouble_soft twoPow63 = _twoPow63; +__gshared longdouble_soft twoPow63 = longdouble_soft(1UL << 63, 0x3fff + 63); ////////////////////////////////////////////////////////////// @@ -790,66 +860,3 @@ size_t ld_sprint(char* str, int fmt, longdouble_soft x) @system str[len] = 0; return len; } - -////////////////////////////////////////////////////////////// - -unittest -{ - import core.stdc.string; - import core.stdc.stdio; - - char[32] buffer; - ld_sprint(buffer.ptr, 'a', ld_pi); - assert(strcmp(buffer.ptr, "0x1.921fb54442d1846ap+1") == 0); - - ld_sprint(buffer.ptr, 'g', longdouble_soft(2.0)); - assert(strcmp(buffer.ptr, "2.00000") == 0); - - ld_sprint(buffer.ptr, 'g', longdouble_soft(1234567.89)); - assert(strcmp(buffer.ptr, "1.23457e+06") == 0); - - longdouble_soft ldb = longdouble_soft(0.4); - long b = cast(long)ldb; - assert(b == 0); - - b = cast(long)longdouble_soft(0.9); - assert(b == 0); - - long x = 0x12345678abcdef78L; - longdouble_soft ldx = longdouble_soft(x); - assert(ldx > ld_zero); - long y = cast(long)ldx; - assert(x == y); - - x = -0x12345678abcdef78L; - ldx = longdouble_soft(x); - assert(ldx < ld_zero); - y = cast(long)ldx; - assert(x == y); - - ulong u = 0x12345678abcdef78L; - longdouble_soft ldu = longdouble_soft(u); - assert(ldu > ld_zero); - ulong v = cast(ulong)ldu; - assert(u == v); - - u = 0xf234567812345678UL; - ldu = longdouble_soft(u); - assert(ldu > ld_zero); - v = cast(ulong)ldu; - assert(u == v); - - u = 0xf2345678; - ldu = longdouble_soft(u); - ldu = ldu * ldu; - ldu = sqrt(ldu); - v = cast(ulong)ldu; - assert(u == v); - - u = 0x123456789A; - ldu = longdouble_soft(u); - ldu = ldu * longdouble_soft(1L << 23); - v = cast(ulong)ldu; - u = u * (1L << 23); - assert(u == v); -} From 75fda8912347cd6ea5036fdcddcf83e50616a9b6 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Thu, 21 Jun 2018 21:50:28 +0200 Subject: [PATCH 13/36] dmd-testsuite: Also disable _compilable_/minimal2.d --- tests/d2/dmd-testsuite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/d2/dmd-testsuite b/tests/d2/dmd-testsuite index 96acc38b0c9..9d6d95ef7d6 160000 --- a/tests/d2/dmd-testsuite +++ b/tests/d2/dmd-testsuite @@ -1 +1 @@ -Subproject commit 96acc38b0c9f7488ebcd289b17468ce9413b9fd6 +Subproject commit 9d6d95ef7d608399a580d6106901934794403009 From 7548b2b89b1f313ab9de6ab835faac5447d3d663 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 23 Jun 2018 01:22:10 +0200 Subject: [PATCH 14/36] Merge upstream stable (663625bce0) --- dmd/aggregate.h | 3 ++- dmd/clone.d | 9 ++++++--- dmd/dclass.d | 17 ++++++++++++++++- dmd/expressionsem.d | 37 ++++++++++++++++++++++++++++++++++++- runtime/druntime | 2 +- runtime/phobos | 2 +- tests/d2/dmd-testsuite | 2 +- 7 files changed, 63 insertions(+), 9 deletions(-) diff --git a/dmd/aggregate.h b/dmd/aggregate.h index 73b279bb9f4..88fc46e557e 100644 --- a/dmd/aggregate.h +++ b/dmd/aggregate.h @@ -324,7 +324,8 @@ class ClassDeclaration : public AggregateDeclaration void addLocalClass(ClassDeclarations *); // Back end - Symbol *vtblsym; + Dsymbol *vtblsym; + Dsymbol *vtblSymbol(); ClassDeclaration *isClassDeclaration() { return (ClassDeclaration *)this; } void accept(Visitor *v) { v->visit(this); } diff --git a/dmd/clone.d b/dmd/clone.d index 1efc9b62c59..1b45934a737 100644 --- a/dmd/clone.d +++ b/dmd/clone.d @@ -1056,11 +1056,12 @@ DtorDeclaration buildExternDDtor(AggregateDeclaration ad, Scope* sc) if (!dtor) return null; - // the only ABI known to be incompatible is Windows/x86 - if (ad.classKind != ClassKind.cpp || !global.params.isWindows || global.params.is64bit) + // ABI incompatible on all (?) x86 32-bit platforms + if (ad.classKind != ClassKind.cpp || global.params.is64bit) return dtor; - // generate member function that adjusts calling convention (EAX used instead of ECX for 'this'): + // generate member function that adjusts calling convention + // (EAX used for 'this' instead of ECX on Windows/stack on others): // extern(D) void __ticppdtor() // { // Class.__dtor(); @@ -1073,6 +1074,7 @@ DtorDeclaration buildExternDDtor(AggregateDeclaration ad, Scope* sc) call.directcall = true; // non-virtual call Class.__dtor(); func.fbody = new ExpStatement(dtor.loc, call); func.generated = true; + func.storage_class |= STC.inference; auto sc2 = sc.push(); sc2.stc &= ~STC.static_; // not a static destructor @@ -1081,6 +1083,7 @@ DtorDeclaration buildExternDDtor(AggregateDeclaration ad, Scope* sc) ad.members.push(func); func.addMember(sc2, ad); func.dsymbolSemantic(sc2); + func.functionSemantic(); // to infer attributes sc2.pop(); return func; diff --git a/dmd/dclass.d b/dmd/dclass.d index b2349548f2c..a57edfbfa1e 100644 --- a/dmd/dclass.d +++ b/dmd/dclass.d @@ -973,7 +973,22 @@ extern (C++) class ClassDeclaration : AggregateDeclaration } // Back end - Symbol* vtblsym; + Dsymbol vtblsym; + + final Dsymbol vtblSymbol() + { + if (!vtblsym) + { + auto vtype = Type.tvoidptr.immutableOf(); + auto var = new VarDeclaration(loc, vtype, Identifier.idPool("__vtbl"), null, STC.immutable_ | STC.static_); + var.addMember(null, this); + var.isdataseg = 1; + var.linkage = LINK.d; + var.semanticRun = PASS.semanticdone; // no more semantic wanted + vtblsym = var; + } + return vtblsym; + } override final inout(ClassDeclaration) isClassDeclaration() inout { diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index a9e177e3c34..8467ee41bc1 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -606,6 +606,14 @@ private bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type tthis, arguments.push(arg); nargs++; } + else + { + if (arg.op == TOK.default_) + { + arg = arg.resolveLoc(loc, sc); + (*arguments)[i] = arg; + } + } if (tf.varargs == 2 && i + 1 == nparams) // https://dlang.org/spec/function.html#variadic { @@ -3109,6 +3117,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return f; } + bool isSuper = false; if (exp.e1.op == TOK.dotVariable && t1.ty == Tfunction || exp.e1.op == TOK.dotTemplateDeclaration) { UnaExp ue = cast(UnaExp)exp.e1; @@ -3254,7 +3263,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor auto ad = sc.func ? sc.func.isThis() : null; auto cd = ad ? ad.isClassDeclaration() : null; - const bool isSuper = exp.e1.op == TOK.super_; + isSuper = exp.e1.op == TOK.super_; if (isSuper) { // Base class constructor call @@ -3592,6 +3601,32 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } result = Expression.combine(argprefix, exp); + + if (isSuper) + { + auto ad = sc.func ? sc.func.isThis() : null; + auto cd = ad ? ad.isClassDeclaration() : null; + if (cd && cd.classKind == ClassKind.cpp) + { + // if super is defined in C++, it sets the vtable pointer to the base class + // so we have to rewrite it, but still return 'this' from super() call: + // (auto tmp = super(), this.__vptr = __vtbl, tmp) + __gshared int superid = 0; + char[20] buf; + sprintf(buf.ptr, "__super%d", superid++); + auto tmp = copyToTemp(0, buf.ptr, result); + Loc loc = exp.loc; + Expression tmpdecl = new DeclarationExp(loc, tmp); + + auto dse = new DsymbolExp(loc, cd.vtblSymbol()); + auto ase = new AddrExp(loc, dse); + auto pte = new DotIdExp(loc, new ThisExp(loc), Id.__vptr); + auto ate = new AssignExp(loc, pte, ase); + + Expression e = new CommaExp(loc, new CommaExp(loc, tmpdecl, ate), new VarExp(loc, tmp)); + result = e.expressionSemantic(sc); + } + } } override void visit(DeclarationExp e) diff --git a/runtime/druntime b/runtime/druntime index 1538618ddb7..4ae24817959 160000 --- a/runtime/druntime +++ b/runtime/druntime @@ -1 +1 @@ -Subproject commit 1538618ddb7ab0c75edfb692e58ac6f46c9d32b3 +Subproject commit 4ae24817959abd87e65be01250c295ff8674fe7b diff --git a/runtime/phobos b/runtime/phobos index 1fd82281390..13a7b4fe9d8 160000 --- a/runtime/phobos +++ b/runtime/phobos @@ -1 +1 @@ -Subproject commit 1fd822813909fb3506342d187b49de4641aadd50 +Subproject commit 13a7b4fe9d846c1f1ac3982a63f926e65e42f977 diff --git a/tests/d2/dmd-testsuite b/tests/d2/dmd-testsuite index 9d6d95ef7d6..4aff81d26d7 160000 --- a/tests/d2/dmd-testsuite +++ b/tests/d2/dmd-testsuite @@ -1 +1 @@ -Subproject commit 9d6d95ef7d608399a580d6106901934794403009 +Subproject commit 4aff81d26d72b0bc3ebe3f9c48fc68e5b93cf4e2 From ef1e655767863b75a8872d263d47a6d178d4bb69 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 23 Jun 2018 15:35:41 +0200 Subject: [PATCH 15/36] Handle new special vtbl symbol There's a new need to access a class' vtable symbol, see dlang/dmd#8362. Use it as alias to the actual vtable symbol with different type (dummy: `i8*`, actual: `[N x i8*]`) and mangled name. I tried matching the special symbol's mangled name and using an appropriate static array front-end type for it, but then casting the symbol address for the assignment leads to issues if the ctor is @safe. So I decided to handle it in DtoSymbolAddress(). Unfortunately, this seems not to solve the extern(C++) issues exposed by LDC self-compilation yet. --- gen/llvmhelpers.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index f487c090633..ea928d83782 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -1574,6 +1574,14 @@ DValue *DtoSymbolAddress(Loc &loc, Type *type, Declaration *decl) { } return new DImValue(type, m); } + // special vtbl symbol, used by LDC as alias to the actual vtbl (with + // different type and mangled name) + if (vd->isClassMember() && vd == vd->isClassMember()->vtblsym) { + Logger::println("vtbl symbol"); + auto cd = vd->isClassMember(); + return new DLValue( + type, DtoBitCast(getIrAggr(cd)->getVtblSymbol(), DtoPtrToType(type))); + } // nested variable if (vd->nestedrefs.dim) { Logger::println("nested variable"); From 14c74efb7ac07c757a94fad7de9c0f7a2df3af31 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 23 Jun 2018 17:19:28 +0200 Subject: [PATCH 16/36] Support new explicitly direct calls of struct methods dlang/dmd#8359 introduces direct call expressions for struct methods (which cannot be virtual anyway and are so always called directly). Our previous code relied on the instance being a class. There are multiple ways to fix this; I went with this one. --- gen/toir.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gen/toir.cpp b/gen/toir.cpp index 3a6ad903d81..b36c33949ba 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -718,7 +718,11 @@ class ToElemVisitor : public Visitor { FuncDeclaration *fdecl = dve->var->isFuncDeclaration(); assert(fdecl); DtoDeclareFunction(fdecl); - fnval = new DFuncValue(fdecl, DtoCallee(fdecl), DtoRVal(dve->e1)); + Expression *thisExp = dve->e1; + LLValue *thisArg = thisExp->type->toBasetype()->ty == Tclass + ? DtoRVal(thisExp) + : DtoLVal(thisExp); // when calling a struct method + fnval = new DFuncValue(fdecl, DtoCallee(fdecl), thisArg); } else { fnval = toElem(e->e1); } From 9aae50e2bb39100ef256e13d92268826971e4bbc Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 23 Jun 2018 18:44:49 +0200 Subject: [PATCH 17/36] dmd.dsymbol: Make sure LDC-specific Dsymbol dtor is not virtual It's the only dtor for all front-end classes. Make sure it doesn't mess up the vtable, as dtors of extern(C++) classes now need an explicit `final` to be non-virtual. This fixes the self-compilation issues. --- dmd/dsymbol.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dmd/dsymbol.d b/dmd/dsymbol.d index 09f940d734a..63d986b41c8 100644 --- a/dmd/dsymbol.d +++ b/dmd/dsymbol.d @@ -232,7 +232,7 @@ extern (C++) class Dsymbol : RootObject version(IN_LLVM) { - extern (D) ~this() + extern (D) final ~this() { deleteIrDsymbol(this.ir); this.ir = null; From 7dbd07dd1e696b623d5f82ab32fb7f96caa5fa15 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 23 Jun 2018 19:31:05 +0200 Subject: [PATCH 18/36] Use new extern(D) dtor shims for struct/class TypeInfos The TypeInfo may need an extern(D) wrapper accounting for ABI differences wrt. extern(C++) dtor implementations. This fixes `runnable/cppa.d` for 32-bit x86 targets. --- gen/classes.cpp | 2 +- gen/typinf.cpp | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/gen/classes.cpp b/gen/classes.cpp index a512b8f1973..6efe4406dcb 100644 --- a/gen/classes.cpp +++ b/gen/classes.cpp @@ -529,7 +529,7 @@ static LLConstant *build_offti_array(ClassDeclaration *cd, LLType *arrayT) { #endif // GENERATE_OFFTI static LLConstant *build_class_dtor(ClassDeclaration *cd) { - FuncDeclaration *dtor = cd->dtor; + FuncDeclaration *dtor = cd->tidtor; // if no destructor emit a null if (!dtor) { diff --git a/gen/typinf.cpp b/gen/typinf.cpp index 1085b7bb251..903ec1024a8 100644 --- a/gen/typinf.cpp +++ b/gen/typinf.cpp @@ -382,6 +382,9 @@ class LLVMDefineVisitor : public Visitor { if (sd->dtor && sd->dtor->semanticRun >= PASSsemantic3) { Declaration_codegen(sd->dtor); } + if (sd->tidtor && sd->tidtor->semanticRun >= PASSsemantic3) { + Declaration_codegen(sd->tidtor); + } } } @@ -422,7 +425,7 @@ class LLVMDefineVisitor : public Visitor { b.push_uint(hasptrs); // function xdtor/xdtorti - b.push_funcptr(sd->dtor); + b.push_funcptr(sd->tidtor); // function xpostblit FuncDeclaration *xpostblit = sd->postblit; From 77e4b38aadf2d317d604bab46d099321f0793152 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 23 Jun 2018 22:20:06 +0200 Subject: [PATCH 19/36] dmd-testsuite: Fix runnable/paranoia.sh for LDC and Win32 --- tests/d2/dmd-testsuite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/d2/dmd-testsuite b/tests/d2/dmd-testsuite index 4aff81d26d7..f1752d2f8dc 160000 --- a/tests/d2/dmd-testsuite +++ b/tests/d2/dmd-testsuite @@ -1 +1 @@ -Subproject commit 4aff81d26d72b0bc3ebe3f9c48fc68e5b93cf4e2 +Subproject commit f1752d2f8dcd103bf9f042b38fba860b287ff3f8 From 64f4ee92a67cb062701cc7db32281ab576ce470c Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 24 Jun 2018 00:13:02 +0200 Subject: [PATCH 20/36] Emit debuginfo for NRVO/result variables It may likely show up as garbage, as it's not a real local variable (allocated by caller, address mostly passed in a register); e.g., this happens on Win64 with the VS debugger ('expression is not an address' or something along these lines), but at least output *some* DI. We may be able to fix this and similar issues with LLVM 7 and new intrinsic llvm.dbg.addr(). --- gen/llvmhelpers.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index ea928d83782..60b38ffe6b0 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -978,6 +978,7 @@ void DtoVarDeclaration(VarDeclaration *vd) { // } assert(!isSpecialRefVar(vd) && "Can this happen?"); getIrLocal(vd, true)->value = gIR->func()->sretArg; + gIR->DBuilder.EmitLocalVariable(gIR->func()->sretArg, vd); } else { // normal stack variable, allocate storage on the stack if it has not // already been done From 6c7cf88cc230d8be12f529940a9ea5923457a0d9 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 24 Jun 2018 03:24:46 +0200 Subject: [PATCH 21/36] Clean up dmd/root/longdouble.{d,h} --- dmd/root/longdouble.d | 257 +++++++++++++++++++----------------------- dmd/root/longdouble.h | 60 +++++----- 2 files changed, 147 insertions(+), 170 deletions(-) diff --git a/dmd/root/longdouble.d b/dmd/root/longdouble.d index bab090da60c..49f7243005d 100644 --- a/dmd/root/longdouble.d +++ b/dmd/root/longdouble.d @@ -81,71 +81,6 @@ void ld_clearfpu() } } -////////////////////////////////////////////////////////////// - -unittest // impure due to accessing mutable globals (ld_pi etc.) -{ - import core.stdc.string; - import core.stdc.stdio; - - char[32] buffer; - ld_sprint(buffer.ptr, 'a', ld_pi); - assert(strcmp(buffer.ptr, "0x1.921fb54442d1846ap+1") == 0); - - ld_sprint(buffer.ptr, 'g', longdouble_soft(2.0)); - assert(strcmp(buffer.ptr, "2.00000") == 0); - - ld_sprint(buffer.ptr, 'g', longdouble_soft(1234567.89)); - assert(strcmp(buffer.ptr, "1.23457e+06") == 0); - - longdouble_soft ldb = longdouble_soft(0.4); - long b = cast(long)ldb; - assert(b == 0); - - b = cast(long)longdouble_soft(0.9); - assert(b == 0); - - long x = 0x12345678abcdef78L; - longdouble_soft ldx = longdouble_soft(x); - assert(ldx > ld_zero); - long y = cast(long)ldx; - assert(x == y); - - x = -0x12345678abcdef78L; - ldx = longdouble_soft(x); - assert(ldx < ld_zero); - y = cast(long)ldx; - assert(x == y); - - ulong u = 0x12345678abcdef78L; - longdouble_soft ldu = longdouble_soft(u); - assert(ldu > ld_zero); - ulong v = cast(ulong)ldu; - assert(u == v); - - u = 0xf234567812345678UL; - ldu = longdouble_soft(u); - assert(ldu > ld_zero); - v = cast(ulong)ldu; - assert(u == v); - - u = 0xf2345678; - ldu = longdouble_soft(u); - ldu = ldu * ldu; - ldu = sqrt(ldu); - v = cast(ulong)ldu; - assert(u == v); - - u = 0x123456789A; - ldu = longdouble_soft(u); - ldu = ldu * longdouble_soft(1L << 23); - v = cast(ulong)ldu; - u = u * (1L << 23); - assert(u == v); -} - -////////////////////////////////////////////////////////////// - pure: @trusted: // LDC: LLVM __asm is @system AND requires taking the address of variables @@ -255,69 +190,49 @@ version(LDC) extern(D): private: - string fld_arg (string arg)() { return `__asm("fldt $0", "*m,~{st}", &` ~ arg ~ `);`; } - string fstp_arg (string arg)() { return `__asm("fstpt $0", "*m,~{st}", &` ~ arg ~ `);`; } - string fld_parg (string arg)() { return `__asm("fldt $0", "*m,~{st}", ` ~ arg ~ `);`; } - string fstp_parg(string arg)() { return `__asm("fstpt $0", "*m,~{st}", ` ~ arg ~ `);`; } - alias fld_local = fld_arg; + string fld_arg (string arg)() { return `__asm("fldt $0", "*m,~{st}", &` ~ arg ~ `);`; } + string fstp_arg (string arg)() { return `__asm("fstpt $0", "=*m,~{st}", &` ~ arg ~ `);`; } + string fld_parg (string arg)() { return `__asm("fldt $0", "*m,~{st}", ` ~ arg ~ `);`; } + string fstp_parg(string arg)() { return `__asm("fstpt $0", "=*m,~{st}", ` ~ arg ~ `);`; } } -else +else version(D_InlineAsm_X86_64) { - version(D_InlineAsm_X86_64) + // longdouble_soft passed by reference + extern(D): + private: + string fld_arg(string arg)() { - enum fld_eax(string arg) = "fld real ptr [RAX];"; - enum fstp_eax = "fstp real ptr [RAX];"; + return "asm nothrow @nogc pure @trusted { mov RAX, " ~ arg ~ "; fld real ptr [RAX]; }"; } - else version(D_InlineAsm_X86) + string fstp_arg(string arg)() { - enum fld_eax(string arg) = "fld real ptr [EAX];"; - enum fstp_eax = "fstp real ptr [EAX];"; + return "asm nothrow @nogc pure @trusted { mov RAX, " ~ arg ~ "; fstp real ptr [RAX]; }"; } - - version(D_InlineAsm_X86_64) + alias fld_parg = fld_arg; + alias fstp_parg = fstp_arg; +} +else version(D_InlineAsm_X86) +{ + // longdouble_soft passed by value + extern(D): + private: + string fld_arg(string arg)() { - // longdouble_soft passed by reference - extern(D): - private: - string fld_arg(string arg)() - { - return "asm nothrow @nogc pure @trusted { mov RAX, " ~ arg ~ "; " ~ fld_eax!arg ~ " }"; - } - string fstp_arg(string arg)() - { - return "asm nothrow @nogc pure @trusted { mov RAX, " ~ arg ~ "; " ~ fstp_eax ~ " }"; - } - alias fld_parg = fld_arg; - alias fstp_parg = fstp_arg; - string fld_local(string arg)() - { - return "asm nothrow @nogc pure @trusted { lea RAX, " ~ arg ~ "; " ~ fld_eax!arg ~ " }"; - } + return "asm nothrow @nogc pure @trusted { lea EAX, " ~ arg ~ "; fld real ptr [EAX]; }"; } - else version(D_InlineAsm_X86) + string fstp_arg(string arg)() { - // longdouble_soft passed by value - extern(D): - private: - string fld_arg(string arg)() - { - return "asm nothrow @nogc pure @trusted { lea EAX, " ~ arg ~ "; " ~ fld_eax!arg ~ " }"; - } - string fstp_arg(string arg)() - { - return "asm nothrow @nogc pure @trusted { lea EAX, " ~ arg ~ "; " ~ fstp_eax ~ " }"; - } - string fld_parg(string arg)() - { - return "asm nothrow @nogc pure @trusted { mov EAX, " ~ arg ~ "; " ~ fld_eax!arg ~ " }"; - } - string fstp_parg(string arg)() - { - return "asm nothrow @nogc pure @trusted { mov EAX, " ~ arg ~ "; " ~ fstp_eax ~ " }"; - } - alias fld_local = fld_arg; + return "asm nothrow @nogc pure @trusted { lea EAX, " ~ arg ~ "; fstp real ptr [EAX]; }"; + } + string fld_parg(string arg)() + { + return "asm nothrow @nogc pure @trusted { mov EAX, " ~ arg ~ "; fld real ptr [EAX]; }"; } -} // !LDC + string fstp_parg(string arg)() + { + return "asm nothrow @nogc pure @trusted { mov EAX, " ~ arg ~ "; fstp real ptr [EAX]; }"; + } +} double ld_read(const longdouble_soft* pthis) { @@ -399,14 +314,8 @@ void ld_setull(longdouble_soft* pthis, ulong d) d ^= (1L << 63); version(AsmX86) { - version (LDC) - { - // cannot access mutable global in pure function - static __gshared immutable _twoPow63 = longdouble_soft(1UL << 63, 0x3fff + 63); - mixin(fld_local!("_twoPow63")); - } - else - mixin(fld_local!("twoPow63")); + auto pTwoPow63 = &twoPow63; + mixin(fld_parg!("pTwoPow63")); asm nothrow @nogc pure @trusted { fild qword ptr d; @@ -748,23 +657,28 @@ longdouble_soft ld_mod(longdouble_soft x, longdouble_soft y) ////////////////////////////////////////////////////////////// -__gshared longdouble_soft ld_qnan = longdouble_soft(0xC000000000000000UL, 0x7fff); -__gshared longdouble_soft ld_snan = longdouble_soft(0xC000000000000001UL, 0x7fff); -__gshared longdouble_soft ld_inf = longdouble_soft(0x8000000000000000UL, 0x7fff); +@safe: -__gshared longdouble_soft ld_zero = longdouble_soft(0, 0); -__gshared longdouble_soft ld_one = longdouble_soft(0x8000000000000000UL, 0x3fff); -__gshared longdouble_soft ld_pi = longdouble_soft(0xc90fdaa22168c235UL, 0x4000); -__gshared longdouble_soft ld_log2t = longdouble_soft(0xd49a784bcd1b8afeUL, 0x4000); -__gshared longdouble_soft ld_log2e = longdouble_soft(0xb8aa3b295c17f0bcUL, 0x3fff); -__gshared longdouble_soft ld_log2 = longdouble_soft(0x9a209a84fbcff799UL, 0x3ffd); -__gshared longdouble_soft ld_ln2 = longdouble_soft(0xb17217f7d1cf79acUL, 0x3ffe); +__gshared const +{ + longdouble_soft ld_qnan = longdouble_soft(0xC000000000000000UL, 0x7fff); + longdouble_soft ld_snan = longdouble_soft(0xC000000000000001UL, 0x7fff); + longdouble_soft ld_inf = longdouble_soft(0x8000000000000000UL, 0x7fff); -__gshared longdouble_soft ld_pi2 = longdouble_soft(0xc90fdaa22168c235UL, 0x4001); -__gshared longdouble_soft ld_piOver2 = longdouble_soft(0xc90fdaa22168c235UL, 0x3fff); -__gshared longdouble_soft ld_piOver4 = longdouble_soft(0xc90fdaa22168c235UL, 0x3ffe); + longdouble_soft ld_zero = longdouble_soft(0, 0); + longdouble_soft ld_one = longdouble_soft(0x8000000000000000UL, 0x3fff); + longdouble_soft ld_pi = longdouble_soft(0xc90fdaa22168c235UL, 0x4000); + longdouble_soft ld_log2t = longdouble_soft(0xd49a784bcd1b8afeUL, 0x4000); + longdouble_soft ld_log2e = longdouble_soft(0xb8aa3b295c17f0bcUL, 0x3fff); + longdouble_soft ld_log2 = longdouble_soft(0x9a209a84fbcff799UL, 0x3ffd); + longdouble_soft ld_ln2 = longdouble_soft(0xb17217f7d1cf79acUL, 0x3ffe); -__gshared longdouble_soft twoPow63 = longdouble_soft(1UL << 63, 0x3fff + 63); + longdouble_soft ld_pi2 = longdouble_soft(0xc90fdaa22168c235UL, 0x4001); + longdouble_soft ld_piOver2 = longdouble_soft(0xc90fdaa22168c235UL, 0x3fff); + longdouble_soft ld_piOver4 = longdouble_soft(0xc90fdaa22168c235UL, 0x3ffe); + + longdouble_soft twoPow63 = longdouble_soft(1UL << 63, 0x3fff + 63); +} ////////////////////////////////////////////////////////////// @@ -860,3 +774,66 @@ size_t ld_sprint(char* str, int fmt, longdouble_soft x) @system str[len] = 0; return len; } + +////////////////////////////////////////////////////////////// + +@system unittest +{ + import core.stdc.string; + import core.stdc.stdio; + + char[32] buffer; + ld_sprint(buffer.ptr, 'a', ld_pi); + assert(strcmp(buffer.ptr, "0x1.921fb54442d1846ap+1") == 0); + + ld_sprint(buffer.ptr, 'g', longdouble_soft(2.0)); + assert(strcmp(buffer.ptr, "2.00000") == 0); + + ld_sprint(buffer.ptr, 'g', longdouble_soft(1234567.89)); + assert(strcmp(buffer.ptr, "1.23457e+06") == 0); + + longdouble_soft ldb = longdouble_soft(0.4); + long b = cast(long)ldb; + assert(b == 0); + + b = cast(long)longdouble_soft(0.9); + assert(b == 0); + + long x = 0x12345678abcdef78L; + longdouble_soft ldx = longdouble_soft(x); + assert(ldx > ld_zero); + long y = cast(long)ldx; + assert(x == y); + + x = -0x12345678abcdef78L; + ldx = longdouble_soft(x); + assert(ldx < ld_zero); + y = cast(long)ldx; + assert(x == y); + + ulong u = 0x12345678abcdef78L; + longdouble_soft ldu = longdouble_soft(u); + assert(ldu > ld_zero); + ulong v = cast(ulong)ldu; + assert(u == v); + + u = 0xf234567812345678UL; + ldu = longdouble_soft(u); + assert(ldu > ld_zero); + v = cast(ulong)ldu; + assert(u == v); + + u = 0xf2345678; + ldu = longdouble_soft(u); + ldu = ldu * ldu; + ldu = sqrt(ldu); + v = cast(ulong)ldu; + assert(u == v); + + u = 0x123456789A; + ldu = longdouble_soft(u); + ldu = ldu * longdouble_soft(1L << 23); + v = cast(ulong)ldu; + u = u * (1L << 23); + assert(u == v); +} diff --git a/dmd/root/longdouble.h b/dmd/root/longdouble.h index eb82617c7cd..827c7cf832a 100644 --- a/dmd/root/longdouble.h +++ b/dmd/root/longdouble.h @@ -86,7 +86,6 @@ longdouble_soft ld_sin(longdouble_soft ld1); longdouble_soft ld_cos(longdouble_soft ld1); longdouble_soft ld_tan(longdouble_soft ld1); -#pragma pack(push, 1) struct longdouble_soft { unsigned long long mantissa; @@ -118,26 +117,23 @@ struct longdouble_soft void set(unsigned long long d) { ld_setull(this, d); } void set(bool d) { ld_set(this, d); } - operator float () { return ld_read(this); } - operator double () { return ld_read(this); } - - operator signed char () { return ld_read(this); } - operator short () { return ld_read(this); } - operator int () { return ld_read(this); } - operator long () { return ld_read(this); } - operator long long () { return ld_readll(this); } - - operator unsigned char () { return ld_read(this); } - operator unsigned short () { return ld_read(this); } - operator unsigned int () { return ld_read(this); } - operator unsigned long () { return ld_read(this); } - operator unsigned long long() { return ld_readull(this); } - operator bool () { return mantissa != 0 || exponent != 0; } // correct? + operator float () const { return ld_read(this); } + operator double () const { return ld_read(this); } + + operator signed char () const { return ld_read(this); } + operator short () const { return ld_read(this); } + operator int () const { return ld_read(this); } + operator long () const { return ld_read(this); } + operator long long () const { return ld_readll(this); } + + operator unsigned char () const { return ld_read(this); } + operator unsigned short () const { return ld_read(this); } + operator unsigned int () const { return ld_read(this); } + operator unsigned long () const { return ld_read(this); } + operator unsigned long long() const { return ld_readull(this); } + operator bool () const { return mantissa != 0 || exponent != 0; } // correct? }; -#pragma pack(pop) -// static_assert(sizeof(longdouble_soft) == 10, "bad sizeof longdouble_soft"); - inline longdouble_soft ldouble(unsigned long long mantissa, int exp, int sign = 0) { longdouble_soft d; @@ -237,17 +233,21 @@ inline longdouble_soft sqrt (longdouble_soft ld) { return sqrtl(ld); } #define LDBL_MAX_10_EXP 4932 #define LDBL_MIN_10_EXP (-4932) -extern longdouble_soft ld_zero; -extern longdouble_soft ld_one; -extern longdouble_soft ld_pi; -extern longdouble_soft ld_log2t; -extern longdouble_soft ld_log2e; -extern longdouble_soft ld_log2; -extern longdouble_soft ld_ln2; - -extern longdouble_soft ld_inf; -extern longdouble_soft ld_qnan; -extern longdouble_soft ld_snan; +extern const longdouble_soft ld_qnan; +extern const longdouble_soft ld_snan; +extern const longdouble_soft ld_inf; + +extern const longdouble_soft ld_zero; +extern const longdouble_soft ld_one; +extern const longdouble_soft ld_pi; +extern const longdouble_soft ld_log2t; +extern const longdouble_soft ld_log2e; +extern const longdouble_soft ld_log2; +extern const longdouble_soft ld_ln2; + +extern const longdouble_soft ld_pi2; +extern const longdouble_soft ld_piOver2; +extern const longdouble_soft ld_piOver4; size_t ld_sprint(char* str, int fmt, longdouble_soft x); From 3da5a12f4ac226ea0130cd41133447056cd92674 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 24 Jun 2018 03:59:14 +0200 Subject: [PATCH 22/36] Use 80-bit compile-time reals on Windows/MSVC hosts Consistent with DMD and enabling seamless cross-compilation to Posix x86(_64) targets. --- dmd/root/ctfloat.d | 31 ++++++------- dmd/root/ctfloat.h | 6 --- dmd/root/longdouble.h | 3 +- dmd/target.d | 103 +++++++++++++++--------------------------- dmd/target.h | 14 +----- gen/ctfloat.cpp | 2 +- gen/target.cpp | 75 ++++++++++++++++-------------- 7 files changed, 95 insertions(+), 139 deletions(-) diff --git a/dmd/root/ctfloat.d b/dmd/root/ctfloat.d index d30f62d8ac2..3fc31130fa8 100644 --- a/dmd/root/ctfloat.d +++ b/dmd/root/ctfloat.d @@ -21,19 +21,13 @@ import core.stdc.string; nothrow: // Type used by the front-end for compile-time reals -version(IN_LLVM_MSVC) - alias real_t = double; -else version(IN_LLVM) - alias real_t = real; -else - public import dmd.root.longdouble : real_t = longdouble; +public import dmd.root.longdouble : real_t = longdouble; private { version(CRuntime_DigitalMars) __gshared extern (C) extern const(char)* __locale_decpoint; - // IN_LLVM replaced: version(CRuntime_Microsoft) extern (C++) - version(none) extern (C++) + version(CRuntime_Microsoft) extern (C++) { public import dmd.root.longdouble : longdouble_soft, ld_sprint; longdouble_soft strtold_dm(const(char)* p, char** endp); @@ -66,8 +60,7 @@ extern (C++) struct CTFloat assert(0); } - // IN_LLVM: changed from `static if (!is(real_t == real))` - version(none) + static if (!is(real_t == real)) { alias sin = dmd.root.longdouble.sinl; alias cos = dmd.root.longdouble.cosl; @@ -86,8 +79,7 @@ extern (C++) struct CTFloat static real_t ldexp(real_t n, int exp) { return core.math.ldexp(n, exp); } } - // IN_LLVM: changed from `static if (!is(real_t == real))` - version(none) + static if (!is(real_t == real)) { static real_t round(real_t x) { return real_t(cast(double)core.stdc.math.roundl(cast(double)x)); } static real_t floor(real_t x) { return real_t(cast(double)core.stdc.math.floor(cast(double)x)); } @@ -125,8 +117,16 @@ extern (C++) struct CTFloat { static import std.math; - static real_t rint(real_t x) { return std.math.rint(x); } - static real_t nearbyint(real_t x) { return std.math.nearbyint(x); } + static if (!is(real_t == real)) + { + static real_t rint(real_t x) { return real_t(cast(double)std.math.rint(cast(double)x)); } + static real_t nearbyint(real_t x) { return real_t(cast(double)std.math.nearbyint(cast(double)x)); } + } + else + { + static real_t rint(real_t x) { return std.math.rint(x); } + static real_t nearbyint(real_t x) { return std.math.nearbyint(x); } + } static void _init(); @@ -213,8 +213,7 @@ else static int sprint(char* str, char fmt, real_t x) { - // IN_LLVM replaced: version(CRuntime_Microsoft) - version(none) + version(CRuntime_Microsoft) { return cast(int)ld_sprint(str, fmt, longdouble_soft(x)); } diff --git a/dmd/root/ctfloat.h b/dmd/root/ctfloat.h index 707c938a688..2819e9194a1 100644 --- a/dmd/root/ctfloat.h +++ b/dmd/root/ctfloat.h @@ -13,13 +13,7 @@ #include "longdouble.h" // Type used by the front-end for compile-time reals -#if IN_LLVM && _MSC_VER -// Make sure LDC built with MSVC uses double-precision compile-time reals, -// independent of whether it was built with DMD (80-bit reals) or LDC. -typedef double real_t; -#else typedef longdouble real_t; -#endif #if IN_LLVM namespace llvm { class APFloat; } diff --git a/dmd/root/longdouble.h b/dmd/root/longdouble.h index 827c7cf832a..017c390dd55 100644 --- a/dmd/root/longdouble.h +++ b/dmd/root/longdouble.h @@ -11,8 +11,7 @@ #ifndef __LONG_DOUBLE_H__ #define __LONG_DOUBLE_H__ -// LDC: Don't provide 'manual' x87 longdouble when compiling with MS compiler. -#if IN_LLVM || !_MSC_VER // has native 10 byte doubles +#if !_MSC_VER // has native 10 byte doubles #include typedef long double longdouble; typedef volatile long double volatile_longdouble; diff --git a/dmd/target.d b/dmd/target.d index 23d640671d1..31dddee592a 100644 --- a/dmd/target.d +++ b/dmd/target.d @@ -65,40 +65,48 @@ struct Target bool twoDtorInVtable; /// target C++ ABI puts deleting and non-deleting destructor into vtable } - version(IN_LLVM) - { - extern (C++): - - struct FPTypeProperties + /** + * Values representing all properties for floating point types + */ + extern (C++) struct FPTypeProperties(T) { - real_t max, min_normal, nan, snan, infinity, epsilon; - d_int64 dig, mant_dig, max_exp, min_exp, max_10_exp, min_10_exp; - - static FPTypeProperties fromDHostCompiler(T)() + static __gshared { - FPTypeProperties p; - - p.max = T.max; - p.min_normal = T.min_normal; - p.nan = T.nan; - p.snan = T.init; - p.infinity = T.infinity; - p.epsilon = T.epsilon; - - p.dig = T.dig; - p.mant_dig = T.mant_dig; - p.max_exp = T.max_exp; - p.min_exp = T.min_exp; - p.max_10_exp = T.max_10_exp; - p.min_10_exp = T.min_10_exp; + real_t max; /// largest representable value that's not infinity + real_t min_normal; /// smallest representable normalized value that's not 0 + real_t nan; /// NaN value + real_t snan; /// signalling NaN value + real_t infinity; /// infinity value + real_t epsilon; /// smallest increment to the value 1 - return p; + d_int64 dig = T.dig; /// number of decimal digits of precision + d_int64 mant_dig = T.mant_dig; /// number of bits in mantissa + d_int64 max_exp = T.max_exp; /// maximum int value such that 2$(SUPERSCRIPT `max_exp-1`) is representable + d_int64 min_exp = T.min_exp; /// minimum int value such that 2$(SUPERSCRIPT `min_exp-1`) is representable as a normalized value + d_int64 max_10_exp = T.max_10_exp; /// maximum int value such that 10$(SUPERSCRIPT `max_10_exp` is representable) + d_int64 min_10_exp = T.min_10_exp; /// minimum int value such that 10$(SUPERSCRIPT `min_10_exp`) is representable as a normalized value + } + static void _init() + { + max = T.max; + min_normal = T.min_normal; + nan = T.nan; + snan = T.init; + infinity = T.infinity; + epsilon = T.epsilon; } } - static __gshared FPTypeProperties FloatProperties = FPTypeProperties.fromDHostCompiler!float(); - static __gshared FPTypeProperties DoubleProperties = FPTypeProperties.fromDHostCompiler!double(); - static __gshared FPTypeProperties RealProperties = FPTypeProperties.fromDHostCompiler!real_t(); + /// + alias FloatProperties = FPTypeProperties!float; + /// + alias DoubleProperties = FPTypeProperties!double; + /// + alias RealProperties = FPTypeProperties!real_t; + + version(IN_LLVM) + { + extern (C++): // implemented in gen/target.cpp: static void _init(); @@ -132,45 +140,6 @@ struct Target } else // !IN_LLVM { - /** - * Values representing all properties for floating point types - */ - extern (C++) struct FPTypeProperties(T) - { - static __gshared - { - real_t max; /// largest representable value that's not infinity - real_t min_normal; /// smallest representable normalized value that's not 0 - real_t nan; /// NaN value - real_t snan; /// signalling NaN value - real_t infinity; /// infinity value - real_t epsilon; /// smallest increment to the value 1 - - d_int64 dig = T.dig; /// number of decimal digits of precision - d_int64 mant_dig = T.mant_dig; /// number of bits in mantissa - d_int64 max_exp = T.max_exp; /// maximum int value such that 2$(SUPERSCRIPT `max_exp-1`) is representable - d_int64 min_exp = T.min_exp; /// minimum int value such that 2$(SUPERSCRIPT `min_exp-1`) is representable as a normalized value - d_int64 max_10_exp = T.max_10_exp; /// maximum int value such that 10$(SUPERSCRIPT `max_10_exp` is representable) - d_int64 min_10_exp = T.min_10_exp; /// minimum int value such that 10$(SUPERSCRIPT `min_10_exp`) is representable as a normalized value - } - static void _init() - { - max = T.max; - min_normal = T.min_normal; - nan = T.nan; - snan = T.init; - infinity = T.infinity; - epsilon = T.epsilon; - } - } - - /// - alias FloatProperties = FPTypeProperties!float; - /// - alias DoubleProperties = FPTypeProperties!double; - /// - alias RealProperties = FPTypeProperties!real_t; - /** * Initialize the Target */ diff --git a/dmd/target.h b/dmd/target.h index 965f268bb61..b63871ba724 100644 --- a/dmd/target.h +++ b/dmd/target.h @@ -48,17 +48,6 @@ struct Target static char uint64Mangle; // mangling character for C++ uint64_t static bool twoDtorInVtable; // target C++ ABI puts deleting and non-deleting destructor into vtable -#if IN_LLVM - struct FPTypeProperties - { - real_t max, min_normal, nan, snan, infinity, epsilon; - d_int64 dig, mant_dig, max_exp, min_exp, max_10_exp, min_10_exp; - }; - - static FPTypeProperties FloatProperties; - static FPTypeProperties DoubleProperties; - static FPTypeProperties RealProperties; -#else template struct FPTypeProperties { @@ -75,12 +64,13 @@ struct Target static d_int64 min_exp; static d_int64 max_10_exp; static d_int64 min_10_exp; + + static void _init(); }; typedef FPTypeProperties FloatProperties; typedef FPTypeProperties DoubleProperties; typedef FPTypeProperties RealProperties; -#endif static void _init(); // Type sizes and support. diff --git a/gen/ctfloat.cpp b/gen/ctfloat.cpp index 89ea8415198..197fffed697 100644 --- a/gen/ctfloat.cpp +++ b/gen/ctfloat.cpp @@ -48,7 +48,7 @@ void CTFloat::_init() { if (sizeof(real_t) == 8) { apSemantics = &(APFloat::IEEEdouble AP_SEMANTICS_PARENS); } else { -#if __i386__ || __x86_64__ +#if __i386__ || __x86_64__ || _M_IX86 || _M_X64 apSemantics = &(APFloat::x87DoubleExtended AP_SEMANTICS_PARENS); #elif __aarch64__ apSemantics = &(APFloat::IEEEquad AP_SEMANTICS_PARENS); diff --git a/gen/target.cpp b/gen/target.cpp index b91a408ab89..644ef91436d 100644 --- a/gen/target.cpp +++ b/gen/target.cpp @@ -25,6 +25,10 @@ using llvm::APFloat; void Target::_init() { + FloatProperties::_init(); + DoubleProperties::_init(); + RealProperties::_init(); + const auto &triple = *global.params.targetTriple; ptrsize = gDataLayout->getPointerSize(); @@ -51,9 +55,7 @@ void Target::_init() { twoDtorInVtable = !triple.isWindowsMSVCEnvironment(); - // {Float,Double,Real}Properties have been initialized with the D host - // compiler's properties. - // Now finalize RealProperties for the target's `real` type. + // Finalize RealProperties for the target's `real` type. const auto targetRealSemantics = &real->getFltSemantics(); #if LDC_LLVM_VER >= 400 @@ -66,44 +68,44 @@ void Target::_init() { const auto IEEEquad = &APFloat::IEEEquad; #endif - RealProperties.nan = CTFloat::nan; - RealProperties.snan = CTFloat::initVal; - RealProperties.infinity = CTFloat::infinity; + RealProperties::nan = CTFloat::nan; + RealProperties::snan = CTFloat::initVal; + RealProperties::infinity = CTFloat::infinity; if (targetRealSemantics == IEEEdouble) { - RealProperties.max = CTFloat::parse("0x1.fffffffffffffp+1023"); - RealProperties.min_normal = CTFloat::parse("0x1p-1022"); - RealProperties.epsilon = CTFloat::parse("0x1p-52"); - RealProperties.dig = 15; - RealProperties.mant_dig = 53; - RealProperties.max_exp = 1024; - RealProperties.min_exp = -1021; - RealProperties.max_10_exp = 308; - RealProperties.min_10_exp = -307; + RealProperties::max = CTFloat::parse("0x1.fffffffffffffp+1023"); + RealProperties::min_normal = CTFloat::parse("0x1p-1022"); + RealProperties::epsilon = CTFloat::parse("0x1p-52"); + RealProperties::dig = 15; + RealProperties::mant_dig = 53; + RealProperties::max_exp = 1024; + RealProperties::min_exp = -1021; + RealProperties::max_10_exp = 308; + RealProperties::min_10_exp = -307; } else if (targetRealSemantics == x87DoubleExtended) { - RealProperties.max = CTFloat::parse("0x1.fffffffffffffffep+16383"); - RealProperties.min_normal = CTFloat::parse("0x1p-16382"); - RealProperties.epsilon = CTFloat::parse("0x1p-63"); - RealProperties.dig = 18; - RealProperties.mant_dig = 64; - RealProperties.max_exp = 16384; - RealProperties.min_exp = -16381; - RealProperties.max_10_exp = 4932; - RealProperties.min_10_exp = -4931; + RealProperties::max = CTFloat::parse("0x1.fffffffffffffffep+16383"); + RealProperties::min_normal = CTFloat::parse("0x1p-16382"); + RealProperties::epsilon = CTFloat::parse("0x1p-63"); + RealProperties::dig = 18; + RealProperties::mant_dig = 64; + RealProperties::max_exp = 16384; + RealProperties::min_exp = -16381; + RealProperties::max_10_exp = 4932; + RealProperties::min_10_exp = -4931; } else if (targetRealSemantics == IEEEquad) { // FIXME: hex constants - RealProperties.max = + RealProperties::max = CTFloat::parse("1.18973149535723176508575932662800702e+4932"); - RealProperties.min_normal = + RealProperties::min_normal = CTFloat::parse("3.36210314311209350626267781732175260e-4932"); - RealProperties.epsilon = + RealProperties::epsilon = CTFloat::parse("1.92592994438723585305597794258492732e-34"); - RealProperties.dig = 33; - RealProperties.mant_dig = 113; - RealProperties.max_exp = 16384; - RealProperties.min_exp = -16381; - RealProperties.max_10_exp = 4932; - RealProperties.min_10_exp = -4931; + RealProperties::dig = 33; + RealProperties::mant_dig = 113; + RealProperties::max_exp = 16384; + RealProperties::min_exp = -16381; + RealProperties::max_10_exp = 4932; + RealProperties::min_10_exp = -4931; } else { // leave initialized with host real_t values warning(Loc(), "unknown properties for target `real` type, relying on D " @@ -242,6 +244,7 @@ Expression *Target::paintAsType(Expression *e, Type *type) { llvm_unreachable("Unsupported source type"); } + real_t r; switch (type->ty) { case Tint32: case Tuns32: @@ -252,10 +255,12 @@ Expression *Target::paintAsType(Expression *e, Type *type) { return createIntegerExp(e->loc, u.int64value, type); case Tfloat32: - return createRealExp(e->loc, u.float32value, type); + r = u.float32value; + return createRealExp(e->loc, r, type); case Tfloat64: - return createRealExp(e->loc, u.float64value, type); + r = u.float64value; + return createRealExp(e->loc, r, type); default: llvm_unreachable("Unsupported target type"); From e8985aeb20cb9cff66471bce0327ea9a0e806d85 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 24 Jun 2018 15:06:05 +0200 Subject: [PATCH 23/36] MSVC: Fix dmd.constfold compile errors with older D host compilers E.g., `d_int32` may alias a `__c_long` magic struct type, which `longdouble_soft` obviously doesn't support as cast target. Since v2.080 IIRC, the magic structs have been replaced by magic enums eliminating these issues (as they simply forward to their base type). --- dmd/constfold.d | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dmd/constfold.d b/dmd/constfold.d index ec7396a122d..9510bc2d9b0 100644 --- a/dmd/constfold.d +++ b/dmd/constfold.d @@ -1136,17 +1136,17 @@ extern (C++) UnionExp Cast(const ref Loc loc, Type type, Type to, Expression e1) result = cast(d_uns16)cast(dinteger_t)r; break; case Tint32: - result = cast(d_int32)r; + result = cast(int)r; break; case Tdchar: case Tuns32: - result = cast(d_uns32)r; + result = cast(uint)r; break; case Tint64: - result = cast(d_int64)r; + result = cast(long)r; break; case Tuns64: - result = cast(d_uns64)r; + result = cast(ulong)r; break; default: assert(0); From 35cb2c460b4130ccb56094f0d5cfba089e0d5f8c Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 24 Jun 2018 15:20:44 +0200 Subject: [PATCH 24/36] MSVC: Fix dmd.root.longdouble for DMD host compiler Where D `real_t = longdouble = real`, while C++ is `longdouble_soft`, so alignment and size must match, and DMD's x87 `real` on Windows is packed. --- dmd/root/longdouble.d | 6 +++++- dmd/root/longdouble.h | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/dmd/root/longdouble.d b/dmd/root/longdouble.d index 49f7243005d..ba6cff08d61 100644 --- a/dmd/root/longdouble.d +++ b/dmd/root/longdouble.d @@ -87,7 +87,8 @@ pure: struct longdouble_soft { nothrow @nogc pure: - ulong mantissa = 0xC000000000000001UL; // default to snan + // DMD's x87 `real` on Windows is packed (alignof = 2 -> sizeof = 10). + align(2) ulong mantissa = 0xC000000000000001UL; // default to snan ushort exp_sign = 0x7fff; // sign is highest bit this(ulong m, ushort es) { mantissa = m; exp_sign = es; } @@ -184,6 +185,9 @@ nothrow @nogc pure: static uint min_10_exp() { return -4932; } }; +static assert(longdouble_soft.alignof == longdouble.alignof); +static assert(longdouble_soft.sizeof == longdouble.sizeof); + version(LDC) { import ldc.llvmasm; diff --git a/dmd/root/longdouble.h b/dmd/root/longdouble.h index 017c390dd55..6c097220887 100644 --- a/dmd/root/longdouble.h +++ b/dmd/root/longdouble.h @@ -85,6 +85,7 @@ longdouble_soft ld_sin(longdouble_soft ld1); longdouble_soft ld_cos(longdouble_soft ld1); longdouble_soft ld_tan(longdouble_soft ld1); +#pragma pack(push, 1) struct longdouble_soft { unsigned long long mantissa; @@ -133,6 +134,9 @@ struct longdouble_soft operator bool () const { return mantissa != 0 || exponent != 0; } // correct? }; +#pragma pack(pop) +// static_assert(sizeof(longdouble_soft) == 10, "bad sizeof longdouble_soft"); + inline longdouble_soft ldouble(unsigned long long mantissa, int exp, int sign = 0) { longdouble_soft d; From 2e19f535bf14def801df5c8320246c0efef05d67 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Tue, 26 Jun 2018 00:38:46 +0200 Subject: [PATCH 25/36] dmd-testsuite: Fix buffer overflow in runnable/cppa.d --- tests/d2/dmd-testsuite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/d2/dmd-testsuite b/tests/d2/dmd-testsuite index f1752d2f8dc..8997a50bf51 160000 --- a/tests/d2/dmd-testsuite +++ b/tests/d2/dmd-testsuite @@ -1 +1 @@ -Subproject commit f1752d2f8dcd103bf9f042b38fba860b287ff3f8 +Subproject commit 8997a50bf51eb33262d015ecc37fdd5686885cf8 From abcd7eebfcdee6d26e29ca39a22664a5261cb8d2 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Tue, 26 Jun 2018 01:23:46 +0200 Subject: [PATCH 26/36] Upgrade to D v2.081.0-beta.2 --- dmd/expressionsem.d | 7 ++++++- runtime/phobos | 2 +- tests/d2/dmd-testsuite | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 8467ee41bc1..7372e542896 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -3692,7 +3692,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor s.isEnumDeclaration() || v && v.isDataseg()) && !sc.func.localsymtab.insert(s)) { - e.error("declaration `%s` is already defined in another scope in `%s`", s.toPrettyChars(), sc.func.toChars()); + // https://issues.dlang.org/show_bug.cgi?id=18266 + // set parent so that type semantic does not assert + s.parent = sc.parent; + Dsymbol originalSymbol = sc.func.localsymtab.lookup(s.ident); + assert(originalSymbol); + e.error("declaration `%s` is already defined in another scope in `%s` at line `%d`", s.toPrettyChars(), sc.func.toChars(), originalSymbol.loc.linnum); return setError(); } else diff --git a/runtime/phobos b/runtime/phobos index 13a7b4fe9d8..e7c9f48b67b 160000 --- a/runtime/phobos +++ b/runtime/phobos @@ -1 +1 @@ -Subproject commit 13a7b4fe9d846c1f1ac3982a63f926e65e42f977 +Subproject commit e7c9f48b67bf63be29b64db205bfe7b3f1c24c5a diff --git a/tests/d2/dmd-testsuite b/tests/d2/dmd-testsuite index 8997a50bf51..23d469e6995 160000 --- a/tests/d2/dmd-testsuite +++ b/tests/d2/dmd-testsuite @@ -1 +1 @@ -Subproject commit 8997a50bf51eb33262d015ecc37fdd5686885cf8 +Subproject commit 23d469e6995cb1e07c9d3027c5b631272af0aa20 From 64906302c98f87b327faaf3200bd82b98672cdfb Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Tue, 26 Jun 2018 02:16:43 +0200 Subject: [PATCH 27/36] [MSVC] dmd-testsuite: Disable NRVO variable debuginfo test for now --- tests/d2/dmd-testsuite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/d2/dmd-testsuite b/tests/d2/dmd-testsuite index 23d469e6995..7a32ec5395c 160000 --- a/tests/d2/dmd-testsuite +++ b/tests/d2/dmd-testsuite @@ -1 +1 @@ -Subproject commit 23d469e6995cb1e07c9d3027c5b631272af0aa20 +Subproject commit 7a32ec5395c29d74de2b0f00f8b79040fcc8c0f1 From cf96500221b56b1ed5a99c5974fc5aab6e948167 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Tue, 26 Jun 2018 02:32:56 +0200 Subject: [PATCH 28/36] Circle & AppVeyor: Upgrade dub to v1.9 and host LDC to v1.10 --- .circleci/config.yml | 8 ++++---- appveyor.yml | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 07905fccaae..637013f4370 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -264,9 +264,9 @@ jobs: environment: - CI_OS: linux - LLVM_VERSION: 6.0.0 - - HOST_LDC_VERSION: 1.8.0 + - HOST_LDC_VERSION: 1.10.0 - EXTRA_CMAKE_FLAGS: "-DMULTILIB=ON -DBUILD_LTO_LIBS=ON -DD_FLAGS='-w;-flto=thin' -DCMAKE_EXE_LINKER_FLAGS=-static-libstdc++ -DLDC_INSTALL_LTOPLUGIN=ON -DLDC_INSTALL_LLVM_RUNTIME_LIBS=ON" - - DUB_VERSION: v1.8.1 + - DUB_VERSION: v1.9.0 build-osx: <<: *commonSteps macos: @@ -276,10 +276,10 @@ jobs: - MACOSX_DEPLOYMENT_TARGET: 10.8 - USE_LIBCPP: true - LLVM_VERSION: 6.0.0 - - HOST_LDC_VERSION: 1.8.0 + - HOST_LDC_VERSION: 1.10.0 - BOOTSTRAP_CMAKE_FLAGS: "-DCMAKE_CXX_FLAGS='-stdlib=libc++' -DCMAKE_EXE_LINKER_FLAGS=-lc++" - EXTRA_CMAKE_FLAGS: "-DMULTILIB=ON -DBUILD_LTO_LIBS=ON -DD_FLAGS='-w;-flto=thin' -DCMAKE_CXX_FLAGS='-stdlib=libc++' -DCMAKE_EXE_LINKER_FLAGS=-lc++" - - DUB_VERSION: v1.8.1 + - DUB_VERSION: v1.9.0 workflows: version: 2 diff --git a/appveyor.yml b/appveyor.yml index 8ca2856b5da..c8337311acf 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -13,13 +13,13 @@ environment: - APPVEYOR_JOB_ARCH: x64 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 LLVM_VERSION: 6.0.0 - HOST_LDC_VERSION: 1.8.0 - DUB_VERSION: v1.8.1 + HOST_LDC_VERSION: 1.10.0 + DUB_VERSION: v1.9.0 - APPVEYOR_JOB_ARCH: x86 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 LLVM_VERSION: 5.0.1 - HOST_LDC_VERSION: 1.8.0 - DUB_VERSION: v1.8.1 + HOST_LDC_VERSION: 1.10.0 + DUB_VERSION: v1.9.0 # scripts that are called at very beginning, before repo cloning init: From 65d14e01555c45d2db23a21bfd2f56b02cf2f64a Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Wed, 27 Jun 2018 01:55:53 +0200 Subject: [PATCH 29/36] dmd-testsuite: Disable OSX-specific runnable/debug_info.d --- tests/d2/dmd-testsuite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/d2/dmd-testsuite b/tests/d2/dmd-testsuite index 7a32ec5395c..82d5b013c0b 160000 --- a/tests/d2/dmd-testsuite +++ b/tests/d2/dmd-testsuite @@ -1 +1 @@ -Subproject commit 7a32ec5395c29d74de2b0f00f8b79040fcc8c0f1 +Subproject commit 82d5b013c0b4e9c153826e7dfc421421ebcba756 From 3c8633220003c2489b8739c71e2121744c7f07da Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Wed, 27 Jun 2018 23:56:39 +0200 Subject: [PATCH 30/36] Slightly revise stripped-down llvm-ar code in driver/archiver.cpp Cross-checked against LLVM 6.0.0. --- driver/archiver.cpp | 80 +++++++++++++++------------------------------ 1 file changed, 27 insertions(+), 53 deletions(-) diff --git a/driver/archiver.cpp b/driver/archiver.cpp index 9fbeac32beb..fcfd77b09eb 100644 --- a/driver/archiver.cpp +++ b/driver/archiver.cpp @@ -37,7 +37,7 @@ using namespace llvm; /* Unlike the llvm-lib driver, llvm-ar is not available as library; it's * unfortunately a separate tool. * The following is a stripped-down version of LLVM's - * `tools/llvm-ar/llvm-ar.cpp` (based on early LLVM 5.0), as LDC only needs + * `tools/llvm-ar/llvm-ar.cpp` (based on LLVM 6.0), as LDC only needs * support for `llvm-ar rcs ...`. * It also makes sure the process isn't simply exited whenever a problem arises. */ @@ -52,33 +52,27 @@ bool Thin = false; void fail(Twine Error) { errs() << "llvm-ar: " << Error << ".\n"; } -void fail(std::error_code EC, std::string Context = {}) { - if (Context.empty()) - fail(EC.message()); - else - fail(Context + ": " + EC.message()); +void fail(std::error_code EC, StringRef Context = {}) { + fail(Context.empty() ? EC.message() : Context + ": " + EC.message()); } -void fail(Error E, std::string Context = {}) { - if (!Context.empty()) - Context += ": "; - +void fail(Error E, StringRef Context = {}) { handleAllErrors(std::move(E), [&](const ErrorInfoBase &EIB) { - if (Context.empty()) - fail(EIB.message()); - else - fail(Context + EIB.message()); + fail(Context.empty() ? EIB.message() : Context + ": " + EIB.message()); }); } +#define failIfError(Error, Context) \ + if (auto _E = (Error)) { \ + fail(std::move(_E), (Context)); \ + return 1; \ + } + int addMember(std::vector &Members, StringRef FileName, int Pos = -1) { Expected NMOrErr = NewArchiveMember::getFile(FileName, Deterministic); - if (auto Error = NMOrErr.takeError()) { - fail(std::move(Error), FileName); - return 1; - } + failIfError(NMOrErr.takeError(), FileName); #if LDC_LLVM_VER >= 500 // Use the basename of the object path for the member name. @@ -89,6 +83,7 @@ int addMember(std::vector &Members, StringRef FileName, Members.push_back(std::move(*NMOrErr)); else Members[Pos] = std::move(*NMOrErr); + return 0; } @@ -100,14 +95,12 @@ int addMember(std::vector &Members, } Expected NMOrErr = NewArchiveMember::getOldMember(M, Deterministic); - if (auto Error = NMOrErr.takeError()) { - fail(std::move(Error)); - return 1; - } + failIfError(NMOrErr.takeError(), ""); if (Pos == -1) Members.push_back(std::move(*NMOrErr)); else Members[Pos] = std::move(*NMOrErr); + return 0; } @@ -116,16 +109,12 @@ int computeNewArchiveMembers(object::Archive *OldArchive, if (OldArchive) { Error Err = Error::success(); for (auto &Child : OldArchive->children(Err)) { -#if LDC_LLVM_VER < 400 auto NameOrErr = Child.getName(); - if (auto Error = NameOrErr.getError()) { +#if LDC_LLVM_VER < 400 + failIfError(NameOrErr.getError(), ""); #else - Expected NameOrErr = Child.getName(); - if (auto Error = NameOrErr.takeError()) { + failIfError(NameOrErr.takeError(), ""); #endif - fail(std::move(Error)); - return 1; - } StringRef Name = NameOrErr.get(); auto MemberI = find_if(Members, [Name](StringRef Path) { @@ -133,18 +122,17 @@ int computeNewArchiveMembers(object::Archive *OldArchive, }); if (MemberI == Members.end()) { + // add old member if (int Status = addMember(Ret, Child)) return Status; } else { + // new member replaces old one with same name at old position if (int Status = addMember(Ret, *MemberI)) return Status; Members.erase(MemberI); } } - if (Err) { - fail(std::move(Err)); - return 1; - } + failIfError(std::move(Err), ""); } const int InsertPos = Ret.size(); @@ -208,23 +196,15 @@ int performWriteOperation(object::Archive *OldArchive, std::move(OldArchiveBuf)); #if LDC_LLVM_VER >= 600 - if (Result) { - handleAllErrors(std::move(Result), [](ErrorInfoBase &EIB) { - fail("error writing '" + ArchiveName + "': " + EIB.message()); - }); - return 1; - } + failIfError(std::move(Result), ("error writing '" + ArchiveName + "'").str()); #else - if (Result.second) { - fail(Result.second, Result.first); - return 1; - } + failIfError(Result.second, ("error writing '" + ArchiveName + "'").str()); #endif return 0; } -int performWriteOperation() { +int performOperation() { if (!sys::fs::exists(ArchiveName)) { return performWriteOperation(nullptr, nullptr); } @@ -232,18 +212,12 @@ int performWriteOperation() { // Open the archive object. auto Buf = MemoryBuffer::getFile(ArchiveName, -1, false); std::error_code EC = Buf.getError(); - if (EC) { - fail(EC, ("error opening '" + ArchiveName + "'").str()); - return 1; - } + failIfError(EC, ("error opening '" + ArchiveName + "'").str()); Error Err = Error::success(); object::Archive Archive(Buf.get()->getMemBufferRef(), Err); EC = errorToErrorCode(std::move(Err)); - if (EC) { - fail(EC, ("error loading '" + ArchiveName + "'").str()); - return 1; - } + failIfError(EC, ("error loading '" + ArchiveName + "'").str()); return performWriteOperation(&Archive, std::move(Buf.get())); } @@ -269,7 +243,7 @@ int internalAr(ArrayRef args) { llvm_ar::Members.insert(llvm_ar::Members.end(), membersSlice.begin(), membersSlice.end()); - return llvm_ar::performWriteOperation(); + return llvm_ar::performOperation(); } int internalLib(ArrayRef args) { From 9bcd7cabf88a4a0b2ce4c769b69dfd96ec2b2660 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Thu, 28 Jun 2018 00:17:28 +0200 Subject: [PATCH 31/36] [macOS] CMake: Work around ranlib potentially corrupting libldc-jit-rt.a When installing the static lib archived by LDC (1 D object file, 1 C++ one) via CMake, `ranlib` is apparently invoked for the installed copy, which may corrupt it, e.g., reproduced via CircleCI SSH with Xcode 9.2 (.a file size unchanged after (successful) ranlib run): /Applications/Xcode-9.2.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/nm: foo.a truncated or malformed archive (terminator characters in archive member "c_" not the correct "`\n" values for the archive member header at offset 784) We already encountered this or a very similar bug (with LLVM 5 and some Xcode version IIRC). Copy the file manually for now as hopefully robust workaround. --- runtime/CMakeLists.txt | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index fcc2407f2d1..aa1166ec518 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -635,7 +635,13 @@ if(MULTILIB AND "${TARGET_SYSTEM}" MATCHES "APPLE") set(libtargets_jit) build_jit_runtime("${D_FLAGS};${D_FLAGS_RELEASE}" "${RT_CFLAGS}" "${LD_FLAGS}" "${LIB_SUFFIX}" libtargets_jit) list(APPEND libtargets ${libtargets_jit}) - # Install separately. + # Install separately; also manually copy libldc-jit-rt.a to prevent ranlib from + # potentially corrupting the file during installation. + if(LDC_DYNAMIC_COMPILE) + list(REMOVE_ITEM libtargets_jit ldc-jit-rt) + install(FILES ${CMAKE_BINARY_DIR}/lib${LIB_SUFFIX}/libldc-jit-rt.a + DESTINATION ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}) + endif() install(TARGETS ${libtargets_jit} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}) @@ -705,6 +711,13 @@ else() # Only build the host version of the jit runtime due to LLVM dependency. set(libtargets_jit) build_jit_runtime("${D_FLAGS};${D_FLAGS_RELEASE}" "${RT_CFLAGS}" "${LD_FLAGS}" "${LIB_SUFFIX}" libtargets_jit) + # Install separately; on Mac, also manually copy libldc-jit-rt.a to prevent ranlib from + # potentially corrupting the file during installation. + if(LDC_DYNAMIC_COMPILE AND "${TARGET_SYSTEM}" MATCHES "APPLE") + list(REMOVE_ITEM libtargets_jit ldc-jit-rt) + install(FILES ${CMAKE_BINARY_DIR}/lib${LIB_SUFFIX}/libldc-jit-rt.a + DESTINATION ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}) + endif() install(TARGETS ${libtargets_jit} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}) From 36ce2ec682bca3d120cbb94d87ccf4780862de58 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Thu, 28 Jun 2018 22:23:10 +0200 Subject: [PATCH 32/36] [macOS] CMake: Avoid ranlib for all installed static libraries I.e., druntime and Phobos too, not just ldc-jit-rt. [Only affecting MULTILIB=OFF builds, as the fat libs are generated and copied manually anyway). --- runtime/CMakeLists.txt | 71 +++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index aa1166ec518..cbe13a6d3cb 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -634,37 +634,36 @@ if(MULTILIB AND "${TARGET_SYSTEM}" MATCHES "APPLE") # Only build the host version of the jit runtime due to LLVM dependency. set(libtargets_jit) build_jit_runtime("${D_FLAGS};${D_FLAGS_RELEASE}" "${RT_CFLAGS}" "${LD_FLAGS}" "${LIB_SUFFIX}" libtargets_jit) - list(APPEND libtargets ${libtargets_jit}) # Install separately; also manually copy libldc-jit-rt.a to prevent ranlib from # potentially corrupting the file during installation. if(LDC_DYNAMIC_COMPILE) - list(REMOVE_ITEM libtargets_jit ldc-jit-rt) - install(FILES ${CMAKE_BINARY_DIR}/lib${LIB_SUFFIX}/libldc-jit-rt.a + install(FILES $ DESTINATION ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}) + list(REMOVE_ITEM libtargets_jit ldc-jit-rt) endif() install(TARGETS ${libtargets_jit} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}) # KLUDGE: Cannot use `$` in custom command. # Set up the list of generated libs (without 'lib' prefix) to be merged manually. - set(libs_to_install) + set(libs_to_merge) if(NOT ${BUILD_SHARED_LIBS} STREQUAL "ON") - list(APPEND libs_to_install druntime-ldc.a druntime-ldc-debug.a - phobos2-ldc.a phobos2-ldc-debug.a + list(APPEND libs_to_merge druntime-ldc.a druntime-ldc-debug.a + phobos2-ldc.a phobos2-ldc-debug.a ) # Only install the release versions of the (static-only) bitcode libraries. if(BUILD_LTO_LIBS) - list(APPEND libs_to_install druntime-ldc-lto.a phobos2-ldc-lto.a) + list(APPEND libs_to_merge druntime-ldc-lto.a phobos2-ldc-lto.a) endif() endif() if(NOT ${BUILD_SHARED_LIBS} STREQUAL "OFF") set(suffix ${SHARED_LIB_SUFFIX}.dylib) - list(APPEND libs_to_install druntime-ldc${suffix} druntime-ldc-debug${suffix} - phobos2-ldc${suffix} phobos2-ldc-debug${suffix} + list(APPEND libs_to_merge druntime-ldc${suffix} druntime-ldc-debug${suffix} + phobos2-ldc${suffix} phobos2-ldc-debug${suffix} ) endif() - foreach(lib ${libs_to_install}) + foreach(lib ${libs_to_merge}) set(target_name "") set(lib_extension "") get_filename_component(target_name ${lib} NAME_WE) @@ -706,37 +705,45 @@ if(MULTILIB AND "${TARGET_SYSTEM}" MATCHES "APPLE") endforeach() else() set(libs_to_install) + # build host versions build_runtime_variants("" "${RT_CFLAGS}" "${LD_FLAGS}" "${LIB_SUFFIX}" libs_to_install) - - # Only build the host version of the jit runtime due to LLVM dependency. - set(libtargets_jit) - build_jit_runtime("${D_FLAGS};${D_FLAGS_RELEASE}" "${RT_CFLAGS}" "${LD_FLAGS}" "${LIB_SUFFIX}" libtargets_jit) - # Install separately; on Mac, also manually copy libldc-jit-rt.a to prevent ranlib from - # potentially corrupting the file during installation. - if(LDC_DYNAMIC_COMPILE AND "${TARGET_SYSTEM}" MATCHES "APPLE") - list(REMOVE_ITEM libtargets_jit ldc-jit-rt) - install(FILES ${CMAKE_BINARY_DIR}/lib${LIB_SUFFIX}/libldc-jit-rt.a - DESTINATION ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}) - endif() - install(TARGETS ${libtargets_jit} - DESTINATION ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}) - - # don't add multilib targets to libs_to_install if(MULTILIB) - build_runtime_variants("-m${MULTILIB_SUFFIX}" "-m${MULTILIB_SUFFIX} ${RT_CFLAGS}" "-m${MULTILIB_SUFFIX} ${LD_FLAGS}" "${MULTILIB_SUFFIX}" dummy) + # build multi versions + build_runtime_variants("-m${MULTILIB_SUFFIX}" "-m${MULTILIB_SUFFIX} ${RT_CFLAGS}" "-m${MULTILIB_SUFFIX} ${LD_FLAGS}" "${MULTILIB_SUFFIX}" libs_to_install) endif() + # Only build the host version of the jit runtime due to LLVM dependency. + build_jit_runtime("${D_FLAGS};${D_FLAGS_RELEASE}" "${RT_CFLAGS}" "${LD_FLAGS}" "${LIB_SUFFIX}" libs_to_install) + # Only install the release versions of the (static-only) bitcode libraries. - if(BUILD_LTO_LIBS) + if(BUILD_LTO_LIBS AND (NOT ${BUILD_SHARED_LIBS} STREQUAL "ON")) list(APPEND libs_to_install druntime-ldc-lto phobos2-ldc-lto) endif() foreach(libname ${libs_to_install}) - install(TARGETS ${libname} - DESTINATION ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}) - if(MULTILIB) - install(TARGETS ${libname}_${MULTILIB_SUFFIX} - DESTINATION ${CMAKE_INSTALL_PREFIX}/lib${MULTILIB_SUFFIX}) + # On Mac, manually copy static libraries to prevent ranlib from + # potentially corrupting the file during installation. + # See https://bugs.llvm.org/show_bug.cgi?id=34808. + set(copy_manually OFF) + if("${TARGET_SYSTEM}" MATCHES "APPLE") + set(target_type) + get_target_property(target_type ${libname} TYPE) + if("${target_type}" STREQUAL "STATIC_LIBRARY") + set(copy_manually ON) + endif() + endif() + + set(destination_dir ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}) + if(${libname} MATCHES "_${MULTILIB_SUFFIX}$") + set(destination_dir ${CMAKE_INSTALL_PREFIX}/lib${MULTILIB_SUFFIX}) + endif() + + if(copy_manually) + install(FILES $ + DESTINATION ${destination_dir}) + else() + install(TARGETS ${libname} + DESTINATION ${destination_dir}) endif() endforeach() endif() From 08d758debb0e9a71fe7cabb21a8c217adf76e762 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Fri, 29 Jun 2018 01:09:59 +0200 Subject: [PATCH 33/36] Merge v2.081.0-rc.1 --- runtime/druntime | 2 +- tests/d2/dmd-testsuite | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/druntime b/runtime/druntime index da6148ec4a2..f3e1da65649 160000 --- a/runtime/druntime +++ b/runtime/druntime @@ -1 +1 @@ -Subproject commit da6148ec4a27608627a8e6ad53801dcdb770121d +Subproject commit f3e1da6564910b10211b210377f002bc8b06769c diff --git a/tests/d2/dmd-testsuite b/tests/d2/dmd-testsuite index 82d5b013c0b..c94f41ec087 160000 --- a/tests/d2/dmd-testsuite +++ b/tests/d2/dmd-testsuite @@ -1 +1 @@ -Subproject commit 82d5b013c0b4e9c153826e7dfc421421ebcba756 +Subproject commit c94f41ec087142fbe75e5b3d820db01162f3cb22 From 2642b3a02def2d0986e6e742ab0b764b27b0e173 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Mon, 2 Jul 2018 21:23:08 +0200 Subject: [PATCH 34/36] dmd-testsuite: Fix runnable/testprofile.d for non-x86 targets --- tests/d2/dmd-testsuite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/d2/dmd-testsuite b/tests/d2/dmd-testsuite index c94f41ec087..8793d6b20d2 160000 --- a/tests/d2/dmd-testsuite +++ b/tests/d2/dmd-testsuite @@ -1 +1 @@ -Subproject commit c94f41ec087142fbe75e5b3d820db01162f3cb22 +Subproject commit 8793d6b20d2bf93078422483789304ff43bc8ec6 From f0d66eb93d91d8430a075bc55d323a5e9e1ff96b Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Mon, 2 Jul 2018 22:31:15 +0200 Subject: [PATCH 35/36] dmd.semantic3: Disable DMD backend hack for Win32 and synchronized methods I happened to stumble upon this by accident. --- dmd/semantic3.d | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dmd/semantic3.d b/dmd/semantic3.d index 3ed5933ba61..4dbeca1003c 100644 --- a/dmd/semantic3.d +++ b/dmd/semantic3.d @@ -1134,7 +1134,8 @@ else ClassDeclaration cd = funcdecl.isThis() ? funcdecl.isThis().isClassDeclaration() : funcdecl.parent.isClassDeclaration(); if (cd) { - if (!global.params.is64bit && global.params.isWindows && !funcdecl.isStatic() && !sbody.usesEH() && !global.params.trace) + // IN_LLVM: disabled via leading `false &&` + if (false && !global.params.is64bit && global.params.isWindows && !funcdecl.isStatic() && !sbody.usesEH() && !global.params.trace) { /* The back end uses the "jmonitor" hack for syncing; * no need to do the sync at this level. From 92842c78f2bd7a109959c6cbe235afa97f758688 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Tue, 3 Jul 2018 21:51:36 +0200 Subject: [PATCH 36/36] Merge upstream stable dlang/dmd@a0f51e40cf0c --- dmd/dsymbolsem.d | 22 ++++++++++++---------- runtime/phobos | 2 +- tests/d2/dmd-testsuite | 2 +- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/dmd/dsymbolsem.d b/dmd/dsymbolsem.d index 618d49a4e22..8c778cd5866 100644 --- a/dmd/dsymbolsem.d +++ b/dmd/dsymbolsem.d @@ -4344,6 +4344,18 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } } + if (sd.type.ty == Tstruct && (cast(TypeStruct)sd.type).sym != sd) + { + // https://issues.dlang.org/show_bug.cgi?id=19024 + StructDeclaration sym = (cast(TypeStruct)sd.type).sym; + version (none) + { + printf("this = %p %s\n", sd, sd.toChars()); + printf("type = %d sym = %p, %s\n", sd.type.ty, sym, sym.toPrettyChars()); + } + sd.error("already exists at %s. Perhaps in another function with the same name?", sym.loc.toChars()); + } + if (global.errors != errors) { // The type is no good. @@ -4358,16 +4370,6 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor sd.deferred.semantic2(sc); sd.deferred.semantic3(sc); } - - version (none) - { - if (sd.type.ty == Tstruct && (cast(TypeStruct)sd.type).sym != sd) - { - printf("this = %p %s\n", sd, sd.toChars()); - printf("type = %d sym = %p\n", sd.type.ty, (cast(TypeStruct)sd.type).sym); - } - } - assert(sd.type.ty != Tstruct || (cast(TypeStruct)sd.type).sym == sd); } final void interfaceSemantic(ClassDeclaration cd) diff --git a/runtime/phobos b/runtime/phobos index ed11f18abe7..e909070753f 160000 --- a/runtime/phobos +++ b/runtime/phobos @@ -1 +1 @@ -Subproject commit ed11f18abe7c5bfb0422aaf53ddec0c2618bc7a3 +Subproject commit e909070753fcd886f0d3d226cf8054b99e49ce71 diff --git a/tests/d2/dmd-testsuite b/tests/d2/dmd-testsuite index 8793d6b20d2..977ef0696f7 160000 --- a/tests/d2/dmd-testsuite +++ b/tests/d2/dmd-testsuite @@ -1 +1 @@ -Subproject commit 8793d6b20d2bf93078422483789304ff43bc8ec6 +Subproject commit 977ef0696f7941357385925c07617544c3527f4c