diff --git a/gcc/d/ChangeLog b/gcc/d/ChangeLog index e5937f581..6b0a1b8ac 100644 --- a/gcc/d/ChangeLog +++ b/gcc/d/ChangeLog @@ -1,3 +1,24 @@ +2018-08-25 Eugene Wissner + + * Make-lang.in (D_FRONTEND_OBJS): Add iasm.o, iasmgcc.o + (d.tags): Scan dmd/root/*.h + * d-builtins.cc (build_frontend_type): Update callers for new + front-end signatures. + (d_init_versions): Add D_ModuleInfo, D_Exceptions, D_TypeInfo. + * d-diagnostic.cc (vwarning): Increment gagged warnings error if + gagging turned on. + (vdeprecation): Likewise. + * d-frontend.cc (asmSemantic): Remove function. + * d-lang.cc (d_handle_option): Remove case for OPT_fproperty. + * d-target.cc (Target::_init): Remove int64Mangle and uint64Mangle. + * lang.opt (fproperty): Remove option. + * toir.cc (IRVisitor::visit(ExtAsmStatement)): Rename override to + GccAsmStatement. + * typeinfo.cc (TypeInfoVisitor::visit(TypeInfoClassDeclaration)): Use + int for collecting ClassFlags. + * (TypeInfoVisitor::visit(TypeInfoClassDeclaration)): Use int for + collecting StructFlags. + 2018-07-23 Eugene Wissner * d-lang.cc (d_handle_option): Change function argument to HOST_WIDE_INT. diff --git a/gcc/d/Make-lang.in b/gcc/d/Make-lang.in index 8033ad6cc..118ac7f4d 100644 --- a/gcc/d/Make-lang.in +++ b/gcc/d/Make-lang.in @@ -125,6 +125,8 @@ D_FRONTEND_OBJS = \ d/gluelayer.o \ d/hash.o \ d/hdrgen.o \ + d/iasm.o \ + d/iasmgcc.o \ d/id.o \ d/identifier.o \ d/impcnvtab.o \ @@ -237,7 +239,7 @@ d.srcextra: d.tags: force cd $(srcdir)/d; \ - etags -o TAGS.sub *.c *.cc *.h dmd/*.h; \ + etags -o TAGS.sub *.c *.cc *.h dmd/*.h dmd/root/*.h; \ etags --include TAGS.sub --include ../TAGS.sub d.man: doc/gdc.1 diff --git a/gcc/d/d-builtins.cc b/gcc/d/d-builtins.cc index b076ff642..794416cbc 100644 --- a/gcc/d/d-builtins.cc +++ b/gcc/d/d-builtins.cc @@ -277,7 +277,7 @@ build_frontend_type (tree type) return NULL; } - args->push (Parameter::create (sc, targ, NULL, NULL)); + args->push (Parameter::create (sc, targ, NULL, NULL, NULL)); } /* GCC generic and placeholder builtins are marked as variadic, yet @@ -430,16 +430,27 @@ d_init_versions (void) VersionCondition::addPredefinedGlobalIdent ("D_Coverage"); if (flag_pic) VersionCondition::addPredefinedGlobalIdent ("D_PIC"); + if (global.params.doDocComments) VersionCondition::addPredefinedGlobalIdent ("D_Ddoc"); + if (global.params.useUnitTests) VersionCondition::addPredefinedGlobalIdent ("unittest"); + if (global.params.useAssert == CHECKENABLEon) VersionCondition::addPredefinedGlobalIdent ("assert"); + if (global.params.useArrayBounds == CHECKENABLEoff) VersionCondition::addPredefinedGlobalIdent ("D_NoBoundsChecks"); + if (global.params.betterC) VersionCondition::addPredefinedGlobalIdent ("D_BetterC"); + else + { + VersionCondition::addPredefinedGlobalIdent ("D_ModuleInfo"); + VersionCondition::addPredefinedGlobalIdent ("D_Exceptions"); + VersionCondition::addPredefinedGlobalIdent ("D_TypeInfo"); + } VersionCondition::addPredefinedGlobalIdent ("all"); diff --git a/gcc/d/d-diagnostic.cc b/gcc/d/d-diagnostic.cc index 6d864e222..c4b2f503e 100644 --- a/gcc/d/d-diagnostic.cc +++ b/gcc/d/d-diagnostic.cc @@ -194,14 +194,16 @@ verrorSupplemental (const Loc& loc, const char *format, va_list ap) void ATTRIBUTE_GCC_DIAG(2,0) vwarning (const Loc& loc, const char *format, va_list ap) { - if (global.params.warnings && !global.gag) + if (!global.gag && global.params.warnings) { - /* Warnings don't count if gagged. */ + /* Warnings don't count if not treated as errors. */ if (global.params.warnings == 1) global.warnings++; d_diagnostic_report_diagnostic (loc, 0, format, ap, DK_WARNING, false); } + else if (global.gag) + global.gaggedWarnings++; } /* Print supplementary message about the last warning with explicit location @@ -241,6 +243,8 @@ vdeprecation (const Loc& loc, const char *format, va_list ap, DK_WARNING, false); free (xformat); } + else if (global.gag) + global.gaggedWarnings++; } /* Print supplementary message about the last deprecation with explicit diff --git a/gcc/d/d-frontend.cc b/gcc/d/d-frontend.cc index a6377255a..37a4be56e 100644 --- a/gcc/d/d-frontend.cc +++ b/gcc/d/d-frontend.cc @@ -368,15 +368,6 @@ CTFloat::hash (real_t r) /* Implements backend-specific interfaces used by the frontend. */ -/* Semantically analyze AsmStatement where SC is the scope. */ - -Statement * -asmSemantic (AsmStatement *s, Scope *sc) -{ - sc->func->hasReturnExp |= 8; - return s; -} - /* Determine if function FD is a builtin one that we can evaluate in CTFE. */ BUILTIN diff --git a/gcc/d/d-lang.cc b/gcc/d/d-lang.cc index 7cfbd41d0..3f65115bb 100644 --- a/gcc/d/d-lang.cc +++ b/gcc/d/d-lang.cc @@ -516,10 +516,6 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value, global.params.useIn = value; break; - case OPT_fproperty: - global.params.enforcePropertySyntax = value; - break; - case OPT_frelease: global.params.release = value; break; diff --git a/gcc/d/d-target.cc b/gcc/d/d-target.cc index 553362513..ebdda6648 100644 --- a/gcc/d/d-target.cc +++ b/gcc/d/d-target.cc @@ -155,8 +155,6 @@ Target::_init (void) /* Set-up target C++ ABI. */ Target::reverseCppOverloads = false; Target::cppExceptions = true; - Target::int64Mangle = 'l'; - Target::uint64Mangle = 'm'; Target::twoDtorInVtable = true; /* Initialize all compile-time properties for floating point types. diff --git a/gcc/d/dmd/access.d b/gcc/d/dmd/access.d index aeea0f2c7..711fab020 100644 --- a/gcc/d/dmd/access.d +++ b/gcc/d/dmd/access.d @@ -49,9 +49,8 @@ private Prot getAccess(AggregateDeclaration ad, Dsymbol smember) } if (ClassDeclaration cd = ad.isClassDeclaration()) { - for (size_t i = 0; i < cd.baseclasses.dim; i++) + foreach (b; *cd.baseclasses) { - BaseClass* b = (*cd.baseclasses)[i]; Prot access = getAccess(b.sym, smember); final switch (access.kind) { @@ -101,10 +100,9 @@ private bool isAccessible(Dsymbol smember, Dsymbol sfunc, AggregateDeclaration d return true; if (ClassDeclaration cdthis = dthis.isClassDeclaration()) { - for (size_t i = 0; i < cdthis.baseclasses.dim; i++) + foreach (b; *cdthis.baseclasses) { - BaseClass* b = (*cdthis.baseclasses)[i]; - Prot access = getAccess(b.sym, smember); + const Prot access = getAccess(b.sym, smember); if (access.kind >= Prot.Kind.protected_ || isAccessible(smember, sfunc, b.sym, cdscope)) { return true; @@ -118,12 +116,9 @@ private bool isAccessible(Dsymbol smember, Dsymbol sfunc, AggregateDeclaration d { if (ClassDeclaration cdthis = dthis.isClassDeclaration()) { - for (size_t i = 0; i < cdthis.baseclasses.dim; i++) - { - BaseClass* b = (*cdthis.baseclasses)[i]; + foreach (b; *cdthis.baseclasses) if (isAccessible(smember, sfunc, b.sym, cdscope)) return true; - } } } } @@ -423,8 +418,7 @@ extern (C++) bool checkAccess(Loc loc, Scope* sc, Expression e, Declaration d) ClassDeclaration cd = (cast(TypeClass)e.type).sym; if (e.op == TOK.super_) { - ClassDeclaration cd2 = sc.func.toParent().isClassDeclaration(); - if (cd2) + if (ClassDeclaration cd2 = sc.func.toParent().isClassDeclaration()) cd = cd2; } return checkAccess(cd, loc, sc, d); diff --git a/gcc/d/dmd/aggregate.d b/gcc/d/dmd/aggregate.d index b25f4e2ba..a66f0cf22 100644 --- a/gcc/d/dmd/aggregate.d +++ b/gcc/d/dmd/aggregate.d @@ -33,6 +33,7 @@ import dmd.mtype; import dmd.semantic2; import dmd.semantic3; import dmd.tokens; +import dmd.typesem; import dmd.visitor; enum Sizeok : int @@ -117,7 +118,7 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol Expression getRTInfo; // pointer to GC info generated by object.RTInfo(this) - final extern (D) this(Loc loc, Identifier id) + final extern (D) this(const ref Loc loc, Identifier id) { super(id); this.loc = loc; diff --git a/gcc/d/dmd/aggregate.h b/gcc/d/dmd/aggregate.h index b13fc9bac..53e3c665d 100644 --- a/gcc/d/dmd/aggregate.h +++ b/gcc/d/dmd/aggregate.h @@ -15,7 +15,7 @@ #pragma once #endif /* __DMC__ */ -#include "root.h" +#include "root/root.h" #include "dsymbol.h" #include "declaration.h" @@ -79,11 +79,11 @@ struct ClassKind { enum Type { - /// the class is a d(efault) class + /// the aggregate is a d(efault) struct/class/interface d, - /// the class is a C++ interface + /// the aggregate is a C++ struct/class/interface cpp, - /// the class is an Objective-C class/interface + /// the aggregate is an Objective-C class/interface objc }; }; @@ -101,7 +101,7 @@ class AggregateDeclaration : public ScopeDsymbol Dsymbol *deferred; // any deferred semantic2() or semantic3() symbol bool isdeprecated; // true if deprecated - ClassKind classKind; // specifies the linkage type + ClassKind::Type classKind; // specifies the linkage type /* !=NULL if is nested * pointing to the dsymbol that directly enclosing it. @@ -168,9 +168,9 @@ class AggregateDeclaration : public ScopeDsymbol struct StructFlags { - typedef unsigned Type; - enum Enum + enum Type { + none = 0x0, hasPointers = 0x1, // NB: should use noPointers as in ClassFlags }; }; @@ -248,9 +248,9 @@ struct BaseClass struct ClassFlags { - typedef unsigned Type; - enum Enum + enum Type { + none = 0x0, isCOMclass = 0x1, noPointers = 0x2, hasOffTi = 0x4, diff --git a/gcc/d/dmd/aliasthis.d b/gcc/d/dmd/aliasthis.d index 37de52433..10dea2c55 100644 --- a/gcc/d/dmd/aliasthis.d +++ b/gcc/d/dmd/aliasthis.d @@ -32,7 +32,7 @@ extern (C++) final class AliasThis : Dsymbol { Identifier ident; - extern (D) this(Loc loc, Identifier ident) + extern (D) this(const ref Loc loc, Identifier ident) { super(null); // it's anonymous (no identifier) this.loc = loc; diff --git a/gcc/d/dmd/arrayop.d b/gcc/d/dmd/arrayop.d index d1c248759..4a5f821dd 100644 --- a/gcc/d/dmd/arrayop.d +++ b/gcc/d/dmd/arrayop.d @@ -60,14 +60,11 @@ extern (C++) bool isArrayOpValid(Expression e) BinExp be = cast(BinExp)e; return be.e1.op == TOK.slice && isArrayOpValid(be.e2); } - if (e.op == TOK.call) - { - return false; // TODO: Decide if [] is required after arrayop calls. - } - else - { - return false; - } + // if (e.op == TOK.call) + // { + // TODO: Decide if [] is required after arrayop calls. + // } + return false; } return true; } diff --git a/gcc/d/dmd/arraytypes.h b/gcc/d/dmd/arraytypes.h index e87be9a7d..03cd68e89 100644 --- a/gcc/d/dmd/arraytypes.h +++ b/gcc/d/dmd/arraytypes.h @@ -16,7 +16,7 @@ #endif /* __DMC__ */ -#include "root.h" +#include "root/root.h" typedef Array TemplateParameters; diff --git a/gcc/d/dmd/attrib.d b/gcc/d/dmd/attrib.d index 3203b6920..3c83e7fe8 100644 --- a/gcc/d/dmd/attrib.d +++ b/gcc/d/dmd/attrib.d @@ -21,6 +21,7 @@ import dmd.dscope; import dmd.dsymbol; import dmd.dsymbolsem; import dmd.expression; +import dmd.expressionsem; import dmd.func; import dmd.globals; import dmd.hdrgen; @@ -595,7 +596,7 @@ extern (C++) final class ProtDeclaration : AttribDeclaration return buf.extractString(); } - override final inout(ProtDeclaration) isProtDeclaration() inout + override inout(ProtDeclaration) isProtDeclaration() inout { return this; } @@ -751,7 +752,7 @@ extern (C++) final class AnonDeclaration : AttribDeclaration return (isunion ? "anonymous union" : "anonymous struct"); } - override final inout(AnonDeclaration) isAnonDeclaration() inout + override inout(AnonDeclaration) isAnonDeclaration() inout { return this; } @@ -956,7 +957,7 @@ extern (C++) final class StaticIfDeclaration : ConditionalDeclaration onStack = true; scope(exit) onStack = false; - if (condition.inc == 0) + if (sc && condition.inc == 0) { assert(scopesym); // addMember is already done assert(_scope); // setScope is already done @@ -1060,7 +1061,7 @@ extern (C++) final class StaticForeachDeclaration : AttribDeclaration Dsymbol.arraySyntaxCopy(decl)); } - override final bool oneMember(Dsymbol* ps, Identifier ident) + override bool oneMember(Dsymbol* ps, Identifier ident) { // Required to support IFTI on a template that contains a // `static foreach` declaration. `super.oneMember` calls @@ -1125,7 +1126,7 @@ extern (C++) final class StaticForeachDeclaration : AttribDeclaration this.scopesym = sds; } - override final void addComment(const(char)* comment) + override void addComment(const(char)* comment) { // do nothing // change this to give semantics to documentation comments on static foreach declarations @@ -1263,6 +1264,7 @@ extern (C++) final class CompileDeclaration : AttribDeclaration /*********************************************************** * User defined attributes look like: + * @foo(args, ...) * @(args, ...) */ extern (C++) final class UserAttributeDeclaration : AttribDeclaration diff --git a/gcc/d/dmd/clone.d b/gcc/d/dmd/clone.d index 7986f04cc..6eadd74ff 100644 --- a/gcc/d/dmd/clone.d +++ b/gcc/d/dmd/clone.d @@ -185,31 +185,70 @@ private bool needOpAssign(StructDeclaration sd) } /****************************************** - * Build opAssign for struct. - * ref S opAssign(S s) { ... } + * Build opAssign for a `struct`. * - * Note that `s` will be constructed onto the stack, and probably - * copy-constructed in caller site. + * The generated `opAssign` function has the following signature: + *--- + *ref S opAssign(S s) // S is the name of the `struct` + *--- * - * If S has copy copy construction and/or destructor, - * the body will make bit-wise object swap: - * S __swap = this; // bit copy - * this = s; // bit copy - * __swap.dtor(); - * Instead of running the destructor on s, run it on tmp instead. + * The opAssign function will be built for a struct `S` if the + * following constraints are met: + * + * 1. `S` does not have an identity `opAssign` defined. + * + * 2. `S` has at least one of the following members: a postblit (user-defined or + * generated for fields that have a defined postblit), a destructor + * (user-defined or generated for fields that have a defined destructor) + * or at least one field that has a defined `opAssign`. + * + * 3. `S` does not have any non-mutable fields. + * + * If `S` has a disabled destructor or at least one field that has a disabled + * `opAssign`, `S.opAssign` is going to be generated, but marked with `@disable` + * + * If `S` defines a destructor, the generated code for `opAssign` is: + * + *--- + *S __swap = void; + *__swap = this; // bit copy + *this = s; // bit copy + *__swap.dtor(); + *--- + * + * Otherwise, if `S` defines a postblit, the generated code for `opAssign` is: + * + *--- + *this = s; + *--- + * + * Note that the parameter to the generated `opAssign` is passed by value, which means + * that the postblit is going to be called (if it is defined) in both of the above + * situations before entering the body of `opAssign`. The assignments in the above generated + * function bodies are blit expressions, so they can be regarded as `memcpy`s + * (`opAssign` is not called as this will result in an infinite recursion; the postblit + * is not called because it has already been called when the parameter was passed by value). + * + * If `S` does not have a postblit or a destructor, but contains at least one field that defines + * an `opAssign` function (which is not disabled), then the body will make member-wise + * assignments: + * + *--- + *this.field1 = s.field1; + *this.field2 = s.field2; + *...; + *--- + * + * In this situation, the assignemnts are actual assign expressions (`opAssign` is used + * if defined). * - * Otherwise, the body will make member-wise assignments: - * Then, the body is: - * this.field1 = s.field1; - * this.field2 = s.field2; - * ...; * References: * https://dlang.org/spec/struct.html#assign-overload * Params: * sd = struct to generate opAssign for * sc = context * Returns: - * generated opAssign function + * generated `opAssign` function */ extern (C++) FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) { @@ -247,6 +286,7 @@ extern (C++) FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) if (sd.dtor || sd.postblit) { + // if the type is not assignable, we cannot generate opAssign if (!sd.type.isAssignable()) // https://issues.dlang.org/show_bug.cgi?id=13044 return null; stc = mergeFuncAttrs(stc, sd.dtor); @@ -255,7 +295,7 @@ extern (C++) FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) } auto fparams = new Parameters(); - fparams.push(new Parameter(STC.nodtor, sd.type, Id.p, null)); + fparams.push(new Parameter(STC.nodtor, sd.type, Id.p, null, null)); auto tf = new TypeFunction(fparams, sd.handleType(), 0, LINK.d, stc | STC.ref_); auto fop = new FuncDeclaration(declLoc, Loc.initial, Id.assign, stc, tf); fop.storage_class |= STC.inference; @@ -265,37 +305,35 @@ extern (C++) FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) { e = null; } - else if (sd.dtor || sd.postblit) + /* Do swap this and rhs. + * __swap = this; this = s; __swap.dtor(); + */ + else if (sd.dtor) { - /* Do swap this and rhs. - * __swap = this; this = s; __swap.dtor(); - */ //printf("\tswap copy\n"); - if (sd.dtor) - { - TypeFunction tdtor = cast(TypeFunction)sd.dtor.type; - assert(tdtor.ty == Tfunction); + TypeFunction tdtor = cast(TypeFunction)sd.dtor.type; + assert(tdtor.ty == Tfunction); - 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) - swap.storage_class |= STC.scope_; - auto e1 = new DeclarationExp(loc, swap); + 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) + 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)); + 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 swap. This avoids needing to copy swap back in to s. - */ - auto e4 = new CallExp(loc, new DotVarExp(loc, new VarExp(loc, swap), sd.dtor, false)); + /* Instead of running the destructor on s, run it + * on swap. This avoids needing to copy swap back in to s. + */ + 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)); + e = Expression.combine(e1, e2, e3, e4); } + /* postblit was called when the value was passed to opAssign, we just need to blit the result */ + else if (sd.postblit) + e = new BlitExp(loc, new ThisExp(loc), new IdentifierExp(loc, Id.p)); else { /* Do memberwise copy. @@ -504,7 +542,7 @@ extern (C++) FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc) /* const bool opEquals(ref const S s); */ auto parameters = new Parameters(); - parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, null, null)); + parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, null, null, null)); tfeqptr = new TypeFunction(parameters, Type.tbool, 0, LINK.d); tfeqptr.mod = MODFlags.const_; tfeqptr = cast(TypeFunction)tfeqptr.typeSemantic(Loc.initial, &scx); @@ -529,8 +567,8 @@ extern (C++) FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc) Loc declLoc; // loc is unnecessary so __xopEquals is never called directly Loc loc; // loc is unnecessary so errors are gagged auto parameters = new Parameters(); - parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.p, null)); - parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.q, null)); + parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.p, null, null)); + parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.q, null, null)); auto tf = new TypeFunction(parameters, Type.tbool, 0, LINK.d); Identifier id = Id.xopEquals; auto fop = new FuncDeclaration(declLoc, Loc.initial, id, STC.static_, tf); @@ -574,7 +612,7 @@ extern (C++) FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) /* const int opCmp(ref const S s); */ auto parameters = new Parameters(); - parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, null, null)); + parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, null, null, null)); tfcmpptr = new TypeFunction(parameters, Type.tint32, 0, LINK.d); tfcmpptr.mod = MODFlags.const_; tfcmpptr = cast(TypeFunction)tfcmpptr.typeSemantic(Loc.initial, &scx); @@ -649,8 +687,8 @@ extern (C++) FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) Loc declLoc; // loc is unnecessary so __xopCmp is never called directly Loc loc; // loc is unnecessary so errors are gagged auto parameters = new Parameters(); - parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.p, null)); - parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.q, null)); + parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.p, null, null)); + parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.q, null, null)); auto tf = new TypeFunction(parameters, Type.tint32, 0, LINK.d); Identifier id = Id.xopCmp; auto fop = new FuncDeclaration(declLoc, Loc.initial, id, STC.static_, tf); @@ -739,7 +777,7 @@ extern (C++) FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc) { if (Dsymbol s = search_function(sd, Id.tohash)) { - static __gshared TypeFunction tftohash; + __gshared TypeFunction tftohash; if (!tftohash) { tftohash = new TypeFunction(null, Type.thash_t, 0, LINK.d); @@ -760,7 +798,7 @@ extern (C++) FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc) Loc declLoc; // loc is unnecessary so __xtoHash is never called directly Loc loc; // internal code should have no loc to prevent coverage auto parameters = new Parameters(); - parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.p, null)); + parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.p, null, null)); auto tf = new TypeFunction(parameters, Type.thash_t, 0, LINK.d, STC.nothrow_ | STC.trusted); Identifier id = Id.xtoHash; auto fop = new FuncDeclaration(declLoc, Loc.initial, id, STC.static_, tf); @@ -1012,7 +1050,7 @@ private DtorDeclaration buildWindowsCppDtor(AggregateDeclaration ad, DtorDeclara // // 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)); + Parameter delparam = new Parameter(STC.undefined_, Type.tuns32, Identifier.idPool("del"), new IntegerExp(dtor.loc, 0, Type.tuns32), null); Parameters* params = new Parameters; params.push(delparam); auto ftype = new TypeFunction(params, Type.tvoidptr, false, LINK.cpp, dtor.storage_class); diff --git a/gcc/d/dmd/complex.d b/gcc/d/dmd/complex.d index 34a15ef99..4ea8973ba 100644 --- a/gcc/d/dmd/complex.d +++ b/gcc/d/dmd/complex.d @@ -83,12 +83,12 @@ struct complex_t } } - bool opCast(T : bool)() + bool opCast(T : bool)() const { return re || im; } - int opEquals(complex_t y) + int opEquals(complex_t y) const { return re == y.re && im == y.im; } diff --git a/gcc/d/dmd/complex_t.h b/gcc/d/dmd/complex_t.h index 1510ec46a..d160c4c63 100644 --- a/gcc/d/dmd/complex_t.h +++ b/gcc/d/dmd/complex_t.h @@ -11,7 +11,7 @@ #ifndef DMD_COMPLEX_T_H #define DMD_COMPLEX_T_H -#include "ctfloat.h" +#include "root/ctfloat.h" /* Roll our own complex type for compilers that don't support complex */ diff --git a/gcc/d/dmd/cond.d b/gcc/d/dmd/cond.d index 581f0062f..3b88621b7 100644 --- a/gcc/d/dmd/cond.d +++ b/gcc/d/dmd/cond.d @@ -102,12 +102,9 @@ extern (C++) final class StaticForeach : RootObject bool needExpansion = false; extern (D) this(const ref Loc loc,ForeachStatement aggrfe,ForeachRangeStatement rangefe) - in { assert(!!aggrfe ^ !!rangefe); - } - body - { + this.loc = loc; this.aggrfe = aggrfe; this.rangefe = rangefe; @@ -291,7 +288,7 @@ extern (C++) final class StaticForeach : RootObject foreach (params; pparams) { auto p = aggrfe ? (*aggrfe.parameters)[i] : rangefe.prm; - params.push(new Parameter(p.storageClass, p.type, p.ident, null)); + params.push(new Parameter(p.storageClass, p.type, p.ident, null, null)); } } Expression[2] res; @@ -372,13 +369,10 @@ extern (C++) final class StaticForeach : RootObject * to finally expand the `static foreach` using * `dmd.statementsem.makeTupleForeach`. */ - final extern(D) void prepare(Scope* sc) - in + extern(D) void prepare(Scope* sc) { assert(sc); - } - body - { + if (aggrfe) { sc = sc.startCTFE(); @@ -414,7 +408,7 @@ extern (C++) final class StaticForeach : RootObject * Returns: * `true` iff ready to call `dmd.statementsem.makeTupleForeach`. */ - final extern(D) bool ready() + extern(D) bool ready() { return aggrfe && aggrfe.aggr && aggrfe.aggr.type && aggrfe.aggr.type.toBasetype().ty == Ttuple; } diff --git a/gcc/d/dmd/constfold.d b/gcc/d/dmd/constfold.d index 22227b9a2..93e1c7ce6 100644 --- a/gcc/d/dmd/constfold.d +++ b/gcc/d/dmd/constfold.d @@ -1384,11 +1384,9 @@ extern (C++) UnionExp Slice(Type type, Expression e1, Expression lwr, Expression cantExp(ue); else { - auto elements = new Expressions(); - elements.setDim(cast(size_t)(iupr - ilwr)); + auto elements = new Expressions(cast(size_t)(iupr - ilwr)); memcpy(elements.tdata(), es1.elements.tdata() + ilwr, cast(size_t)(iupr - ilwr) * ((*es1.elements)[0]).sizeof); - emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, elements); - ue.exp().type = type; + emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, type, elements); } } else @@ -1498,6 +1496,7 @@ extern (C++) UnionExp Cat(Type type, Expression e1, Expression e2) utf_encode(sz, s, cast(dchar)v); emplaceExp!(StringExp)(&ue, loc, s, len); StringExp es = cast(StringExp)ue.exp(); + es.type = type; es.sz = sz; es.committed = 1; } @@ -1506,9 +1505,8 @@ extern (C++) UnionExp Cat(Type type, Expression e1, Expression e2) // Create an ArrayLiteralExp auto elements = new Expressions(); elements.push(e); - emplaceExp!(ArrayLiteralExp)(&ue, e.loc, elements); + emplaceExp!(ArrayLiteralExp)(&ue, e.loc, type, elements); } - ue.exp().type = type; assert(ue.exp().type); return ue; } @@ -1519,8 +1517,7 @@ extern (C++) UnionExp Cat(Type type, Expression e1, Expression e2) // Handle null ~= null if (t1.ty == Tarray && t2 == t1.nextOf()) { - emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, e2); - ue.exp().type = type; + emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, type, e2); assert(ue.exp().type); return ue; } @@ -1575,15 +1572,13 @@ extern (C++) UnionExp Cat(Type type, Expression e1, Expression e2) StringExp es = cast(StringExp)e2; ArrayLiteralExp ea = cast(ArrayLiteralExp)e1; size_t len = es.len + ea.elements.dim; - auto elems = new Expressions(); - elems.setDim(len); + auto elems = new Expressions(len); for (size_t i = 0; i < ea.elements.dim; ++i) { (*elems)[i] = ea.getElement(i); } - emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, elems); + emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, type, elems); ArrayLiteralExp dest = cast(ArrayLiteralExp)ue.exp(); - dest.type = type; sliceAssignArrayLiteralFromString(dest, es, ea.elements.dim); assert(ue.exp().type); return ue; @@ -1594,15 +1589,13 @@ extern (C++) UnionExp Cat(Type type, Expression e1, Expression e2) StringExp es = cast(StringExp)e1; ArrayLiteralExp ea = cast(ArrayLiteralExp)e2; size_t len = es.len + ea.elements.dim; - auto elems = new Expressions(); - elems.setDim(len); + auto elems = new Expressions(len); for (size_t i = 0; i < ea.elements.dim; ++i) { (*elems)[es.len + i] = ea.getElement(i); } - emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, elems); + emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, type, elems); ArrayLiteralExp dest = cast(ArrayLiteralExp)ue.exp(); - dest.type = type; sliceAssignArrayLiteralFromString(dest, es, 0); assert(ue.exp().type); return ue; @@ -1656,7 +1649,7 @@ extern (C++) UnionExp Cat(Type type, Expression e1, Expression e2) // Concatenate the arrays auto elems = ArrayLiteralExp.copyElements(e1, e2); - emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, elems); + emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, cast(Type)null, elems); e = ue.exp(); if (type.toBasetype().ty == Tsarray) @@ -1680,7 +1673,7 @@ extern (C++) UnionExp Cat(Type type, Expression e1, Expression e2) // Concatenate the array with null auto elems = ArrayLiteralExp.copyElements(e); - emplaceExp!(ArrayLiteralExp)(&ue, e.loc, elems); + emplaceExp!(ArrayLiteralExp)(&ue, e.loc, cast(Type)null, elems); e = ue.exp(); if (type.toBasetype().ty == Tsarray) @@ -1698,7 +1691,7 @@ extern (C++) UnionExp Cat(Type type, Expression e1, Expression e2) ? ArrayLiteralExp.copyElements(e1) : new Expressions(); elems.push(e2); - emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, elems); + emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, cast(Type)null, elems); e = ue.exp(); if (type.toBasetype().ty == Tsarray) @@ -1714,7 +1707,7 @@ extern (C++) UnionExp Cat(Type type, Expression e1, Expression e2) { auto elems = ArrayLiteralExp.copyElements(e1, e2); - emplaceExp!(ArrayLiteralExp)(&ue, e2.loc, elems); + emplaceExp!(ArrayLiteralExp)(&ue, e2.loc, cast(Type)null, elems); e = ue.exp(); if (type.toBasetype().ty == Tsarray) @@ -1742,9 +1735,8 @@ extern (C++) UnionExp Cat(Type type, Expression e1, Expression e2) { auto expressions = new Expressions(); expressions.push(e); - emplaceExp!(ArrayLiteralExp)(&ue, loc, expressions); + emplaceExp!(ArrayLiteralExp)(&ue, loc, t, expressions); e = ue.exp(); - e.type = t; } else { diff --git a/gcc/d/dmd/cppmangle.d b/gcc/d/dmd/cppmangle.d index 9ee567cc8..7a024cefe 100644 --- a/gcc/d/dmd/cppmangle.d +++ b/gcc/d/dmd/cppmangle.d @@ -2,6 +2,8 @@ * Compiler implementation of the $(LINK2 http://www.dlang.org, D programming language) * * Do mangling for C++ linkage. + * This is the POSIX side of the implementation. + * It exports two functions to C++, `toCppMangleItanium` and `cppTypeInfoMangleItanium`. * * Copyright: Copyright (C) 1999-2018 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, http://www.digitalmars.com @@ -44,7 +46,6 @@ import dmd.tokens; import dmd.typesem; 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 } @@ -61,7 +62,8 @@ package CppOperator isCppOperator(Identifier id) return CppOperator.Unknown; } -const(char)* toCppMangleItanium(Dsymbol s) +/// +extern(C++) const(char)* toCppMangleItanium(Dsymbol s) { //printf("toCppMangleItanium(%s)\n", s.toChars()); OutBuffer buf; @@ -70,7 +72,8 @@ const(char)* toCppMangleItanium(Dsymbol s) return buf.extractString(); } -const(char)* cppTypeInfoMangleItanium(Dsymbol s) +/// +extern(C++) const(char)* cppTypeInfoMangleItanium(Dsymbol s) { //printf("cppTypeInfoMangle(%s)\n", s.toChars()); OutBuffer buf; @@ -100,12 +103,74 @@ bool isPrimaryDtor(const Dsymbol sym) private final class CppMangleVisitor : Visitor { - alias visit = Visitor.visit; Objects components; // array of components available for substitution OutBuffer* buf; // append the mangling to buf[] Loc loc; // location for use in error messages - final: + /** + * Constructor + * + * Params: + * buf = `OutBuffer` to write the mangling to + * loc = `Loc` of the symbol being mangled + */ + this(OutBuffer* buf, Loc loc) + { + this.buf = buf; + this.loc = loc; + } + + /***** + * Entry point. Append mangling to buf[] + * Params: + * s = symbol to mangle + */ + void mangleOf(Dsymbol s) + { + if (VarDeclaration vd = s.isVarDeclaration()) + { + mangle_variable(vd, false); + } + else if (FuncDeclaration fd = s.isFuncDeclaration()) + { + mangle_function(fd); + } + else + { + assert(0); + } + } + + /** + * Write a seq-id from an index number, excluding the terminating '_' + * + * Params: + * idx = the index in a substitution list. + * Note that index 0 has no value, and `S0_` would be the + * substitution at index 1 in the list. + * + * See-Also: + * https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.seq-id + */ + private void writeSequenceFromIndex(size_t idx) + { + if (idx) + { + void write_seq_id(size_t i) + { + if (i >= 36) + { + write_seq_id(i / 36); + i %= 36; + } + i += (i < 10) ? '0' : 'A' - 10; + buf.writeByte(cast(char)i); + } + + write_seq_id(idx - 1); + } + } + bool substitute(RootObject p) { //printf("substitute %s\n", p ? p.toChars() : null); @@ -116,22 +181,7 @@ private final class CppMangleVisitor : Visitor /* Sequence is S_, S0_, .., S9_, SA_, ..., SZ_, S10_, ... */ buf.writeByte('S'); - if (i) - { - // Write to buf - void write_seq_id(size_t i) - { - if (i >= 36) - { - write_seq_id(i / 36); - i %= 36; - } - i += (i < 10) ? '0' : 'A' - 10; - buf.writeByte(cast(char)i); - } - - write_seq_id(i - 1); - } + writeSequenceFromIndex(i); buf.writeByte('_'); return true; } @@ -209,6 +259,19 @@ private final class CppMangleVisitor : Visitor components.push(p); } + /** + * Write an identifier preceded by its length + * + * Params: + * ident = `Identifier` to write to `this.buf` + */ + void writeIdentifier(const ref Identifier ident) + { + const name = ident.toString(); + this.buf.print(name.length); + this.buf.writestring(name); + } + /************************ * Determine if symbol is indeed the global ::std namespace. * Params: @@ -319,6 +382,8 @@ private final class CppMangleVisitor : Visitor * Write the mangled representation of the template arguments. * Params: * ti = the template instance + * firstArg = index of the first template argument to mangle + * (used for operator overloading) * Returns: * true if any arguments were written */ @@ -372,18 +437,12 @@ private final class CppMangleVisitor : Visitor if (!substitute(ti.tempdecl)) { append(ti.tempdecl); - const name = ti.tempdecl.toAlias().ident.toString(); - buf.print(name.length); - buf.writestring(name); + this.writeIdentifier(ti.tempdecl.toAlias().ident); } template_args(ti); } else - { - const name = s.ident.toString(); - buf.print(name.length); - buf.writestring(name); - } + this.writeIdentifier(s.ident); } /******** @@ -465,7 +524,7 @@ private final class CppMangleVisitor : Visitor * Returns: * true if found */ - extern (D) bool char_std_char_traits_char(TemplateInstance ti, string st) + bool char_std_char_traits_char(TemplateInstance ti, string st) { if (ti.tiargs.dim == 2 && isChar((*ti.tiargs)[0]) && @@ -688,156 +747,14 @@ 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.isCtorDeclaration()) - { - buf.writestring("C1"); - appendReturnType = false; - } - else if (d.isPrimaryDtor()) - { - buf.writestring("D1"); - appendReturnType = false; - } - else - { - 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); - if (appendReturnType) - headOfType(tf.nextOf()); // mangle return type + this.mangleTemplatedFunction(d, tf, ftd, ti); } else { Dsymbol p = d.toParent(); if (p && !p.isModule() && tf.linkage == LINK.cpp) { - /* ::= N [] E - * ::= N [] E - */ - buf.writeByte('N'); - CV_qualifiers(d.type); - - /* ::= - * ::= - * ::= - * ::= # empty - * ::= - * ::= - */ - prefix_name(p); - //printf("p: %s\n", buf.peekString()); + this.mangleNestedFuncPrefix(tf, p); if (d.isCtorDeclaration()) buf.writestring("C1"); @@ -868,6 +785,151 @@ private final class CppMangleVisitor : Visitor } } + /** + * Mangles a function template to C++ + * + * Params: + * d = Function declaration + * tf = Function type (casted d.type) + * ftd = Template declaration (ti.templdecl) + * ti = Template instance (d.parent) + */ + void mangleTemplatedFunction(FuncDeclaration d, TypeFunction tf, + TemplateDeclaration ftd, TemplateInstance ti) + { + Dsymbol p = ti.toParent(); + // Check if this function is *not* nested + if (!p || p.isModule() || tf.linkage != LINK.cpp) + { + source_name(ti); + headOfType(tf.nextOf()); // mangle return type + return; + } + + // It's a nested function (e.g. a member of an aggregate) + this.mangleNestedFuncPrefix(tf, p); + + if (d.isCtorDeclaration()) + { + buf.writestring("C1"); + } + else if (d.isPrimaryDtor()) + { + buf.writestring("D1"); + } + else + { + int firstTemplateArg = 0; + bool appendReturnType = true; + 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'); + if (appendReturnType) + headOfType(tf.nextOf()); // mangle return type + } + } + + void mangleFunctionParameters(Parameters* parameters, int varargs) { int numparams = 0; @@ -895,34 +957,6 @@ private final class CppMangleVisitor : Visitor buf.writeByte('v'); // encode (void) parameters } -public: - extern (D) this(OutBuffer* buf, Loc loc) - { - this.buf = buf; - this.loc = loc; - } - - /***** - * Entry point. Append mangling to buf[] - * Params: - * s = symbol to mangle - */ - void mangleOf(Dsymbol s) - { - if (VarDeclaration vd = s.isVarDeclaration()) - { - mangle_variable(vd, false); - } - else if (FuncDeclaration fd = s.isFuncDeclaration()) - { - mangle_function(fd); - } - else - { - assert(0); - } - } - /****** The rest is type mangling ************/ void error(Type t) @@ -957,11 +991,6 @@ public: } } - override void visit(Type t) - { - error(t); - } - /****** * Write out 1 or 2 character basic type mangling. * Handle const and substitutions. @@ -985,6 +1014,124 @@ public: buf.writeByte(c); } + + /**************** + * Write structs and enums. + * Params: + * t = TypeStruct or TypeEnum + */ + void doSymbol(Type t) + { + if (substitute(t)) + return; + CV_qualifiers(t); + + // Handle any target-specific struct types. + if (auto tm = Target.cppTypeMangle(t)) + { + buf.writestring(tm); + } + else + { + Dsymbol s = t.toDsymbol(null); + Dsymbol p = s.toParent(); + if (p && p.isTemplateInstance()) + { + /* https://issues.dlang.org/show_bug.cgi?id=17947 + * Substitute the template instance symbol, not the struct/enum symbol + */ + if (substitute(p)) + return; + } + if (!substitute(s)) + { + cpp_mangle_name(s, t.isConst()); + } + } + if (t.isConst()) + append(t); + } + + + + /************************ + * Mangle a class type. + * If it's the head, treat the initial pointer as a value type. + * Params: + * t = class type + * head = true for head of a type + */ + void mangleTypeClass(TypeClass t, bool head) + { + if (t.isImmutable() || t.isShared()) + return error(t); + + /* Mangle as a + */ + if (substitute(t)) + return; + if (!head) + CV_qualifiers(t); + buf.writeByte('P'); + + CV_qualifiers(t); + + { + Dsymbol s = t.toDsymbol(null); + Dsymbol p = s.toParent(); + if (p && p.isTemplateInstance()) + { + /* https://issues.dlang.org/show_bug.cgi?id=17947 + * Substitute the template instance symbol, not the class symbol + */ + if (substitute(p)) + return; + } + } + + if (!substitute(t.sym)) + { + cpp_mangle_name(t.sym, t.isConst()); + } + if (t.isConst()) + append(null); // C++ would have an extra type here + append(t); + } + + /** + * Mangle the prefix of a nested (e.g. member) function + * + * Params: + * tf = Type of the nested function + * parent = Parent in which the function is nested + */ + void mangleNestedFuncPrefix(TypeFunction tf, Dsymbol parent) + { + /* ::= N [] E + * ::= N [] E + */ + buf.writeByte('N'); + CV_qualifiers(tf); + + /* ::= + * ::= + * ::= + * ::= # empty + * ::= + * ::= + */ + prefix_name(parent); + } + +extern(C++): + + alias visit = Visitor.visit; + + override void visit(Type t) + { + error(t); + } + override void visit(TypeNull t) { if (t.isImmutable() || t.isShared()) @@ -1041,10 +1188,10 @@ public: case Tuns32: c = 'j'; break; case Tfloat32: c = 'f'; break; case Tint64: - c = Target.c_longsize == 8 ? Target.int64Mangle : 'x'; + c = Target.c_longsize == 8 ? 'l' : 'x'; break; case Tuns64: - c = Target.c_longsize == 8 ? Target.uint64Mangle : 'y'; + c = Target.c_longsize == 8 ? 'm' : 'y'; break; case Tint128: c = 'n'; break; case Tuns128: c = 'o'; break; @@ -1222,89 +1369,8 @@ public: doSymbol(t); } - /**************** - * Write structs and enums. - * Params: - * t = TypeStruct or TypeEnum - */ - final void doSymbol(Type t) - { - if (substitute(t)) - return; - CV_qualifiers(t); - - // Handle any target-specific struct types. - if (auto tm = Target.cppTypeMangle(t)) - { - buf.writestring(tm); - } - else - { - Dsymbol s = t.toDsymbol(null); - Dsymbol p = s.toParent(); - if (p && p.isTemplateInstance()) - { - /* https://issues.dlang.org/show_bug.cgi?id=17947 - * Substitute the template instance symbol, not the struct/enum symbol - */ - if (substitute(p)) - return; - } - if (!substitute(s)) - { - cpp_mangle_name(s, t.isConst()); - } - } - if (t.isConst()) - append(t); - } - override void visit(TypeClass t) { mangleTypeClass(t, false); } - - /************************ - * Mangle a class type. - * If it's the head, treat the initial pointer as a value type. - * Params: - * t = class type - * head = true for head of a type - */ - void mangleTypeClass(TypeClass t, bool head) - { - if (t.isImmutable() || t.isShared()) - return error(t); - - /* Mangle as a - */ - if (substitute(t)) - return; - if (!head) - CV_qualifiers(t); - buf.writeByte('P'); - - CV_qualifiers(t); - - { - Dsymbol s = t.toDsymbol(null); - Dsymbol p = s.toParent(); - if (p && p.isTemplateInstance()) - { - /* https://issues.dlang.org/show_bug.cgi?id=17947 - * Substitute the template instance symbol, not the class symbol - */ - if (substitute(p)) - return; - } - } - - if (!substitute(t.sym)) - { - cpp_mangle_name(t.sym, t.isConst()); - } - if (t.isConst()) - append(null); // C++ would have an extra type here - append(t); - } } diff --git a/gcc/d/dmd/ctfeexpr.d b/gcc/d/dmd/ctfeexpr.d index 74bd6b46c..fceee76e5 100644 --- a/gcc/d/dmd/ctfeexpr.d +++ b/gcc/d/dmd/ctfeexpr.d @@ -39,14 +39,14 @@ import dmd.visitor; */ struct CtfeStatus { - extern (C++) static __gshared int callDepth = 0; // current number of recursive calls + extern (C++) __gshared int callDepth = 0; // current number of recursive calls // When printing a stack trace, suppress this number of calls - extern (C++) static __gshared int stackTraceCallsToSuppress = 0; + extern (C++) __gshared int stackTraceCallsToSuppress = 0; - extern (C++) static __gshared int maxCallDepth = 0; // highest number of recursive calls - extern (C++) static __gshared int numArrayAllocs = 0; // Number of allocated arrays - extern (C++) static __gshared int numAssignments = 0; // total number of assignments executed + extern (C++) __gshared int maxCallDepth = 0; // highest number of recursive calls + extern (C++) __gshared int numArrayAllocs = 0; // Number of allocated arrays + extern (C++) __gshared int numAssignments = 0; // total number of assignments executed } /*********************************************************** @@ -191,7 +191,7 @@ extern (C++) final class ThrownExceptionExp : Expression * (eg, in ScopeStatement) */ if (loc.isValid() && !loc.equals(thrown.loc)) - errorSupplemental(loc, "thrown from here"); + .errorSupplemental(loc, "thrown from here"); } override void accept(Visitor v) @@ -219,6 +219,8 @@ extern (C++) final class CTFEExp : Expression return ""; case TOK.voidExpression: return ""; + case TOK.showCtfeContext: + return ""; case TOK.break_: return ""; case TOK.continue_: @@ -230,11 +232,15 @@ extern (C++) final class CTFEExp : Expression } } - extern (C++) static __gshared CTFEExp cantexp; - extern (C++) static __gshared CTFEExp voidexp; - extern (C++) static __gshared CTFEExp breakexp; - extern (C++) static __gshared CTFEExp continueexp; - extern (C++) static __gshared CTFEExp gotoexp; + extern (C++) __gshared CTFEExp cantexp; + extern (C++) __gshared CTFEExp voidexp; + extern (C++) __gshared CTFEExp breakexp; + extern (C++) __gshared CTFEExp continueexp; + extern (C++) __gshared CTFEExp gotoexp; + /* Used when additional information is needed regarding + * a ctfe error. + */ + extern (C++) __gshared CTFEExp showcontext; static bool isCantExp(const Expression e) { @@ -250,7 +256,7 @@ extern (C++) final class CTFEExp : Expression // True if 'e' is CTFEExp::cantexp, or an exception extern (C++) bool exceptionOrCantInterpret(const Expression e) { - return e && (e.op == TOK.cantExpression || e.op == TOK.thrownException); + return e && (e.op == TOK.cantExpression || e.op == TOK.thrownException || e.op == TOK.showCtfeContext); } /************** Aggregate literals (AA/string/array/struct) ******************/ @@ -299,8 +305,7 @@ private Expressions* copyLiteralArray(Expressions* oldelems, Expression basis = if (!oldelems) return oldelems; CtfeStatus.numArrayAllocs++; - auto newelems = new Expressions(); - newelems.setDim(oldelems.dim); + auto newelems = new Expressions(oldelems.dim); foreach (i, el; *oldelems) { (*newelems)[i] = copyLiteral(el ? el : basis).copy(); @@ -332,10 +337,9 @@ extern (C++) UnionExp copyLiteral(Expression e) auto ale = cast(ArrayLiteralExp)e; auto elements = copyLiteralArray(ale.elements, ale.basis); - emplaceExp!(ArrayLiteralExp)(&ue, e.loc, elements); + emplaceExp!(ArrayLiteralExp)(&ue, e.loc, e.type, elements); ArrayLiteralExp r = cast(ArrayLiteralExp)ue.exp(); - r.type = e.type; r.ownedByCtfe = OwnedBy.ctfe; return ue; } @@ -356,8 +360,7 @@ extern (C++) UnionExp copyLiteral(Expression e) */ auto sle = cast(StructLiteralExp)e; auto oldelems = sle.elements; - auto newelems = new Expressions(); - newelems.setDim(oldelems.dim); + auto newelems = new Expressions(oldelems.dim); foreach (i, ref el; *newelems) { // We need the struct definition to detect block assignment @@ -622,14 +625,12 @@ extern (C++) ArrayLiteralExp createBlockDuplicatedArrayLiteral(const ref Loc loc const tb = elem.type.toBasetype(); const mustCopy = tb.ty == Tstruct || tb.ty == Tsarray; - auto elements = new Expressions(); - elements.setDim(dim); + auto elements = new Expressions(dim); foreach (i, ref el; *elements) { el = mustCopy && i ? copyLiteral(elem).copy() : elem; } - auto ale = new ArrayLiteralExp(loc, elements); - ale.type = type; + auto ale = new ArrayLiteralExp(loc, type, elements); ale.ownedByCtfe = OwnedBy.ctfe; return ale; } @@ -1489,10 +1490,9 @@ extern (C++) UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expre // [ e1 ] ~ [ e2 ] ---> [ e1, e2 ] ArrayLiteralExp es1 = cast(ArrayLiteralExp)e1; ArrayLiteralExp es2 = cast(ArrayLiteralExp)e2; - emplaceExp!(ArrayLiteralExp)(&ue, es1.loc, copyLiteralArray(es1.elements)); + emplaceExp!(ArrayLiteralExp)(&ue, es1.loc, type, copyLiteralArray(es1.elements)); es1 = cast(ArrayLiteralExp)ue.exp(); es1.elements.insert(es1.elements.dim, copyLiteralArray(es2.elements)); - es1.type = type; return ue; } if (e1.op == TOK.arrayLiteral && e2.op == TOK.null_ && t1.nextOf().equals(t2.nextOf())) @@ -1709,8 +1709,7 @@ extern (C++) UnionExp changeArrayLiteralLength(const ref Loc loc, TypeArray arra Type elemType = arrayType.next; assert(elemType); Expression defaultElem = elemType.defaultInitLiteral(loc); - auto elements = new Expressions(); - elements.setDim(newlen); + auto elements = new Expressions(newlen); // Resolve slices size_t indxlo = 0; if (oldval.op == TOK.slice) @@ -1771,9 +1770,8 @@ extern (C++) UnionExp changeArrayLiteralLength(const ref Loc loc, TypeArray arra foreach (size_t i; copylen .. newlen) (*elements)[i] = defaultElem; } - emplaceExp!(ArrayLiteralExp)(&ue, loc, elements); + emplaceExp!(ArrayLiteralExp)(&ue, loc, arrayType, elements); ArrayLiteralExp aae = cast(ArrayLiteralExp)ue.exp(); - aae.type = arrayType; aae.ownedByCtfe = OwnedBy.ctfe; } return ue; @@ -1989,25 +1987,22 @@ extern (C++) UnionExp voidInitLiteral(Type t, VarDeclaration var) // For aggregate value types (structs, static arrays) we must // create an a separate copy for each element. const mustCopy = (elem.op == TOK.arrayLiteral || elem.op == TOK.structLiteral); - auto elements = new Expressions(); const d = cast(size_t)tsa.dim.toInteger(); - elements.setDim(d); + auto elements = new Expressions(d); foreach (i; 0 .. d) { if (mustCopy && i > 0) elem = copyLiteral(elem).copy(); (*elements)[i] = elem; } - emplaceExp!(ArrayLiteralExp)(&ue, var.loc, elements); + emplaceExp!(ArrayLiteralExp)(&ue, var.loc, tsa, elements); ArrayLiteralExp ae = cast(ArrayLiteralExp)ue.exp(); - ae.type = tsa; ae.ownedByCtfe = OwnedBy.ctfe; } else if (t.ty == Tstruct) { TypeStruct ts = cast(TypeStruct)t; - auto exps = new Expressions(); - exps.setDim(ts.sym.fields.dim); + auto exps = new Expressions(ts.sym.fields.dim); 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/gcc/d/dmd/ctorflow.d b/gcc/d/dmd/ctorflow.d index 870d08b32..6b10d3f1c 100644 --- a/gcc/d/dmd/ctorflow.d +++ b/gcc/d/dmd/ctorflow.d @@ -17,6 +17,7 @@ module dmd.ctorflow; import core.stdc.stdio; import dmd.root.rmem; +import dmd.globals : Loc; enum CSX : ushort { @@ -30,6 +31,13 @@ enum CSX : ushort deprecate_18719 = 0x40, // issue deprecation for Issue 18719 - delete when deprecation period is over } +/// Individual field in the Ctor with information about its callees and location. +struct FieldInit +{ + CSX csx; /// information about the field's callees + Loc loc; /// location of the field initialization +} + /*********** * Primitive flow analysis for constructors */ @@ -37,30 +45,19 @@ struct CtorFlow { CSX callSuper; /// state of calling other constructors - CSX[] fieldinit; /// state of field initializations + FieldInit[] fieldinit; /// state of field initializations void allocFieldinit(size_t dim) { - fieldinit = (cast(CSX*)mem.xcalloc(CSX.sizeof, dim))[0 .. dim]; + fieldinit = (cast(FieldInit*)mem.xcalloc(FieldInit.sizeof, dim))[0 .. dim]; } void freeFieldinit() { if (fieldinit.ptr) mem.xfree(fieldinit.ptr); - fieldinit = null; - } - CSX[] saveFieldInit() - { - CSX[] fi = null; - if (fieldinit.length) // copy - { - const dim = fieldinit.length; - fi = (cast(CSX*)mem.xmalloc(CSX.sizeof * dim))[0 .. dim]; - fi[] = fieldinit[]; - } - return fi; + fieldinit = null; } /*********************** @@ -70,7 +67,7 @@ struct CtorFlow */ CtorFlow clone() { - return CtorFlow(callSuper, saveFieldInit()); + return CtorFlow(callSuper, fieldinit.arraydup); } /********************************** @@ -82,7 +79,7 @@ struct CtorFlow { callSuper |= csx; foreach (ref u; fieldinit) - u |= csx; + u.csx |= csx; } /****************************** @@ -97,7 +94,12 @@ struct CtorFlow { assert(fieldinit.length == ctorflow.fieldinit.length); foreach (i, u; ctorflow.fieldinit) - fieldinit[i] |= u; + { + auto fi = &fieldinit[i]; + fi.csx |= u.csx; + if (fi.loc == Loc.init) + fi.loc = u.loc; + } } } } diff --git a/gcc/d/dmd/dcast.d b/gcc/d/dmd/dcast.d index 1b93851e3..de520d0d2 100644 --- a/gcc/d/dmd/dcast.d +++ b/gcc/d/dmd/dcast.d @@ -144,7 +144,7 @@ extern (C++) Expression implicitCastTo(Expression e, Scope* sc, Type t) visit(cast(Expression)e); Type tb = result.type.toBasetype(); - if (tb.ty == Tarray) + if (tb.ty == Tarray && global.params.useTypeInfo && Type.dtypeinfo) semanticTypeInfo(sc, (cast(TypeDArray)tb).next); } @@ -2308,7 +2308,7 @@ extern (C++) Expression castTo(Expression e, Scope* sc, Type t) { printf("DelegateExp::castTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars()); } - static __gshared const(char)* msg = "cannot form delegate due to covariant return type"; + __gshared const(char)* msg = "cannot form delegate due to covariant return type"; Type tb = t.toBasetype(); Type typeb = e.type.toBasetype(); diff --git a/gcc/d/dmd/dclass.d b/gcc/d/dmd/dclass.d index cc2c9fdf5..69a31ccd6 100644 --- a/gcc/d/dmd/dclass.d +++ b/gcc/d/dmd/dclass.d @@ -147,32 +147,18 @@ struct BaseClass } } -struct ClassFlags +enum ClassFlags : int { - alias Type = uint; - - enum Enum : int - { - isCOMclass = 0x1, - noPointers = 0x2, - hasOffTi = 0x4, - hasCtor = 0x8, - hasGetMembers = 0x10, - hasTypeInfo = 0x20, - isAbstract = 0x40, - isCPPclass = 0x80, - hasDtor = 0x100, - } - - alias isCOMclass = Enum.isCOMclass; - alias noPointers = Enum.noPointers; - alias hasOffTi = Enum.hasOffTi; - alias hasCtor = Enum.hasCtor; - alias hasGetMembers = Enum.hasGetMembers; - alias hasTypeInfo = Enum.hasTypeInfo; - alias isAbstract = Enum.isAbstract; - alias isCPPclass = Enum.isCPPclass; - alias hasDtor = Enum.hasDtor; + none = 0x0, + isCOMclass = 0x1, + noPointers = 0x2, + hasOffTi = 0x4, + hasCtor = 0x8, + hasGetMembers = 0x10, + hasTypeInfo = 0x20, + isAbstract = 0x40, + isCPPclass = 0x80, + hasDtor = 0x100, } /*********************************************************** @@ -237,7 +223,7 @@ extern (C++) class ClassDeclaration : AggregateDeclaration Symbol* cpp_type_info_ptr_sym; // cached instance of class Id.cpp_type_info_ptr - final extern (D) this(Loc loc, Identifier id, BaseClasses* baseclasses, Dsymbols* members, bool inObject) + final extern (D) this(const ref Loc loc, Identifier id, BaseClasses* baseclasses, Dsymbols* members, bool inObject) { if (!id) { @@ -248,7 +234,7 @@ extern (C++) class ClassDeclaration : AggregateDeclaration super(loc, id); - static __gshared const(char)* msg = "only object.d can define this reserved class name"; + __gshared const(char)* msg = "only object.d can define this reserved class name"; if (baseclasses) { @@ -1005,7 +991,7 @@ extern (C++) class ClassDeclaration : AggregateDeclaration */ extern (C++) final class InterfaceDeclaration : ClassDeclaration { - extern (D) this(Loc loc, Identifier id, BaseClasses* baseclasses) + extern (D) this(const ref Loc loc, Identifier id, BaseClasses* baseclasses) { super(loc, id, baseclasses, null, false); if (id == Id.IUnknown) // IUnknown is the root of all COM interfaces diff --git a/gcc/d/dmd/declaration.d b/gcc/d/dmd/declaration.d index 5436accf2..9f71265d7 100644 --- a/gcc/d/dmd/declaration.d +++ b/gcc/d/dmd/declaration.d @@ -12,6 +12,7 @@ module dmd.declaration; +import core.stdc.stdio; import dmd.aggregate; import dmd.arraytypes; import dmd.ctorflow; @@ -112,7 +113,8 @@ bool modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1) break; } assert(i < dim); - const fi = sc.ctorflow.fieldinit[i]; + auto fieldInit = &sc.ctorflow.fieldinit[i]; + const fi = fieldInit.csx; if (fi & CSX.this_ctor) { @@ -127,9 +129,14 @@ bool modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1) // dmd.ctorflow.CSX enum. // @@@DEPRECATED_2019-01@@@. if (fi & CSX.deprecate_18719) - .deprecation(loc, "%s field `%s` initialized multiple times", modStr, var.toChars()); + { + .deprecation(loc, "%s field `%s` was initialized in a previous constructor call", modStr, var.toChars()); + } else + { .error(loc, "%s field `%s` initialized multiple times", modStr, var.toChars()); + .errorSupplemental(fieldInit.loc, "Previous initialization is here."); + } } } else if (sc.inLoop || (fi & CSX.label)) @@ -143,7 +150,8 @@ bool modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1) } } - sc.ctorflow.fieldinit[i] |= CSX.this_ctor; + fieldInit.csx |= CSX.this_ctor; + fieldInit.loc = e1.loc; if (var.overlapped) // https://issues.dlang.org/show_bug.cgi?id=15258 { foreach (j, v; ad.fields) @@ -151,7 +159,7 @@ bool modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1) if (v is var || !var.isOverlappedWith(v)) continue; v.ctorinit = true; - sc.ctorflow.fieldinit[j] = CSX.this_ctor; + sc.ctorflow.fieldinit[j].csx = CSX.this_ctor; } } } @@ -340,7 +348,7 @@ extern (C++) abstract class Declaration : Dsymbol return false; } } - error(loc, "is not callable because it is annotated with `@disable`"); + error(loc, "cannot be used because it is annotated with `@disable`"); } } return true; @@ -553,7 +561,7 @@ extern (C++) final class TupleDeclaration : Declaration bool isexp; // true: expression tuple TypeTuple tupletype; // !=null if this is a type tuple - extern (D) this(Loc loc, Identifier id, Objects* objects) + extern (D) this(const ref Loc loc, Identifier id, Objects* objects) { super(id); this.loc = loc; @@ -595,8 +603,7 @@ extern (C++) final class TupleDeclaration : Declaration /* We know it's a type tuple, so build the TypeTuple */ Types* types = cast(Types*)objects; - auto args = new Parameters(); - args.setDim(objects.dim); + auto args = new Parameters(objects.dim); OutBuffer buf; int hasdeco = 1; for (size_t i = 0; i < types.dim; i++) @@ -613,7 +620,7 @@ extern (C++) final class TupleDeclaration : Declaration } else { - auto arg = new Parameter(0, t, null, null); + auto arg = new Parameter(0, t, null, null, null); } (*args)[i] = arg; if (!t.deco) @@ -684,7 +691,7 @@ extern (C++) final class AliasDeclaration : Declaration Dsymbol overnext; // next in overload list Dsymbol _import; // !=null if unresolved internal alias for selective import - extern (D) this(Loc loc, Identifier id, Type type) + extern (D) this(const ref Loc loc, Identifier id, Type type) { super(id); //printf("AliasDeclaration(id = '%s', type = %p)\n", id.toChars(), type); @@ -694,7 +701,7 @@ extern (C++) final class AliasDeclaration : Declaration assert(type); } - extern (D) this(Loc loc, Identifier id, Dsymbol s) + extern (D) this(const ref Loc loc, Identifier id, Dsymbol s) { super(id); //printf("AliasDeclaration(id = '%s', s = %p)\n", id.toChars(), s); @@ -1088,7 +1095,9 @@ extern (C++) class VarDeclaration : Declaration Expression edtor; // if !=null, does the destruction of the variable IntRange* range; // if !=null, the variable is known to be within the range - final extern (D) this(Loc loc, Type type, Identifier id, Initializer _init, StorageClass storage_class = STC.undefined_) + VarDeclarations* maybes; // STC.maybescope variables that are assigned to this STC.maybescope variable + + final extern (D) this(const ref Loc loc, Type type, Identifier id, Initializer _init, StorageClass storage_class = STC.undefined_) { super(id); //printf("VarDeclaration('%s')\n", id.toChars()); @@ -1632,6 +1641,23 @@ extern (C++) class VarDeclaration : Declaration { return sequenceNumber < v.sequenceNumber; } + + /*************************************** + * Add variable to maybes[]. + * When a maybescope variable `v` is assigned to a maybescope variable `this`, + * we cannot determine if `this` is actually scope until the semantic + * analysis for the function is completed. Thus, we save the data + * until then. + * Params: + * v = an STC.maybescope variable that was assigned to `this` + */ + final void addMaybe(VarDeclaration v) + { + //printf("add %s to %s's list of dependencies\n", v.toChars(), toChars()); + if (!maybes) + maybes = new VarDeclarations(); + maybes.push(v); + } } /*********************************************************** @@ -1641,7 +1667,7 @@ extern (C++) final class SymbolDeclaration : Declaration { StructDeclaration dsym; - extern (D) this(Loc loc, StructDeclaration dsym) + extern (D) this(const ref Loc loc, StructDeclaration dsym) { super(dsym.ident); this.loc = loc; @@ -2113,7 +2139,7 @@ extern (C++) final class TypeInfoVectorDeclaration : TypeInfoDeclaration */ extern (C++) final class ThisDeclaration : VarDeclaration { - extern (D) this(Loc loc, Type t) + extern (D) this(const ref Loc loc, Type t) { super(loc, t, Id.This, null); storage_class |= STC.nodtor; diff --git a/gcc/d/dmd/declaration.h b/gcc/d/dmd/declaration.h index c8ba91d28..abe921041 100644 --- a/gcc/d/dmd/declaration.h +++ b/gcc/d/dmd/declaration.h @@ -265,6 +265,8 @@ class VarDeclaration : public Declaration Expression *edtor; // if !=NULL, does the destruction of the variable IntRange *range; // if !NULL, the variable is known to be within the range + VarDeclarations *maybes; // STCmaybescope variables that are assigned to this STCmaybescope variable + Dsymbol *syntaxCopy(Dsymbol *); void setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion); const char *kind() const; diff --git a/gcc/d/dmd/denum.d b/gcc/d/dmd/denum.d index 8b976a195..2d6b39b2d 100644 --- a/gcc/d/dmd/denum.d +++ b/gcc/d/dmd/denum.d @@ -14,6 +14,7 @@ module dmd.denum; import core.stdc.stdio; +import dmd.attrib; import dmd.gluelayer; import dmd.declaration; import dmd.dscope; @@ -263,7 +264,7 @@ extern (C++) final class EnumDeclaration : ScopeDsymbol * Returns: * true if special */ - final bool isSpecial() const nothrow @nogc + bool isSpecial() const nothrow @nogc { return (ident == Id.__c_long || ident == Id.__c_ulong || @@ -382,6 +383,15 @@ extern (C++) final class EnumMember : VarDeclaration this.origType = origType; } + extern(D) this(Loc loc, Identifier id, Expression value, Type memtype, + StorageClass stc, UserAttributeDeclaration uad, DeprecatedDeclaration dd) + { + this(loc, id, value, memtype); + storage_class = stc; + userAttribDecl = uad; + depdecl = dd; + } + override Dsymbol syntaxCopy(Dsymbol s) { assert(!s); @@ -396,6 +406,9 @@ extern (C++) final class EnumMember : VarDeclaration Expression getVarExp(const ref Loc loc, Scope* sc) { dsymbolSemantic(this, sc); + if (errors) + return new ErrorExp(); + checkDisabled(loc, sc); if (errors) return new ErrorExp(); Expression e = new VarExp(loc, this); diff --git a/gcc/d/dmd/dimport.d b/gcc/d/dmd/dimport.d index bd3feadd9..bda52fbd0 100644 --- a/gcc/d/dmd/dimport.d +++ b/gcc/d/dmd/dimport.d @@ -47,7 +47,7 @@ extern (C++) final class Import : Dsymbol // corresponding AliasDeclarations for alias=name pairs AliasDeclarations aliasdecls; - extern (D) this(Loc loc, Identifiers* packages, Identifier id, Identifier aliasId, int isstatic) + extern (D) this(const ref Loc loc, Identifiers* packages, Identifier id, Identifier aliasId, int isstatic) { super(null); assert(id); @@ -120,10 +120,16 @@ extern (C++) final class Import : Dsymbol return si; } - void load(Scope* sc) + /******************************* + * Load this module. + * Returns: + * true for errors, false for success + */ + bool load(Scope* sc) { //printf("Import::load('%s') %p\n", toPrettyChars(), this); // See if existing module + const errors = global.errors; DsymbolTable dst = Package.resolve(packages, null, &pkg); version (none) { @@ -131,7 +137,7 @@ extern (C++) final class Import : Dsymbol { .error(loc, "can only import from a module, not from a member of module `%s`. Did you mean `import %s : %s`?", pkg.toChars(), pkg.toPrettyChars(), id.toChars()); mod = pkg.isModule(); // Error recovery - treat as import of that module - return; + return true; } } Dsymbol s = dst.lookup(id); @@ -194,6 +200,7 @@ extern (C++) final class Import : Dsymbol if (!pkg) pkg = mod; //printf("-Import::load('%s'), pkg = %p\n", toChars(), pkg); + return global.errors != errors; } override void importAll(Scope* sc) diff --git a/gcc/d/dmd/dinterpret.d b/gcc/d/dmd/dinterpret.d index f25c4480c..fdbf51758 100644 --- a/gcc/d/dmd/dinterpret.d +++ b/gcc/d/dmd/dinterpret.d @@ -110,8 +110,7 @@ public extern (C++) Expression ctfeInterpretForPragmaMsg(Expression e) { if (!expsx) { - expsx = new Expressions(); - expsx.setDim(tup.exps.dim); + expsx = new Expressions(tup.exps.dim); for (size_t j = 0; j < tup.exps.dim; j++) (*expsx)[j] = (*tup.exps)[j]; } @@ -702,18 +701,6 @@ public: // we can't compile asm statements } - version (IN_GCC) - { - override void visit(ExtAsmStatement s) - { - debug (LOGCOMPILE) - { - printf("%s ExtAsmStatement::ctfeCompile\n", s.loc.toChars()); - } - // we can't compile extended asm statements - } - } - void ctfeCompile(Statement s) { s.accept(this); @@ -807,14 +794,13 @@ private Expression interpretFunction(FuncDeclaration fd, InterState* istate, Exp // Place to hold all the arguments to the function while // we are evaluating them. - Expressions eargs; size_t dim = arguments ? arguments.dim : 0; assert((fd.parameters ? fd.parameters.dim : 0) == dim); /* Evaluate all the arguments to the function, * store the results in eargs[] */ - eargs.setDim(dim); + Expressions eargs = Expressions(dim); for (size_t i = 0; i < dim; i++) { Expression earg = (*arguments)[i]; @@ -1971,25 +1957,6 @@ public: result = CTFEExp.cantexp; } - version (IN_GCC) - { - override void visit(ExtAsmStatement s) - { - debug (LOG) - { - printf("%s ExtAsmStatement::interpret()\n", s.loc.toChars()); - } - if (istate.start) - { - if (istate.start != s) - return; - istate.start = null; - } - s.error("extended `asm` statements cannot be interpreted at compile time"); - result = CTFEExp.cantexp; - } - } - override void visit(ImportStatement s) { debug (LOG) @@ -2761,9 +2728,8 @@ public: result = CTFEExp.cantexp; return; } - emplaceExp!(ArrayLiteralExp)(pue, e.loc, basis, expsx); + emplaceExp!(ArrayLiteralExp)(pue, e.loc, e.type, basis, expsx); auto ale = cast(ArrayLiteralExp)pue.exp(); - ale.type = e.type; ale.ownedByCtfe = OwnedBy.ctfe; result = ale; } @@ -2959,12 +2925,10 @@ public: if (exceptionOrCantInterpret(elem)) return elem; - auto elements = new Expressions(); - elements.setDim(len); + auto elements = new Expressions(len); for (size_t i = 0; i < len; i++) (*elements)[i] = copyLiteral(elem).copy(); - auto ae = new ArrayLiteralExp(loc, elements); - ae.type = newtype; + auto ae = new ArrayLiteralExp(loc, newtype, elements); ae.ownedByCtfe = OwnedBy.ctfe; return ae; } @@ -3053,8 +3017,7 @@ public: size_t totalFieldCount = 0; for (ClassDeclaration c = cd; c; c = c.baseClass) totalFieldCount += c.fields.dim; - auto elems = new Expressions(); - elems.setDim(totalFieldCount); + auto elems = new Expressions(totalFieldCount); size_t fieldsSoFar = totalFieldCount; for (ClassDeclaration c = cd; c; c = c.baseClass) { @@ -3131,11 +3094,9 @@ public: return; // Create a CTFE pointer &[newval][0] - auto elements = new Expressions(); - elements.setDim(1); + auto elements = new Expressions(1); (*elements)[0] = newval; - auto ae = new ArrayLiteralExp(e.loc, elements); - ae.type = e.newtype.arrayOf(); + auto ae = new ArrayLiteralExp(e.loc, e.newtype.arrayOf(), elements); ae.ownedByCtfe = OwnedBy.ctfe; auto ei = new IndexExp(e.loc, ae, new IntegerExp(Loc.initial, 0, Type.tsize_t)); @@ -5035,7 +4996,7 @@ public: if (!fd.fbody) { e.error("`%s` cannot be interpreted at compile time, because it has no available source code", fd.toChars()); - result = CTFEExp.cantexp; + result = CTFEExp.showcontext; return; } @@ -6607,9 +6568,8 @@ private Expression interpret_keys(InterState* istate, Expression earg, Type retu return null; assert(earg.op == TOK.assocArrayLiteral); AssocArrayLiteralExp aae = cast(AssocArrayLiteralExp)earg; - auto ae = new ArrayLiteralExp(aae.loc, aae.keys); + auto ae = new ArrayLiteralExp(aae.loc, returnType, aae.keys); ae.ownedByCtfe = aae.ownedByCtfe; - ae.type = returnType; return copyLiteral(ae).copy(); } @@ -6628,9 +6588,8 @@ private Expression interpret_values(InterState* istate, Expression earg, Type re return null; assert(earg.op == TOK.assocArrayLiteral); AssocArrayLiteralExp aae = cast(AssocArrayLiteralExp)earg; - auto ae = new ArrayLiteralExp(aae.loc, aae.values); + auto ae = new ArrayLiteralExp(aae.loc, returnType, aae.values); ae.ownedByCtfe = aae.ownedByCtfe; - ae.type = returnType; //printf("result is %s\n", e.toChars()); return copyLiteral(ae).copy(); } @@ -6689,8 +6648,7 @@ private Expression interpret_aaApply(InterState* istate, Expression aa, Expressi Parameter fparam = Parameter.getNth((cast(TypeFunction)fd.type).parameters, numParams - 1); bool wantRefValue = 0 != (fparam.storageClass & (STC.out_ | STC.ref_)); - Expressions args; - args.setDim(numParams); + Expressions args = Expressions(numParams); AssocArrayLiteralExp ae = cast(AssocArrayLiteralExp)aa; if (!ae.keys || ae.keys.dim == 0) @@ -6764,8 +6722,7 @@ private Expression foreachApplyUtf(InterState* istate, Expression str, Expressio str.error("CTFE internal error: cannot foreach `%s`", str.toChars()); return CTFEExp.cantexp; } - Expressions args; - args.setDim(numParams); + Expressions args = Expressions(numParams); Expression eresult = null; // ded-store to prevent spurious warning @@ -6990,8 +6947,7 @@ private Expression evaluateIfBuiltin(InterState* istate, const ref Loc loc, Func { if (isBuiltin(fd) == BUILTIN.yes) { - Expressions args; - args.setDim(nargs); + Expressions args = Expressions(nargs); for (size_t i = 0; i < args.dim; i++) { Expression earg = (*arguments)[i]; diff --git a/gcc/d/dmd/dmacro.d b/gcc/d/dmd/dmacro.d index 8246d384e..c203496de 100644 --- a/gcc/d/dmd/dmacro.d +++ b/gcc/d/dmd/dmacro.d @@ -81,8 +81,8 @@ public: printf("Buf is: '%.*s'\n", *pend - start, buf.data + start); } // limit recursive expansion - static __gshared int nest; - static __gshared const(int) nestLimit = 1000; + __gshared int nest; + __gshared const(int) nestLimit = 1000; if (nest > nestLimit) { error(Loc.initial, "DDoc macro expansion limit exceeded; more than %d expansions.", nestLimit); diff --git a/gcc/d/dmd/dmangle.d b/gcc/d/dmd/dmangle.d index 1ba23c11e..b68db08e9 100644 --- a/gcc/d/dmd/dmangle.d +++ b/gcc/d/dmd/dmangle.d @@ -191,7 +191,7 @@ public: * Params: * pos = relative position to encode */ - final void writeBackRef(size_t pos) + void writeBackRef(size_t pos) { buf.writeByte('Q'); enum base = 26; @@ -221,7 +221,7 @@ public: * true if the type was found. A back reference has been encoded. * false if the type was not found. The current position is saved for later back references. */ - final bool backrefType(Type t) + bool backrefType(Type t) { if (!t.isTypeBasic()) { @@ -249,7 +249,7 @@ public: * true if the identifier was found. A back reference has been encoded. * false if the identifier was not found. The current position is saved for later back references. */ - final bool backrefIdentifier(Identifier id) + bool backrefIdentifier(Identifier id) { auto p = idents.getLvalue(id); if (*p) @@ -261,18 +261,18 @@ public: return false; } - final void mangleSymbol(Dsymbol s) + void mangleSymbol(Dsymbol s) { s.accept(this); } - final void mangleType(Type t) + void mangleType(Type t) { if (!backrefType(t)) t.accept(this); } - final void mangleIdentifier(Identifier id, Dsymbol s) + void mangleIdentifier(Identifier id, Dsymbol s) { if (!backrefIdentifier(id)) toBuffer(id.toString(), s); @@ -282,7 +282,7 @@ public: /************************************************** * Type mangling */ - final void visitWithMask(Type t, ubyte modMask) + void visitWithMask(Type t, ubyte modMask) { if (modMask != t.mod) { @@ -338,7 +338,7 @@ public: mangleFuncType(t, t, t.mod, t.next); } - final void mangleFuncType(TypeFunction t, TypeFunction ta, ubyte modMask, Type tret) + void mangleFuncType(TypeFunction t, TypeFunction ta, ubyte modMask, Type tret) { //printf("mangleFuncType() %s\n", t.toChars()); if (t.inuse && tret) @@ -456,7 +456,7 @@ public: } //////////////////////////////////////////////////////////////////////////// - final void mangleDecl(Declaration sthis) + void mangleDecl(Declaration sthis) { mangleParent(sthis); assert(sthis.ident); @@ -473,7 +473,7 @@ public: assert(0); } - final void mangleParent(Dsymbol s) + void mangleParent(Dsymbol s) { Dsymbol p; if (TemplateInstance ti = s.isTemplateInstance()) @@ -499,7 +499,7 @@ public: } } - final void mangleFunc(FuncDeclaration fd, bool inParent) + void mangleFunc(FuncDeclaration fd, bool inParent) { //printf("deco = '%s'\n", fd.type.deco ? fd.type.deco : "null"); //printf("fd.type = %s\n", fd.type.toChars()); @@ -530,12 +530,12 @@ public: /************************************************************ * Write length prefixed string to buf. */ - final void toBuffer(const(char)* id, Dsymbol s) + void toBuffer(const(char)* id, Dsymbol s) { toBuffer(id[0 .. strlen(id)], s); } - extern (D) final void toBuffer(const(char)[] id, Dsymbol s) + extern (D) void toBuffer(const(char)[] id, Dsymbol s) { const len = id.length; if (buf.offset + len >= 8 * 1024 * 1024) // 8 megs ought be enough for anyone @@ -591,12 +591,8 @@ public: assert(slice.length); foreach (const char c; slice) { - assert(c == '_' || - c == '@' || - c == '?' || - c == '$' || - isalnum(c) || - c & 0x80); + assert(c.isValidMangling, "The mangled name '" ~ slice ~ "' " ~ + "contains an invalid character: " ~ c); } } } @@ -676,7 +672,7 @@ public: visit(cast(Dsymbol)od); } - final void mangleExact(FuncDeclaration fd) + void mangleExact(FuncDeclaration fd) { assert(!fd.isFuncAliasDeclaration()); if (fd.mangleOverride) @@ -746,7 +742,7 @@ public: mangleTemplateInstance(ti); } - final void mangleTemplateInstance(TemplateInstance ti) + void mangleTemplateInstance(TemplateInstance ti) { TemplateDeclaration tempdecl = ti.tempdecl.isTemplateDeclaration(); assert(tempdecl); @@ -902,7 +898,7 @@ public: realToMangleBuffer(e.value); } - final void realToMangleBuffer(real_t value) + void realToMangleBuffer(real_t value) { /* Rely on %A to get portable mangling. * Must munge result to get only identifier characters. @@ -1056,7 +1052,7 @@ public: } //////////////////////////////////////////////////////////////////////////// - final void paramsToDecoBuffer(Parameters* parameters) + void paramsToDecoBuffer(Parameters* parameters) { //printf("Parameter.paramsToDecoBuffer()\n"); @@ -1101,6 +1097,34 @@ public: } } +/// Returns: `true` if the given character is a valid mangled character +package bool isValidMangling(dchar c) +{ + return + c >= 'A' && c <= 'Z' || + c >= 'a' && c <= 'z' || + c >= '0' && c <= '9' || + c != 0 && strchr("$%().:?@[]_", c); +} + +// valid mangled characters +unittest +{ + assert('a'.isValidMangling); + assert('B'.isValidMangling); + assert('2'.isValidMangling); + assert('@'.isValidMangling); + assert('_'.isValidMangling); +} + +// invalid mangled characters +unittest +{ + assert(!'-'.isValidMangling); + assert(!0.isValidMangling); + assert(!'/'.isValidMangling); + assert(!'\\'.isValidMangling); +} /****************************************************************************** * Returns exact mangled name of function. diff --git a/gcc/d/dmd/dmodule.d b/gcc/d/dmd/dmodule.d index af5989267..aa7a6b422 100644 --- a/gcc/d/dmd/dmodule.d +++ b/gcc/d/dmd/dmodule.d @@ -38,6 +38,7 @@ import dmd.root.port; import dmd.semantic2; import dmd.semantic3; import dmd.target; +import dmd.utils; import dmd.visitor; version(Windows) { @@ -284,26 +285,26 @@ extern (C++) class Package : ScopeDsymbol */ extern (C++) final class Module : Package { - extern (C++) static __gshared Module rootModule; - extern (C++) static __gshared DsymbolTable modules; // symbol table of all modules - extern (C++) static __gshared Modules amodules; // array of all modules - extern (C++) static __gshared Dsymbols deferred; // deferred Dsymbol's needing semantic() run on them - extern (C++) static __gshared Dsymbols deferred2; // deferred Dsymbol's needing semantic2() run on them - extern (C++) static __gshared Dsymbols deferred3; // deferred Dsymbol's needing semantic3() run on them - extern (C++) static __gshared uint dprogress; // progress resolving the deferred list + extern (C++) __gshared Module rootModule; + extern (C++) __gshared DsymbolTable modules; // symbol table of all modules + extern (C++) __gshared Modules amodules; // array of all modules + extern (C++) __gshared Dsymbols deferred; // deferred Dsymbol's needing semantic() run on them + extern (C++) __gshared Dsymbols deferred2; // deferred Dsymbol's needing semantic2() run on them + extern (C++) __gshared Dsymbols deferred3; // deferred Dsymbol's needing semantic3() run on them + extern (C++) __gshared uint dprogress; // progress resolving the deferred list /** * A callback function that is called once an imported module is * parsed. If the callback returns true, then it tells the * frontend that the driver intends on compiling the import. */ - extern (C++) static __gshared bool function(Module mod) onImport; + extern (C++) __gshared bool function(Module mod) onImport; static void _init() { modules = new DsymbolTable(); } - extern (C++) static __gshared AggregateDeclaration moduleinfo; + extern (C++) __gshared AggregateDeclaration moduleinfo; const(char)* arg; // original argument name ModuleDeclaration* md; // if !=null, the contents of the ModuleDeclaration declaration @@ -624,7 +625,8 @@ extern (C++) final class Module : Package { .error(loc, "cannot find source code for runtime library file 'object.d'"); errorSupplemental(loc, "dmd might not be correctly installed. Run 'dmd -man' for installation instructions."); - errorSupplemental(loc, "config file: %s", FileName.canonicalName(global.inifilename)); + const dmdConfFile = FileName.canonicalName(global.inifilename); + errorSupplemental(loc, "config file: %s", dmdConfFile ? dmdConfFile : "not found".ptr); } else { @@ -1146,7 +1148,7 @@ extern (C++) final class Module : Package if (dprogress == 0) return; - static __gshared int nested; + __gshared int nested; if (nested) return; //if (deferred.dim) printf("+Module::runDeferredSemantic(), len = %d\n", deferred.dim); @@ -1327,7 +1329,7 @@ struct ModuleDeclaration bool isdeprecated; // if it is a deprecated module Expression msg; - extern (D) this(Loc loc, Identifiers* packages, Identifier id, Expression msg, bool isdeprecated) + extern (D) this(const ref Loc loc, Identifiers* packages, Identifier id, Expression msg, bool isdeprecated) { this.loc = loc; this.packages = packages; @@ -1336,19 +1338,24 @@ struct ModuleDeclaration this.isdeprecated = isdeprecated; } - extern (C++) const(char)* toChars() + extern (C++) const(char)* toChars() const { OutBuffer buf; if (packages && packages.dim) { - for (size_t i = 0; i < packages.dim; i++) + foreach (pid; *packages) { - Identifier pid = (*packages)[i]; - buf.writestring(pid.toChars()); + buf.writestring(pid.toString()); buf.writeByte('.'); } } - buf.writestring(id.toChars()); + buf.writestring(id.toString()); return buf.extractString(); } + + /// Provide a human readable representation + extern (D) const(char)[] toString() const + { + return this.toChars().toDString; + } } diff --git a/gcc/d/dmd/doc.d b/gcc/d/dmd/doc.d index c17195a2a..44fc81782 100644 --- a/gcc/d/dmd/doc.d +++ b/gcc/d/dmd/doc.d @@ -102,7 +102,7 @@ extern (C++) class Section assert(a.dim); if (namelen) { - static __gshared const(char)** table = + static immutable table = [ "AUTHORS", "BUGS", @@ -117,13 +117,12 @@ extern (C++) class Section "STANDARDS", "THROWS", "VERSION", - null ]; - for (size_t i = 0; table[i]; i++) + foreach (entry; table) { - if (icmp(table[i], name, namelen) == 0) + if (iequals(entry, name[0 .. namelen])) { - buf.printf("$(DDOC_%s ", table[i]); + buf.printf("$(DDOC_%s ", entry.ptr); goto L1; } } @@ -352,25 +351,25 @@ private TemplateDeclaration getEponymousParent(Dsymbol s) return (td && getEponymousMember(td)) ? td : null; } -extern (C++) __gshared const(char)* ddoc_default = import("default_ddoc_theme.ddoc"); -extern (C++) __gshared const(char)* ddoc_decl_s = "$(DDOC_DECL "; -extern (C++) __gshared const(char)* ddoc_decl_e = ")\n"; -extern (C++) __gshared const(char)* ddoc_decl_dd_s = "$(DDOC_DECL_DD "; -extern (C++) __gshared const(char)* ddoc_decl_dd_e = ")\n"; +private immutable ddoc_default = import("default_ddoc_theme.ddoc"); +private immutable ddoc_decl_s = "$(DDOC_DECL "; +private immutable ddoc_decl_e = ")\n"; +private immutable ddoc_decl_dd_s = "$(DDOC_DECL_DD "; +private immutable ddoc_decl_dd_e = ")\n"; /**************************************************** */ extern (C++) void gendocfile(Module m) { - static __gshared OutBuffer mbuf; - static __gshared int mbuf_done; + __gshared OutBuffer mbuf; + __gshared int mbuf_done; OutBuffer buf; //printf("Module::gendocfile()\n"); if (!mbuf_done) // if not already read the ddoc files { mbuf_done = 1; // Use our internal default - mbuf.write(ddoc_default, strlen(ddoc_default)); + mbuf.writestring(ddoc_default); // Override with DDOCFILE specified in the sc.ini file char* p = getenv("DDOCFILE"); if (p) @@ -1432,11 +1431,11 @@ struct DocComment for (size_t i = 0; i < dc.sections.dim; i++) { Section sec = dc.sections[i]; - if (icmp("copyright", sec.name, sec.namelen) == 0) + if (iequals("copyright", sec.name[0 .. sec.namelen])) { dc.copyright = sec; } - if (icmp("macros", sec.name, sec.namelen) == 0) + if (iequals("macros", sec.name[0 .. sec.namelen])) { dc.macros = sec; } @@ -1520,7 +1519,7 @@ struct DocComment // Output existing macro L1: //printf("macro '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart); - if (icmp("ESCAPES", namestart, namelen) == 0) + if (iequals("ESCAPES", namestart[0 .. namelen])) parseEscapes(pescapetable, textstart, textlen); else Macro.define(pmacrotable, namestart[0 ..namelen], textstart[0 .. textlen]); @@ -1699,9 +1698,9 @@ struct DocComment if (namelen || pstart < pend) { Section s; - if (icmp("Params", name, namelen) == 0) + if (iequals("Params", name[0 .. namelen])) s = new ParamSection(); - else if (icmp("Macros", name, namelen) == 0) + else if (iequals("Macros", name[0 .. namelen])) s = new MacroSection(); else s = new Section(); @@ -1815,14 +1814,6 @@ extern (C++) int cmp(const(char)* stringz, const(void)* s, size_t slen) return memcmp(stringz, s, slen); } -extern (C++) int icmp(const(char)* stringz, const(void)* s, size_t slen) -{ - size_t len1 = strlen(stringz); - if (len1 != slen) - return cast(int)(len1 - slen); - return Port.memicmp(stringz, cast(char*)s, slen); -} - /***************************************** * Return true if comment consists entirely of "ditto". */ @@ -1842,20 +1833,25 @@ extern (C++) bool isDitto(const(char)* comment) */ extern (C++) const(char)* skipwhitespace(const(char)* p) { - for (; 1; p++) + return skipwhitespace(p.toDString).ptr; +} + +/// Ditto +extern (D) const(char)[] skipwhitespace(const(char)[] p) +{ + foreach (idx, char c; p) { - switch (*p) + switch (c) { case ' ': case '\t': case '\n': continue; default: - break; + return p[idx .. $]; } - break; } - return p; + return p[$ .. $]; } /************************************************ @@ -2417,7 +2413,7 @@ extern (C++) void highlightText(Scope* sc, Dsymbols* a, OutBuffer* buf, size_t o } else { - static __gshared const(char)* d_code = "$(D_CODE "; + __gshared const(char)* d_code = "$(D_CODE "; inCode = 1; codeIndent = istart - iLineStart; // save indent count i = buf.insert(i, d_code, strlen(d_code)); diff --git a/gcc/d/dmd/dscope.d b/gcc/d/dmd/dscope.d index 45f3ccc0f..8d40ad509 100644 --- a/gcc/d/dmd/dscope.d +++ b/gcc/d/dmd/dscope.d @@ -125,7 +125,7 @@ struct Scope uint[void*] anchorCounts; /// lookup duplicate anchor name count Identifier prevAnchor; /// qualified symbol name of last doc anchor - extern (C++) static __gshared Scope* freelist; + extern (C++) __gshared Scope* freelist; extern (C++) static Scope* alloc() { @@ -191,7 +191,7 @@ struct Scope } s.slabel = null; s.nofree = false; - s.ctorflow.fieldinit = ctorflow.saveFieldInit(); + s.ctorflow.fieldinit = ctorflow.fieldinit.arraydup; s.flags = (flags & SCOPEpush); s.lastdc = null; assert(&this != s); @@ -290,7 +290,11 @@ struct Scope foreach (i, v; ad.fields) { bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested()); - if (!mergeFieldInit(this.ctorflow.fieldinit[i], fies[i]) && mustInit) + auto fieldInit = &this.ctorflow.fieldinit[i]; + const fiesCurrent = fies[i]; + if (fieldInit.loc == Loc.init) + fieldInit.loc = fiesCurrent.loc; + if (!mergeFieldInit(this.ctorflow.fieldinit[i].csx, fiesCurrent.csx) && mustInit) { error(loc, "one path skips field `%s`", v.toChars()); } diff --git a/gcc/d/dmd/dstruct.d b/gcc/d/dmd/dstruct.d index 94002b4b0..7b4bdb9f9 100644 --- a/gcc/d/dmd/dstruct.d +++ b/gcc/d/dmd/dstruct.d @@ -50,7 +50,7 @@ extern (C++) FuncDeclaration search_toString(StructDeclaration sd) FuncDeclaration fd = s ? s.isFuncDeclaration() : null; if (fd) { - static __gshared TypeFunction tftostring; + __gshared TypeFunction tftostring; if (!tftostring) { tftostring = new TypeFunction(null, Type.tstring, 0, LINK.d); @@ -202,16 +202,10 @@ extern (C++) void semanticTypeInfo(Scope* sc, Type t) t.accept(v); } -struct StructFlags +enum StructFlags : int { - alias Type = uint; - - enum Enum : int - { - hasPointers = 0x1, // NB: should use noPointers as in ClassFlags - } - - alias hasPointers = Enum.hasPointers; + none = 0x0, + hasPointers = 0x1, // NB: should use noPointers as in ClassFlags } enum StructPOD : int @@ -236,8 +230,8 @@ extern (C++) class StructDeclaration : AggregateDeclaration FuncDeclaration xeq; // TypeInfo_Struct.xopEquals FuncDeclaration xcmp; // TypeInfo_Struct.xopCmp FuncDeclaration xhash; // TypeInfo_Struct.xtoHash - extern (C++) static __gshared FuncDeclaration xerreq; // object.xopEquals - extern (C++) static __gshared FuncDeclaration xerrcmp; // object.xopCmp + extern (C++) __gshared FuncDeclaration xerreq; // object.xopEquals + extern (C++) __gshared FuncDeclaration xerrcmp; // object.xopCmp structalign_t alignment; // alignment applied outside of the struct StructPOD ispod; // if struct is POD diff --git a/gcc/d/dmd/dsymbol.d b/gcc/d/dmd/dsymbol.d index 305f610a0..1bc46da52 100644 --- a/gcc/d/dmd/dsymbol.d +++ b/gcc/d/dmd/dsymbol.d @@ -1554,7 +1554,7 @@ public: version (none) { // Finish - static __gshared TypeFunction tfgetmembers; + __gshared TypeFunction tfgetmembers; if (!tfgetmembers) { Scope sc; diff --git a/gcc/d/dmd/dsymbol.h b/gcc/d/dmd/dsymbol.h index 4f7c27c75..a0fca3cbb 100644 --- a/gcc/d/dmd/dsymbol.h +++ b/gcc/d/dmd/dsymbol.h @@ -15,8 +15,8 @@ #pragma once #endif /* __DMC__ */ -#include "root.h" -#include "stringtable.h" +#include "root/root.h" +#include "root/stringtable.h" #include "mars.h" #include "arraytypes.h" diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d index 76d20bb14..34c595670 100644 --- a/gcc/d/dmd/dsymbolsem.d +++ b/gcc/d/dmd/dsymbolsem.d @@ -28,6 +28,7 @@ import dmd.declaration; import dmd.denum; import dmd.dimport; import dmd.dinterpret; +import dmd.dmangle; import dmd.dmodule; import dmd.dscope; import dmd.dstruct; @@ -817,8 +818,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } } - auto exps = new Objects(); - exps.setDim(nelems); + auto exps = new Objects(nelems); for (size_t i = 0; i < nelems; i++) { Parameter arg = Parameter.getNth(tt.arguments, i); @@ -1219,6 +1219,11 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor // https://issues.dlang.org/show_bug.cgi?id=14166 // Don't run CTFE for the temporary variables inside typeof dsym._init = dsym._init.initializerSemantic(sc, dsym.type, sc.intypeof == 1 ? INITnointerpret : INITinterpret); + const init_err = dsym._init.isExpInitializer(); + if (init_err && init_err.exp.op == TOK.showCtfeContext) + { + errorSupplemental(dsym.loc, "compile time context created here"); + } } } else if (parent.isAggregateDeclaration()) @@ -1371,9 +1376,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor bool loadErrored = false; if (!imp.mod) { - const errors = global.errors; - imp.load(sc); - loadErrored = global.errors != errors; + loadErrored = imp.load(sc); if (imp.mod) imp.mod.importAll(null); } @@ -1747,7 +1750,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor dchar c = p[i]; if (c < 0x80) { - if (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c >= '0' && c <= '9' || c != 0 && strchr("$%().:?@[]_", c)) + if (c.isValidMangling) { ++i; continue; @@ -2172,6 +2175,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor return errorReturn(); } assert(em.ed); + em.ed.dsymbolSemantic(sc); if (em.ed.errors) return errorReturn(); @@ -2187,8 +2191,16 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor em.protection = em.ed.isAnonymous() ? em.ed.protection : Prot(Prot.Kind.public_); em.linkage = LINK.d; - em.storage_class = STC.manifest; - em.userAttribDecl = em.ed.isAnonymous() ? em.ed.userAttribDecl : null; + em.storage_class |= STC.manifest; + + // https://issues.dlang.org/show_bug.cgi?id=9701 + if (em.ed.isAnonymous()) + { + if (em.userAttribDecl) + em.userAttribDecl.userAttribDecl = em.ed.userAttribDecl; + else + em.userAttribDecl = em.ed.userAttribDecl; + } // The first enum member is special bool first = (em == (*em.ed.members)[0]); @@ -2434,8 +2446,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (global.params.doDocComments) { - tempdecl.origParameters = new TemplateParameters(); - tempdecl.origParameters.setDim(tempdecl.parameters.dim); + tempdecl.origParameters = new TemplateParameters(tempdecl.parameters.dim); for (size_t i = 0; i < tempdecl.parameters.dim; i++) { TemplateParameter tp = (*tempdecl.parameters)[i]; @@ -2464,8 +2475,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor /* Calculate TemplateParameter.dependent */ - TemplateParameters tparams; - tparams.setDim(1); + TemplateParameters tparams = TemplateParameters(1); for (size_t i = 0; i < tempdecl.parameters.dim; i++) { TemplateParameter tp = (*tempdecl.parameters)[i]; @@ -2702,7 +2712,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor Scope* sc2 = argscope.push(tm); //size_t deferred_dim = Module.deferred.dim; - static __gshared int nest; + __gshared int nest; //printf("%d\n", nest); if (++nest > 500) { @@ -3557,7 +3567,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor funcdecl._scope = sc.copy(); funcdecl._scope.setNoFree(); - static __gshared bool printedMain = false; // semantic might run more than once + __gshared bool printedMain = false; // semantic might run more than once if (global.params.verbose && !printedMain) { const(char)* type = funcdecl.isMain() ? "main" : funcdecl.isWinMain() ? "winmain" : funcdecl.isDllMain() ? "dllmain" : cast(const(char)*)null; @@ -3575,6 +3585,14 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor genCmain(sc); assert(funcdecl.type.ty != Terror || funcdecl.errors); + + // semantic for parameters' UDAs + foreach (i; 0 .. Parameter.dim(f.parameters)) + { + Parameter param = Parameter.getNth(f.parameters, i); + if (param && param.userAttribDecl) + param.userAttribDecl.dsymbolSemantic(sc); + } } /// Do the semantic analysis on the external interface to the function. @@ -4320,7 +4338,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } } - final void interfaceSemantic(ClassDeclaration cd) + void interfaceSemantic(ClassDeclaration cd) { cd.vtblInterfaces = new BaseClasses(); cd.vtblInterfaces.reserve(cd.interfaces.length); @@ -4606,7 +4624,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.object && !cldec.classKind == ClassKind.cpp) + if (!cldec.baseClass && cldec.ident != Id.Object && cldec.object && cldec.classKind == ClassKind.d) { void badObjectDotD() { diff --git a/gcc/d/dmd/dtemplate.d b/gcc/d/dmd/dtemplate.d index bdc201317..613737f62 100644 --- a/gcc/d/dmd/dtemplate.d +++ b/gcc/d/dmd/dtemplate.d @@ -99,6 +99,13 @@ extern (C++) inout(Parameter) isParameter(inout RootObject o) return cast(inout(Parameter))o; } +extern (C++) inout(TemplateParameter) isTemplateParameter(inout RootObject o) + { + if (!o || o.dyncast() != DYNCAST.templateparameter) + return null; + return cast(inout(TemplateParameter))o; + } + /************************************** * Is this Object an error? */ @@ -469,6 +476,17 @@ extern (C++) final class Tuple : RootObject { Objects objects; + extern (D) this() {} + + /** + Params: + numObjects = The initial number of objects. + */ + extern (D) this(size_t numObjects) + { + objects.setDim(numObjects); + } + // kludge for template.isType() override DYNCAST dyncast() const { @@ -565,8 +583,7 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol TemplateParameters* p = null; if (parameters) { - p = new TemplateParameters(); - p.setDim(parameters.dim); + p = new TemplateParameters(parameters.dim); for (size_t i = 0; i < p.dim; i++) (*p)[i] = (*parameters)[i].syntaxCopy(); } @@ -1019,8 +1036,7 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol scope TemplateInstance ti = new TemplateInstance(Loc.initial, ident, tiargs); // create dummy template instance // Temporary Array to hold deduced types - Objects dedtypes; - dedtypes.setDim(td2.parameters.dim); + Objects dedtypes = Objects(td2.parameters.dim); // Attempt a type deduction MATCH m = td2.matchWithInstance(sc, ti, &dedtypes, fargs, 1); @@ -1146,11 +1162,10 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol /* The extra initial template arguments * now form the tuple argument. */ - auto t = new Tuple(); + auto t = new Tuple(ntargs - n); assert(parameters.dim); (*dedargs)[parameters.dim - 1] = t; - t.objects.setDim(ntargs - n); for (size_t i = 0; i < t.objects.dim; i++) { t.objects[i] = (*tiargs)[n + i]; @@ -2560,8 +2575,7 @@ void functionResolve(Match* m, Dsymbol dstart, Loc loc, Scope* sc, Objects* tiar if (!tiargs) tiargs = new Objects(); auto ti = new TemplateInstance(loc, td, tiargs); - Objects dedtypes; - dedtypes.setDim(td.parameters.dim); + Objects dedtypes = Objects(td.parameters.dim); assert(td.semanticRun != PASS.init); MATCH mta = td.matchWithInstance(sc, ti, &dedtypes, fargs, 0); //printf("matchWithInstance = %d\n", mta); @@ -3723,8 +3737,7 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, TemplateParameters* param else { // Create new tuple - auto tup = new Tuple(); - tup.objects.setDim(tuple_dim); + auto tup = new Tuple(tuple_dim); for (size_t i = 0; i < tuple_dim; i++) { Parameter arg = Parameter.getNth(t.parameters, nfparams - 1 + i); @@ -3901,9 +3914,8 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, TemplateParameters* param /* Create tuple from remaining args */ - auto vt = new Tuple(); size_t vtdim = (tempdecl.isVariadic() ? t.tempinst.tiargs.dim : t.tempinst.tdtypes.dim) - i; - vt.objects.setDim(vtdim); + auto vt = new Tuple(vtdim); for (size_t k = 0; k < vtdim; k++) { RootObject o; @@ -4151,8 +4163,7 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, TemplateParameters* param if (parti) { // Make a temporary copy of dedtypes so we don't destroy it - auto tmpdedtypes = new Objects(); - tmpdedtypes.setDim(dedtypes.dim); + auto tmpdedtypes = new Objects(dedtypes.dim); memcpy(tmpdedtypes.tdata(), dedtypes.tdata(), dedtypes.dim * (void*).sizeof); auto t = new TypeInstance(Loc.initial, parti); @@ -4241,8 +4252,7 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, TemplateParameters* param int numBaseClassMatches = 0; // Have we found an interface match? // Our best guess at dedtypes - auto best = new Objects(); - best.setDim(dedtypes.dim); + auto best = new Objects(dedtypes.dim); ClassDeclaration s = t.sym; while (s && s.baseclasses.dim > 0) @@ -5041,7 +5051,7 @@ bool reliesOnTident(Type t, TemplateParameters* tparams = null, size_t iStart = /*********************************************************** */ -extern (C++) class TemplateParameter +extern (C++) class TemplateParameter : RootObject { Loc loc; Identifier ident; @@ -5100,6 +5110,12 @@ extern (C++) class TemplateParameter abstract bool hasDefaultArg(); + override const(char)* toChars() { return this.ident.toChars(); } + override DYNCAST dyncast() const pure @nogc nothrow @safe + { + return DYNCAST.templateparameter; + } + /******************************************* * Match to a particular TemplateParameter. * Input: @@ -5158,7 +5174,7 @@ extern (C++) class TemplateTypeParameter : TemplateParameter Type specType; // if !=null, this is the type specialization Type defaultType; - extern (C++) static __gshared Type tdummy = null; + extern (C++) __gshared Type tdummy = null; extern (D) this(const ref Loc loc, Identifier ident, Type specType, Type defaultType) { @@ -5354,7 +5370,7 @@ extern (C++) final class TemplateValueParameter : TemplateParameter Expression specValue; Expression defaultValue; - extern (D) static __gshared Expression[void*] edummies; + extern (D) __gshared Expression[void*] edummies; extern (D) this(const ref Loc loc, Identifier ident, Type valType, Expression specValue, Expression defaultValue) @@ -5583,7 +5599,7 @@ extern (C++) final class TemplateAliasParameter : TemplateParameter RootObject specAlias; RootObject defaultAlias; - extern (C++) static __gshared Dsymbol sdummy = null; + extern (C++) __gshared Dsymbol sdummy = null; extern (D) this(const ref Loc loc, Identifier ident, Type specType, RootObject specAlias, RootObject defaultAlias) { @@ -6004,8 +6020,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol Objects* a = null; if (objs) { - a = new Objects(); - a.setDim(objs.dim); + a = new Objects(objs.dim); for (size_t i = 0; i < objs.dim; i++) (*a)[i] = objectSyntaxCopy((*objs)[i]); } @@ -6684,7 +6699,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol for (size_t i = 0; i < dim; i++) { Parameter arg = (*tt.arguments)[i]; - if (flags & 2 && arg.ident) + if (flags & 2 && (arg.ident || arg.userAttribDecl)) tiargs.insert(j + i, arg); else tiargs.insert(j + i, arg.type); @@ -7486,7 +7501,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol final void tryExpandMembers(Scope* sc2) { - static __gshared int nest; + __gshared int nest; // extracted to a function to allow windows SEH to work without destructors in the same function //printf("%d\n", nest); if (++nest > 500) @@ -7504,7 +7519,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol final void trySemantic3(Scope* sc2) { // extracted to a function to allow windows SEH to work without destructors in the same function - static __gshared int nest; + __gshared int nest; //printf("%d\n", nest); if (++nest > 300) { diff --git a/gcc/d/dmd/enum.h b/gcc/d/dmd/enum.h index 1e876ad4e..04dd0bb3b 100644 --- a/gcc/d/dmd/enum.h +++ b/gcc/d/dmd/enum.h @@ -15,7 +15,7 @@ #pragma once #endif /* __DMC__ */ -#include "root.h" +#include "root/root.h" #include "dsymbol.h" #include "declaration.h" #include "tokens.h" diff --git a/gcc/d/dmd/errors.d b/gcc/d/dmd/errors.d index 471fdc44a..1ec3379cc 100644 --- a/gcc/d/dmd/errors.d +++ b/gcc/d/dmd/errors.d @@ -56,10 +56,7 @@ extern (D) void error(Loc loc, const(char)* format, ...) */ extern (C++) void error(const(char)* filename, uint linnum, uint charnum, const(char)* format, ...) { - Loc loc; - loc.filename = filename; - loc.linnum = linnum; - loc.charnum = charnum; + const loc = Loc(filename, linnum, charnum); va_list ap; va_start(ap, format); verror(loc, format, ap); diff --git a/gcc/d/dmd/escape.d b/gcc/d/dmd/escape.d index 8f11cb9c2..f9a9d689d 100644 --- a/gcc/d/dmd/escape.d +++ b/gcc/d/dmd/escape.d @@ -96,7 +96,7 @@ bool checkAssocArrayLiteralEscape(Scope *sc, AssocArrayLiteralExp ae, bool gag) bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Identifier par, Expression arg, bool gag) { enum log = false; - if (log) printf("checkParamArgumentEscape(arg: %s par: %s)\n", arg.toChars(), par.toChars()); + if (log) printf("checkParamArgumentEscape(arg: %s par: %s)\n", arg ? arg.toChars() : "null", par ? par.toChars() : "null"); //printf("type = %s, %d\n", arg.type.toChars(), arg.type.hasPointers()); if (!arg.type.hasPointers()) @@ -153,14 +153,15 @@ bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Identifier par, Ex /* v is not 'scope', and is assigned to a parameter that may escape. * Therefore, v can never be 'scope'. */ - if (log) printf("no infer for %s in %s, fdc %s, %d\n", - v.toChars(), sc.func.ident.toChars(), fdc.ident.toChars(), __LINE__); + if (log) printf("no infer for %s in %s loc %s, fdc %s, %d\n", + v.toChars(), sc.func.ident.toChars(), sc.func.loc.toChars(), fdc.ident.toChars(), __LINE__); v.doNotInferScope = true; } } foreach (VarDeclaration v; er.byref) { + if (log) printf("byref %s\n", v.toChars()); if (v.isDataseg()) continue; @@ -283,6 +284,11 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag) bool inferScope = false; if (va && sc.func && sc.func.type && sc.func.type.ty == Tfunction) inferScope = (cast(TypeFunction)sc.func.type).trust != TRUST.system; + //printf("inferScope = %d, %d\n", inferScope, (va.storage_class & STCmaybescope) != 0); + + // Determine if va is a parameter that is an indirect reference + const bool vaIsRef = va && va.storage_class & STC.parameter && + (va.storage_class & (STC.ref_ | STC.out_) || va.type.toBasetype().ty == Tclass); bool result = false; foreach (VarDeclaration v; er.byvalue) @@ -296,7 +302,17 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag) Dsymbol p = v.toParent2(); - if (!(va && va.isScope())) + if (va && !vaIsRef && !va.isScope() && !v.isScope() && + (va.storage_class & v.storage_class & (STC.maybescope | STC.variadic)) == STC.maybescope && + p == sc.func) + { + /* Add v to va's list of dependencies + */ + va.addMaybe(v); + continue; + } + + if (!(va && va.isScope()) || vaIsRef) notMaybeScope(v); if (v.isScope()) @@ -314,8 +330,9 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag) if (va && (va.enclosesLifetimeOf(v) && !(v.storage_class & (STC.parameter | STC.temp)) || // va is class reference - ae.e1.op == TOK.dotVariable && va.type.toBasetype().ty == Tclass && (va.enclosesLifetimeOf(v) || !va.isScope) || - va.storage_class & STC.ref_ && !(v.storage_class & STC.temp)) && + ae.e1.op == TOK.dotVariable && va.type.toBasetype().ty == Tclass && (va.enclosesLifetimeOf(v) || !va.isScope()) || + vaIsRef || + va.storage_class & (STC.ref_ | STC.out_) && !(v.storage_class & STC.temp)) && sc.func.setUnsafe()) { if (!gag) @@ -1580,3 +1597,55 @@ else } } + +/********************************************** + * Have some variables that are maybescopes that were + * assigned values from other maybescope variables. + * Now that semantic analysis of the function is + * complete, we can finalize this by turning off + * maybescope for array elements that cannot be scope. + * + * `va` `v` => `va` `v` + * maybe maybe => scope scope + * scope scope => scope scope + * scope maybe => scope scope + * maybe scope => scope scope + * - - => - - + * - maybe => - - + * - scope => error + * maybe - => scope - + * scope - => scope - + * Params: + * array = array of variables that were assigned to from maybescope variables + */ +void eliminateMaybeScopes(VarDeclaration[] array) +{ + enum log = false; + if (log) printf("eliminateMaybeScopes()\n"); + bool changes; + do + { + changes = false; + foreach (va; array) + { + if (log) printf(" va = %s\n", va.toChars()); + if (!(va.storage_class & (STC.maybescope | STC.scope_))) + { + if (va.maybes) + { + foreach (v; *va.maybes) + { + if (log) printf(" v = %s\n", v.toChars()); + if (v.storage_class & STC.maybescope) + { + // v cannot be scope since it is assigned to a non-scope va + notMaybeScope(v); + changes = true; + } + } + } + } + } + } while (changes); +} + diff --git a/gcc/d/dmd/expression.d b/gcc/d/dmd/expression.d index b8cddfae2..5dc6abf8c 100644 --- a/gcc/d/dmd/expression.d +++ b/gcc/d/dmd/expression.d @@ -86,7 +86,6 @@ void emplaceExp(T : UnionExp)(T* p, Expression e) * Returns: * left-most non-comma expression */ - inout(Expression) firstComma(inout Expression e) { Expression ex = cast()e; @@ -96,7 +95,6 @@ inout(Expression) firstComma(inout Expression e) } - /**************************************** * Find the last non-comma expression. * Params: @@ -114,310 +112,6 @@ inout(Expression) lastComma(inout Expression e) } - -/************************************************************* - * Given var, get the - * right `this` pointer if var is in an outer class, but our - * existing `this` pointer is in an inner class. - * Params: - * loc = location to use for error messages - * sc = context - * ad = struct or class we need the correct `this` for - * e1 = existing `this` - * var = the specific member of ad we're accessing - * flag = if true, return `null` instead of throwing an error - * Returns: - * Expression representing the `this` for the var - */ -extern (C++) Expression getRightThis(const ref Loc loc, Scope* sc, AggregateDeclaration ad, Expression e1, Declaration var, int flag = 0) -{ - //printf("\ngetRightThis(e1 = %s, ad = %s, var = %s)\n", e1.toChars(), ad.toChars(), var.toChars()); -L1: - Type t = e1.type.toBasetype(); - //printf("e1.type = %s, var.type = %s\n", e1.type.toChars(), var.type.toChars()); - - if (e1.op == TOK.objcClassReference) - { - // We already have an Objective-C class reference, just use that as 'this'. - return e1; - } - else if (ad && ad.isClassDeclaration && ad.isClassDeclaration.classKind == ClassKind.objc && - var.isFuncDeclaration && var.isFuncDeclaration.isStatic && - var.isFuncDeclaration.selector) - { - return new ObjcClassReferenceExp(e1.loc, cast(ClassDeclaration) ad); - } - - /* If e1 is not the 'this' pointer for ad - */ - if (ad && - !(t.ty == Tpointer && t.nextOf().ty == Tstruct && (cast(TypeStruct)t.nextOf()).sym == ad) && - !(t.ty == Tstruct && (cast(TypeStruct)t).sym == ad)) - { - ClassDeclaration cd = ad.isClassDeclaration(); - ClassDeclaration tcd = t.isClassHandle(); - - /* e1 is the right this if ad is a base class of e1 - */ - if (!cd || !tcd || !(tcd == cd || cd.isBaseOf(tcd, null))) - { - /* Only classes can be inner classes with an 'outer' - * member pointing to the enclosing class instance - */ - if (tcd && tcd.isNested()) - { - /* e1 is the 'this' pointer for an inner class: tcd. - * Rewrite it as the 'this' pointer for the outer class. - */ - e1 = new DotVarExp(loc, e1, tcd.vthis); - e1.type = tcd.vthis.type; - e1.type = e1.type.addMod(t.mod); - // Do not call ensureStaticLinkTo() - //e1 = e1.semantic(sc); - - // Skip up over nested functions, and get the enclosing - // class type. - int n = 0; - Dsymbol s; - for (s = tcd.toParent(); s && s.isFuncDeclaration(); s = s.toParent()) - { - FuncDeclaration f = s.isFuncDeclaration(); - if (f.vthis) - { - //printf("rewriting e1 to %s's this\n", f.toChars()); - n++; - e1 = new VarExp(loc, f.vthis); - } - else - { - e1.error("need `this` of type `%s` to access member `%s` from static function `%s`", ad.toChars(), var.toChars(), f.toChars()); - e1 = new ErrorExp(); - return e1; - } - } - if (s && s.isClassDeclaration()) - { - e1.type = s.isClassDeclaration().type; - e1.type = e1.type.addMod(t.mod); - if (n > 1) - e1 = e1.expressionSemantic(sc); - } - else - e1 = e1.expressionSemantic(sc); - goto L1; - } - - /* Can't find a path from e1 to ad - */ - if (flag) - return null; - e1.error("`this` for `%s` needs to be type `%s` not type `%s`", var.toChars(), ad.toChars(), t.toChars()); - return new ErrorExp(); - } - } - return e1; -} - -/**************************************** - * Resolve a symbol `s` and wraps it in an expression object. - * - * Params: - * loc = location of use of `s` - * sc = context - * s = symbol to resolve - * hasOverloads = applies if `s` represents a function. - * true means it's overloaded and will be resolved later, - * false means it's the exact function symbol. - * Returns: - * `s` turned into an expression, `ErrorExp` if an error occurred - */ -Expression resolve(const ref Loc loc, Scope *sc, Dsymbol s, bool hasOverloads) -{ - static if (LOGSEMANTIC) - { - printf("DsymbolExp::resolve(%s %s)\n", s.kind(), s.toChars()); - } - -Lagain: - Expression e; - - //printf("DsymbolExp:: %p '%s' is a symbol\n", this, toChars()); - //printf("s = '%s', s.kind = '%s'\n", s.toChars(), s.kind()); - Dsymbol olds = s; - Declaration d = s.isDeclaration(); - if (d && (d.storage_class & STC.templateparameter)) - { - s = s.toAlias(); - } - else - { - if (!s.isFuncDeclaration()) // functions are checked after overloading - { - s.checkDeprecated(loc, sc); - if (d) - d.checkDisabled(loc, sc); - } - - // https://issues.dlang.org/show_bug.cgi?id=12023 - // if 's' is a tuple variable, the tuple is returned. - s = s.toAlias(); - - //printf("s = '%s', s.kind = '%s', s.needThis() = %p\n", s.toChars(), s.kind(), s.needThis()); - if (s != olds && !s.isFuncDeclaration()) - { - s.checkDeprecated(loc, sc); - if (d) - d.checkDisabled(loc, sc); - } - } - - if (auto em = s.isEnumMember()) - { - return em.getVarExp(loc, sc); - } - if (auto v = s.isVarDeclaration()) - { - //printf("Identifier '%s' is a variable, type '%s'\n", s.toChars(), v.type.toChars()); - if (!v.type || // during variable type inference - !v.type.deco && v.inuse) // during variable type semantic - { - if (v.inuse) // variable type depends on the variable itself - error(loc, "circular reference to %s `%s`", v.kind(), v.toPrettyChars()); - else // variable type cannot be determined - error(loc, "forward reference to %s `%s`", v.kind(), v.toPrettyChars()); - return new ErrorExp(); - } - if (v.type.ty == Terror) - return new ErrorExp(); - - if ((v.storage_class & STC.manifest) && v._init) - { - if (v.inuse) - { - error(loc, "circular initialization of %s `%s`", v.kind(), v.toPrettyChars()); - return new ErrorExp(); - } - e = v.expandInitializer(loc); - v.inuse++; - e = e.expressionSemantic(sc); - v.inuse--; - return e; - } - - // Change the ancestor lambdas to delegate before hasThis(sc) call. - if (v.checkNestedReference(sc, loc)) - return new ErrorExp(); - - if (v.needThis() && hasThis(sc)) - e = new DotVarExp(loc, new ThisExp(loc), v); - else - e = new VarExp(loc, v); - e = e.expressionSemantic(sc); - return e; - } - if (auto fld = s.isFuncLiteralDeclaration()) - { - //printf("'%s' is a function literal\n", fld.toChars()); - e = new FuncExp(loc, fld); - return e.expressionSemantic(sc); - } - if (auto f = s.isFuncDeclaration()) - { - f = f.toAliasFunc(); - if (!f.functionSemantic()) - return new ErrorExp(); - - if (!hasOverloads && f.checkForwardRef(loc)) - return new ErrorExp(); - - auto fd = s.isFuncDeclaration(); - fd.type = f.type; - return new VarExp(loc, fd, hasOverloads); - } - if (OverDeclaration od = s.isOverDeclaration()) - { - e = new VarExp(loc, od, true); - e.type = Type.tvoid; - return e; - } - if (OverloadSet o = s.isOverloadSet()) - { - //printf("'%s' is an overload set\n", o.toChars()); - return new OverExp(loc, o); - } - - if (Import imp = s.isImport()) - { - if (!imp.pkg) - { - .error(loc, "forward reference of import `%s`", imp.toChars()); - return new ErrorExp(); - } - auto ie = new ScopeExp(loc, imp.pkg); - return ie.expressionSemantic(sc); - } - if (Package pkg = s.isPackage()) - { - auto ie = new ScopeExp(loc, pkg); - return ie.expressionSemantic(sc); - } - if (Module mod = s.isModule()) - { - auto ie = new ScopeExp(loc, mod); - return ie.expressionSemantic(sc); - } - if (Nspace ns = s.isNspace()) - { - auto ie = new ScopeExp(loc, ns); - return ie.expressionSemantic(sc); - } - - if (Type t = s.getType()) - { - return (new TypeExp(loc, t)).expressionSemantic(sc); - } - - if (TupleDeclaration tup = s.isTupleDeclaration()) - { - if (tup.needThis() && hasThis(sc)) - e = new DotVarExp(loc, new ThisExp(loc), tup); - else - e = new TupleExp(loc, tup); - e = e.expressionSemantic(sc); - return e; - } - - if (TemplateInstance ti = s.isTemplateInstance()) - { - ti.dsymbolSemantic(sc); - if (!ti.inst || ti.errors) - return new ErrorExp(); - s = ti.toAlias(); - if (!s.isTemplateInstance()) - goto Lagain; - e = new ScopeExp(loc, ti); - e = e.expressionSemantic(sc); - return e; - } - if (TemplateDeclaration td = s.isTemplateDeclaration()) - { - Dsymbol p = td.toParent2(); - FuncDeclaration fdthis = hasThis(sc); - AggregateDeclaration ad = p ? p.isAggregateDeclaration() : null; - if (fdthis && ad && isAggregate(fdthis.vthis.type) == ad && (td._scope.stc & STC.static_) == 0) - { - e = new DotTemplateExp(loc, new ThisExp(loc), td); - } - else - e = new TemplateExp(loc, td); - e = e.expressionSemantic(sc); - return e; - } - - .error(loc, "%s `%s` is not a variable", s.kind(), s.toChars()); - return new ErrorExp(); -} - /***************************************** * Determine if `this` is available by walking up the enclosing * scopes until a function is found. @@ -512,257 +206,6 @@ extern (C++) bool isNeedThisScope(Scope* sc, Declaration d) return true; } -/****************************** - * Check the tail CallExp is really property function call. - * Bugs: - * This doesn't appear to do anything. - */ -private bool checkPropertyCall(Expression e) -{ - e = lastComma(e); - - if (e.op == TOK.call) - { - CallExp ce = cast(CallExp)e; - TypeFunction tf; - if (ce.f) - { - tf = cast(TypeFunction)ce.f.type; - /* If a forward reference to ce.f, try to resolve it - */ - if (!tf.deco && ce.f.semanticRun < PASS.semanticdone) - { - ce.f.dsymbolSemantic(null); - tf = cast(TypeFunction)ce.f.type; - } - } - else if (ce.e1.type.ty == Tfunction) - tf = cast(TypeFunction)ce.e1.type; - else if (ce.e1.type.ty == Tdelegate) - tf = cast(TypeFunction)ce.e1.type.nextOf(); - else if (ce.e1.type.ty == Tpointer && ce.e1.type.nextOf().ty == Tfunction) - tf = cast(TypeFunction)ce.e1.type.nextOf(); - else - assert(0); - } - return false; -} - -/****************************** - * If e1 is a property function (template), resolve it. - */ -extern (C++) Expression resolvePropertiesOnly(Scope* sc, Expression e1) -{ - //printf("e1 = %s %s\n", Token::toChars(e1.op), e1.toChars()); - OverloadSet os; - FuncDeclaration fd; - TemplateDeclaration td; - - if (e1.op == TOK.dot) - { - DotExp de = cast(DotExp)e1; - if (de.e2.op == TOK.overloadSet) - { - os = (cast(OverExp)de.e2).vars; - goto Los; - } - } - else if (e1.op == TOK.overloadSet) - { - os = (cast(OverExp)e1).vars; - Los: - assert(os); - foreach (s; os.a) - { - fd = s.isFuncDeclaration(); - td = s.isTemplateDeclaration(); - if (fd) - { - if ((cast(TypeFunction)fd.type).isproperty) - return resolveProperties(sc, e1); - } - else if (td && td.onemember && (fd = td.onemember.isFuncDeclaration()) !is null) - { - if ((cast(TypeFunction)fd.type).isproperty || (fd.storage_class2 & STC.property) || (td._scope.stc & STC.property)) - { - return resolveProperties(sc, e1); - } - } - } - } - else if (e1.op == TOK.dotTemplateInstance) - { - DotTemplateInstanceExp dti = cast(DotTemplateInstanceExp)e1; - if (dti.ti.tempdecl && (td = dti.ti.tempdecl.isTemplateDeclaration()) !is null) - goto Ltd; - } - else if (e1.op == TOK.dotTemplateDeclaration) - { - td = (cast(DotTemplateExp)e1).td; - goto Ltd; - } - else if (e1.op == TOK.scope_) - { - Dsymbol s = (cast(ScopeExp)e1).sds; - TemplateInstance ti = s.isTemplateInstance(); - if (ti && !ti.semanticRun && ti.tempdecl) - { - if ((td = ti.tempdecl.isTemplateDeclaration()) !is null) - goto Ltd; - } - } - else if (e1.op == TOK.template_) - { - td = (cast(TemplateExp)e1).td; - Ltd: - assert(td); - if (td.onemember && (fd = td.onemember.isFuncDeclaration()) !is null) - { - if ((cast(TypeFunction)fd.type).isproperty || (fd.storage_class2 & STC.property) || (td._scope.stc & STC.property)) - { - return resolveProperties(sc, e1); - } - } - } - else if (e1.op == TOK.dotVariable && e1.type.ty == Tfunction) - { - DotVarExp dve = cast(DotVarExp)e1; - fd = dve.var.isFuncDeclaration(); - goto Lfd; - } - else if (e1.op == TOK.variable && e1.type.ty == Tfunction && (sc.intypeof || !(cast(VarExp)e1).var.needThis())) - { - fd = (cast(VarExp)e1).var.isFuncDeclaration(); - Lfd: - assert(fd); - if ((cast(TypeFunction)fd.type).isproperty) - return resolveProperties(sc, e1); - } - return e1; -} - -/****************************** - * Find symbol in accordance with the UFCS name look up rule - */ -private Expression searchUFCS(Scope* sc, UnaExp ue, Identifier ident) -{ - //printf("searchUFCS(ident = %s)\n", ident.toChars()); - Loc loc = ue.loc; - - // TODO: merge with Scope.search.searchScopes() - Dsymbol searchScopes(int flags) - { - Dsymbol s = null; - for (Scope* scx = sc; scx; scx = scx.enclosing) - { - if (!scx.scopesym) - continue; - if (scx.scopesym.isModule()) - flags |= SearchUnqualifiedModule; // tell Module.search() that SearchLocalsOnly is to be obeyed - s = scx.scopesym.search(loc, ident, flags); - if (s) - { - // overload set contains only module scope symbols. - if (s.isOverloadSet()) - break; - // selective/renamed imports also be picked up - if (AliasDeclaration ad = s.isAliasDeclaration()) - { - if (ad._import) - break; - } - // See only module scope symbols for UFCS target. - Dsymbol p = s.toParent2(); - if (p && p.isModule()) - break; - } - s = null; - - // Stop when we hit a module, but keep going if that is not just under the global scope - if (scx.scopesym.isModule() && !(scx.enclosing && !scx.enclosing.enclosing)) - break; - } - return s; - } - - int flags = 0; - Dsymbol s; - - if (sc.flags & SCOPE.ignoresymbolvisibility) - flags |= IgnoreSymbolVisibility; - - Dsymbol sold = void; - if (global.params.bug10378 || global.params.check10378) - { - sold = searchScopes(flags | IgnoreSymbolVisibility); - if (!global.params.check10378) - { - s = sold; - goto Lsearchdone; - } - } - - // First look in local scopes - s = searchScopes(flags | SearchLocalsOnly); - if (!s) - { - // Second look in imported modules - s = searchScopes(flags | SearchImportsOnly); - - /** Still find private symbols, so that symbols that weren't access - * checked by the compiler remain usable. Once the deprecation is over, - * this should be moved to search_correct instead. - */ - if (!s && !(flags & IgnoreSymbolVisibility)) - { - s = searchScopes(flags | SearchLocalsOnly | IgnoreSymbolVisibility); - if (!s) - s = searchScopes(flags | SearchImportsOnly | IgnoreSymbolVisibility); - - if (s) - .deprecation(loc, "`%s` is not visible from module `%s`", s.toPrettyChars(), sc._module.toChars()); - } - } - if (global.params.check10378) - { - alias snew = s; - if (sold !is snew) - Scope.deprecation10378(loc, sold, snew); - if (global.params.bug10378) - s = sold; - } -Lsearchdone: - - if (!s) - return ue.e1.type.Type.getProperty(loc, ident, 0); - - FuncDeclaration f = s.isFuncDeclaration(); - if (f) - { - TemplateDeclaration td = getFuncTemplateDecl(f); - if (td) - { - if (td.overroot) - td = td.overroot; - s = td; - } - } - - if (ue.op == TOK.dotTemplateInstance) - { - DotTemplateInstanceExp dti = cast(DotTemplateInstanceExp)ue; - auto ti = new TemplateInstance(loc, s.ident, dti.ti.tiargs); - if (!ti.updateTempDecl(sc, s)) - return new ErrorExp(); - return new ScopeExp(loc, ti); - } - else - { - //printf("-searchUFCS() %s\n", s.toChars()); - return new DsymbolExp(loc, s); - } -} - /****************************** * check e is exp.opDispatch!(tiargs) or not * It's used to switch to UFCS the semantic analysis path @@ -772,217 +215,6 @@ extern (C++) bool isDotOpDispatch(Expression e) return e.op == TOK.dotTemplateInstance && (cast(DotTemplateInstanceExp)e).ti.name == Id.opDispatch; } -/****************************** - * Pull out callable entity with UFCS. - */ -extern (C++) Expression resolveUFCS(Scope* sc, CallExp ce) -{ - Loc loc = ce.loc; - Expression eleft; - Expression e; - - if (ce.e1.op == TOK.dotIdentifier) - { - DotIdExp die = cast(DotIdExp)ce.e1; - Identifier ident = die.ident; - - Expression ex = die.semanticX(sc); - if (ex != die) - { - ce.e1 = ex; - return null; - } - eleft = die.e1; - - Type t = eleft.type.toBasetype(); - if (t.ty == Tarray || t.ty == Tsarray || t.ty == Tnull || (t.isTypeBasic() && t.ty != Tvoid)) - { - /* Built-in types and arrays have no callable properties, so do shortcut. - * It is necessary in: e.init() - */ - } - else if (t.ty == Taarray) - { - if (ident == Id.remove) - { - /* Transform: - * aa.remove(arg) into delete aa[arg] - */ - if (!ce.arguments || ce.arguments.dim != 1) - { - ce.error("expected key as argument to `aa.remove()`"); - return new ErrorExp(); - } - if (!eleft.type.isMutable()) - { - ce.error("cannot remove key from `%s` associative array `%s`", MODtoChars(t.mod), eleft.toChars()); - return new ErrorExp(); - } - Expression key = (*ce.arguments)[0]; - key = key.expressionSemantic(sc); - key = resolveProperties(sc, key); - - TypeAArray taa = cast(TypeAArray)t; - key = key.implicitCastTo(sc, taa.index); - - if (key.checkValue()) - return new ErrorExp(); - - semanticTypeInfo(sc, taa.index); - - return new RemoveExp(loc, eleft, key); - } - } - else - { - if (Expression ey = die.semanticY(sc, 1)) - { - if (ey.op == TOK.error) - return ey; - ce.e1 = ey; - if (isDotOpDispatch(ey)) - { - uint errors = global.startGagging(); - e = ce.syntaxCopy().expressionSemantic(sc); - if (!global.endGagging(errors)) - return e; - /* fall down to UFCS */ - } - else - return null; - } - } - e = searchUFCS(sc, die, ident); - } - else if (ce.e1.op == TOK.dotTemplateInstance) - { - DotTemplateInstanceExp dti = cast(DotTemplateInstanceExp)ce.e1; - if (Expression ey = dti.semanticY(sc, 1)) - { - ce.e1 = ey; - return null; - } - eleft = dti.e1; - e = searchUFCS(sc, dti, dti.ti.name); - } - else - return null; - - // Rewrite - ce.e1 = e; - if (!ce.arguments) - ce.arguments = new Expressions(); - ce.arguments.shift(eleft); - - return null; -} - -/****************************** - * Pull out property with UFCS. - */ -extern (C++) Expression resolveUFCSProperties(Scope* sc, Expression e1, Expression e2 = null) -{ - Loc loc = e1.loc; - Expression eleft; - Expression e; - - if (e1.op == TOK.dotIdentifier) - { - DotIdExp die = cast(DotIdExp)e1; - eleft = die.e1; - e = searchUFCS(sc, die, die.ident); - } - else if (e1.op == TOK.dotTemplateInstance) - { - DotTemplateInstanceExp dti; - dti = cast(DotTemplateInstanceExp)e1; - eleft = dti.e1; - e = searchUFCS(sc, dti, dti.ti.name); - } - else - return null; - - if (e is null) - return null; - - // Rewrite - if (e2) - { - // run semantic without gagging - e2 = e2.expressionSemantic(sc); - - /* f(e1) = e2 - */ - Expression ex = e.copy(); - auto a1 = new Expressions(); - a1.setDim(1); - (*a1)[0] = eleft; - ex = new CallExp(loc, ex, a1); - ex = ex.trySemantic(sc); - - /* f(e1, e2) - */ - auto a2 = new Expressions(); - a2.setDim(2); - (*a2)[0] = eleft; - (*a2)[1] = e2; - e = new CallExp(loc, e, a2); - if (ex) - { - // if fallback setter exists, gag errors - e = e.trySemantic(sc); - if (!e) - { - checkPropertyCall(ex); - ex = new AssignExp(loc, ex, e2); - return ex.expressionSemantic(sc); - } - } - else - { - // strict setter prints errors if fails - e = e.expressionSemantic(sc); - } - checkPropertyCall(e); - return e; - } - else - { - /* f(e1) - */ - auto arguments = new Expressions(); - arguments.setDim(1); - (*arguments)[0] = eleft; - e = new CallExp(loc, e, arguments); - e = e.expressionSemantic(sc); - checkPropertyCall(e); - return e.expressionSemantic(sc); - } -} - -/****************************** - * Perform semantic() on an array of Expressions. - */ -extern (C++) bool arrayExpressionSemantic(Expressions* exps, Scope* sc, bool preserveErrors = false) -{ - bool err = false; - if (exps) - { - foreach (ref e; *exps) - { - if (e) - { - auto e2 = e.expressionSemantic(sc); - if (e2.op == TOK.error) - err = true; - if (preserveErrors || e2.op != TOK.error) - e = e2; - } - } - } - return err; -} - /**************************************** * Expand tuples. * Input: @@ -1373,200 +605,6 @@ private Expression opAssignToOp(const ref Loc loc, TOK op, Expression e1, Expres return e; } -/****************************************************************/ - -private Expression extractOpDollarSideEffect(Scope* sc, UnaExp ue) -{ - Expression e0; - Expression e1 = Expression.extractLast(ue.e1, &e0); - // https://issues.dlang.org/show_bug.cgi?id=12585 - // Extract the side effect part if ue.e1 is comma. - - if (!isTrivialExp(e1)) - { - /* Even if opDollar is needed, 'e1' should be evaluate only once. So - * Rewrite: - * e1.opIndex( ... use of $ ... ) - * e1.opSlice( ... use of $ ... ) - * as: - * (ref __dop = e1, __dop).opIndex( ... __dop.opDollar ...) - * (ref __dop = e1, __dop).opSlice( ... __dop.opDollar ...) - */ - e1 = extractSideEffect(sc, "__dop", e0, e1, false); - assert(e1.op == TOK.variable); - VarExp ve = cast(VarExp)e1; - ve.var.storage_class |= STC.exptemp; // lifetime limited to expression - } - ue.e1 = e1; - return e0; -} - -/************************************** - * Runs semantic on ae.arguments. Declares temporary variables - * if '$' was used. - */ -extern (C++) Expression resolveOpDollar(Scope* sc, ArrayExp ae, Expression* pe0) -{ - assert(!ae.lengthVar); - *pe0 = null; - AggregateDeclaration ad = isAggregate(ae.e1.type); - Dsymbol slice = search_function(ad, Id.slice); - //printf("slice = %s %s\n", slice.kind(), slice.toChars()); - foreach (i, e; *ae.arguments) - { - if (i == 0) - *pe0 = extractOpDollarSideEffect(sc, ae); - - if (e.op == TOK.interval && !(slice && slice.isTemplateDeclaration())) - { - Lfallback: - if (ae.arguments.dim == 1) - return null; - ae.error("multi-dimensional slicing requires template `opSlice`"); - return new ErrorExp(); - } - //printf("[%d] e = %s\n", i, e.toChars()); - - // Create scope for '$' variable for this dimension - auto sym = new ArrayScopeSymbol(sc, ae); - sym.loc = ae.loc; - sym.parent = sc.scopesym; - sc = sc.push(sym); - ae.lengthVar = null; // Create it only if required - ae.currentDimension = i; // Dimension for $, if required - - e = e.expressionSemantic(sc); - e = resolveProperties(sc, e); - - if (ae.lengthVar && sc.func) - { - // If $ was used, declare it now - Expression de = new DeclarationExp(ae.loc, ae.lengthVar); - de = de.expressionSemantic(sc); - *pe0 = Expression.combine(*pe0, de); - } - sc = sc.pop(); - - if (e.op == TOK.interval) - { - IntervalExp ie = cast(IntervalExp)e; - - auto tiargs = new Objects(); - Expression edim = new IntegerExp(ae.loc, i, Type.tsize_t); - edim = edim.expressionSemantic(sc); - tiargs.push(edim); - - auto fargs = new Expressions(); - fargs.push(ie.lwr); - fargs.push(ie.upr); - - uint xerrors = global.startGagging(); - sc = sc.push(); - FuncDeclaration fslice = resolveFuncCall(ae.loc, sc, slice, tiargs, ae.e1.type, fargs, 1); - sc = sc.pop(); - global.endGagging(xerrors); - if (!fslice) - goto Lfallback; - - e = new DotTemplateInstanceExp(ae.loc, ae.e1, slice.ident, tiargs); - e = new CallExp(ae.loc, e, fargs); - e = e.expressionSemantic(sc); - } - - if (!e.type) - { - ae.error("`%s` has no value", e.toChars()); - e = new ErrorExp(); - } - if (e.op == TOK.error) - return e; - - (*ae.arguments)[i] = e; - } - return ae; -} - -/************************************** - * Runs semantic on se.lwr and se.upr. Declares a temporary variable - * if '$' was used. - */ -extern (C++) Expression resolveOpDollar(Scope* sc, ArrayExp ae, IntervalExp ie, Expression* pe0) -{ - //assert(!ae.lengthVar); - if (!ie) - return ae; - - VarDeclaration lengthVar = ae.lengthVar; - - // create scope for '$' - auto sym = new ArrayScopeSymbol(sc, ae); - sym.loc = ae.loc; - sym.parent = sc.scopesym; - sc = sc.push(sym); - - foreach (i; 0 .. 2) - { - Expression e = i == 0 ? ie.lwr : ie.upr; - e = e.expressionSemantic(sc); - e = resolveProperties(sc, e); - if (!e.type) - { - ae.error("`%s` has no value", e.toChars()); - return new ErrorExp(); - } - (i == 0 ? ie.lwr : ie.upr) = e; - } - - if (lengthVar != ae.lengthVar && sc.func) - { - // If $ was used, declare it now - Expression de = new DeclarationExp(ae.loc, ae.lengthVar); - de = de.expressionSemantic(sc); - *pe0 = Expression.combine(*pe0, de); - } - - sc = sc.pop(); - - return ae; -} - -/*********************************************************** - * Resolve `exp` as a compile-time known string. - * Params: - * sc = scope - * exp = Expression which expected as a string - * s = What the string is expected for, will be used in error diagnostic. - * Returns: - * String literal, or `null` if error happens. - */ -StringExp semanticString(Scope *sc, Expression exp, const char* s) -{ - sc = sc.startCTFE(); - exp = exp.expressionSemantic(sc); - exp = resolveProperties(sc, exp); - sc = sc.endCTFE(); - - if (exp.op == TOK.error) - return null; - - auto e = exp; - if (exp.type.isString()) - { - e = e.ctfeInterpret(); - if (e.op == TOK.error) - return null; - } - - auto se = e.toStringExp(); - if (!se) - { - exp.error("`string` expected for %s, not `(%s)` of type `%s`", - s, exp.toChars(), exp.type.toChars()); - return null; - } - return se; -} - /*************************************************** * Given an Expression, find the variable it really is. * @@ -1655,6 +693,7 @@ extern (C++) abstract class Expression : RootObject CTFEExp.breakexp = new CTFEExp(TOK.break_); CTFEExp.continueexp = new CTFEExp(TOK.continue_); CTFEExp.gotoexp = new CTFEExp(TOK.goto_); + CTFEExp.showcontext = new CTFEExp(TOK.showCtfeContext); } /********************************* @@ -1716,6 +755,17 @@ extern (C++) abstract class Expression : RootObject } } + final void errorSupplemental(const(char)* format, ...) + { + if (type == Type.terror) + return; + + va_list ap; + va_start(ap, format); + .verrorSupplemental(loc, format, ap); + va_end(ap); + } + final void warning(const(char)* format, ...) const { if (type != Type.terror) @@ -1808,8 +858,7 @@ extern (C++) abstract class Expression : RootObject Expressions* a = null; if (exps) { - a = new Expressions(); - a.setDim(exps.dim); + a = new Expressions(exps.dim); foreach (i, e; *exps) { (*a)[i] = e ? e.syntaxCopy() : null; @@ -2299,12 +1348,16 @@ extern (C++) abstract class Expression : RootObject if (!f.isSafe() && !f.isTrusted()) { - if (sc.flags & SCOPE.compile ? sc.func.isSafeBypassingInference() : sc.func.setUnsafe()) + if (sc.flags & SCOPE.compile ? sc.func.isSafeBypassingInference() : sc.func.setUnsafe() && !(sc.flags & SCOPE.debug_)) { if (!loc.isValid()) // e.g. implicitly generated dtor loc = sc.func.loc; + + const prettyChars = f.toPrettyChars(); error("`@safe` %s `%s` cannot call `@system` %s `%s`", - sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars()); + sc.func.kind(), sc.func.toPrettyChars(), f.kind(), + prettyChars); + .errorSupplemental(f.loc, "`%s` is declared here", prettyChars); return true; } } @@ -2768,7 +1821,7 @@ extern (C++) final class ErrorExp : Expression v.visit(this); } - extern (C++) static __gshared ErrorExp errorexp; // handy shared value + extern (C++) __gshared ErrorExp errorexp; // handy shared value } /*********************************************************** @@ -3578,34 +2631,39 @@ extern (C++) final class ArrayLiteralExp : Expression Expressions* elements; OwnedBy ownedByCtfe = OwnedBy.code; - extern (D) this(const ref Loc loc, Expressions* elements) + + extern (D) this(const ref Loc loc, Type type, Expressions* elements) { super(loc, TOK.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp)); + this.type = type; this.elements = elements; } - extern (D) this(const ref Loc loc, Expression e) + extern (D) this(const ref Loc loc, Type type, Expression e) { super(loc, TOK.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp)); + this.type = type; elements = new Expressions(); elements.push(e); } - extern (D) this(const ref Loc loc, Expression basis, Expressions* elements) + extern (D) this(const ref Loc loc, Type type, Expression basis, Expressions* elements) { super(loc, TOK.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp)); + this.type = type; this.basis = basis; this.elements = elements; } static ArrayLiteralExp create(Loc loc, Expressions* elements) { - return new ArrayLiteralExp(loc, elements); + return new ArrayLiteralExp(loc, null, elements); } override Expression syntaxCopy() { return new ArrayLiteralExp(loc, + null, basis ? basis.syntaxCopy() : null, arraySyntaxCopy(elements)); } @@ -3638,7 +2696,7 @@ extern (C++) final class ArrayLiteralExp : Expression return false; } - final Expression getElement(size_t i) + Expression getElement(size_t i) { auto el = (*elements)[i]; if (!el) @@ -3932,12 +2990,10 @@ extern (C++) final class StructLiteralExp : Expression { TypeSArray tsa = cast(TypeSArray)type; size_t length = cast(size_t)tsa.dim.toInteger(); - auto z = new Expressions(); - z.setDim(length); + auto z = new Expressions(length); foreach (ref q; *z) q = e.copy(); - e = new ArrayLiteralExp(loc, z); - e.type = type; + e = new ArrayLiteralExp(loc, type, z); } else { @@ -4291,6 +3347,11 @@ extern (C++) final class SymOffExp : SymbolExp */ extern (C++) final class VarExp : SymbolExp { + /** + * Semantic can be called multiple times for a single expression. + * This field is needed to ensure the deprecation message will be printed only once. + */ + bool hasCheckedAttrs; extern (D) this(const ref Loc loc, Declaration var, bool hasOverloads = true) { if (var.isVarDeclaration()) @@ -4300,6 +3361,7 @@ extern (C++) final class VarExp : SymbolExp //printf("VarExp(this = %p, '%s', loc = %s)\n", this, var.toChars(), loc.toChars()); //if (strcmp(var.ident.toChars(), "func") == 0) assert(0); this.type = var.type; + this.hasCheckedAttrs = false; } static VarExp create(Loc loc, Declaration var, bool hasOverloads = true) @@ -4379,6 +3441,13 @@ extern (C++) final class VarExp : SymbolExp { v.visit(this); } + + override Expression syntaxCopy() + { + auto ret = super.syntaxCopy(); + (cast(VarExp)ret).hasCheckedAttrs = this.hasCheckedAttrs; + return ret; + } } /*********************************************************** @@ -4830,8 +3899,7 @@ extern (C++) final class IsExp : Expression TemplateParameters* p = null; if (parameters) { - p = new TemplateParameters(); - p.setDim(parameters.dim); + p = new TemplateParameters(parameters.dim); foreach (i, el; *parameters) (*p)[i] = el.syntaxCopy(); } @@ -5425,7 +4493,7 @@ extern (C++) final class DotVarExp : UnaExp { if (f == vd) { - if (!(sc.ctorflow.fieldinit[i] & CSX.this_ctor)) + if (!(sc.ctorflow.fieldinit[i].csx & CSX.this_ctor)) { /* If the address of vd is taken, assume it is thereby initialized * https://issues.dlang.org/show_bug.cgi?id=15869 @@ -5605,8 +4673,7 @@ extern (C++) final class CallExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Expression earg1, Expression earg2) { super(loc, TOK.call, __traits(classInstanceSize, CallExp), e); - auto arguments = new Expressions(); - arguments.setDim(2); + auto arguments = new Expressions(2); (*arguments)[0] = earg1; (*arguments)[1] = earg2; this.arguments = arguments; @@ -7434,7 +6501,7 @@ extern (C++) final class ObjcClassReferenceExp : Expression { ClassDeclaration classDeclaration; - extern (D) this(Loc loc, ClassDeclaration classDeclaration) + extern (D) this(const ref Loc loc, ClassDeclaration classDeclaration) { super(loc, TOK.objcClassReference, __traits(classInstanceSize, ObjcClassReferenceExp)); diff --git a/gcc/d/dmd/expression.h b/gcc/d/dmd/expression.h index e957ae2c7..083bfed64 100644 --- a/gcc/d/dmd/expression.h +++ b/gcc/d/dmd/expression.h @@ -18,7 +18,7 @@ #include "visitor.h" #include "tokens.h" -#include "rmem.h" +#include "root/rmem.h" class Type; class TypeVector; @@ -568,6 +568,12 @@ class SymOffExp : public SymbolExp class VarExp : public SymbolExp { public: + /** + * Semantic can be called multiple times for a single expression. + * This field is needed to ensure the deprecation message will be printed only once. + */ + bool hasCheckedAttrs; + static VarExp *create(Loc loc, Declaration *var, bool hasOverloads = true); bool equals(RootObject *o); int checkModifiable(Scope *sc, int flag); diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d index 88ae28707..0f39a351d 100644 --- a/gcc/d/dmd/expressionsem.d +++ b/gcc/d/dmd/expressionsem.d @@ -73,6 +73,964 @@ import dmd.visitor; enum LOGSEMANTIC = false; +/*********************************************************** + * Resolve `exp` as a compile-time known string. + * Params: + * sc = scope + * exp = Expression which expected as a string + * s = What the string is expected for, will be used in error diagnostic. + * Returns: + * String literal, or `null` if error happens. + */ +StringExp semanticString(Scope *sc, Expression exp, const char* s) +{ + sc = sc.startCTFE(); + exp = exp.expressionSemantic(sc); + exp = resolveProperties(sc, exp); + sc = sc.endCTFE(); + + if (exp.op == TOK.error) + return null; + + auto e = exp; + if (exp.type.isString()) + { + e = e.ctfeInterpret(); + if (e.op == TOK.error) + return null; + } + + auto se = e.toStringExp(); + if (!se) + { + exp.error("`string` expected for %s, not `(%s)` of type `%s`", + s, exp.toChars(), exp.type.toChars()); + return null; + } + return se; +} + +private Expression extractOpDollarSideEffect(Scope* sc, UnaExp ue) +{ + Expression e0; + Expression e1 = Expression.extractLast(ue.e1, &e0); + // https://issues.dlang.org/show_bug.cgi?id=12585 + // Extract the side effect part if ue.e1 is comma. + + if (!isTrivialExp(e1)) + { + /* Even if opDollar is needed, 'e1' should be evaluate only once. So + * Rewrite: + * e1.opIndex( ... use of $ ... ) + * e1.opSlice( ... use of $ ... ) + * as: + * (ref __dop = e1, __dop).opIndex( ... __dop.opDollar ...) + * (ref __dop = e1, __dop).opSlice( ... __dop.opDollar ...) + */ + e1 = extractSideEffect(sc, "__dop", e0, e1, false); + assert(e1.op == TOK.variable); + VarExp ve = cast(VarExp)e1; + ve.var.storage_class |= STC.exptemp; // lifetime limited to expression + } + ue.e1 = e1; + return e0; +} + +/************************************** + * Runs semantic on ae.arguments. Declares temporary variables + * if '$' was used. + */ +extern (C++) Expression resolveOpDollar(Scope* sc, ArrayExp ae, Expression* pe0) +{ + assert(!ae.lengthVar); + *pe0 = null; + AggregateDeclaration ad = isAggregate(ae.e1.type); + Dsymbol slice = search_function(ad, Id.slice); + //printf("slice = %s %s\n", slice.kind(), slice.toChars()); + foreach (i, e; *ae.arguments) + { + if (i == 0) + *pe0 = extractOpDollarSideEffect(sc, ae); + + if (e.op == TOK.interval && !(slice && slice.isTemplateDeclaration())) + { + Lfallback: + if (ae.arguments.dim == 1) + return null; + ae.error("multi-dimensional slicing requires template `opSlice`"); + return new ErrorExp(); + } + //printf("[%d] e = %s\n", i, e.toChars()); + + // Create scope for '$' variable for this dimension + auto sym = new ArrayScopeSymbol(sc, ae); + sym.loc = ae.loc; + sym.parent = sc.scopesym; + sc = sc.push(sym); + ae.lengthVar = null; // Create it only if required + ae.currentDimension = i; // Dimension for $, if required + + e = e.expressionSemantic(sc); + e = resolveProperties(sc, e); + + if (ae.lengthVar && sc.func) + { + // If $ was used, declare it now + Expression de = new DeclarationExp(ae.loc, ae.lengthVar); + de = de.expressionSemantic(sc); + *pe0 = Expression.combine(*pe0, de); + } + sc = sc.pop(); + + if (e.op == TOK.interval) + { + IntervalExp ie = cast(IntervalExp)e; + + auto tiargs = new Objects(); + Expression edim = new IntegerExp(ae.loc, i, Type.tsize_t); + edim = edim.expressionSemantic(sc); + tiargs.push(edim); + + auto fargs = new Expressions(); + fargs.push(ie.lwr); + fargs.push(ie.upr); + + uint xerrors = global.startGagging(); + sc = sc.push(); + FuncDeclaration fslice = resolveFuncCall(ae.loc, sc, slice, tiargs, ae.e1.type, fargs, 1); + sc = sc.pop(); + global.endGagging(xerrors); + if (!fslice) + goto Lfallback; + + e = new DotTemplateInstanceExp(ae.loc, ae.e1, slice.ident, tiargs); + e = new CallExp(ae.loc, e, fargs); + e = e.expressionSemantic(sc); + } + + if (!e.type) + { + ae.error("`%s` has no value", e.toChars()); + e = new ErrorExp(); + } + if (e.op == TOK.error) + return e; + + (*ae.arguments)[i] = e; + } + return ae; +} + +/************************************** + * Runs semantic on se.lwr and se.upr. Declares a temporary variable + * if '$' was used. + */ +extern (C++) Expression resolveOpDollar(Scope* sc, ArrayExp ae, IntervalExp ie, Expression* pe0) +{ + //assert(!ae.lengthVar); + if (!ie) + return ae; + + VarDeclaration lengthVar = ae.lengthVar; + + // create scope for '$' + auto sym = new ArrayScopeSymbol(sc, ae); + sym.loc = ae.loc; + sym.parent = sc.scopesym; + sc = sc.push(sym); + + foreach (i; 0 .. 2) + { + Expression e = i == 0 ? ie.lwr : ie.upr; + e = e.expressionSemantic(sc); + e = resolveProperties(sc, e); + if (!e.type) + { + ae.error("`%s` has no value", e.toChars()); + return new ErrorExp(); + } + (i == 0 ? ie.lwr : ie.upr) = e; + } + + if (lengthVar != ae.lengthVar && sc.func) + { + // If $ was used, declare it now + Expression de = new DeclarationExp(ae.loc, ae.lengthVar); + de = de.expressionSemantic(sc); + *pe0 = Expression.combine(*pe0, de); + } + + sc = sc.pop(); + + return ae; +} + +/****************************** + * Perform semantic() on an array of Expressions. + */ +extern (C++) bool arrayExpressionSemantic(Expressions* exps, Scope* sc, bool preserveErrors = false) +{ + bool err = false; + if (exps) + { + foreach (ref e; *exps) + { + if (e) + { + auto e2 = e.expressionSemantic(sc); + if (e2.op == TOK.error) + err = true; + if (preserveErrors || e2.op != TOK.error) + e = e2; + } + } + } + return err; +} + +/****************************** + * Check the tail CallExp is really property function call. + * Bugs: + * This doesn't appear to do anything. + */ +private bool checkPropertyCall(Expression e) +{ + e = lastComma(e); + + if (e.op == TOK.call) + { + CallExp ce = cast(CallExp)e; + TypeFunction tf; + if (ce.f) + { + tf = cast(TypeFunction)ce.f.type; + /* If a forward reference to ce.f, try to resolve it + */ + if (!tf.deco && ce.f.semanticRun < PASS.semanticdone) + { + ce.f.dsymbolSemantic(null); + tf = cast(TypeFunction)ce.f.type; + } + } + else if (ce.e1.type.ty == Tfunction) + tf = cast(TypeFunction)ce.e1.type; + else if (ce.e1.type.ty == Tdelegate) + tf = cast(TypeFunction)ce.e1.type.nextOf(); + else if (ce.e1.type.ty == Tpointer && ce.e1.type.nextOf().ty == Tfunction) + tf = cast(TypeFunction)ce.e1.type.nextOf(); + else + assert(0); + } + return false; +} + +/****************************** + * Find symbol in accordance with the UFCS name look up rule + */ +private Expression searchUFCS(Scope* sc, UnaExp ue, Identifier ident) +{ + //printf("searchUFCS(ident = %s)\n", ident.toChars()); + Loc loc = ue.loc; + + // TODO: merge with Scope.search.searchScopes() + Dsymbol searchScopes(int flags) + { + Dsymbol s = null; + for (Scope* scx = sc; scx; scx = scx.enclosing) + { + if (!scx.scopesym) + continue; + if (scx.scopesym.isModule()) + flags |= SearchUnqualifiedModule; // tell Module.search() that SearchLocalsOnly is to be obeyed + s = scx.scopesym.search(loc, ident, flags); + if (s) + { + // overload set contains only module scope symbols. + if (s.isOverloadSet()) + break; + // selective/renamed imports also be picked up + if (AliasDeclaration ad = s.isAliasDeclaration()) + { + if (ad._import) + break; + } + // See only module scope symbols for UFCS target. + Dsymbol p = s.toParent2(); + if (p && p.isModule()) + break; + } + s = null; + + // Stop when we hit a module, but keep going if that is not just under the global scope + if (scx.scopesym.isModule() && !(scx.enclosing && !scx.enclosing.enclosing)) + break; + } + return s; + } + + int flags = 0; + Dsymbol s; + + if (sc.flags & SCOPE.ignoresymbolvisibility) + flags |= IgnoreSymbolVisibility; + + Dsymbol sold = void; + if (global.params.bug10378 || global.params.check10378) + { + sold = searchScopes(flags | IgnoreSymbolVisibility); + if (!global.params.check10378) + { + s = sold; + goto Lsearchdone; + } + } + + // First look in local scopes + s = searchScopes(flags | SearchLocalsOnly); + if (!s) + { + // Second look in imported modules + s = searchScopes(flags | SearchImportsOnly); + + /** Still find private symbols, so that symbols that weren't access + * checked by the compiler remain usable. Once the deprecation is over, + * this should be moved to search_correct instead. + */ + if (!s && !(flags & IgnoreSymbolVisibility)) + { + s = searchScopes(flags | SearchLocalsOnly | IgnoreSymbolVisibility); + if (!s) + s = searchScopes(flags | SearchImportsOnly | IgnoreSymbolVisibility); + + if (s) + .deprecation(loc, "`%s` is not visible from module `%s`", s.toPrettyChars(), sc._module.toChars()); + } + } + if (global.params.check10378) + { + alias snew = s; + if (sold !is snew) + Scope.deprecation10378(loc, sold, snew); + if (global.params.bug10378) + s = sold; + } +Lsearchdone: + + if (!s) + return ue.e1.type.Type.getProperty(loc, ident, 0); + + FuncDeclaration f = s.isFuncDeclaration(); + if (f) + { + TemplateDeclaration td = getFuncTemplateDecl(f); + if (td) + { + if (td.overroot) + td = td.overroot; + s = td; + } + } + + if (ue.op == TOK.dotTemplateInstance) + { + DotTemplateInstanceExp dti = cast(DotTemplateInstanceExp)ue; + auto ti = new TemplateInstance(loc, s.ident, dti.ti.tiargs); + if (!ti.updateTempDecl(sc, s)) + return new ErrorExp(); + return new ScopeExp(loc, ti); + } + else + { + //printf("-searchUFCS() %s\n", s.toChars()); + return new DsymbolExp(loc, s); + } +} + +/****************************** + * Pull out callable entity with UFCS. + */ +private Expression resolveUFCS(Scope* sc, CallExp ce) +{ + Loc loc = ce.loc; + Expression eleft; + Expression e; + + if (ce.e1.op == TOK.dotIdentifier) + { + DotIdExp die = cast(DotIdExp)ce.e1; + Identifier ident = die.ident; + + Expression ex = die.semanticX(sc); + if (ex != die) + { + ce.e1 = ex; + return null; + } + eleft = die.e1; + + Type t = eleft.type.toBasetype(); + if (t.ty == Tarray || t.ty == Tsarray || t.ty == Tnull || (t.isTypeBasic() && t.ty != Tvoid)) + { + /* Built-in types and arrays have no callable properties, so do shortcut. + * It is necessary in: e.init() + */ + } + else if (t.ty == Taarray) + { + if (ident == Id.remove) + { + /* Transform: + * aa.remove(arg) into delete aa[arg] + */ + if (!ce.arguments || ce.arguments.dim != 1) + { + ce.error("expected key as argument to `aa.remove()`"); + return new ErrorExp(); + } + if (!eleft.type.isMutable()) + { + ce.error("cannot remove key from `%s` associative array `%s`", MODtoChars(t.mod), eleft.toChars()); + return new ErrorExp(); + } + Expression key = (*ce.arguments)[0]; + key = key.expressionSemantic(sc); + key = resolveProperties(sc, key); + + TypeAArray taa = cast(TypeAArray)t; + key = key.implicitCastTo(sc, taa.index); + + if (key.checkValue()) + return new ErrorExp(); + + semanticTypeInfo(sc, taa.index); + + return new RemoveExp(loc, eleft, key); + } + } + else + { + // even opDispatch and ufcs must have valid arguments. + if (arrayExpressionSemantic(ce.arguments, sc)) + return new ErrorExp(); + + if (Expression ey = die.semanticY(sc, 1)) + { + if (ey.op == TOK.error) + return ey; + ce.e1 = ey; + if (isDotOpDispatch(ey)) + { + uint errors = global.startGagging(); + e = ce.syntaxCopy().expressionSemantic(sc); + if (!global.endGagging(errors)) + return e; + /* fall down to UFCS */ + } + else + return null; + } + } + e = searchUFCS(sc, die, ident); + } + else if (ce.e1.op == TOK.dotTemplateInstance) + { + DotTemplateInstanceExp dti = cast(DotTemplateInstanceExp)ce.e1; + if (Expression ey = dti.semanticY(sc, 1)) + { + ce.e1 = ey; + return null; + } + eleft = dti.e1; + e = searchUFCS(sc, dti, dti.ti.name); + } + else + return null; + + // Rewrite + ce.e1 = e; + if (!ce.arguments) + ce.arguments = new Expressions(); + ce.arguments.shift(eleft); + + return null; +} + +/****************************** + * Pull out property with UFCS. + */ +private Expression resolveUFCSProperties(Scope* sc, Expression e1, Expression e2 = null) +{ + Loc loc = e1.loc; + Expression eleft; + Expression e; + + if (e1.op == TOK.dotIdentifier) + { + DotIdExp die = cast(DotIdExp)e1; + eleft = die.e1; + e = searchUFCS(sc, die, die.ident); + } + else if (e1.op == TOK.dotTemplateInstance) + { + DotTemplateInstanceExp dti; + dti = cast(DotTemplateInstanceExp)e1; + eleft = dti.e1; + e = searchUFCS(sc, dti, dti.ti.name); + } + else + return null; + + if (e is null) + return null; + + // Rewrite + if (e2) + { + // run semantic without gagging + e2 = e2.expressionSemantic(sc); + + /* f(e1) = e2 + */ + Expression ex = e.copy(); + auto a1 = new Expressions(1); + (*a1)[0] = eleft; + ex = new CallExp(loc, ex, a1); + ex = ex.trySemantic(sc); + + /* f(e1, e2) + */ + auto a2 = new Expressions(2); + (*a2)[0] = eleft; + (*a2)[1] = e2; + e = new CallExp(loc, e, a2); + if (ex) + { + // if fallback setter exists, gag errors + e = e.trySemantic(sc); + if (!e) + { + checkPropertyCall(ex); + ex = new AssignExp(loc, ex, e2); + return ex.expressionSemantic(sc); + } + } + else + { + // strict setter prints errors if fails + e = e.expressionSemantic(sc); + } + checkPropertyCall(e); + return e; + } + else + { + /* f(e1) + */ + auto arguments = new Expressions(1); + (*arguments)[0] = eleft; + e = new CallExp(loc, e, arguments); + e = e.expressionSemantic(sc); + checkPropertyCall(e); + return e.expressionSemantic(sc); + } +} + +/****************************** + * If e1 is a property function (template), resolve it. + */ +extern (C++) Expression resolvePropertiesOnly(Scope* sc, Expression e1) +{ + //printf("e1 = %s %s\n", Token::toChars(e1.op), e1.toChars()); + OverloadSet os; + FuncDeclaration fd; + TemplateDeclaration td; + + if (e1.op == TOK.dot) + { + DotExp de = cast(DotExp)e1; + if (de.e2.op == TOK.overloadSet) + { + os = (cast(OverExp)de.e2).vars; + goto Los; + } + } + else if (e1.op == TOK.overloadSet) + { + os = (cast(OverExp)e1).vars; + Los: + assert(os); + foreach (s; os.a) + { + fd = s.isFuncDeclaration(); + td = s.isTemplateDeclaration(); + if (fd) + { + if ((cast(TypeFunction)fd.type).isproperty) + return resolveProperties(sc, e1); + } + else if (td && td.onemember && (fd = td.onemember.isFuncDeclaration()) !is null) + { + if ((cast(TypeFunction)fd.type).isproperty || (fd.storage_class2 & STC.property) || (td._scope.stc & STC.property)) + { + return resolveProperties(sc, e1); + } + } + } + } + else if (e1.op == TOK.dotTemplateInstance) + { + DotTemplateInstanceExp dti = cast(DotTemplateInstanceExp)e1; + if (dti.ti.tempdecl && (td = dti.ti.tempdecl.isTemplateDeclaration()) !is null) + goto Ltd; + } + else if (e1.op == TOK.dotTemplateDeclaration) + { + td = (cast(DotTemplateExp)e1).td; + goto Ltd; + } + else if (e1.op == TOK.scope_) + { + Dsymbol s = (cast(ScopeExp)e1).sds; + TemplateInstance ti = s.isTemplateInstance(); + if (ti && !ti.semanticRun && ti.tempdecl) + { + if ((td = ti.tempdecl.isTemplateDeclaration()) !is null) + goto Ltd; + } + } + else if (e1.op == TOK.template_) + { + td = (cast(TemplateExp)e1).td; + Ltd: + assert(td); + if (td.onemember && (fd = td.onemember.isFuncDeclaration()) !is null) + { + if ((cast(TypeFunction)fd.type).isproperty || (fd.storage_class2 & STC.property) || (td._scope.stc & STC.property)) + { + return resolveProperties(sc, e1); + } + } + } + else if (e1.op == TOK.dotVariable && e1.type.ty == Tfunction) + { + DotVarExp dve = cast(DotVarExp)e1; + fd = dve.var.isFuncDeclaration(); + goto Lfd; + } + else if (e1.op == TOK.variable && e1.type.ty == Tfunction && (sc.intypeof || !(cast(VarExp)e1).var.needThis())) + { + fd = (cast(VarExp)e1).var.isFuncDeclaration(); + Lfd: + assert(fd); + if ((cast(TypeFunction)fd.type).isproperty) + return resolveProperties(sc, e1); + } + return e1; +} + +/**************************************** + * Resolve a symbol `s` and wraps it in an expression object. + * + * Params: + * loc = location of use of `s` + * sc = context + * s = symbol to resolve + * hasOverloads = applies if `s` represents a function. + * true means it's overloaded and will be resolved later, + * false means it's the exact function symbol. + * Returns: + * `s` turned into an expression, `ErrorExp` if an error occurred + */ +Expression resolve(const ref Loc loc, Scope *sc, Dsymbol s, bool hasOverloads) +{ + static if (LOGSEMANTIC) + { + printf("DsymbolExp::resolve(%s %s)\n", s.kind(), s.toChars()); + } + +Lagain: + Expression e; + + //printf("DsymbolExp:: %p '%s' is a symbol\n", this, toChars()); + //printf("s = '%s', s.kind = '%s'\n", s.toChars(), s.kind()); + Dsymbol olds = s; + Declaration d = s.isDeclaration(); + if (d && (d.storage_class & STC.templateparameter)) + { + s = s.toAlias(); + } + else + { + if (!s.isFuncDeclaration()) // functions are checked after overloading + { + s.checkDeprecated(loc, sc); + if (d) + d.checkDisabled(loc, sc); + } + + // https://issues.dlang.org/show_bug.cgi?id=12023 + // if 's' is a tuple variable, the tuple is returned. + s = s.toAlias(); + + //printf("s = '%s', s.kind = '%s', s.needThis() = %p\n", s.toChars(), s.kind(), s.needThis()); + if (s != olds && !s.isFuncDeclaration()) + { + s.checkDeprecated(loc, sc); + if (d) + d.checkDisabled(loc, sc); + } + } + + if (auto em = s.isEnumMember()) + { + return em.getVarExp(loc, sc); + } + if (auto v = s.isVarDeclaration()) + { + //printf("Identifier '%s' is a variable, type '%s'\n", s.toChars(), v.type.toChars()); + if (!v.type || // during variable type inference + !v.type.deco && v.inuse) // during variable type semantic + { + if (v.inuse) // variable type depends on the variable itself + error(loc, "circular reference to %s `%s`", v.kind(), v.toPrettyChars()); + else // variable type cannot be determined + error(loc, "forward reference to %s `%s`", v.kind(), v.toPrettyChars()); + return new ErrorExp(); + } + if (v.type.ty == Terror) + return new ErrorExp(); + + if ((v.storage_class & STC.manifest) && v._init) + { + if (v.inuse) + { + error(loc, "circular initialization of %s `%s`", v.kind(), v.toPrettyChars()); + return new ErrorExp(); + } + e = v.expandInitializer(loc); + v.inuse++; + e = e.expressionSemantic(sc); + v.inuse--; + return e; + } + + // Change the ancestor lambdas to delegate before hasThis(sc) call. + if (v.checkNestedReference(sc, loc)) + return new ErrorExp(); + + if (v.needThis() && hasThis(sc)) + e = new DotVarExp(loc, new ThisExp(loc), v); + else + e = new VarExp(loc, v); + e = e.expressionSemantic(sc); + return e; + } + if (auto fld = s.isFuncLiteralDeclaration()) + { + //printf("'%s' is a function literal\n", fld.toChars()); + e = new FuncExp(loc, fld); + return e.expressionSemantic(sc); + } + if (auto f = s.isFuncDeclaration()) + { + f = f.toAliasFunc(); + if (!f.functionSemantic()) + return new ErrorExp(); + + if (!hasOverloads && f.checkForwardRef(loc)) + return new ErrorExp(); + + auto fd = s.isFuncDeclaration(); + fd.type = f.type; + return new VarExp(loc, fd, hasOverloads); + } + if (OverDeclaration od = s.isOverDeclaration()) + { + e = new VarExp(loc, od, true); + e.type = Type.tvoid; + return e; + } + if (OverloadSet o = s.isOverloadSet()) + { + //printf("'%s' is an overload set\n", o.toChars()); + return new OverExp(loc, o); + } + + if (Import imp = s.isImport()) + { + if (!imp.pkg) + { + .error(loc, "forward reference of import `%s`", imp.toChars()); + return new ErrorExp(); + } + auto ie = new ScopeExp(loc, imp.pkg); + return ie.expressionSemantic(sc); + } + if (Package pkg = s.isPackage()) + { + auto ie = new ScopeExp(loc, pkg); + return ie.expressionSemantic(sc); + } + if (Module mod = s.isModule()) + { + auto ie = new ScopeExp(loc, mod); + return ie.expressionSemantic(sc); + } + if (Nspace ns = s.isNspace()) + { + auto ie = new ScopeExp(loc, ns); + return ie.expressionSemantic(sc); + } + + if (Type t = s.getType()) + { + return (new TypeExp(loc, t)).expressionSemantic(sc); + } + + if (TupleDeclaration tup = s.isTupleDeclaration()) + { + if (tup.needThis() && hasThis(sc)) + e = new DotVarExp(loc, new ThisExp(loc), tup); + else + e = new TupleExp(loc, tup); + e = e.expressionSemantic(sc); + return e; + } + + if (TemplateInstance ti = s.isTemplateInstance()) + { + ti.dsymbolSemantic(sc); + if (!ti.inst || ti.errors) + return new ErrorExp(); + s = ti.toAlias(); + if (!s.isTemplateInstance()) + goto Lagain; + e = new ScopeExp(loc, ti); + e = e.expressionSemantic(sc); + return e; + } + if (TemplateDeclaration td = s.isTemplateDeclaration()) + { + Dsymbol p = td.toParent2(); + FuncDeclaration fdthis = hasThis(sc); + AggregateDeclaration ad = p ? p.isAggregateDeclaration() : null; + if (fdthis && ad && isAggregate(fdthis.vthis.type) == ad && (td._scope.stc & STC.static_) == 0) + { + e = new DotTemplateExp(loc, new ThisExp(loc), td); + } + else + e = new TemplateExp(loc, td); + e = e.expressionSemantic(sc); + return e; + } + + .error(loc, "%s `%s` is not a variable", s.kind(), s.toChars()); + return new ErrorExp(); +} + +/************************************************************* + * Given var, get the + * right `this` pointer if var is in an outer class, but our + * existing `this` pointer is in an inner class. + * Params: + * loc = location to use for error messages + * sc = context + * ad = struct or class we need the correct `this` for + * e1 = existing `this` + * var = the specific member of ad we're accessing + * flag = if true, return `null` instead of throwing an error + * Returns: + * Expression representing the `this` for the var + */ +private Expression getRightThis(const ref Loc loc, Scope* sc, AggregateDeclaration ad, Expression e1, Declaration var, int flag = 0) +{ + //printf("\ngetRightThis(e1 = %s, ad = %s, var = %s)\n", e1.toChars(), ad.toChars(), var.toChars()); +L1: + Type t = e1.type.toBasetype(); + //printf("e1.type = %s, var.type = %s\n", e1.type.toChars(), var.type.toChars()); + + if (e1.op == TOK.objcClassReference) + { + // We already have an Objective-C class reference, just use that as 'this'. + return e1; + } + else if (ad && ad.isClassDeclaration && ad.isClassDeclaration.classKind == ClassKind.objc && + var.isFuncDeclaration && var.isFuncDeclaration.isStatic && + var.isFuncDeclaration.selector) + { + return new ObjcClassReferenceExp(e1.loc, cast(ClassDeclaration) ad); + } + + /* If e1 is not the 'this' pointer for ad + */ + if (ad && + !(t.ty == Tpointer && t.nextOf().ty == Tstruct && (cast(TypeStruct)t.nextOf()).sym == ad) && + !(t.ty == Tstruct && (cast(TypeStruct)t).sym == ad)) + { + ClassDeclaration cd = ad.isClassDeclaration(); + ClassDeclaration tcd = t.isClassHandle(); + + /* e1 is the right this if ad is a base class of e1 + */ + if (!cd || !tcd || !(tcd == cd || cd.isBaseOf(tcd, null))) + { + /* Only classes can be inner classes with an 'outer' + * member pointing to the enclosing class instance + */ + if (tcd && tcd.isNested()) + { + /* e1 is the 'this' pointer for an inner class: tcd. + * Rewrite it as the 'this' pointer for the outer class. + */ + e1 = new DotVarExp(loc, e1, tcd.vthis); + e1.type = tcd.vthis.type; + e1.type = e1.type.addMod(t.mod); + // Do not call ensureStaticLinkTo() + //e1 = e1.semantic(sc); + + // Skip up over nested functions, and get the enclosing + // class type. + int n = 0; + Dsymbol s; + for (s = tcd.toParent(); s && s.isFuncDeclaration(); s = s.toParent()) + { + FuncDeclaration f = s.isFuncDeclaration(); + if (f.vthis) + { + //printf("rewriting e1 to %s's this\n", f.toChars()); + n++; + e1 = new VarExp(loc, f.vthis); + } + else + { + e1.error("need `this` of type `%s` to access member `%s` from static function `%s`", ad.toChars(), var.toChars(), f.toChars()); + e1 = new ErrorExp(); + return e1; + } + } + if (s && s.isClassDeclaration()) + { + e1.type = s.isClassDeclaration().type; + e1.type = e1.type.addMod(t.mod); + if (n > 1) + e1 = e1.expressionSemantic(sc); + } + else + e1 = e1.expressionSemantic(sc); + goto L1; + } + + /* Can't find a path from e1 to ad + */ + if (flag) + return null; + e1.error("`this` for `%s` needs to be type `%s` not type `%s`", var.toChars(), ad.toChars(), t.toChars()); + return new ErrorExp(); + } + } + return e1; +} + /*************************************** * Pull out any properties. */ @@ -510,23 +1468,25 @@ private bool checkDefCtor(Loc loc, Type t) * 3. do default promotions on arguments corresponding to ... * 4. add hidden _arguments[] argument * 5. call copy constructor for struct value arguments - * Input: - * tf type of the function - * fd the function being called, NULL if called indirectly - * Output: - * *prettype return type of function - * *peprefix expression to execute before arguments[] are evaluated, NULL if none + * Params: + * tf = type of the function + * tthis = type of `this` argument, `null` if no `this` argument + * arguments = array of actual arguments to function call + * fd = the function being called, `null` if called indirectly + * prettype = set to return type of function + * peprefix = set to expression to execute before `arguments[]` are evaluated, `null` if none * Returns: * true errors happened */ -private bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type tthis, Expressions* arguments, FuncDeclaration fd, Type* prettype, Expression* peprefix) +private bool functionParameters(const ref Loc loc, Scope* sc, + TypeFunction tf, Type tthis, Expressions* arguments, FuncDeclaration fd, Type* prettype, Expression* peprefix) { //printf("functionParameters() %s\n", fd ? fd.toChars() : ""); assert(arguments); assert(fd || tf.next); size_t nargs = arguments ? arguments.dim : 0; - size_t nparams = Parameter.dim(tf.parameters); - uint olderrors = global.errors; + const size_t nparams = Parameter.dim(tf.parameters); + const olderrors = global.errors; bool err = false; *prettype = Type.terror; Expression eprefix = null; @@ -551,41 +1511,30 @@ private bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type tthis, fd.functionSemantic3(); } } - bool isCtorCall = fd && fd.needThis() && fd.isCtorDeclaration(); + const isCtorCall = fd && fd.needThis() && fd.isCtorDeclaration(); - size_t n = (nargs > nparams) ? nargs : nparams; // n = max(nargs, nparams) + const size_t n = (nargs > nparams) ? nargs : nparams; // n = max(nargs, nparams) /* If the function return type has wildcards in it, we'll need to figure out the actual type * based on the actual argument types. + * Start with the `this` argument, later on merge into wildmatch the mod bits of the rest + * of the arguments. */ - MOD wildmatch = 0; - if (tthis && tf.isWild() && !isCtorCall) - { - Type t = tthis; - if (t.isImmutable()) - wildmatch = MODFlags.immutable_; - else if (t.isWildConst()) - wildmatch = MODFlags.wildconst; - else if (t.isWild()) - wildmatch = MODFlags.wild; - else if (t.isConst()) - wildmatch = MODFlags.const_; - else - wildmatch = MODFlags.mutable; - } + MOD wildmatch = (tthis && !isCtorCall) ? tthis.Type.deduceWild(tf, false) : 0; - int done = 0; - for (size_t i = 0; i < n; i++) + bool done = false; + foreach (const i; 0 .. n) { - Expression arg; - - if (i < nargs) - arg = (*arguments)[i]; - else - arg = null; + Expression arg = (i < nargs) ? (*arguments)[i] : null; if (i < nparams) { + bool errorArgs() + { + error(loc, "expected %llu function arguments, not %llu", cast(ulong)nparams, cast(ulong)nargs); + return true; + } + Parameter p = Parameter.getNth(tf.parameters, i); if (!arg) @@ -594,8 +1543,7 @@ private bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type tthis, { if (tf.varargs == 2 && i + 1 == nparams) goto L2; - error(loc, "expected %llu function arguments, not %llu", cast(ulong)nparams, cast(ulong)nargs); - return true; + return errorArgs(); } arg = p.defaultArg; arg = inlineCopy(arg, sc); @@ -623,16 +1571,12 @@ private bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type tthis, if (p.type.nextOf() && arg.implicitConvTo(p.type.nextOf()) >= m) goto L2; else if (nargs != nparams) - { - error(loc, "expected %llu function arguments, not %llu", cast(ulong)nparams, cast(ulong)nargs); - return true; - } + return errorArgs(); goto L1; } } L2: Type tb = p.type.toBasetype(); - Type tret = p.isLazyArray(); switch (tb.ty) { case Tsarray: @@ -645,19 +1589,19 @@ private bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type tthis, * is now optimized. * https://issues.dlang.org/show_bug.cgi?id=2356 */ - Type tbn = (cast(TypeArray)tb).next; - Type tsa = tbn.sarrayOf(nargs - i); + Type tbn = (cast(TypeArray)tb).next; // array element type + Type tret = p.isLazyArray(); - auto elements = new Expressions(); - elements.setDim(nargs - i); - for (size_t u = 0; u < elements.dim; u++) + auto elements = new Expressions(nargs - i); + foreach (u; 0 .. elements.dim) { Expression a = (*arguments)[i + u]; if (tret && a.implicitConvTo(tret)) { - a = a.implicitCastTo(sc, tret); - a = a.optimize(WANTvalue); - a = toDelegate(a, a.type, sc); + // p is a lazy array of delegates, tret is return type of the delegates + a = a.implicitCastTo(sc, tret) + .optimize(WANTvalue) + .toDelegate(tret, sc); } else a = a.implicitCastTo(sc, tbn); @@ -665,8 +1609,7 @@ private bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type tthis, } // https://issues.dlang.org/show_bug.cgi?id=14395 // Convert to a static array literal, or its slice. - arg = new ArrayLiteralExp(loc, elements); - arg.type = tsa; + arg = new ArrayLiteralExp(loc, tbn.sarrayOf(nargs - i), elements); if (tb.ty == Tarray) { arg = new SliceExp(loc, arg, null, null); @@ -679,9 +1622,8 @@ private bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type tthis, /* Set arg to be: * new Tclass(arg0, arg1, ..., argn) */ - auto args = new Expressions(); - args.setDim(nargs - i); - for (size_t u = i; u < nargs; u++) + auto args = new Expressions(nargs - i); + foreach (u; i .. nargs) (*args)[u - i] = (*arguments)[u]; arg = new NewExp(loc, null, null, p.type, args); break; @@ -699,7 +1641,7 @@ private bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type tthis, arguments.setDim(i + 1); (*arguments)[i] = arg; nargs = i + 1; - done = 1; + done = true; } L1: @@ -708,10 +1650,7 @@ private bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type tthis, bool isRef = (p.storageClass & (STC.ref_ | STC.out_)) != 0; if (ubyte wm = arg.type.deduceWild(p.type, isRef)) { - if (wildmatch) - wildmatch = MODmerge(wildmatch, wm); - else - wildmatch = wm; + wildmatch = wildmatch ? MODmerge(wildmatch, wm) : wm; //printf("[%d] p = %s, a = %s, wm = %d, wildmatch = %d\n", i, p.type.toChars(), arg.type.toChars(), wm, wildmatch); } } @@ -719,8 +1658,17 @@ private bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type tthis, if (done) break; } - if ((wildmatch == MODFlags.mutable || wildmatch == MODFlags.immutable_) && tf.next.hasWild() && (tf.isref || !tf.next.implicitConvTo(tf.next.immutableOf()))) + if ((wildmatch == MODFlags.mutable || wildmatch == MODFlags.immutable_) && + tf.next.hasWild() && + (tf.isref || !tf.next.implicitConvTo(tf.next.immutableOf()))) { + bool errorInout(MOD wildmatch) + { + const(char)* s = wildmatch == MODFlags.mutable ? "mutable" : MODtoChars(wildmatch); + error(loc, "modify `inout` to `%s` is not allowed inside `inout` function", s); + return true; + } + if (fd) { /* If the called function may return the reference to @@ -747,7 +1695,7 @@ private bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type tthis, if (auto ff = s.isFuncDeclaration()) { if ((cast(TypeFunction)ff.type).iswild) - goto Linouterr; + return errorInout(wildmatch); if (ff.isNested() || ff.isThis()) continue; @@ -756,27 +1704,21 @@ private bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type tthis, } } else if (tf.isWild()) - { - Linouterr: - const(char)* s = wildmatch == MODFlags.mutable ? "mutable" : MODtoChars(wildmatch); - error(loc, "modify `inout` to `%s` is not allowed inside `inout` function", s); - return true; - } + return errorInout(wildmatch); } assert(nargs >= nparams); - for (size_t i = 0; i < nargs; i++) + foreach (const i, arg; (*arguments)[0 .. nargs]) { - Expression arg = (*arguments)[i]; assert(arg); if (i < nparams) { Parameter p = Parameter.getNth(tf.parameters, i); if (!(p.storageClass & STC.lazy_ && p.type.ty == Tvoid)) { - Type tprm = p.type; - if (p.type.hasWild()) - tprm = p.type.substWildTo(wildmatch); + Type tprm = p.type.hasWild() + ? p.type.substWildTo(wildmatch) + : p.type; if (!tprm.equals(arg.type)) { //printf("arg.type = %s, p.type = %s\n", arg.type.toChars(), p.type.toChars()); @@ -810,10 +1752,8 @@ private bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type tthis, else if (p.storageClass & STC.lazy_) { // Convert lazy argument to a delegate - if (p.type.ty == Tvoid) - arg = toDelegate(arg, p.type, sc); - else - arg = toDelegate(arg, arg.type, sc); + auto t = (p.type.ty == Tvoid) ? p.type : arg.type; + arg = toDelegate(arg, t, sc); } //printf("arg: %s\n", arg.toChars()); //printf("type: %s\n", arg.type.toChars()); @@ -1102,16 +2042,14 @@ private bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type tthis, { assert(arguments.dim >= nparams); - auto args = new Parameters(); - args.setDim(arguments.dim - nparams); + auto args = new Parameters(arguments.dim - nparams); for (size_t i = 0; i < arguments.dim - nparams; i++) { - auto arg = new Parameter(STC.in_, (*arguments)[nparams + i].type, null, null); + auto arg = new Parameter(STC.in_, (*arguments)[nparams + i].type, null, null, null); (*args)[i] = arg; } auto tup = new TypeTuple(args); - Expression e = new TypeidExp(loc, tup); - e = e.expressionSemantic(sc); + Expression e = (new TypeidExp(loc, tup)).expressionSemantic(sc); arguments.insert(0, e); } @@ -1154,7 +2092,7 @@ private bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type tthis, private Module loadStdMath() { - static __gshared Import impStdMath = null; + __gshared Import impStdMath = null; if (!impStdMath) { auto a = new Identifiers(); @@ -1733,7 +2671,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setError(); } - semanticTypeInfo(sc, e.type); + if (global.params.useTypeInfo && Type.dtypeinfo) + semanticTypeInfo(sc, e.type); result = e; } @@ -1844,7 +2783,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Type t; Dsymbol s; - exp.type.resolve(exp.loc, sc, &e, &t, &s, true); + dmd.typesem.resolve(exp.type, exp.loc, sc, &e, &t, &s, true); if (e) { //printf("e = %s %s\n", Token::toChars(e.op), e.toChars()); @@ -2515,6 +3454,16 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor * variables as alias template parameters. */ //checkAccess(loc, sc, NULL, var); + if (!e.hasCheckedAttrs && e.var.isEnumMember()) + { + e.hasCheckedAttrs = true; + if (e.var.depdecl && !e.var.depdecl._scope) + { + e.var.depdecl._scope = sc; + } + e.checkDeprecated(sc, e.var); + + } if (auto vd = e.var.isVarDeclaration()) { @@ -2892,7 +3841,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } else { - static __gshared int nest; + __gshared int nest; if (++nest > 500) { exp.error("recursive evaluation of `%s`", exp.toChars()); @@ -3290,7 +4239,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // initialized after this call. foreach (ref field; sc.ctorflow.fieldinit) { - field |= CSX.this_ctor | CSX.deprecate_18719; + field.csx |= CSX.this_ctor | CSX.deprecate_18719; } } @@ -3472,7 +4421,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toChars()); err = true; } - if (tf.trust <= TRUST.system && sc.func.setUnsafe()) + if (tf.trust <= TRUST.system && sc.func.setUnsafe() && !(sc.flags & SCOPE.debug_)) { exp.error("`@safe` %s `%s` cannot call `@system` %s `%s`", sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toChars()); @@ -3755,7 +4704,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (ta) { - ta.resolve(exp.loc, sc, &ea, &ta, &sa, true); + dmd.typesem.resolve(ta, exp.loc, sc, &ea, &ta, &sa, true); } if (ea) @@ -3781,13 +4730,22 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor ta.checkComplexTransition(exp.loc, sc); Expression e; - if (ea && ta.toBasetype().ty == Tclass) + auto tb = ta.toBasetype(); + if (ea && tb.ty == Tclass) { - /* Get the dynamic type, which is .classinfo - */ - ea = ea.expressionSemantic(sc); - e = new TypeidExp(ea.loc, ea); - e.type = Type.typeinfoclass.type; + if (tb.toDsymbol(sc).isClassDeclaration().classKind == ClassKind.cpp) + { + error(exp.loc, "Runtime type information is not supported for `extern(C++)` classes"); + e = new ErrorExp(); + } + else + { + /* Get the dynamic type, which is .classinfo + */ + ea = ea.expressionSemantic(sc); + e = new TypeidExp(ea.loc, ea); + e.type = Type.typeinfoclass.type; + } } else if (ta.ty == Terror) { @@ -3924,7 +4882,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor for (size_t i = 0; i < cd.baseclasses.dim; i++) { BaseClass* b = (*cd.baseclasses)[i]; - args.push(new Parameter(STC.in_, b.type, null, null)); + args.push(new Parameter(STC.in_, b.type, null, null, null)); } tded = new TypeTuple(args); } @@ -3971,7 +4929,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ if (e.tok2 == TOK.parameters && arg.defaultArg && arg.defaultArg.op == TOK.error) return setError(); - args.push(new Parameter(arg.storageClass, arg.type, (e.tok2 == TOK.parameters) ? arg.ident : null, (e.tok2 == TOK.parameters) ? arg.defaultArg : null)); + args.push(new Parameter(arg.storageClass, arg.type, (e.tok2 == TOK.parameters) ? arg.ident : null, (e.tok2 == TOK.parameters) ? arg.defaultArg : null, arg.userAttribDecl)); } tded = new TypeTuple(args); break; @@ -4062,8 +5020,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Identifier tid = e.id ? e.id : Identifier.generateId("__isexp_id"); e.parameters.insert(0, new TemplateTypeParameter(e.loc, tid, null, null)); - Objects dedtypes; - dedtypes.setDim(e.parameters.dim); + Objects dedtypes = Objects(e.parameters.dim); dedtypes.zero(); MATCH m = deduceType(e.targ, sc, e.tspec, e.parameters, &dedtypes); @@ -4078,8 +5035,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor tded = cast(Type)dedtypes[0]; if (!tded) tded = e.targ; - Objects tiargs; - tiargs.setDim(1); + Objects tiargs = Objects(1); tiargs[0] = e.targ; /* Declare trailing parameters @@ -4153,9 +5109,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } - if (exp.e1.checkReadModifyWrite(exp.op, exp.e2)) - return setError(); - if (exp.e1.op == TOK.arrayLength) { // arr.length op= e2; @@ -4192,7 +5145,16 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.e1 = exp.e1.optimize(WANTvalue); exp.e1 = exp.e1.modifiableLvalue(sc, exp.e1); exp.type = exp.e1.type; - if (exp.checkScalar()) + + if (auto ad = isAggregate(exp.e1.type)) + { + if (const s = search_function(ad, Id.opOpAssign)) + { + error(exp.loc, "none of the `opOpAssign` overloads of `%s` are callable for `%s` of type `%s`", ad.toChars(), exp.e1.toChars(), exp.e1.type.toChars()); + return setError(); + } + } + if (exp.e1.checkScalar() || exp.e1.checkReadModifyWrite(exp.op, exp.e2)) return setError(); int arith = (exp.op == TOK.addAssign || exp.op == TOK.minAssign || exp.op == TOK.mulAssign || exp.op == TOK.divAssign || exp.op == TOK.modAssign || exp.op == TOK.powAssign); @@ -4882,7 +5844,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } if (sc.func && !sc.intypeof) { - if (sc.func.setUnsafe()) + if (sc.func.setUnsafe() && !(sc.flags & SCOPE.debug_)) { exp.error("`this` reference necessary to take address of member `%s` in `@safe` function `%s`", f.toChars(), sc.func.toChars()); } @@ -4904,7 +5866,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (ce.e1.type.ty == Tfunction) { TypeFunction tf = cast(TypeFunction)ce.e1.type; - if (tf.isref && sc.func && !sc.intypeof && sc.func.setUnsafe()) + if (tf.isref && sc.func && !sc.intypeof && sc.func.setUnsafe() && !(sc.flags & SCOPE.debug_)) { exp.error("cannot take address of `ref return` of `%s()` in `@safe` function `%s`", ce.e1.toChars(), sc.func.toChars()); @@ -5284,7 +6246,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!sc.intypeof && sc.func && !exp.isRAII && - sc.func.setUnsafe()) + sc.func.setUnsafe() && !(sc.flags & SCOPE.debug_)) { exp.error("`%s` is not `@safe` but is used in `@safe` function `%s`", exp.toChars(), sc.func.toChars()); err = true; @@ -5419,7 +6381,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // Check for unsafe casts if (sc.func && !sc.intypeof && !isSafeCast(ex, t1b, tob) && - sc.func.setUnsafe()) + sc.func.setUnsafe() && !(sc.flags & SCOPE.debug_)) { exp.error("cast from `%s` to `%s` not allowed in safe code", exp.e1.type.toChars(), exp.to.toChars()); return setError(); @@ -5562,7 +6524,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.error("need upper and lower bound to slice pointer"); return setError(); } - if (sc.func && !sc.intypeof && sc.func.setUnsafe()) + if (sc.func && !sc.intypeof && sc.func.setUnsafe() && !(sc.flags & SCOPE.debug_)) { exp.error("pointer slicing not allowed in safe functions"); return setError(); @@ -5695,8 +6657,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Expression e; if (exp.e1.op == TOK.tuple) { - auto exps = new Expressions(); - exps.setDim(j2 - j1); + auto exps = new Expressions(j2 - j1); for (size_t i = 0; i < j2 - j1; i++) { (*exps)[i] = (*te.exps)[j1 + i]; @@ -6056,7 +7017,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (exp.e2.op == TOK.int64 && exp.e2.toInteger() == 0) { } - else if (sc.func && sc.func.setUnsafe()) + else if (sc.func && sc.func.setUnsafe() && !(sc.flags & SCOPE.debug_)) { exp.error("safe function `%s` cannot index pointer `%s`", sc.func.toPrettyChars(), exp.e1.toChars()); return setError(); @@ -6566,8 +7527,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } else { - auto exps = new Expressions(); - exps.setDim(dim); + auto exps = new Expressions(dim); for (size_t i = 0; i < dim; i++) { Expression ex1 = (*tup1.exps)[i]; @@ -7271,7 +8231,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } if (t1n.toBasetype.ty == Tvoid && t2n.toBasetype.ty == Tvoid) { - if (!sc.intypeof && sc.func && sc.func.setUnsafe()) + if (!sc.intypeof && sc.func && sc.func.setUnsafe() && !(sc.flags & SCOPE.debug_)) { exp.error("cannot copy `void[]` to `void[]` in `@safe` code"); return setError(); @@ -7922,8 +8882,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (tb2.ty == Tarray || tb2.ty == Tsarray) { // Make e2 into [e2] - exp.e2 = new ArrayLiteralExp(exp.e2.loc, exp.e2); - exp.e2.type = exp.type; + exp.e2 = new ArrayLiteralExp(exp.e2.loc, exp.type, exp.e2); } else if (checkNewEscape(sc, exp.e2, false)) return setError(); @@ -7961,8 +8920,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (tb1.ty == Tarray || tb1.ty == Tsarray) { // Make e1 into [e1] - exp.e1 = new ArrayLiteralExp(exp.e1.loc, exp.e1); - exp.e1.type = exp.type; + exp.e1 = new ArrayLiteralExp(exp.e1.loc, exp.type, exp.e1); } else if (checkNewEscape(sc, exp.e1, false)) return setError(); @@ -9029,7 +9987,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (t.ty != Tstruct) return false; - semanticTypeInfo(sc, t); + if (global.params.useTypeInfo && Type.dtypeinfo) + semanticTypeInfo(sc, t); + return (cast(TypeStruct)t).sym.hasIdentityEquals; } @@ -9504,8 +10464,7 @@ Expression semanticX(DotIdExp exp, Scope* sc) /* 'distribute' the .offsetof to each of the tuple elements. */ TupleExp te = cast(TupleExp)exp.e1; - auto exps = new Expressions(); - exps.setDim(te.exps.dim); + auto exps = new Expressions(te.exps.dim); for (size_t i = 0; i < exps.dim; i++) { Expression e = (*te.exps)[i]; @@ -9840,6 +10799,11 @@ Expression semanticY(DotTemplateInstanceExp exp, Scope* sc, int flag) printf("DotTemplateInstanceExpY::semantic('%s')\n", exp.toChars()); } + static Expression errorExp() + { + return new ErrorExp(); + } + auto die = new DotIdExp(exp.loc, exp.e1, exp.ti.name); Expression e = die.semanticX(sc); @@ -9856,19 +10820,20 @@ Expression semanticY(DotTemplateInstanceExp exp, Scope* sc, int flag) return null; } e = die.semanticY(sc, flag); - if (flag && e && isDotOpDispatch(e)) + if (flag) { - /* opDispatch!tiargs would be a function template that needs IFTI, - * so it's not a template - */ - e = null; /* fall down to UFCS */ + if (!e || + isDotOpDispatch(e)) + { + /* opDispatch!tiargs would be a function template that needs IFTI, + * so it's not a template + */ + return null; + } } - if (flag && !e) - return null; } assert(e); -L1: if (e.op == TOK.error) return e; if (e.op == TOK.dotVariable) @@ -9876,8 +10841,7 @@ L1: DotVarExp dve = cast(DotVarExp)e; if (FuncDeclaration fd = dve.var.isFuncDeclaration()) { - TemplateDeclaration td = fd.findTemplateDeclRoot(); - if (td) + if (TemplateDeclaration td = fd.findTemplateDeclRoot()) { e = new DotTemplateExp(dve.loc, dve.e1, td); e = e.expressionSemantic(sc); @@ -9893,22 +10857,17 @@ L1: return exp; exp.ti.dsymbolSemantic(sc); if (!exp.ti.inst || exp.ti.errors) // if template failed to expand - return new ErrorExp(); + return errorExp(); - Dsymbol s = exp.ti.toAlias(); - Declaration v = s.isDeclaration(); - if (v) + if (Declaration v = exp.ti.toAlias().isDeclaration()) { if (v.type && !v.type.deco) v.type = v.type.typeSemantic(v.loc, sc); - e = new DotVarExp(exp.loc, exp.e1, v); - e = e.expressionSemantic(sc); - return e; + return new DotVarExp(exp.loc, exp.e1, v) + .expressionSemantic(sc); } - e = new ScopeExp(exp.loc, exp.ti); - e = new DotExp(exp.loc, exp.e1, e); - e = e.expressionSemantic(sc); - return e; + return new DotExp(exp.loc, exp.e1, new ScopeExp(exp.loc, exp.ti)) + .expressionSemantic(sc); } } else if (e.op == TOK.variable) @@ -9916,21 +10875,20 @@ L1: VarExp ve = cast(VarExp)e; if (FuncDeclaration fd = ve.var.isFuncDeclaration()) { - TemplateDeclaration td = fd.findTemplateDeclRoot(); - if (td) + if (TemplateDeclaration td = fd.findTemplateDeclRoot()) { - e = new TemplateExp(ve.loc, td); - e = e.expressionSemantic(sc); + e = new TemplateExp(ve.loc, td) + .expressionSemantic(sc); } } else if (OverDeclaration od = ve.var.isOverDeclaration()) { exp.ti.tempdecl = od; - e = new ScopeExp(exp.loc, exp.ti); - e = e.expressionSemantic(sc); - return e; + return new ScopeExp(exp.loc, exp.ti) + .expressionSemantic(sc); } } + if (e.op == TOK.dotTemplateDeclaration) { DotTemplateExp dte = cast(DotTemplateExp)e; @@ -9938,32 +10896,29 @@ L1: exp.ti.tempdecl = dte.td; if (!exp.ti.semanticTiargs(sc)) - return new ErrorExp(); + return errorExp(); if (exp.ti.needsTypeInference(sc)) return exp; exp.ti.dsymbolSemantic(sc); if (!exp.ti.inst || exp.ti.errors) // if template failed to expand - return new ErrorExp(); + return errorExp(); - Dsymbol s = exp.ti.toAlias(); - Declaration v = s.isDeclaration(); - if (v && (v.isFuncDeclaration() || v.isVarDeclaration())) + if (Declaration v = exp.ti.toAlias().isDeclaration()) { - e = new DotVarExp(exp.loc, exp.e1, v); - e = e.expressionSemantic(sc); - return e; + if (v.isFuncDeclaration() || v.isVarDeclaration()) + { + return new DotVarExp(exp.loc, exp.e1, v) + .expressionSemantic(sc); + } } - e = new ScopeExp(exp.loc, exp.ti); - e = new DotExp(exp.loc, exp.e1, e); - e = e.expressionSemantic(sc); - return e; + return new DotExp(exp.loc, exp.e1, new ScopeExp(exp.loc, exp.ti)) + .expressionSemantic(sc); } else if (e.op == TOK.template_) { exp.ti.tempdecl = (cast(TemplateExp)e).td; - e = new ScopeExp(exp.loc, exp.ti); - e = e.expressionSemantic(sc); - return e; + return new ScopeExp(exp.loc, exp.ti) + .expressionSemantic(sc); } else if (e.op == TOK.dot) { @@ -9973,42 +10928,36 @@ L1: { if (!exp.findTempDecl(sc) || !exp.ti.semanticTiargs(sc)) { - return new ErrorExp(); + return errorExp(); } if (exp.ti.needsTypeInference(sc)) return exp; exp.ti.dsymbolSemantic(sc); if (!exp.ti.inst || exp.ti.errors) // if template failed to expand - return new ErrorExp(); + return errorExp(); - Dsymbol s = exp.ti.toAlias(); - Declaration v = s.isDeclaration(); - if (v) + if (Declaration v = exp.ti.toAlias().isDeclaration()) { if (v.type && !v.type.deco) v.type = v.type.typeSemantic(v.loc, sc); - e = new DotVarExp(exp.loc, exp.e1, v); - e = e.expressionSemantic(sc); - return e; + return new DotVarExp(exp.loc, exp.e1, v) + .expressionSemantic(sc); } - e = new ScopeExp(exp.loc, exp.ti); - e = new DotExp(exp.loc, exp.e1, e); - e = e.expressionSemantic(sc); - return e; + return new DotExp(exp.loc, exp.e1, new ScopeExp(exp.loc, exp.ti)) + .expressionSemantic(sc); } } else if (e.op == TOK.overloadSet) { OverExp oe = cast(OverExp)e; exp.ti.tempdecl = oe.vars; - e = new ScopeExp(exp.loc, exp.ti); - e = e.expressionSemantic(sc); - return e; + return new ScopeExp(exp.loc, exp.ti) + .expressionSemantic(sc); } Lerr: exp.error("`%s` isn't a template", e.toChars()); - return new ErrorExp(); + return errorExp(); } @@ -10039,13 +10988,13 @@ private bool checkAddressVar(Scope* sc, UnaExp exp, VarDeclaration v) // Taking the address of v means it cannot be set to 'scope' later v.storage_class &= ~STC.maybescope; v.doNotInferScope = true; - if (v.storage_class & STC.scope_ && sc.func.setUnsafe()) + if (v.storage_class & STC.scope_ && sc.func.setUnsafe() && !(sc.flags & SCOPE.debug_)) { exp.error("cannot take address of `scope` %s `%s` in `@safe` function `%s`", p, v.toChars(), sc.func.toChars()); return false; } } - else if (sc.func.setUnsafe()) + else if (sc.func.setUnsafe() && !(sc.flags & SCOPE.debug_)) { exp.error("cannot take address of %s `%s` in `@safe` function `%s`", p, v.toChars(), sc.func.toChars()); return false; diff --git a/gcc/d/dmd/func.d b/gcc/d/dmd/func.d index db7108271..a617411a3 100644 --- a/gcc/d/dmd/func.d +++ b/gcc/d/dmd/func.d @@ -945,8 +945,7 @@ extern (C++) class FuncDeclaration : Declaration /* Create a dummy array of arguments out of the parameters to f() */ - Expressions args; - args.setDim(nfparams); + Expressions args = Expressions(nfparams); for (size_t u = 0; u < nfparams; u++) { Parameter p = Parameter.getNth(tf.parameters, u); @@ -1442,7 +1441,7 @@ extern (C++) class FuncDeclaration : Declaration * which could have come from the function's parameters, mutable * globals, or uplevel functions. */ - private final bool isTypeIsolatedIndirect(Type t) + private bool isTypeIsolatedIndirect(Type t) { //printf("isTypeIsolatedIndirect(t: %s)\n", t.toChars()); assert(t); @@ -2170,7 +2169,7 @@ extern (C++) class FuncDeclaration : Declaration Parameter p = null; if (canBuildResultVar()) { - p = new Parameter(STC.ref_ | STC.const_, f.nextOf(), Id.result, null); + p = new Parameter(STC.ref_ | STC.const_, f.nextOf(), Id.result, null, null); fparams.push(p); } auto tf = new TypeFunction(fparams, Type.tvoid, 0, LINK.d); @@ -2297,7 +2296,7 @@ extern (C++) class FuncDeclaration : Declaration FuncDeclaration fd; TypeFunction tf; Dsymbol s; - static __gshared DsymbolTable st = null; + __gshared DsymbolTable st = null; //printf("genCfunc(name = '%s')\n", id.toChars()); //printf("treturn\n\t"); treturn.print(); diff --git a/gcc/d/dmd/globals.d b/gcc/d/dmd/globals.d index 62e8cdf04..68f44b2c4 100644 --- a/gcc/d/dmd/globals.d +++ b/gcc/d/dmd/globals.d @@ -139,7 +139,6 @@ struct Param ubyte covPercent; // 0..100 code coverage percentage required bool nofloat; // code should not pull in floating point support bool ignoreUnsupportedPragmas; // rather than error on them - bool enforcePropertySyntax; bool useModuleInfo = true; // generate runtime module information bool useTypeInfo = true; // generate runtime type information bool useExceptions = true; // support exception handling @@ -265,6 +264,7 @@ struct Global uint warnings; // number of warnings reported so far uint gag; // !=0 means gag reporting of errors & warnings uint gaggedErrors; // number of errors reported while gagged + uint gaggedWarnings; // number of warnings reported while gagged void* console; // opaque pointer to console for controlling text attributes @@ -276,6 +276,7 @@ struct Global extern (C++) uint startGagging() { ++gag; + gaggedWarnings = 0; return gaggedErrors; } @@ -376,7 +377,7 @@ struct Loc static immutable Loc initial; /// use for default initialization of const ref Loc's nothrow: - extern (D) this(const(char)* filename, uint linnum, uint charnum) + extern (D) this(const(char)* filename, uint linnum, uint charnum) pure { this.linnum = linnum; this.charnum = charnum; diff --git a/gcc/d/dmd/globals.h b/gcc/d/dmd/globals.h index 5194daaeb..8d20e1548 100644 --- a/gcc/d/dmd/globals.h +++ b/gcc/d/dmd/globals.h @@ -15,9 +15,10 @@ #pragma once #endif -#include "ctfloat.h" -#include "outbuffer.h" -#include "filename.h" +#include "root/dcompat.h" +#include "root/ctfloat.h" +#include "root/outbuffer.h" +#include "root/filename.h" #include "compiler.h" // Can't include arraytypes.h here, need to declare these directly. @@ -116,7 +117,6 @@ struct Param unsigned char covPercent; // 0..100 code coverage percentage required bool nofloat; // code should not pull in floating point support bool ignoreUnsupportedPragmas; // rather than error on them - bool enforcePropertySyntax; bool useModuleInfo; // generate runtime module information bool useTypeInfo; // generate runtime type information bool useExceptions; // support exception handling @@ -231,10 +231,11 @@ struct Global Compiler compiler; Param params; - unsigned errors; // number of errors reported so far - unsigned warnings; // number of warnings reported so far - unsigned gag; // !=0 means gag reporting of errors & warnings - unsigned gaggedErrors; // number of errors reported while gagged + unsigned errors; // number of errors reported so far + unsigned warnings; // number of warnings reported so far + unsigned gag; // !=0 means gag reporting of errors & warnings + unsigned gaggedErrors; // number of errors reported while gagged + unsigned gaggedWarnings; // number of warnings reported while gagged void* console; // opaque pointer to console for controlling text attributes @@ -292,14 +293,6 @@ typedef uint32_t d_uns32; typedef int64_t d_int64; typedef uint64_t d_uns64; -// Represents a D [ ] array -template -struct DArray -{ - size_t length; - T *ptr; -}; - // file location struct Loc { diff --git a/gcc/d/dmd/hdrgen.d b/gcc/d/dmd/hdrgen.d index 52031cef0..ef0fef36b 100644 --- a/gcc/d/dmd/hdrgen.d +++ b/gcc/d/dmd/hdrgen.d @@ -685,67 +685,6 @@ public: buf.writenl(); } - version (IN_GCC) - { - override void visit(ExtAsmStatement s) - { - buf.writestring ("gcc asm { "); - - if (s.insn) - buf.writestring (s.insn.toChars()); - - buf.writestring (" : "); - - if (s.args) - { - for (size_t i = 0; i < s.args.dim; i++) - { - Identifier name = (*s.names)[i]; - Expression constr = (*s.constraints)[i]; - Expression arg = (*s.args)[i]; - - if (name) - { - buf.writestring ("["); - buf.writestring (name.toChars()); - buf.writestring ("] "); - } - - if (constr) - { - buf.writestring (constr.toChars()); - buf.writestring (" "); - } - - if (arg) - buf.writestring (arg.toChars()); - - if (i < s.outputargs - 1) - buf.writestring (", "); - else if (i == s.outputargs - 1) - buf.writestring (" : "); - else if (i < s.args.dim - 1) - buf.writestring (", "); - } - } - - if (s.clobbers) - { - buf.writestring (" : "); - for (size_t i = 0; i < s.clobbers.dim; i++) - { - Expression clobber = (*s.clobbers)[i]; - buf.writestring (clobber.toChars()); - if (i < s.clobbers.dim - 1) - buf.writestring (", "); - } - } - - buf.writestring ("; }"); - buf.writenl(); - } - } - override void visit(ImportStatement s) { foreach (imp; *s.imports) @@ -1101,7 +1040,7 @@ public: override void visit(TypeEnum t) { - buf.writestring(t.sym.toChars()); + buf.writestring(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars()); } override void visit(TypeStruct t) @@ -3175,6 +3114,18 @@ public: //////////////////////////////////////////////////////////////////////////// override void visit(Parameter p) { + if (p.userAttribDecl) + { + buf.writestring("@"); + scope(exit) buf.writestring(" "); + + bool isAnonymous = p.userAttribDecl.atts.dim > 0 && (*p.userAttribDecl.atts)[0].op != TOK.call; + if (isAnonymous) + buf.writestring("("); + argsToBuffer(p.userAttribDecl.atts); + if (isAnonymous) + buf.writestring(")"); + } if (p.storageClass & STC.auto_) buf.writestring("auto "); if (p.storageClass & STC.return_) @@ -3340,7 +3291,7 @@ extern (C++) const(char)* stcToChars(ref StorageClass stc) const(char)* id; } - static __gshared SCstring* table = + __gshared SCstring* table = [ SCstring(STC.auto_, TOK.auto_), SCstring(STC.scope_, TOK.scope_), @@ -3396,6 +3347,16 @@ extern (C++) const(char)* stcToChars(ref StorageClass stc) return null; } + +/** + * Returns: + * a human readable representation of `stc` + */ +extern (D) const(char)[] stcToString(ref StorageClass stc) +{ + return stcToChars(stc).toDString; +} + extern (C++) void trustToBuffer(OutBuffer* buf, TRUST trust) { const(char)* p = trustToChars(trust); @@ -3403,7 +3364,19 @@ extern (C++) void trustToBuffer(OutBuffer* buf, TRUST trust) buf.writestring(p); } +/** + * Returns: + * a human readable representation of `trust`, + * which is the token `trust` corresponds to + */ extern (C++) const(char)* trustToChars(TRUST trust) +{ + /// Works because we return a literal + return trustToString(trust).ptr; +} + +/// Ditto +extern (D) string trustToString(TRUST trust) { final switch (trust) { @@ -3465,7 +3438,18 @@ extern (C++) void protectionToBuffer(OutBuffer* buf, Prot prot) } } +/** + * Returns: + * a human readable representation of `kind` + */ extern (C++) const(char)* protectionToChars(Prot.Kind kind) +{ + // Null terminated because we return a literal + return protectionToString(kind).ptr; +} + +/// Ditto +extern (D) string protectionToString(Prot.Kind kind) { final switch (kind) { diff --git a/gcc/d/dmd/iasm.d b/gcc/d/dmd/iasm.d new file mode 100644 index 000000000..2dff55777 --- /dev/null +++ b/gcc/d/dmd/iasm.d @@ -0,0 +1,61 @@ +/** + * Compiler implementation of the + * $(LINK2 http://www.dlang.org, D programming language). + * + * Copyright (C) 2018 by The D Language Foundation, All Rights Reserved + * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/iasm.d, _iasm.d) + * Documentation: https://dlang.org/phobos/dmd_iasm.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/iasm.d + */ + +/* Inline assembler for the D programming language compiler + */ + +module dmd.iasm; + +import dmd.dscope; +import dmd.func; +import dmd.statement; + +version (MARS) +{ + import dmd.iasmdmd; +} +else version (IN_GCC) +{ + import dmd.iasmgcc; +} + +/************************ AsmStatement ***************************************/ + +extern(C++) Statement asmSemantic(AsmStatement s, Scope *sc) +{ + //printf("AsmStatement.semantic()\n"); + + FuncDeclaration fd = sc.parent.isFuncDeclaration(); + assert(fd); + + if (!s.tokens) + return null; + + // Assume assembler code takes care of setting the return value + sc.func.hasReturnExp |= 8; + + version (MARS) + { + auto ias = new InlineAsmStatement(s.loc, s.tokens); + return inlineAsmSemantic(ias, sc); + } + else version (IN_GCC) + { + auto eas = new GccAsmStatement(s.loc, s.tokens); + return gccAsmSemantic(eas, sc); + } + else + { + s.error("D inline assembler statements are not supported"); + return new ErrorStatement(); + } +} diff --git a/gcc/d/dmd/iasmgcc.d b/gcc/d/dmd/iasmgcc.d new file mode 100644 index 000000000..a8902759a --- /dev/null +++ b/gcc/d/dmd/iasmgcc.d @@ -0,0 +1,449 @@ +/** + * Compiler implementation of the + * $(LINK2 http://www.dlang.org, D programming language). + * + * Copyright (C) 2018 by The D Language Foundation, All Rights Reserved + * Authors: Iain Buclaw + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/iasmgcc.d, _iasmgcc.d) + * Documentation: https://dlang.org/phobos/dmd_iasmgcc.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/iasmgcc.d + */ + +/* Inline assembler for the GCC D compiler. + */ + +module dmd.iasmgcc; + +import core.stdc.string; + +import dmd.arraytypes; +import dmd.astcodegen; +import dmd.dscope; +import dmd.expression; +import dmd.expressionsem; +import dmd.identifier; +import dmd.globals; +import dmd.parse; +import dmd.tokens; +import dmd.statement; +import dmd.statementsem; + +private: + +/*********************************** + * Parse list of extended asm input or output operands. + * Grammar: + * | Operands: + * | SymbolicName(opt) StringLiteral AssignExpression + * | SymbolicName(opt) StringLiteral AssignExpression , Operands + * | + * | SymbolicName: + * | [ Identifier ] + * Params: + * p = parser state + * s = asm statement to parse + * Returns: + * number of operands added to the gcc asm statement + */ +int parseExtAsmOperands(Parser)(Parser p, GccAsmStatement s) +{ + int numargs = 0; + + while (1) + { + Expression arg; + Identifier name; + Expression constraint; + + switch (p.token.value) + { + case TOK.semicolon: + case TOK.colon: + case TOK.endOfFile: + return numargs; + + case TOK.leftBracket: + if (p.peekNext() == TOK.identifier) + { + p.nextToken(); + name = p.token.ident; + p.nextToken(); + } + else + { + p.error(s.loc, "expected identifier after `[`"); + goto Lerror; + } + p.check(TOK.rightBracket); + goto case; + + case TOK.string_: + constraint = p.parsePrimaryExp(); + arg = p.parseAssignExp(); + + if (!s.args) + { + s.names = new Identifiers(); + s.constraints = new Expressions(); + s.args = new Expressions(); + } + s.names.push(name); + s.args.push(arg); + s.constraints.push(constraint); + numargs++; + + if (p.token.value == TOK.comma) + p.nextToken(); + break; + + default: + p.error("expected constant string constraint for operand, not `%s`", + p.token.toChars()); + goto Lerror; + } + } +Lerror: + while (p.token.value != TOK.rightCurly && + p.token.value != TOK.semicolon && + p.token.value != TOK.endOfFile) + p.nextToken(); + + return numargs; +} + +/*********************************** + * Parse list of extended asm clobbers. + * Grammar: + * | Clobbers: + * | StringLiteral + * | StringLiteral , Clobbers + * Params: + * p = parser state + * Returns: + * array of parsed clobber expressions + */ +Expressions *parseExtAsmClobbers(Parser)(Parser p) +{ + Expressions *clobbers; + + while (1) + { + Expression clobber; + + switch (p.token.value) + { + case TOK.semicolon: + case TOK.colon: + case TOK.endOfFile: + return clobbers; + + case TOK.string_: + clobber = p.parsePrimaryExp(); + if (!clobbers) + clobbers = new Expressions(); + clobbers.push(clobber); + + if (p.token.value == TOK.comma) + p.nextToken(); + break; + + default: + p.error("expected constant string constraint for clobber name, not `%s`", + p.token.toChars()); + goto Lerror; + } + } +Lerror: + while (p.token.value != TOK.rightCurly && + p.token.value != TOK.semicolon && + p.token.value != TOK.endOfFile) + p.nextToken(); + + return clobbers; +} + +/*********************************** + * Parse list of extended asm goto labels. + * Grammar: + * | GotoLabels: + * | Identifier + * | Identifier , GotoLabels + * Params: + * p = parser state + * Returns: + * array of parsed goto labels + */ +Identifiers *parseExtAsmGotoLabels(Parser)(Parser p) +{ + Identifiers *labels; + + while (1) + { + switch (p.token.value) + { + case TOK.semicolon: + case TOK.endOfFile: + return labels; + + case TOK.identifier: + if (!labels) + labels = new Identifiers(); + labels.push(p.token.ident); + + if (p.nextToken() == TOK.comma) + p.nextToken(); + break; + + default: + p.error("expected identifier for goto label name, not `%s`", + p.token.toChars()); + goto Lerror; + } + } +Lerror: + while (p.token.value != TOK.rightCurly && + p.token.value != TOK.semicolon && + p.token.value != TOK.endOfFile) + p.nextToken(); + + return labels; +} + +/*********************************** + * Parse a gcc asm statement. + * There are three forms of inline asm statements, basic, extended, and goto. + * Grammar: + * | AsmInstruction: + * | BasicAsmInstruction + * | ExtAsmInstruction + * | GotoAsmInstruction + * | + * | BasicAsmInstruction: + * | Expression + * | + * | ExtAsmInstruction: + * | Expression : Operands(opt) : Operands(opt) : Clobbers(opt) + * | + * | GotoAsmInstruction: + * | Expression : : Operands(opt) : Clobbers(opt) : GotoLabels(opt) + * Params: + * p = parser state + * s = asm statement to parse + * Returns: + * the parsed gcc asm statement + */ +GccAsmStatement parseGccAsm(Parser)(Parser p, GccAsmStatement s) +{ + s.insn = p.parseExpression(); + if (p.token.value == TOK.semicolon) + goto Ldone; + + // No semicolon followed after instruction template, treat as extended asm. + foreach (section; 0 .. 4) + { + p.check(TOK.colon); + + final switch (section) + { + case 0: + s.outputargs = p.parseExtAsmOperands(s); + break; + + case 1: + p.parseExtAsmOperands(s); + break; + + case 2: + s.clobbers = p.parseExtAsmClobbers(); + break; + + case 3: + s.labels = p.parseExtAsmGotoLabels(); + break; + } + + if (p.token.value == TOK.semicolon) + goto Ldone; + } +Ldone: + p.check(TOK.semicolon); + + return s; +} + +/*********************************** + * Parse and run semantic analysis on a GccAsmStatement. + * Params: + * s = gcc asm statement being parsed + * sc = the scope where the asm statement is located + * Returns: + * the completed gcc asm statement, or null if errors occurred + */ +public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc) +{ + //printf("GccAsmStatement.semantic()\n"); + scope p = new Parser!ASTCodegen(sc._module, ";", false); + + // Make a safe copy of the token list before parsing. + Token *toklist = null; + Token **ptoklist = &toklist; + + for (Token *token = s.tokens; token; token = token.next) + { + *ptoklist = Token.alloc(); + memcpy(*ptoklist, token, Token.sizeof); + ptoklist = &(*ptoklist).next; + *ptoklist = null; + } + p.token = *toklist; + + // Parse the gcc asm statement. + s = p.parseGccAsm(s); + if (p.errors) + return null; + s.stc = sc.stc; + + // Fold the instruction template string. + if (s.insn.op == TOK.mixin_) + s.insn = (cast(CompileExp)s.insn).e1; + s.insn = semanticString(sc, s.insn, "asm instruction template"); + + if (s.labels && s.outputargs) + s.error("extended asm statements with labels cannot have output constraints"); + + // Analyse all input and output operands. + if (s.args) + { + foreach (i; 0 .. s.args.dim) + { + Expression e = (*s.args)[i]; + e = e.expressionSemantic(sc); + // Check argument is a valid lvalue/rvalue. + if (i < s.outputargs) + e = e.modifiableLvalue(sc, null); + else if (e.checkValue()) + e = new ErrorExp(); + (*s.args)[i] = e; + + e = (*s.constraints)[i]; + e = e.expressionSemantic(sc); + assert(e.op == TOK.string_ && (cast(StringExp) e).sz == 1); + (*s.constraints)[i] = e; + } + } + + // Analyse all clobbers. + if (s.clobbers) + { + foreach (i; 0 .. s.clobbers.dim) + { + Expression e = (*s.clobbers)[i]; + e = e.expressionSemantic(sc); + assert(e.op == TOK.string_ && (cast(StringExp) e).sz == 1); + (*s.clobbers)[i] = e; + } + } + + // Analyse all goto labels. + if (s.labels) + { + foreach (i; 0 .. s.labels.dim) + { + Identifier ident = (*s.labels)[i]; + GotoStatement gs = new GotoStatement(s.loc, ident); + if (!s.gotos) + s.gotos = new GotoStatements(); + s.gotos.push(gs); + gs.statementSemantic(sc); + } + } + + return s; +} + +unittest +{ + uint errors = global.startGagging(); + + // Immitates asmSemantic if version = IN_GCC. + static int semanticAsm(Token* tokens) + { + scope gas = new GccAsmStatement(Loc.initial, tokens); + scope p = new Parser!ASTCodegen(null, ";", false); + p.token = *tokens; + p.parseGccAsm(gas); + return p.errors; + } + + // Immitates parseStatement for asm statements. + static void parseAsm(string input, bool expectError) + { + // Generate tokens from input test. + scope p = new Parser!ASTCodegen(null, input, false); + p.nextToken(); + + Token* toklist = null; + Token** ptoklist = &toklist; + p.check(TOK.asm_); + p.check(TOK.leftCurly); + while (1) + { + if (p.token.value == TOK.rightCurly || p.token.value == TOK.endOfFile) + break; + *ptoklist = Token.alloc(); + memcpy(*ptoklist, &p.token, Token.sizeof); + ptoklist = &(*ptoklist).next; + *ptoklist = null; + p.nextToken(); + } + p.check(TOK.rightCurly); + + auto res = semanticAsm(toklist); + assert(res == 0 || expectError); + } + + /// Assembly Tests, all should pass. + /// Note: Frontend is not initialized, use only strings and identifiers. + immutable string[] passAsmTests = [ + // Basic asm statement + q{ asm { "nop"; + } }, + + // Extended asm statement + q{ asm { "cpuid" + : "=a" (a), "=b" (b), "=c" (c), "=d" (d) + : "a" input; + } }, + + // Assembly with symbolic names + q{ asm { "bts %[base], %[offset]" + : [base] "+rm" *ptr, + : [offset] "Ir" bitnum; + } }, + + // Assembly with clobbers + q{ asm { "cpuid" + : "=a" a + : "a" input + : "ebx", "ecx", "edx"; + } }, + + // Goto asm statement + q{ asm { "jmp %l0" + : + : + : + : Ljmplabel; + } }, + + // Any CTFE-able string allowed as instruction template. + q{ asm { generateAsm() + } }, + ]; + + foreach (test; passAsmTests) + parseAsm(test, false); + + global.endGagging(errors); +} diff --git a/gcc/d/dmd/identifier.d b/gcc/d/dmd/identifier.d index 68d8dc294..bd4648a6a 100644 --- a/gcc/d/dmd/identifier.d +++ b/gcc/d/dmd/identifier.d @@ -74,12 +74,12 @@ nothrow: return name; } - extern (D) final const(char)[] toString() const pure + extern (D) override const(char)[] toString() const pure { return name[0 .. len]; } - final int getValue() const pure + int getValue() const pure { return value; } @@ -122,11 +122,11 @@ nothrow: return DYNCAST.identifier; } - extern (C++) static __gshared StringTable stringtable; + extern (C++) __gshared StringTable stringtable; static Identifier generateId(const(char)* prefix) { - static __gshared size_t i; + __gshared size_t i; return generateId(prefix, ++i); } diff --git a/gcc/d/dmd/identifier.h b/gcc/d/dmd/identifier.h index 9c89a7578..7da809f03 100644 --- a/gcc/d/dmd/identifier.h +++ b/gcc/d/dmd/identifier.h @@ -15,9 +15,9 @@ #pragma once #endif /* __DMC__ */ -#include "root.h" -#include "rmem.h" -#include "stringtable.h" +#include "root/root.h" +#include "root/rmem.h" +#include "root/stringtable.h" class Identifier : public RootObject { diff --git a/gcc/d/dmd/imphint.d b/gcc/d/dmd/imphint.d index fa08984c5..57631750a 100644 --- a/gcc/d/dmd/imphint.d +++ b/gcc/d/dmd/imphint.d @@ -12,7 +12,7 @@ module dmd.imphint; -import core.stdc.string; +import dmd.utils; /****************************************** * Looks for undefined identifier s to see @@ -23,7 +23,9 @@ import core.stdc.string; */ extern (C++) const(char)* importHint(const(char)* s) { - return hints.get(cast(string) s[0..strlen(s)], null).ptr; + if (auto entry = s.toDString() in hints) + return entry.ptr; + return null; } private immutable string[string] hints; @@ -32,9 +34,13 @@ shared static this() { // in alphabetic order hints = [ + "calloc": "core.stdc.stdlib", "cos": "std.math", "fabs": "std.math", + "free": "core.stdc.stdlib", + "malloc": "core.stdc.stdlib", "printf": "core.stdc.stdio", + "realloc": "core.stdc.stdlib", "sin": "std.math", "sqrt": "std.math", "writefln": "std.stdio", diff --git a/gcc/d/dmd/init.d b/gcc/d/dmd/init.d index 35a2f113b..89f95cdb8 100644 --- a/gcc/d/dmd/init.d +++ b/gcc/d/dmd/init.d @@ -16,16 +16,11 @@ import core.stdc.stdio; import core.checkedint; import dmd.arraytypes; -import dmd.dscope; import dmd.dsymbol; -import dmd.dtemplate; -import dmd.errors; import dmd.expression; -import dmd.expressionsem; import dmd.globals; import dmd.hdrgen; import dmd.identifier; -import dmd.initsem; import dmd.mtype; import dmd.root.outbuffer; import dmd.root.rootobject; @@ -59,8 +54,7 @@ extern (C++) class Initializer : RootObject Initializers* a = null; if (ai) { - a = new Initializers(); - a.setDim(ai.dim); + a = new Initializers(ai.dim); for (size_t i = 0; i < a.dim; i++) (*a)[i] = (*ai)[i].syntaxCopy(); } @@ -250,39 +244,6 @@ extern (C++) final class ArrayInitializer : Initializer return false; } - /******************************** - * If possible, convert array initializer to associative array initializer. - */ - Expression toAssocArrayLiteral() - { - Expression e; - //printf("ArrayInitializer::toAssocArrayInitializer()\n"); - //static int i; if (++i == 2) assert(0); - auto keys = new Expressions(); - keys.setDim(value.dim); - auto values = new Expressions(); - values.setDim(value.dim); - for (size_t i = 0; i < value.dim; i++) - { - e = index[i]; - if (!e) - goto Lno; - (*keys)[i] = e; - Initializer iz = value[i]; - if (!iz) - goto Lno; - e = iz.initializerToExpression(); - if (!e) - goto Lno; - (*values)[i] = e; - } - e = new AssocArrayLiteralExp(loc, keys, values); - return e; - Lno: - error(loc, "not an associative array initializer"); - return new ErrorExp(); - } - override ArrayInitializer isArrayInitializer() { return this; diff --git a/gcc/d/dmd/init.h b/gcc/d/dmd/init.h index ac6ce0f19..1bbdb70a4 100644 --- a/gcc/d/dmd/init.h +++ b/gcc/d/dmd/init.h @@ -11,7 +11,7 @@ #ifndef INIT_H #define INIT_H -#include "root.h" +#include "root/root.h" #include "mars.h" #include "arraytypes.h" diff --git a/gcc/d/dmd/initsem.d b/gcc/d/dmd/initsem.d index 4062c004e..1987f3e21 100644 --- a/gcc/d/dmd/initsem.d +++ b/gcc/d/dmd/initsem.d @@ -36,8 +36,48 @@ import dmd.mtype; import dmd.statement; import dmd.target; import dmd.tokens; +import dmd.typesem; import dmd.visitor; +/******************************** + * If possible, convert array initializer to associative array initializer. + * + * Params: + * ai = array initializer to be converted + * + * Returns: + * The converted associative array initializer or ErrorExp if `ai` + * is not an associative array initializer. + */ +Expression toAssocArrayLiteral(ArrayInitializer ai) +{ + Expression e; + //printf("ArrayInitializer::toAssocArrayInitializer()\n"); + //static int i; if (++i == 2) assert(0); + const dim = ai.value.dim; + auto keys = new Expressions(dim); + auto values = new Expressions(dim); + for (size_t i = 0; i < dim; i++) + { + e = ai.index[i]; + if (!e) + goto Lno; + (*keys)[i] = e; + Initializer iz = ai.value[i]; + if (!iz) + goto Lno; + e = iz.initializerToExpression(); + if (!e) + goto Lno; + (*values)[i] = e; + } + e = new AssocArrayLiteralExp(ai.loc, keys, values); + return e; +Lno: + error(ai.loc, "not an associative array initializer"); + return new ErrorExp(); +} + /*********************** * Translate init to an `Expression` in order to infer the type. * Params: @@ -140,8 +180,7 @@ private extern(C++) final class InitializerSemanticVisitor : Visitor } size_t nfields = sd.fields.dim - sd.isNested(); //expandTuples for non-identity arguments? - auto elements = new Expressions(); - elements.setDim(nfields); + auto elements = new Expressions(nfields); for (size_t j = 0; j < elements.dim; j++) (*elements)[j] = null; // Run semantic for explicitly given initializers @@ -395,7 +434,7 @@ private extern(C++) final class InitializerSemanticVisitor : Visitor override void visit(ExpInitializer i) { - //printf("ExpInitializer::semantic(%s), type = %s\n", exp.toChars(), t.toChars()); + //printf("ExpInitializer::semantic(%s), type = %s\n", i.exp.toChars(), t.toChars()); if (needInterpret) sc = sc.startCTFE(); i.exp = i.exp.expressionSemantic(sc); @@ -547,7 +586,7 @@ private extern(C++) final class InitializerSemanticVisitor : Visitor i.exp = i.exp.ctfeInterpret(); else i.exp = i.exp.optimize(WANTvalue); - //printf("-ExpInitializer::semantic(): "); exp.print(); + //printf("-ExpInitializer::semantic(): "); i.exp.print(); result = i; } } @@ -588,10 +627,8 @@ private extern(C++) final class InferTypeVisitor : Visitor Expressions* values; if (init.isAssociativeArray()) { - keys = new Expressions(); - keys.setDim(init.value.dim); - values = new Expressions(); - values.setDim(init.value.dim); + keys = new Expressions(init.value.dim); + values = new Expressions(init.value.dim); for (size_t i = 0; i < init.value.dim; i++) { Expression e = init.index[i]; @@ -618,8 +655,7 @@ private extern(C++) final class InferTypeVisitor : Visitor } else { - auto elements = new Expressions(); - elements.setDim(init.value.dim); + auto elements = new Expressions(init.value.dim); elements.zero(); for (size_t i = 0; i < init.value.dim; i++) { @@ -637,7 +673,7 @@ private extern(C++) final class InferTypeVisitor : Visitor (*elements)[i] = (cast(ExpInitializer)iz).exp; assert((*elements)[i].op != TOK.error); } - Expression e = new ArrayLiteralExp(init.loc, elements); + Expression e = new ArrayLiteralExp(init.loc, null, elements); auto ei = new ExpInitializer(init.loc, e); result = ei.inferType(sc); return; @@ -812,8 +848,7 @@ private extern(C++) final class InitToExpressionVisitor : Visitor edim = cast(uint)(j + 1); } } - elements = new Expressions(); - elements.setDim(edim); + elements = new Expressions(edim); elements.zero(); for (size_t i = 0, j = 0; i < init.value.dim; i++, j++) { @@ -860,12 +895,10 @@ private extern(C++) final class InitToExpressionVisitor : Visitor { if (te.equals(e.type)) { - auto elements2 = new Expressions(); - elements2.setDim(dim); + auto elements2 = new Expressions(dim); foreach (ref e2; *elements2) e2 = e; - e = new ArrayLiteralExp(e.loc, elements2); - e.type = tn; + e = new ArrayLiteralExp(e.loc, tn, elements2); } } } @@ -883,8 +916,7 @@ private extern(C++) final class InitToExpressionVisitor : Visitor } } - Expression e = new ArrayLiteralExp(init.loc, elements); - e.type = init.type; + Expression e = new ArrayLiteralExp(init.loc, init.type, elements); result = e; return; } @@ -896,19 +928,17 @@ private extern(C++) final class InitToExpressionVisitor : Visitor { if (itype) { - //printf("ExpInitializer::toExpression(t = %s) exp = %s\n", t.toChars(), exp.toChars()); + //printf("ExpInitializer::toExpression(t = %s) exp = %s\n", itype.toChars(), i.exp.toChars()); Type tb = itype.toBasetype(); Expression e = (i.exp.op == TOK.construct || i.exp.op == TOK.blit) ? (cast(AssignExp)i.exp).e2 : i.exp; if (tb.ty == Tsarray && e.implicitConvTo(tb.nextOf())) { TypeSArray tsa = cast(TypeSArray)tb; size_t d = cast(size_t)tsa.dim.toInteger(); - auto elements = new Expressions(); - elements.setDim(d); + auto elements = new Expressions(d); for (size_t j = 0; j < d; j++) (*elements)[j] = e; - auto ae = new ArrayLiteralExp(e.loc, elements); - ae.type = itype; + auto ae = new ArrayLiteralExp(e.loc, itype, elements); result = ae; return; } diff --git a/gcc/d/dmd/json.d b/gcc/d/dmd/json.d index 87c22eda9..d222c623b 100644 --- a/gcc/d/dmd/json.d +++ b/gcc/d/dmd/json.d @@ -33,6 +33,8 @@ import dmd.id; import dmd.identifier; import dmd.mtype; import dmd.root.outbuffer; +import dmd.root.rootobject; +import dmd.utils; import dmd.visitor; version(Windows) { @@ -47,13 +49,14 @@ private extern (C++) final class ToJsonVisitor : Visitor public: OutBuffer* buf; int indentLevel; - const(char)* filename; + const(char)[] filename; extern (D) this(OutBuffer* buf) { this.buf = buf; } + void indent() { if (buf.offset >= 1 && buf.data[buf.offset - 1] == '\n') @@ -83,11 +86,10 @@ public: buf.writeByte('\"'); } - void stringPart(const(char)* s) + extern(D) void stringPart(const(char)[] s) { - for (; *s; s++) + foreach (char c; s) { - char c = cast(char)*s; switch (c) { case '\n': @@ -128,7 +130,7 @@ public: /********************************* * Encode string into buf, and wrap it in double quotes. */ - void value(const(char)* s) + extern(D) void value(const(char)[] s) { stringStart(); stringPart(s); @@ -153,7 +155,7 @@ public: /********************************* * Item is an intented value and a comma, for use in arrays */ - void item(const(char)* s) + extern(D) void item(const(char)[] s) { indent(); value(s); @@ -167,7 +169,7 @@ public: comma(); } - void itemBool(bool b) + void itemBool(const bool b) { indent(); valueBool(b); @@ -221,7 +223,7 @@ public: } // Json object property functions - void propertyStart(const(char)* name) + extern(D) void propertyStart(const(char)[] name) { indent(); value(name); @@ -235,7 +237,7 @@ public: name = the name of the object property s = the string value of the object property */ - void property(const(char)* name, const(char)* s) + extern(D) void property(const(char)[] name, const(char)[] s) { if (s is null) return; @@ -251,7 +253,7 @@ public: name = the name of the object property s = the string value of the object property */ - void requiredProperty(const(char)* name, const(char)* s) + extern(D) void requiredProperty(const(char)[] name, const(char)[] s) { propertyStart(name); if (s is null) @@ -261,21 +263,21 @@ public: comma(); } - void property(const(char)* name, int i) + extern(D) void property(const(char)[] name, int i) { propertyStart(name); value(i); comma(); } - void propertyBool(const(char)* name, bool b) + extern(D) void propertyBool(const(char)[] name, const bool b) { propertyStart(name); valueBool(b); comma(); } - void property(const(char)* name, TRUST trust) + extern(D) void property(const(char)[] name, TRUST trust) { final switch (trust) { @@ -295,7 +297,7 @@ public: } } - void property(const(char)* name, PURE purity) + extern(D) void property(const(char)[] name, PURE purity) { final switch (purity) { @@ -318,7 +320,7 @@ public: } } - void property(const(char)* name, LINK linkage) + extern(D) void property(const(char)[] name, const LINK linkage) { final switch (linkage) { @@ -352,7 +354,7 @@ public: } } - void propertyStorageClass(const(char)* name, StorageClass stc) + extern(D) void propertyStorageClass(const(char)[] name, StorageClass stc) { stc &= STCStorageClass; if (stc) @@ -361,22 +363,21 @@ public: arrayStart(); while (stc) { - const(char)* p = stcToChars(stc); - assert(p); + auto p = stcToString(stc); + assert(p.length); item(p); } arrayEnd(); } } - void property(const(char)* linename, const(char)* charname, Loc* loc) + extern(D) void property(const(char)[] linename, const(char)[] charname, Loc* loc) { if (loc) { - const(char)* filename = loc.filename; - if (filename) + if (auto filename = loc.filename.toDString) { - if (!this.filename || strcmp(filename, this.filename)) + if (filename != this.filename) { this.filename = filename; property("file", filename); @@ -391,26 +392,26 @@ public: } } - void property(const(char)* name, Type type) + extern(D) void property(const(char)[] name, Type type) { if (type) { - property(name, type.toChars()); + property(name, type.toString()); } } - void property(const(char)* name, const(char)* deconame, Type type) + extern(D) void property(const(char)[] name, const(char)[] deconame, Type type) { if (type) { if (type.deco) - property(deconame, type.deco); + property(deconame, type.deco.toDString); else - property(name, type.toChars()); + property(name, type.toString()); } } - void property(const(char)* name, Parameters* parameters) + extern(D) void property(const(char)[] name, Parameters* parameters) { if (parameters is null || parameters.dim == 0) return; @@ -423,11 +424,11 @@ public: Parameter p = (*parameters)[i]; objectStart(); if (p.ident) - property("name", p.ident.toChars()); + property("name", p.ident.toString()); property("type", "deco", p.type); propertyStorageClass("storageClass", p.storageClass); if (p.defaultArg) - property("default", p.defaultArg.toChars()); + property("default", p.defaultArg.toString()); objectEnd(); } } @@ -441,17 +442,17 @@ public: return; if (!s.isTemplateDeclaration()) // TemplateDeclaration::kind() acts weird sometimes { - property("name", s.toChars()); - property("kind", s.kind()); + property("name", s.toString()); + property("kind", s.kind.toDString); } if (s.prot().kind != Prot.Kind.public_) // TODO: How about package(names)? - property("protection", protectionToChars(s.prot().kind)); + property("protection", protectionToString(s.prot().kind)); if (EnumMember em = s.isEnumMember()) { if (em.origValue) - property("value", em.origValue.toChars()); + property("value", em.origValue.toString()); } - property("comment", s.comment); + property("comment", s.comment.toDString); property("line", "char", &s.loc); } @@ -466,11 +467,11 @@ public: // Emit originalType if it differs from type if (d.type != d.originalType && d.originalType) { - const(char)* ostr = d.originalType.toChars(); + auto ostr = d.originalType.toString(); if (d.type) { - const(char)* tstr = d.type.toChars(); - if (strcmp(tstr, ostr)) + auto tstr = d.type.toString(); + if (ostr != tstr) { //printf("tstr = %s, ostr = %s\n", tstr, ostr); property("originalType", ostr); @@ -487,7 +488,7 @@ public: if (td.onemember && td.onemember.isCtorDeclaration()) property("name", "this"); // __ctor -> this else - property("name", td.ident.toChars()); // Foo(T) -> Foo + property("name", td.ident.toString()); // Foo(T) -> Foo } /* ========================================================================== */ @@ -499,11 +500,11 @@ public: { objectStart(); if (s.md) - property("name", s.md.toChars()); - property("kind", s.kind()); - filename = s.srcfile.toChars(); + property("name", s.md.toString()); + property("kind", s.kind.toDString); + filename = s.srcfile.toString(); property("file", filename); - property("comment", s.comment); + property("comment", s.comment.toDString); propertyStart("members"); arrayStart(); for (size_t i = 0; i < s.members.dim; i++) @@ -526,20 +527,20 @@ public: for (size_t i = 0; i < s.packages.dim; i++) { Identifier pid = (*s.packages)[i]; - stringPart(pid.toChars()); + stringPart(pid.toString()); buf.writeByte('.'); } } - stringPart(s.id.toChars()); + stringPart(s.id.toString()); stringEnd(); comma(); - property("kind", s.kind()); - property("comment", s.comment); + property("kind", s.kind.toDString); + property("comment", s.comment.toDString); property("line", "char", &s.loc); if (s.prot().kind != Prot.Kind.public_) - property("protection", protectionToChars(s.prot().kind)); + property("protection", protectionToString(s.prot().kind)); if (s.aliasId) - property("alias", s.aliasId.toChars()); + property("alias", s.aliasId.toString()); bool hasRenamed = false; bool hasSelective = false; for (size_t i = 0; i < s.aliases.dim; i++) @@ -562,7 +563,7 @@ public: Identifier name = s.names[i]; Identifier _alias = s.aliases[i]; if (_alias) - property(_alias.toChars(), name.toChars()); + property(_alias.toString(), name.toString()); } objectEnd(); } @@ -571,11 +572,10 @@ public: // import foo : target1; propertyStart("selective"); arrayStart(); - for (size_t i = 0; i < s.names.dim; i++) + foreach (i, name; s.names) { - Identifier name = s.names[i]; if (!s.aliases[i]) - item(name.toChars()); + item(name.toString()); } arrayEnd(); } @@ -634,7 +634,7 @@ public: { if (cd.baseClass && cd.baseClass.ident != Id.Object) { - property("base", cd.baseClass.toPrettyChars(true)); + property("base", cd.baseClass.toPrettyChars(true).toDString); } if (cd.interfaces.length) { @@ -642,7 +642,7 @@ public: arrayStart(); foreach (b; cd.interfaces) { - item(b.sym.toPrettyChars(true)); + item(b.sym.toPrettyChars(true).toDString); } arrayEnd(); } @@ -676,7 +676,7 @@ public: for (size_t i = 0; i < d.foverrides.dim; i++) { FuncDeclaration fd = d.foverrides[i]; - item(fd.toPrettyChars()); + item(fd.toPrettyChars().toDString); } arrayEnd(); } @@ -705,7 +705,7 @@ public: { TemplateParameter s = (*d.parameters)[i]; objectStart(); - property("name", s.ident.toChars()); + property("name", s.ident.toString()); TemplateTypeParameter type = s.isTemplateTypeParameter(); if (type) { @@ -722,9 +722,9 @@ public: property("kind", "value"); property("type", "deco", value.valType); if (value.specValue) - property("specValue", value.specValue.toChars()); + property("specValue", value.specValue.toString()); if (value.defaultValue) - property("defaultValue", value.defaultValue.toChars()); + property("defaultValue", value.defaultValue.toString()); } TemplateAliasParameter _alias = s.isTemplateAliasParameter(); if (_alias) @@ -732,9 +732,9 @@ public: property("kind", "alias"); property("type", "deco", _alias.specType); if (_alias.specAlias) - property("specAlias", _alias.specAlias.toChars()); + property("specAlias", _alias.specAlias.toString()); if (_alias.defaultAlias) - property("defaultAlias", _alias.defaultAlias.toChars()); + property("defaultAlias", _alias.defaultAlias.toString()); } TemplateTupleParameter tuple = s.isTemplateTupleParameter(); if (tuple) @@ -747,7 +747,7 @@ public: Expression expression = d.constraint; if (expression) { - property("constraint", expression.toChars()); + property("constraint", expression.toString()); } propertyStart("members"); arrayStart(); @@ -806,7 +806,7 @@ public: objectStart(); jsonProperties(d); if (d._init) - property("init", d._init.toChars()); + property("init", d._init.toString()); if (d.isField()) property("offset", d.offset); if (d.alignment && d.alignment != STRUCTALIGN_DEFAULT) @@ -850,8 +850,8 @@ public: private void generateCompilerInfo() { objectStart(); - requiredProperty("vendor", global.compiler.vendor); - requiredProperty("version", global._version); + requiredProperty("vendor", global.compiler.vendor.toDString); + requiredProperty("version", global._version.toDString); property("__VERSION__", global.versionNumber()); requiredProperty("interface", determineCompilerInterface()); property("size_t", size_t.sizeof); @@ -900,7 +900,7 @@ public: { foreach (const versionid; *global.versionids) { - item(versionid.toChars()); + item(versionid.toString()); } } arrayEnd(); @@ -921,10 +921,10 @@ public: private void generateBuildInfo() { objectStart(); - requiredProperty("cwd", getcwd(null, 0)); - requiredProperty("argv0", global.params.argv0); - requiredProperty("config", global.inifilename); - requiredProperty("libName", global.params.libname); + requiredProperty("cwd", getcwd(null, 0).toDString); + requiredProperty("argv0", global.params.argv0.toDString); + requiredProperty("config", global.inifilename.toDString); + requiredProperty("libName", global.params.libname.toDString); propertyStart("importPaths"); arrayStart(); @@ -932,7 +932,7 @@ public: { foreach (importPath; *global.params.imppath) { - item(importPath); + item(importPath.toDString); } } arrayEnd(); @@ -941,7 +941,7 @@ public: arrayStart(); foreach (objfile; global.params.objfiles) { - item(objfile); + item(objfile.toDString); } arrayEnd(); @@ -949,7 +949,7 @@ public: arrayStart(); foreach (lib; global.params.libfiles) { - item(lib); + item(lib.toDString); } arrayEnd(); @@ -957,13 +957,13 @@ public: arrayStart(); foreach (ddocFile; global.params.ddocfiles) { - item(ddocFile); + item(ddocFile.toDString); } arrayEnd(); - requiredProperty("mapFile", global.params.mapfile); - requiredProperty("resourceFile", global.params.resfile); - requiredProperty("defFile", global.params.deffile); + requiredProperty("mapFile", global.params.mapfile.toDString); + requiredProperty("resourceFile", global.params.resfile.toDString); + requiredProperty("defFile", global.params.deffile.toDString); objectEnd(); } @@ -981,8 +981,8 @@ public: foreach (m; Module.amodules) { objectStart(); - requiredProperty("name", m.md ? m.md.toChars() : null); - requiredProperty("file", m.srcfile.toChars()); + requiredProperty("name", m.md ? m.md.toString() : null); + requiredProperty("file", m.srcfile.toString()); propertyBool("isRoot", m.isRoot()); if(m.contentImportedFiles.dim > 0) { @@ -990,7 +990,7 @@ public: arrayStart(); foreach (file; m.contentImportedFiles) { - item(file); + item(file.toDString); } arrayEnd(); } @@ -1088,7 +1088,7 @@ JsonFieldFlags tryParseJsonField(const(char)* fieldName) Determines and returns the compiler interface which is one of `dmd`, `ldc`, `gdc` or `sdc`. Returns `null` if no interface can be determined. */ -private const(char)* determineCompilerInterface() +private extern(D) string determineCompilerInterface() { if (!strcmp(global.compiler.vendor, "Digital Mars D")) return "dmd"; diff --git a/gcc/d/dmd/lexer.d b/gcc/d/dmd/lexer.d index 69290c30c..1664b932f 100644 --- a/gcc/d/dmd/lexer.d +++ b/gcc/d/dmd/lexer.d @@ -96,44 +96,44 @@ enum CMzerosecond = 0x8; enum CMdigitsecond = 0x10; enum CMsinglechar = 0x20; -bool isoctal(char c) +bool isoctal(const char c) { return (cmtable[c] & CMoctal) != 0; } -bool ishex(char c) +bool ishex(const char c) { return (cmtable[c] & CMhex) != 0; } -bool isidchar(char c) +bool isidchar(const char c) { return (cmtable[c] & CMidchar) != 0; } -bool isZeroSecond(char c) +bool isZeroSecond(const char c) { return (cmtable[c] & CMzerosecond) != 0; } -bool isDigitSecond(char c) +bool isDigitSecond(const char c) { return (cmtable[c] & CMdigitsecond) != 0; } -bool issinglechar(char c) +bool issinglechar(const char c) { return (cmtable[c] & CMsinglechar) != 0; } -private bool c_isxdigit(int c) +private bool c_isxdigit(const int c) { return (( c >= '0' && c <= '9') || ( c >= 'a' && c <= 'f') || ( c >= 'A' && c <= 'F')); } -private bool c_isalnum(int c) +private bool c_isalnum(const int c) { return (( c >= '0' && c <= '9') || ( c >= 'a' && c <= 'z') || @@ -1803,6 +1803,8 @@ class Lexer : ErrorHandler int d; bool err = false; bool overflow = false; + bool anyBinaryDigitsUS = false; + bool anyHexDigitsNoSingleUS = false; dchar c = *p; if (c == '0') { @@ -1822,6 +1824,14 @@ class Lexer : ErrorHandler ++p; base = 8; break; + case '8': + case '9': + n = c - '0'; + ++p; + base = 8; + error("radix %d digit expected, not `%c`", base, c); + err = true; + break; case 'x': case 'X': ++p; @@ -1861,6 +1871,10 @@ class Lexer : ErrorHandler { case '0': case '1': + if (base == 2 && !anyBinaryDigitsUS) + anyBinaryDigitsUS = true; + else if (base == 16 && !anyHexDigitsNoSingleUS) + anyHexDigitsNoSingleUS = true; ++p; d = c - '0'; break; @@ -1870,6 +1884,8 @@ class Lexer : ErrorHandler case '5': case '6': case '7': + if (base == 16 && !anyHexDigitsNoSingleUS) + anyHexDigitsNoSingleUS = true; if (base == 2 && !err) { error("binary digit expected"); @@ -1880,6 +1896,8 @@ class Lexer : ErrorHandler break; case '8': case '9': + if (base == 16 && !anyHexDigitsNoSingleUS) + anyHexDigitsNoSingleUS = true; ++p; if (base < 10 && !err) { @@ -1900,6 +1918,8 @@ class Lexer : ErrorHandler case 'D': case 'E': case 'F': + if (base == 16 && !anyHexDigitsNoSingleUS) + anyHexDigitsNoSingleUS = true; ++p; if (base != 16) { @@ -1933,6 +1953,8 @@ class Lexer : ErrorHandler p = start; return inreal(t); case '_': + if (base == 2 && !anyBinaryDigitsUS) + anyBinaryDigitsUS = true; ++p; continue; default: @@ -1955,6 +1977,12 @@ class Lexer : ErrorHandler error("integer overflow"); err = true; } + // Deprecated in 2018-06. + // Change to error in 2019-06. + // @@@DEPRECATED_2019-06@@@ + if ((base == 2 && !anyBinaryDigitsUS) || + (base == 16 && !anyHexDigitsNoSingleUS)) + deprecation("`%.*s` isn't a valid integer literal, use `%.*s0` instead", cast(int)(p - start), start, 2, start); enum FLAGS : int { none = 0, @@ -2584,7 +2612,7 @@ class Lexer : ErrorHandler } private: - final void endOfLine() + void endOfLine() { scanloc.linnum++; line = p; diff --git a/gcc/d/dmd/mars.h b/gcc/d/dmd/mars.h index e360a1f33..739322347 100644 --- a/gcc/d/dmd/mars.h +++ b/gcc/d/dmd/mars.h @@ -78,7 +78,7 @@ struct OutBuffer; #include "globals.h" -#include "ctfloat.h" +#include "root/ctfloat.h" #include "complex_t.h" diff --git a/gcc/d/dmd/module.h b/gcc/d/dmd/module.h index 7482adee9..4a85bf256 100644 --- a/gcc/d/dmd/module.h +++ b/gcc/d/dmd/module.h @@ -15,7 +15,7 @@ #pragma once #endif /* __DMC__ */ -#include "root.h" +#include "root/root.h" #include "dsymbol.h" class ClassDeclaration; @@ -177,7 +177,7 @@ struct ModuleDeclaration bool isdeprecated; // if it is a deprecated module Expression *msg; - const char *toChars(); + const char *toChars() const; }; #endif /* DMD_MODULE_H */ diff --git a/gcc/d/dmd/mtype.d b/gcc/d/dmd/mtype.d index b4672a5a0..da7757a50 100644 --- a/gcc/d/dmd/mtype.d +++ b/gcc/d/dmd/mtype.d @@ -18,15 +18,13 @@ import core.stdc.stdio; import core.stdc.stdlib; import core.stdc.string; -import dmd.access; import dmd.aggregate; import dmd.arraytypes; +import dmd.attrib; import dmd.gluelayer; -import dmd.complex; import dmd.dclass; import dmd.declaration; import dmd.denum; -import dmd.dimport; import dmd.dmangle; import dmd.dscope; import dmd.dstruct; @@ -35,13 +33,11 @@ import dmd.dsymbolsem; import dmd.dtemplate; import dmd.errors; import dmd.expression; -import dmd.expressionsem; import dmd.func; import dmd.globals; import dmd.hdrgen; import dmd.id; import dmd.identifier; -import dmd.imphint; import dmd.init; import dmd.opover; import dmd.root.ctfloat; @@ -245,45 +241,6 @@ private enum TFlags complex = 0x20, } -Expression semanticLength(Scope* sc, TupleDeclaration tup, Expression exp) -{ - ScopeDsymbol sym = new ArrayScopeSymbol(sc, tup); - sym.parent = sc.scopesym; - - sc = sc.push(sym); - sc = sc.startCTFE(); - exp = exp.expressionSemantic(sc); - sc = sc.endCTFE(); - sc.pop(); - - return exp; -} - -/************************** - * This evaluates exp while setting length to be the number - * of elements in the tuple t. - */ -Expression semanticLength(Scope* sc, Type t, Expression exp) -{ - if (t.ty == Ttuple) - { - ScopeDsymbol sym = new ArrayScopeSymbol(sc, cast(TypeTuple)t); - sym.parent = sc.scopesym; - sc = sc.push(sym); - sc = sc.startCTFE(); - exp = exp.expressionSemantic(sc); - sc = sc.endCTFE(); - sc.pop(); - } - else - { - sc = sc.startCTFE(); - exp = exp.expressionSemantic(sc); - sc = sc.endCTFE(); - } - return exp; -} - enum ENUMTY : int { Tarray, // slice array, aka T[] @@ -437,69 +394,69 @@ extern (C++) abstract class Type : RootObject type* ctype; // for back end - extern (C++) static __gshared Type tvoid; - extern (C++) static __gshared Type tint8; - extern (C++) static __gshared Type tuns8; - extern (C++) static __gshared Type tint16; - extern (C++) static __gshared Type tuns16; - extern (C++) static __gshared Type tint32; - extern (C++) static __gshared Type tuns32; - extern (C++) static __gshared Type tint64; - extern (C++) static __gshared Type tuns64; - extern (C++) static __gshared Type tint128; - extern (C++) static __gshared Type tuns128; - extern (C++) static __gshared Type tfloat32; - extern (C++) static __gshared Type tfloat64; - extern (C++) static __gshared Type tfloat80; - extern (C++) static __gshared Type timaginary32; - extern (C++) static __gshared Type timaginary64; - extern (C++) static __gshared Type timaginary80; - extern (C++) static __gshared Type tcomplex32; - extern (C++) static __gshared Type tcomplex64; - extern (C++) static __gshared Type tcomplex80; - extern (C++) static __gshared Type tbool; - extern (C++) static __gshared Type tchar; - extern (C++) static __gshared Type twchar; - extern (C++) static __gshared Type tdchar; + extern (C++) __gshared Type tvoid; + extern (C++) __gshared Type tint8; + extern (C++) __gshared Type tuns8; + extern (C++) __gshared Type tint16; + extern (C++) __gshared Type tuns16; + extern (C++) __gshared Type tint32; + extern (C++) __gshared Type tuns32; + extern (C++) __gshared Type tint64; + extern (C++) __gshared Type tuns64; + extern (C++) __gshared Type tint128; + extern (C++) __gshared Type tuns128; + extern (C++) __gshared Type tfloat32; + extern (C++) __gshared Type tfloat64; + extern (C++) __gshared Type tfloat80; + extern (C++) __gshared Type timaginary32; + extern (C++) __gshared Type timaginary64; + extern (C++) __gshared Type timaginary80; + extern (C++) __gshared Type tcomplex32; + extern (C++) __gshared Type tcomplex64; + extern (C++) __gshared Type tcomplex80; + extern (C++) __gshared Type tbool; + extern (C++) __gshared Type tchar; + extern (C++) __gshared Type twchar; + extern (C++) __gshared Type tdchar; // Some special types - extern (C++) static __gshared Type tshiftcnt; - extern (C++) static __gshared Type tvoidptr; // void* - extern (C++) static __gshared Type tstring; // immutable(char)[] - extern (C++) static __gshared Type twstring; // immutable(wchar)[] - extern (C++) static __gshared Type tdstring; // immutable(dchar)[] - extern (C++) static __gshared Type tvalist; // va_list alias - extern (C++) static __gshared Type terror; // for error recovery - extern (C++) static __gshared Type tnull; // for null type - - extern (C++) static __gshared Type tsize_t; // matches size_t alias - extern (C++) static __gshared Type tptrdiff_t; // matches ptrdiff_t alias - extern (C++) static __gshared Type thash_t; // matches hash_t alias - - extern (C++) static __gshared ClassDeclaration dtypeinfo; - extern (C++) static __gshared ClassDeclaration typeinfoclass; - extern (C++) static __gshared ClassDeclaration typeinfointerface; - extern (C++) static __gshared ClassDeclaration typeinfostruct; - extern (C++) static __gshared ClassDeclaration typeinfopointer; - extern (C++) static __gshared ClassDeclaration typeinfoarray; - extern (C++) static __gshared ClassDeclaration typeinfostaticarray; - extern (C++) static __gshared ClassDeclaration typeinfoassociativearray; - extern (C++) static __gshared ClassDeclaration typeinfovector; - extern (C++) static __gshared ClassDeclaration typeinfoenum; - extern (C++) static __gshared ClassDeclaration typeinfofunction; - extern (C++) static __gshared ClassDeclaration typeinfodelegate; - extern (C++) static __gshared ClassDeclaration typeinfotypelist; - extern (C++) static __gshared ClassDeclaration typeinfoconst; - extern (C++) static __gshared ClassDeclaration typeinfoinvariant; - extern (C++) static __gshared ClassDeclaration typeinfoshared; - extern (C++) static __gshared ClassDeclaration typeinfowild; - - extern (C++) static __gshared TemplateDeclaration rtinfo; - - extern (C++) static __gshared Type[TMAX] basic; - extern (C++) static __gshared StringTable stringtable; - - extern (C++) static __gshared ubyte[TMAX] sizeTy = () + extern (C++) __gshared Type tshiftcnt; + extern (C++) __gshared Type tvoidptr; // void* + extern (C++) __gshared Type tstring; // immutable(char)[] + extern (C++) __gshared Type twstring; // immutable(wchar)[] + extern (C++) __gshared Type tdstring; // immutable(dchar)[] + extern (C++) __gshared Type tvalist; // va_list alias + extern (C++) __gshared Type terror; // for error recovery + extern (C++) __gshared Type tnull; // for null type + + extern (C++) __gshared Type tsize_t; // matches size_t alias + extern (C++) __gshared Type tptrdiff_t; // matches ptrdiff_t alias + extern (C++) __gshared Type thash_t; // matches hash_t alias + + extern (C++) __gshared ClassDeclaration dtypeinfo; + extern (C++) __gshared ClassDeclaration typeinfoclass; + extern (C++) __gshared ClassDeclaration typeinfointerface; + extern (C++) __gshared ClassDeclaration typeinfostruct; + extern (C++) __gshared ClassDeclaration typeinfopointer; + extern (C++) __gshared ClassDeclaration typeinfoarray; + extern (C++) __gshared ClassDeclaration typeinfostaticarray; + extern (C++) __gshared ClassDeclaration typeinfoassociativearray; + extern (C++) __gshared ClassDeclaration typeinfovector; + extern (C++) __gshared ClassDeclaration typeinfoenum; + extern (C++) __gshared ClassDeclaration typeinfofunction; + extern (C++) __gshared ClassDeclaration typeinfodelegate; + extern (C++) __gshared ClassDeclaration typeinfotypelist; + extern (C++) __gshared ClassDeclaration typeinfoconst; + extern (C++) __gshared ClassDeclaration typeinfoinvariant; + extern (C++) __gshared ClassDeclaration typeinfoshared; + extern (C++) __gshared ClassDeclaration typeinfowild; + + extern (C++) __gshared TemplateDeclaration rtinfo; + + extern (C++) __gshared Type[TMAX] basic; + extern (C++) __gshared StringTable stringtable; + + extern (C++) __gshared ubyte[TMAX] sizeTy = () { ubyte[TMAX] sizeTy = __traits(classInstanceSize, TypeBasic); sizeTy[Tsarray] = __traits(classInstanceSize, TypeSArray); @@ -548,7 +505,7 @@ extern (C++) abstract class Type : RootObject assert(0); } - override bool equals(RootObject o) + override bool equals(RootObject o) const { Type t = cast(Type)o; //printf("Type::equals(%s, %s)\n", toChars(), t.toChars()); @@ -840,7 +797,7 @@ extern (C++) abstract class Type : RootObject stringtable._init(14000); // Set basic types - static __gshared TY* basetab = + __gshared TY* basetab = [ Tvoid, Tint8, @@ -952,12 +909,24 @@ extern (C++) abstract class Type : RootObject final Type trySemantic(const ref Loc loc, Scope* sc) { //printf("+trySemantic(%s) %d\n", toChars(), global.errors); + + // Needed to display any deprecations that were gagged + auto tcopy = this.syntaxCopy(); + uint errors = global.startGagging(); Type t = typeSemantic(this, loc, sc); if (global.endGagging(errors) || t.ty == Terror) // if any errors happened { t = null; } + else + { + // If `typeSemantic` succeeded, there may have been deprecations that + // were gagged due the the `startGagging` above. Run again to display + // those deprecations. https://issues.dlang.org/show_bug.cgi?id=19107 + if (global.gaggedWarnings > 0) + typeSemantic(tcopy, loc, sc); + } //printf("-trySemantic(%s) %d\n", toChars(), global.errors); return t; } @@ -2260,9 +2229,14 @@ extern (C++) abstract class Type : RootObject } /*************************************** - * Return MOD bits matching this type to wild parameter type (tprm). + * Compute MOD bits matching `this` argument type to wild parameter type. + * Params: + * t = corresponding parameter type + * isRef = parameter is `ref` or `out` + * Returns: + * MOD bits */ - ubyte deduceWild(Type t, bool isRef) + MOD deduceWild(Type t, bool isRef) { //printf("Type::deduceWild this = '%s', tprm = '%s'\n", toChars(), tprm.toChars()); if (t.isWild()) @@ -2411,7 +2385,7 @@ extern (C++) abstract class Type : RootObject return mutableOf(); } - ClassDeclaration isClassHandle() + inout(ClassDeclaration) isClassHandle() inout { return null; } @@ -2424,15 +2398,6 @@ extern (C++) abstract class Type : RootObject return STRUCTALIGN_DEFAULT; } - Expression defaultInit(const ref Loc loc) - { - static if (LOGDEFAULTINIT) - { - printf("Type::defaultInit() '%s'\n", toChars()); - } - return null; - } - /*************************************** * Use when we prefer the default initializer to be a literal, * rather than a global immutable variable. @@ -2443,7 +2408,7 @@ extern (C++) abstract class Type : RootObject { printf("Type::defaultInitLiteral() '%s'\n", toChars()); } - return defaultInit(loc); + return defaultInit(this, loc); } // if initializer is 0 @@ -2790,11 +2755,6 @@ extern (C++) final class TypeError : Type return SIZE_INVALID; } - override Expression defaultInit(const ref Loc loc) - { - return new ErrorExp(); - } - override Expression defaultInitLiteral(const ref Loc loc) { return new ErrorExp(); @@ -3068,7 +3028,7 @@ extern (C++) abstract class TypeNext : Type return m; } - override final ubyte deduceWild(Type t, bool isRef) + override final MOD deduceWild(Type t, bool isRef) { if (ty == Tfunction) return 0; @@ -3467,52 +3427,6 @@ extern (C++) final class TypeBasic : Type return MATCH.convert; } - override Expression defaultInit(const ref Loc loc) - { - static if (LOGDEFAULTINIT) - { - printf("TypeBasic::defaultInit() '%s'\n", toChars()); - } - dinteger_t value = 0; - - switch (ty) - { - case Tchar: - value = 0xFF; - break; - - case Twchar: - case Tdchar: - value = 0xFFFF; - break; - - case Timaginary32: - case Timaginary64: - case Timaginary80: - case Tfloat32: - case Tfloat64: - case Tfloat80: - return new RealExp(loc, Target.RealProperties.snan, this); - - case Tcomplex32: - case Tcomplex64: - case Tcomplex80: - { - // Can't use fvalue + I*fvalue (the im part becomes a quiet NaN). - const cvalue = complex_t(Target.RealProperties.snan, Target.RealProperties.snan); - return new ComplexExp(loc, cvalue, this); - } - - case Tvoid: - error(loc, "`void` does not have a default initializer"); - return new ErrorExp(); - - default: - break; - } - return new IntegerExp(loc, value, this); - } - override bool isZeroInit(const ref Loc loc) const { switch (ty) @@ -3638,17 +3552,6 @@ extern (C++) final class TypeVector : Type return MATCH.nomatch; } - override Expression defaultInit(const ref Loc loc) - { - //printf("TypeVector::defaultInit()\n"); - assert(basetype.ty == Tsarray); - Expression e = basetype.defaultInit(loc); - auto ve = new VectorExp(loc, e, this); - ve.type = this; - ve.dim = cast(int)(basetype.size(loc) / elementType().size(loc)); - return ve; - } - override Expression defaultInitLiteral(const ref Loc loc) { //printf("TypeVector::defaultInitLiteral()\n"); @@ -3819,18 +3722,6 @@ extern (C++) final class TypeSArray : TypeArray return MATCH.nomatch; } - override Expression defaultInit(const ref Loc loc) - { - static if (LOGDEFAULTINIT) - { - printf("TypeSArray::defaultInit() '%s'\n", toChars()); - } - if (next.ty == Tvoid) - return tuns8.defaultInit(loc); - else - return next.defaultInit(loc); - } - override Expression defaultInitLiteral(const ref Loc loc) { static if (LOGDEFAULTINIT) @@ -3843,12 +3734,10 @@ extern (C++) final class TypeSArray : TypeArray elementinit = tuns8.defaultInitLiteral(loc); else elementinit = next.defaultInitLiteral(loc); - auto elements = new Expressions(); - elements.setDim(d); + auto elements = new Expressions(d); for (size_t i = 0; i < d; i++) (*elements)[i] = null; - auto ae = new ArrayLiteralExp(Loc.initial, elementinit, elements); - ae.type = this; + auto ae = new ArrayLiteralExp(Loc.initial, this, elementinit, elements); return ae; } @@ -3978,15 +3867,6 @@ extern (C++) final class TypeDArray : TypeArray return Type.implicitConvTo(to); } - override Expression defaultInit(const ref Loc loc) - { - static if (LOGDEFAULTINIT) - { - printf("TypeDArray::defaultInit() '%s'\n", toChars()); - } - return new NullExp(loc, this); - } - override bool hasPointers() const { return true; @@ -4041,15 +3921,6 @@ extern (C++) final class TypeAArray : TypeArray return Target.ptrsize; } - override Expression defaultInit(const ref Loc loc) - { - static if (LOGDEFAULTINIT) - { - printf("TypeAArray::defaultInit() '%s'\n", toChars()); - } - return new NullExp(loc, this); - } - override bool isZeroInit(const ref Loc loc) const { return true; @@ -4233,15 +4104,6 @@ extern (C++) final class TypePointer : TypeNext return true; } - override Expression defaultInit(const ref Loc loc) - { - static if (LOGDEFAULTINIT) - { - printf("TypePointer::defaultInit() '%s'\n", toChars()); - } - return new NullExp(loc, this); - } - override bool isZeroInit(const ref Loc loc) const { return true; @@ -4291,15 +4153,6 @@ extern (C++) final class TypeReference : TypeNext return Target.ptrsize; } - override Expression defaultInit(const ref Loc loc) - { - static if (LOGDEFAULTINIT) - { - printf("TypeReference::defaultInit() '%s'\n", toChars()); - } - return new NullExp(loc, this); - } - override bool isZeroInit(const ref Loc loc) const { return true; @@ -4582,7 +4435,7 @@ extern (C++) final class TypeFunction : TypeNext * Returns: * storage class with STC.scope_ or STC.return_ OR'd in */ - final StorageClass parameterStorageClass(Type tthis, Parameter p) + StorageClass parameterStorageClass(Type tthis, Parameter p) { //printf("parameterStorageClass(p: %s)\n", p.toChars()); auto stc = p.storageClass; @@ -4792,7 +4645,7 @@ extern (C++) final class TypeFunction : TypeNext continue; if (params == parameters) params = parameters.copy(); - (*params)[i] = new Parameter(p.storageClass, t, null, null); + (*params)[i] = new Parameter(p.storageClass, t, null, null, null); } if (next == tret && params == parameters) return this; @@ -5145,12 +4998,6 @@ extern (C++) final class TypeFunction : TypeNext return false; } - override Expression defaultInit(const ref Loc loc) const - { - error(loc, "`function` does not have a default initializer"); - return new ErrorExp(); - } - override void accept(Visitor v) { v.visit(this); @@ -5258,15 +5105,6 @@ extern (C++) final class TypeDelegate : TypeNext return MATCH.nomatch; } - override Expression defaultInit(const ref Loc loc) - { - static if (LOGDEFAULTINIT) - { - printf("TypeDelegate::defaultInit() '%s'\n", toChars()); - } - return new NullExp(loc, this); - } - override bool isZeroInit(const ref Loc loc) const { return true; @@ -5354,328 +5192,6 @@ extern (C++) abstract class TypeQualified : Type return SIZE_INVALID; } - /************************************* - * Resolve a tuple index. - */ - final void resolveTupleIndex(const ref Loc loc, Scope* sc, Dsymbol s, Expression* pe, Type* pt, Dsymbol* ps, RootObject oindex) - { - *pt = null; - *ps = null; - *pe = null; - - auto tup = s.isTupleDeclaration(); - - auto eindex = isExpression(oindex); - auto tindex = isType(oindex); - auto sindex = isDsymbol(oindex); - - if (!tup) - { - // It's really an index expression - if (tindex) - eindex = new TypeExp(loc, tindex); - else if (sindex) - eindex = .resolve(loc, sc, sindex, false); - Expression e = new IndexExp(loc, .resolve(loc, sc, s, false), eindex); - e = e.expressionSemantic(sc); - resolveExp(e, pt, pe, ps); - return; - } - - // Convert oindex to Expression, then try to resolve to constant. - if (tindex) - tindex.resolve(loc, sc, &eindex, &tindex, &sindex); - if (sindex) - eindex = .resolve(loc, sc, sindex, false); - if (!eindex) - { - .error(loc, "index `%s` is not an expression", oindex.toChars()); - *pt = Type.terror; - return; - } - - eindex = semanticLength(sc, tup, eindex); - eindex = eindex.ctfeInterpret(); - if (eindex.op == TOK.error) - { - *pt = Type.terror; - return; - } - const(uinteger_t) d = eindex.toUInteger(); - if (d >= tup.objects.dim) - { - .error(loc, "tuple index `%llu` exceeds length %u", d, tup.objects.dim); - *pt = Type.terror; - return; - } - - RootObject o = (*tup.objects)[cast(size_t)d]; - *pt = isType(o); - *ps = isDsymbol(o); - *pe = isExpression(o); - if (*pt) - *pt = (*pt).typeSemantic(loc, sc); - if (*pe) - resolveExp(*pe, pt, pe, ps); - } - - /************************************* - * Takes an array of Identifiers and figures out if - * it represents a Type or an Expression. - * Output: - * if expression, *pe is set - * if type, *pt is set - */ - final void resolveHelper(const ref Loc loc, Scope* sc, Dsymbol s, Dsymbol scopesym, - Expression* pe, Type* pt, Dsymbol* ps, bool intypeid = false) - { - version (none) - { - printf("TypeQualified::resolveHelper(sc = %p, idents = '%s')\n", sc, toChars()); - if (scopesym) - printf("\tscopesym = '%s'\n", scopesym.toChars()); - } - *pe = null; - *pt = null; - *ps = null; - if (s) - { - //printf("\t1: s = '%s' %p, kind = '%s'\n",s.toChars(), s, s.kind()); - Declaration d = s.isDeclaration(); - if (d && (d.storage_class & STC.templateparameter)) - s = s.toAlias(); - else - { - // check for deprecated or disabled aliases - s.checkDeprecated(loc, sc); - if (d) - d.checkDisabled(loc, sc, true); - } - s = s.toAlias(); - //printf("\t2: s = '%s' %p, kind = '%s'\n",s.toChars(), s, s.kind()); - for (size_t i = 0; i < idents.dim; i++) - { - RootObject id = idents[i]; - if (id.dyncast() == DYNCAST.expression || - id.dyncast() == DYNCAST.type) - { - Type tx; - Expression ex; - Dsymbol sx; - resolveTupleIndex(loc, sc, s, &ex, &tx, &sx, id); - if (sx) - { - s = sx.toAlias(); - continue; - } - if (tx) - ex = new TypeExp(loc, tx); - assert(ex); - - ex = typeToExpressionHelper(this, ex, i + 1); - ex = ex.expressionSemantic(sc); - resolveExp(ex, pt, pe, ps); - return; - } - - Type t = s.getType(); // type symbol, type alias, or type tuple? - uint errorsave = global.errors; - int flags = t is null ? SearchLocalsOnly : IgnorePrivateImports; - - Dsymbol sm = s.searchX(loc, sc, id, flags); - if (sm && !(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc, sm)) - { - .deprecation(loc, "`%s` is not visible from module `%s`", sm.toPrettyChars(), sc._module.toChars()); - // sm = null; - } - if (global.errors != errorsave) - { - *pt = Type.terror; - return; - } - //printf("\t3: s = %p %s %s, sm = %p\n", s, s.kind(), s.toChars(), sm); - if (intypeid && !t && sm && sm.needThis()) - goto L3; - if (VarDeclaration v = s.isVarDeclaration()) - { - if (v.storage_class & (STC.const_ | STC.immutable_ | STC.manifest) || - v.type.isConst() || v.type.isImmutable()) - { - // https://issues.dlang.org/show_bug.cgi?id=13087 - // this.field is not constant always - if (!v.isThisDeclaration()) - goto L3; - } - } - if (!sm) - { - if (!t) - { - if (s.isDeclaration()) // var, func, or tuple declaration? - { - t = s.isDeclaration().type; - if (!t && s.isTupleDeclaration()) // expression tuple? - goto L3; - } - else if (s.isTemplateInstance() || - s.isImport() || s.isPackage() || s.isModule()) - { - goto L3; - } - } - if (t) - { - sm = t.toDsymbol(sc); - if (sm && id.dyncast() == DYNCAST.identifier) - { - sm = sm.search(loc, cast(Identifier)id /*, IgnorePrivateImports*/); - // Deprecated in 2018-01. - // Change to error by deleting the deprecation line and uncommenting - // the above parameter. The error will be issued in Type.getProperty. - // The deprecation is highlighted here to avoid a redundant call to - // ScopeDsymbol.search. - // @@@DEPRECATED_2019-01@@@. - if (sm) - { - .deprecation(loc, "`%s` is not visible from module `%s`", sm.toPrettyChars(), sc._module.toChars()); - goto L2; - } - } - L3: - Expression e; - VarDeclaration v = s.isVarDeclaration(); - FuncDeclaration f = s.isFuncDeclaration(); - if (intypeid || !v && !f) - e = .resolve(loc, sc, s, true); - else - e = new VarExp(loc, s.isDeclaration(), true); - - e = typeToExpressionHelper(this, e, i); - e = e.expressionSemantic(sc); - resolveExp(e, pt, pe, ps); - return; - } - else - { - if (id.dyncast() == DYNCAST.dsymbol) - { - // searchX already handles errors for template instances - assert(global.errors); - } - else - { - assert(id.dyncast() == DYNCAST.identifier); - sm = s.search_correct(cast(Identifier)id); - if (sm) - error(loc, "identifier `%s` of `%s` is not defined, did you mean %s `%s`?", id.toChars(), toChars(), sm.kind(), sm.toChars()); - else - error(loc, "identifier `%s` of `%s` is not defined", id.toChars(), toChars()); - } - *pe = new ErrorExp(); - } - return; - } - L2: - s = sm.toAlias(); - } - - if (auto em = s.isEnumMember()) - { - // It's not a type, it's an expression - *pe = em.getVarExp(loc, sc); - return; - } - if (auto v = s.isVarDeclaration()) - { - /* This is mostly same with DsymbolExp::semantic(), but we cannot use it - * because some variables used in type context need to prevent lowering - * to a literal or contextful expression. For example: - * - * enum a = 1; alias b = a; - * template X(alias e){ alias v = e; } alias x = X!(1); - * struct S { int v; alias w = v; } - * // TypeIdentifier 'a', 'e', and 'v' should be TOK.variable, - * // because getDsymbol() need to work in AliasDeclaration::semantic(). - */ - if (!v.type || - !v.type.deco && v.inuse) - { - if (v.inuse) // https://issues.dlang.org/show_bug.cgi?id=9494 - error(loc, "circular reference to %s `%s`", v.kind(), v.toPrettyChars()); - else - error(loc, "forward reference to %s `%s`", v.kind(), v.toPrettyChars()); - *pt = Type.terror; - return; - } - if (v.type.ty == Terror) - *pt = Type.terror; - else - *pe = new VarExp(loc, v); - return; - } - if (auto fld = s.isFuncLiteralDeclaration()) - { - //printf("'%s' is a function literal\n", fld.toChars()); - *pe = new FuncExp(loc, fld); - *pe = (*pe).expressionSemantic(sc); - return; - } - version (none) - { - if (FuncDeclaration fd = s.isFuncDeclaration()) - { - *pe = new DsymbolExp(loc, fd); - return; - } - } - - L1: - Type t = s.getType(); - if (!t) - { - // If the symbol is an import, try looking inside the import - if (Import si = s.isImport()) - { - s = si.search(loc, s.ident); - if (s && s != si) - goto L1; - s = si; - } - *ps = s; - return; - } - if (t.ty == Tinstance && t != this && !t.deco) - { - if (!(cast(TypeInstance)t).tempinst.errors) - error(loc, "forward reference to `%s`", t.toChars()); - *pt = Type.terror; - return; - } - - if (t.ty == Ttuple) - *pt = t; - else - *pt = t.merge(); - } - if (!s) - { - /* Look for what user might have intended - */ - const p = mutableOf().unSharedOf().toChars(); - auto id = Identifier.idPool(p, cast(uint)strlen(p)); - if (const n = importHint(p)) - error(loc, "`%s` is not defined, perhaps `import %s;` ?", p, n); - else if (auto s2 = sc.search_correct(id)) - error(loc, "undefined identifier `%s`, did you mean %s `%s`?", p, s2.kind(), s2.toChars()); - else if (const q = Scope.search_correct_C(id)) - error(loc, "undefined identifier `%s`, did you mean `%s`?", p, q); - else - error(loc, "undefined identifier `%s`", p); - - *pt = Type.terror; - } - } - override void accept(Visitor v) { v.visit(this); @@ -5934,19 +5450,6 @@ extern (C++) final class TypeStruct : Type return sym.alignment; } - override Expression defaultInit(const ref Loc loc) - { - static if (LOGDEFAULTINIT) - { - printf("TypeStruct::defaultInit() '%s'\n", toChars()); - } - Declaration d = new SymbolDeclaration(sym.loc, sym); - assert(d); - d.type = this; - d.storage_class |= STC.rvalue; // https://issues.dlang.org/show_bug.cgi?id=14398 - return new VarExp(sym.loc, d); - } - /*************************************** * Use when we prefer the default initializer to be a literal, * rather than a global immutable variable. @@ -5961,8 +5464,7 @@ extern (C++) final class TypeStruct : Type if (sym.sizeok != Sizeok.done) return new ErrorExp(); - auto structelems = new Expressions(); - structelems.setDim(sym.fields.dim - sym.isNested()); + auto structelems = new Expressions(sym.fields.dim - sym.isNested()); uint offset = 0; for (size_t j = 0; j < structelems.dim; j++) { @@ -6179,7 +5681,7 @@ extern (C++) final class TypeStruct : Type return MATCH.nomatch; } - override ubyte deduceWild(Type t, bool isRef) + override MOD deduceWild(Type t, bool isRef) { if (ty == t.ty && sym == (cast(TypeStruct)t).sym) return Type.deduceWild(t, isRef); @@ -6340,20 +5842,6 @@ extern (C++) final class TypeEnum : Type return tb.castMod(mod); // retain modifier bits from 'this' } - override Expression defaultInit(const ref Loc loc) - { - static if (LOGDEFAULTINIT) - { - printf("TypeEnum::defaultInit() '%s'\n", toChars()); - } - // Initialize to first member of enum - Expression e = sym.getDefaultValue(loc); - e = e.copy(); - e.loc = loc; - e.type = this; // to deal with const, immutable, etc., variants - return e; - } - override bool isZeroInit(const ref Loc loc) { return sym.getDefaultValue(loc).isBool(false); @@ -6414,7 +5902,7 @@ extern (C++) final class TypeClass : Type return sym; } - override ClassDeclaration isClassHandle() + override inout(ClassDeclaration) isClassHandle() inout { return sym; } @@ -6488,7 +5976,7 @@ extern (C++) final class TypeClass : Type return MATCH.nomatch; } - override ubyte deduceWild(Type t, bool isRef) + override MOD deduceWild(Type t, bool isRef) { ClassDeclaration cd = t.isClassHandle(); if (cd && (sym == cd || cd.isBaseOf(sym, null))) @@ -6514,15 +6002,6 @@ extern (C++) final class TypeClass : Type return this; } - override Expression defaultInit(const ref Loc loc) - { - static if (LOGDEFAULTINIT) - { - printf("TypeClass::defaultInit() '%s'\n", toChars()); - } - return new NullExp(loc, this); - } - override bool isZeroInit(const ref Loc loc) const { return true; @@ -6590,7 +6069,7 @@ extern (C++) final class TypeTuple : Type Expression e = (*exps)[i]; if (e.type.ty == Ttuple) e.error("cannot form tuple of tuples"); - auto arg = new Parameter(STC.undefined_, e.type, null, null); + auto arg = new Parameter(STC.undefined_, e.type, null, null, null); (*arguments)[i] = arg; } } @@ -6616,15 +6095,15 @@ extern (C++) final class TypeTuple : Type { super(Ttuple); arguments = new Parameters(); - arguments.push(new Parameter(0, t1, null, null)); + arguments.push(new Parameter(0, t1, null, null, null)); } extern (D) this(Type t1, Type t2) { super(Ttuple); arguments = new Parameters(); - arguments.push(new Parameter(0, t1, null, null)); - arguments.push(new Parameter(0, t2, null, null)); + arguments.push(new Parameter(0, t1, null, null, null)); + arguments.push(new Parameter(0, t2, null, null, null)); } override const(char)* kind() const @@ -6640,7 +6119,7 @@ extern (C++) final class TypeTuple : Type return t; } - override bool equals(RootObject o) + override bool equals(RootObject o) const { Type t = cast(Type)o; //printf("TypeTuple::equals(%s, %s)\n", toChars(), t.toChars()); @@ -6653,7 +6132,7 @@ extern (C++) final class TypeTuple : Type { for (size_t i = 0; i < tt.arguments.dim; i++) { - Parameter arg1 = (*arguments)[i]; + const Parameter arg1 = (*arguments)[i]; Parameter arg2 = (*tt.arguments)[i]; if (!arg1.type.equals(arg2.type)) return false; @@ -6664,22 +6143,6 @@ extern (C++) final class TypeTuple : Type return false; } - override Expression defaultInit(const ref Loc loc) - { - auto exps = new Expressions(); - exps.setDim(arguments.dim); - for (size_t i = 0; i < arguments.dim; i++) - { - Parameter p = (*arguments)[i]; - assert(p.type); - Expression e = p.type.defaultInitLiteral(loc); - if (e.op == TOK.error) - return e; - (*exps)[i] = e; - } - return new TupleExp(loc, exps); - } - override void accept(Visitor v) { v.visit(this); @@ -6771,11 +6234,6 @@ extern (C++) final class TypeNull : Type return tvoidptr.size(loc); } - override Expression defaultInit(const ref Loc loc) const - { - return new NullExp(Loc.initial, Type.tnull); - } - override void accept(Visitor v) { v.visit(this); @@ -6786,27 +6244,31 @@ extern (C++) final class TypeNull : Type */ extern (C++) final class Parameter : RootObject { + import dmd.attrib : UserAttributeDeclaration; + StorageClass storageClass; Type type; Identifier ident; Expression defaultArg; + UserAttributeDeclaration userAttribDecl; // user defined attributes - extern (D) this(StorageClass storageClass, Type type, Identifier ident, Expression defaultArg) + extern (D) this(StorageClass storageClass, Type type, Identifier ident, Expression defaultArg, UserAttributeDeclaration userAttribDecl) { this.type = type; this.ident = ident; this.storageClass = storageClass; this.defaultArg = defaultArg; + this.userAttribDecl = userAttribDecl; } - static Parameter create(StorageClass storageClass, Type type, Identifier ident, Expression defaultArg) + static Parameter create(StorageClass storageClass, Type type, Identifier ident, Expression defaultArg, UserAttributeDeclaration userAttribDecl) { - return new Parameter(storageClass, type, ident, defaultArg); + return new Parameter(storageClass, type, ident, defaultArg, userAttribDecl); } Parameter syntaxCopy() { - return new Parameter(storageClass, type ? type.syntaxCopy() : null, ident, defaultArg ? defaultArg.syntaxCopy() : null); + return new Parameter(storageClass, type ? type.syntaxCopy() : null, ident, defaultArg ? defaultArg.syntaxCopy() : null, userAttribDecl ? cast(UserAttributeDeclaration) userAttribDecl.syntaxCopy(null) : null); } /**************************************************** @@ -6853,8 +6315,7 @@ extern (C++) final class Parameter : RootObject Parameters* params = null; if (parameters) { - params = new Parameters(); - params.setDim(parameters.dim); + params = new Parameters(parameters.dim); for (size_t i = 0; i < params.dim; i++) (*params)[i] = (*parameters)[i].syntaxCopy(); } @@ -6957,7 +6418,7 @@ extern (C++) final class Parameter : RootObject * true = `this` can be used in place of `p` * false = nope */ - final bool isCovariant(bool returnByRef, const Parameter p) const pure nothrow @nogc @safe + bool isCovariant(bool returnByRef, const Parameter p) const pure nothrow @nogc @safe { enum stc = STC.ref_ | STC.in_ | STC.out_ | STC.lazy_; if ((this.storageClass & stc) != (p.storageClass & stc)) diff --git a/gcc/d/dmd/mtype.h b/gcc/d/dmd/mtype.h index 5281130e0..1acf87d4f 100644 --- a/gcc/d/dmd/mtype.h +++ b/gcc/d/dmd/mtype.h @@ -15,9 +15,9 @@ #pragma once #endif /* __DMC__ */ -#include "root.h" -#include "stringtable.h" -#include "rmem.h" // for d_size_t +#include "root/root.h" +#include "root/stringtable.h" +#include "root/rmem.h" // for d_size_t #include "arraytypes.h" #include "expression.h" @@ -319,7 +319,6 @@ class Type : public RootObject virtual Type *toHeadMutable(); virtual ClassDeclaration *isClassHandle(); virtual structalign_t alignment(); - virtual Expression *defaultInit(const Loc &loc = Loc()); virtual Expression *defaultInitLiteral(const Loc &loc); virtual bool isZeroInit(const Loc &loc = Loc()); // if initializer is 0 Identifier *getTypeInfoIdent(); @@ -348,7 +347,6 @@ class TypeError : public Type Type *syntaxCopy(); d_uns64 size(const Loc &loc); - Expression *defaultInit(const Loc &loc); Expression *defaultInitLiteral(const Loc &loc); void accept(Visitor *v) { v->visit(this); } }; @@ -394,7 +392,6 @@ class TypeBasic : public Type bool isscalar() /*const*/; bool isunsigned() /*const*/; MATCH implicitConvTo(Type *to); - Expression *defaultInit(const Loc &loc); bool isZeroInit(const Loc &loc) /*const*/; // For eliminating dynamic_cast @@ -418,7 +415,6 @@ class TypeVector : public Type bool isunsigned(); bool isBoolean() /*const*/; MATCH implicitConvTo(Type *to); - Expression *defaultInit(const Loc &loc); Expression *defaultInitLiteral(const Loc &loc); TypeBasic *elementType(); bool isZeroInit(const Loc &loc); @@ -447,7 +443,6 @@ class TypeSArray : public TypeArray structalign_t alignment(); MATCH constConv(Type *to); MATCH implicitConvTo(Type *to); - Expression *defaultInit(const Loc &loc); Expression *defaultInitLiteral(const Loc &loc); bool hasPointers(); bool needsDestruction(); @@ -468,7 +463,6 @@ class TypeDArray : public TypeArray bool isZeroInit(const Loc &loc) /*const*/; bool isBoolean() /*const*/; MATCH implicitConvTo(Type *to); - Expression *defaultInit(const Loc &loc); bool hasPointers() /*const*/; void accept(Visitor *v) { v->visit(this); } @@ -485,7 +479,6 @@ class TypeAArray : public TypeArray const char *kind(); Type *syntaxCopy(); d_uns64 size(const Loc &loc); - Expression *defaultInit(const Loc &loc); bool isZeroInit(const Loc &loc) /*const*/; bool isBoolean() /*const*/; bool hasPointers() /*const*/; @@ -505,7 +498,6 @@ class TypePointer : public TypeNext MATCH implicitConvTo(Type *to); MATCH constConv(Type *to); bool isscalar() /*const*/; - Expression *defaultInit(const Loc &loc); bool isZeroInit(const Loc &loc) /*const*/; bool hasPointers() /*const*/; @@ -518,7 +510,6 @@ class TypeReference : public TypeNext const char *kind(); Type *syntaxCopy(); d_uns64 size(const Loc &loc) /*const*/; - Expression *defaultInit(const Loc &loc); bool isZeroInit(const Loc &loc) /*const*/; void accept(Visitor *v) { v->visit(this); } }; @@ -596,7 +587,6 @@ class TypeFunction : public TypeNext MATCH callMatch(Type *tthis, Expressions *toargs, int flag = 0); bool checkRetType(const Loc &loc); - Expression *defaultInit(const Loc &loc) /*const*/; void accept(Visitor *v) { v->visit(this); } }; @@ -612,7 +602,6 @@ class TypeDelegate : public TypeNext d_uns64 size(const Loc &loc) /*const*/; unsigned alignsize() /*const*/; MATCH implicitConvTo(Type *to); - Expression *defaultInit(const Loc &loc); bool isZeroInit(const Loc &loc) /*const*/; bool isBoolean() /*const*/; bool hasPointers() /*const*/; @@ -634,11 +623,6 @@ class TypeQualified : public Type void addIndex(RootObject *expr); d_uns64 size(const Loc &loc); - void resolveTupleIndex(const Loc &loc, Scope *sc, Dsymbol *s, - Expression **pe, Type **pt, Dsymbol **ps, RootObject *oindex); - void resolveHelper(const Loc &loc, Scope *sc, Dsymbol *s, Dsymbol *scopesym, - Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false); - void accept(Visitor *v) { v->visit(this); } }; @@ -715,7 +699,6 @@ class TypeStruct : public Type Type *syntaxCopy(); Dsymbol *toDsymbol(Scope *sc); structalign_t alignment(); - Expression *defaultInit(const Loc &loc); Expression *defaultInitLiteral(const Loc &loc); bool isZeroInit(const Loc &loc) /*const*/; bool isAssignable(); @@ -757,7 +740,6 @@ class TypeEnum : public Type MATCH implicitConvTo(Type *to); MATCH constConv(Type *to); Type *toBasetype(); - Expression *defaultInit(const Loc &loc); bool isZeroInit(const Loc &loc); bool hasPointers(); bool hasVoidInitPointers(); @@ -783,7 +765,6 @@ class TypeClass : public Type MATCH constConv(Type *to); unsigned char deduceWild(Type *t, bool isRef); Type *toHeadMutable(); - Expression *defaultInit(const Loc &loc); bool isZeroInit(const Loc &loc) /*const*/; bool isscope() /*const*/; bool isBoolean() /*const*/; @@ -801,7 +782,6 @@ class TypeTuple : public Type const char *kind(); Type *syntaxCopy(); bool equals(RootObject *o); - Expression *defaultInit(const Loc &loc); void accept(Visitor *v) { v->visit(this); } }; @@ -826,7 +806,6 @@ class TypeNull : public Type bool isBoolean() /*const*/; d_uns64 size(const Loc &loc) /*const*/; - Expression *defaultInit(const Loc &loc) /*const*/; void accept(Visitor *v) { v->visit(this); } }; @@ -842,8 +821,10 @@ class Parameter : public RootObject Type *type; Identifier *ident; Expression *defaultArg; + UserAttributeDeclaration *userAttribDecl; // user defined attributes - static Parameter *create(StorageClass storageClass, Type *type, Identifier *ident, Expression *defaultArg); + static Parameter *create(StorageClass storageClass, Type *type, Identifier *ident, + Expression *defaultArg, UserAttributeDeclaration *userAttribDecl); Parameter *syntaxCopy(); Type *isLazyArray(); // kludge for template.isType() diff --git a/gcc/d/dmd/nspace.d b/gcc/d/dmd/nspace.d index 121eaf7d2..cdc17bca6 100644 --- a/gcc/d/dmd/nspace.d +++ b/gcc/d/dmd/nspace.d @@ -96,7 +96,7 @@ extern (C++) final class Nspace : ScopeDsymbol return Dsymbol.oneMember(ps, ident); } - override final Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly) + override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly) { //printf("%s.Nspace.search('%s')\n", toChars(), ident.toChars()); if (_scope && !symtab) diff --git a/gcc/d/dmd/objc.d b/gcc/d/dmd/objc.d index 22fc59975..a812697ef 100644 --- a/gcc/d/dmd/objc.d +++ b/gcc/d/dmd/objc.d @@ -22,6 +22,7 @@ import dmd.dmodule; import dmd.dscope; import dmd.dstruct; import dmd.expression; +import dmd.expressionsem; import dmd.func; import dmd.globals; import dmd.gluelayer; @@ -34,9 +35,9 @@ import dmd.root.stringtable; struct ObjcSelector { // MARK: Selector - extern (C++) static __gshared StringTable stringtable; - extern (C++) static __gshared StringTable vTableDispatchSelectors; - extern (C++) static __gshared int incnum = 0; + extern (C++) __gshared StringTable stringtable; + extern (C++) __gshared StringTable vTableDispatchSelectors; + extern (C++) __gshared int incnum = 0; const(char)* stringvalue; size_t stringlen; size_t paramCount; @@ -120,7 +121,7 @@ struct ObjcSelector return lookup(cast(const(char)*)buf.data, buf.size, pcount); } - extern (D) final const(char)[] toString() const pure + extern (D) const(char)[] toString() const pure { return stringvalue[0 .. stringlen]; } @@ -208,6 +209,39 @@ extern(C++) abstract class Objc * Returns: the Objective-C runtime metaclass of the given class declaration */ abstract ClassDeclaration getRuntimeMetaclass(ClassDeclaration classDeclaration) const; + + /** + * Issues a compile time error if the `.offsetof`/`.tupleof` property is + * used on a field of an Objective-C class. + * + * To solve the fragile base class problem in Objective-C, fields have a + * dynamic offset instead of a static offset. The compiler outputs a + * statically known offset which later the dynamic loader can update, if + * necessary, when the application is loaded. Due to this behavior it + * doesn't make sense to be able to get the offset of a field at compile + * time, because this offset might not actually be the same at runtime. + * + * To get the offset of a field that is correct at runtime, functionality + * from the Objective-C runtime can be used instead. + * + * Params: + * expression = the `.offsetof`/`.tupleof` expression + * aggregateDeclaration = the aggregate declaration the field of the + * `.offsetof`/`.tupleof` expression belongs to + * type = the type of the receiver of the `.tupleof` expression + * + * See_Also: + * $(LINK2 https://en.wikipedia.org/wiki/Fragile_binary_interface_problem, + * Fragile Binary Interface Problem) + * + * See_Also: + * $(LINK2 https://developer.apple.com/documentation/objectivec/objective_c_runtime, + * Objective-C Runtime) + */ + abstract void checkOffsetof(Expression expression, AggregateDeclaration aggregateDeclaration) const; + + /// ditto + abstract void checkTupleof(Expression expression, TypeClass type) const; } extern(C++) private final class Unsupported : Objc @@ -261,6 +295,16 @@ extern(C++) private final class Unsupported : Objc { assert(0, "Should never be called when Objective-C is not supported"); } + + override void checkOffsetof(Expression expression, AggregateDeclaration aggregateDeclaration) const + { + // noop + } + + override void checkTupleof(Expression expression, TypeClass type) const + { + // noop + } } extern(C++) private final class Supported : Objc @@ -393,6 +437,32 @@ extern(C++) private final class Supported : Objc return classDeclaration.objc.metaclass; } + override void checkOffsetof(Expression expression, AggregateDeclaration aggregateDeclaration) const + { + if (aggregateDeclaration.classKind != ClassKind.objc) + return; + + enum errorMessage = "no property `offsetof` for member `%s` of type " ~ + "`%s`"; + + enum supplementalMessage = "`offsetof` is not available for members " ~ + "of Objective-C classes. Please use the Objective-C runtime instead"; + + expression.error(errorMessage, expression.toChars(), + expression.type.toChars()); + expression.errorSupplemental(supplementalMessage); + } + + override void checkTupleof(Expression expression, TypeClass type) const + { + if (type.sym.classKind != ClassKind.objc) + return; + + expression.error("no property `tupleof` for type `%s`", type.toChars()); + expression.errorSupplemental("`tupleof` is not available for members " ~ + "of Objective-C classes. Please use the Objective-C runtime instead"); + } + extern(D) private bool isUdaSelector(StructDeclaration sd) { if (sd.ident != Id.udaSelector || !sd.parent) @@ -413,7 +483,7 @@ extern(C++) private final class Supported : Objc * classDeclaration = the class/interface declaration to set the metaclass on */ private void setMetaclass(alias newMetaclass, T)(T classDeclaration) - if (is(T == ClassDeclaration) || is(T == InterfaceDeclaration)) +if (is(T == ClassDeclaration) || is(T == InterfaceDeclaration)) { static if (is(T == ClassDeclaration)) enum errorType = "class"; diff --git a/gcc/d/dmd/objc.h b/gcc/d/dmd/objc.h index e2ad0b2a1..48b60af98 100644 --- a/gcc/d/dmd/objc.h +++ b/gcc/d/dmd/objc.h @@ -11,8 +11,8 @@ #ifndef DMD_OBJC_H #define DMD_OBJC_H -#include "root.h" -#include "stringtable.h" +#include "root/root.h" +#include "root/stringtable.h" class AggregateDeclaration; class Identifier; diff --git a/gcc/d/dmd/opover.d b/gcc/d/dmd/opover.d index b37dcd201..682b4011d 100644 --- a/gcc/d/dmd/opover.d +++ b/gcc/d/dmd/opover.d @@ -1097,7 +1097,9 @@ extern (C++) Expression op_overload(Expression e, Scope* sc) if (t.ty != Tstruct) return false; - semanticTypeInfo(sc, t); + if (global.params.useTypeInfo && Type.dtypeinfo) + semanticTypeInfo(sc, t); + return (cast(TypeStruct)t).sym.hasIdentityEquals; } @@ -1565,12 +1567,10 @@ private Expression compare_overload(BinExp e, Scope* sc, Identifier id) * b.opEquals(a) * and see which is better. */ - Expressions args1; - Expressions args2; - args1.setDim(1); + Expressions args1 = Expressions(1); args1[0] = e.e1; expandTuples(&args1); - args2.setDim(1); + Expressions args2 = Expressions(1); args2[0] = e.e2; expandTuples(&args2); Match m; diff --git a/gcc/d/dmd/parse.d b/gcc/d/dmd/parse.d index 772198ec4..bff1394ff 100644 --- a/gcc/d/dmd/parse.d +++ b/gcc/d/dmd/parse.d @@ -235,7 +235,7 @@ final class Parser(AST) : Lexer * Input: * loc location in source file of mixin */ - extern (D) this(Loc loc, AST.Module _module, const(char)[] input, bool doDocComment) + extern (D) this(const ref Loc loc, AST.Module _module, const(char)[] input, bool doDocComment) { super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false); @@ -392,6 +392,23 @@ final class Parser(AST) : Lexer return new AST.Dsymbols(); } + private StorageClass parseDeprecatedAttribute(ref AST.Expression msg) + { + if (peek(&token).value != TOK.leftParentheses) + return AST.STC.deprecated_; + + nextToken(); + check(TOK.leftParentheses); + AST.Expression e = parseAssignExp(); + check(TOK.rightParentheses); + if (msg) + { + error("conflicting storage class `deprecated(%s)` and `deprecated(%s)`", msg.toChars(), e.toChars()); + } + msg = e; + return AST.STC.undefined_; + } + AST.Dsymbols* parseDeclDefs(int once, AST.Dsymbol* pLastDecl = null, PrefixAttributes!AST* pAttrs = null) { AST.Dsymbol lastDecl = null; // used to link unittest to its previous declaration @@ -811,20 +828,12 @@ final class Parser(AST) : Lexer case TOK.deprecated_: { - if (peek(&token).value != TOK.leftParentheses) + AST.Expression e; + if (StorageClass _stc = parseDeprecatedAttribute(pAttrs.depmsg)) { - stc = AST.STC.deprecated_; + stc = _stc; goto Lstc; } - nextToken(); - check(TOK.leftParentheses); - AST.Expression e = parseAssignExp(); - check(TOK.rightParentheses); - if (pAttrs.depmsg) - { - error("conflicting storage class `deprecated(%s)` and `deprecated(%s)`", pAttrs.depmsg.toChars(), e.toChars()); - } - pAttrs.depmsg = e; a = parseBlock(pLastDecl, pAttrs); if (pAttrs.depmsg) { @@ -1419,7 +1428,7 @@ final class Parser(AST) : Lexer // Disallow: // void function() @uda fp; // () @uda { return 1; } - error("user defined attributes cannot appear as postfixes"); + error("user-defined attributes cannot appear as postfixes"); } continue; } @@ -2756,9 +2765,10 @@ final class Parser(AST) : Lexer StorageClass storageClass = 0; StorageClass stc; AST.Expression ae; - + AST.Expressions* udas = null; for (; 1; nextToken()) { + L3: switch (token.value) { case TOK.rightParentheses: @@ -2792,7 +2802,27 @@ final class Parser(AST) : Lexer goto default; stc = AST.STC.wild; goto L2; - + case TOK.at: + { + AST.Expressions* exps = null; + StorageClass stc2 = parseAttribute(&exps); + if (stc2 == AST.STC.property || stc2 == AST.STC.nogc || + stc2 == AST.STC.disable || stc2 == AST.STC.safe || + stc2 == AST.STC.trusted || stc2 == AST.STC.system) + { + error("`@%s` attribute for function parameter is not supported", token.toChars()); + } + else + { + udas = AST.UserAttributeDeclaration.concat(udas, exps); + } + if (token.value == TOK.dotDotDot) + error("variadic parameter cannot have user-defined attributes"); + if (stc2) + nextToken(); + goto L3; + // Don't call nextToken again. + } case TOK.in_: stc = AST.STC.in_; goto L2; @@ -2909,6 +2939,30 @@ final class Parser(AST) : Lexer if (hasdefault) error("default argument expected for `%s`", ai ? ai.toChars() : at.toChars()); } + auto param = new AST.Parameter(storageClass, at, ai, ae, null); + if (udas) + { + auto a = new AST.Dsymbols(); + auto udad = new AST.UserAttributeDeclaration(udas, a); + param.userAttribDecl = udad; + } + if (token.value == TOK.at) + { + AST.Expressions* exps = null; + StorageClass stc2 = parseAttribute(&exps); + if (stc2 == AST.STC.property || stc2 == AST.STC.nogc || + stc2 == AST.STC.disable || stc2 == AST.STC.safe || + stc2 == AST.STC.trusted || stc2 == AST.STC.system) + { + error("`@%s` attribute for function parameter is not supported", token.toChars()); + } + else + { + error("user-defined attributes cannot appear as postfixes", token.toChars()); + } + if (stc2) + nextToken(); + } if (token.value == TOK.dotDotDot) { /* This is: @@ -2917,11 +2971,11 @@ final class Parser(AST) : Lexer if (storageClass & (AST.STC.out_ | AST.STC.ref_)) error("variadic argument cannot be `out` or `ref`"); varargs = 2; - parameters.push(new AST.Parameter(storageClass, at, ai, ae)); + parameters.push(param); nextToken(); break; } - parameters.push(new AST.Parameter(storageClass, at, ai, ae)); + parameters.push(param); if (token.value == TOK.comma) { nextToken(); @@ -2950,7 +3004,7 @@ final class Parser(AST) : Lexer AST.Type memtype; auto loc = token.loc; - //printf("Parser::parseEnum()\n"); + // printf("Parser::parseEnum()\n"); nextToken(); if (token.value == TOK.identifier) { @@ -2977,34 +3031,93 @@ final class Parser(AST) : Lexer nextToken(); else if (token.value == TOK.leftCurly) { + bool isAnonymousEnum = !id; + //printf("enum definition\n"); e.members = new AST.Dsymbols(); nextToken(); const(char)* comment = token.blockComment; while (token.value != TOK.rightCurly) { - /* Can take the following forms: + /* Can take the following forms... * 1. ident * 2. ident = value * 3. type ident = value + * ... prefixed by valid attributes */ loc = token.loc; AST.Type type = null; Identifier ident = null; - Token* tp = peek(&token); - if (token.value == TOK.identifier && (tp.value == TOK.assign || tp.value == TOK.comma || tp.value == TOK.rightCurly)) + + AST.Expressions* udas; + StorageClass stc; + AST.Expression deprecationMessage; + enum attributeErrorMessage = "`%s` is not a valid attribute for enum members"; + while(token.value != TOK.rightCurly + && token.value != TOK.comma + && token.value != TOK.assign) { - ident = token.ident; - type = null; - nextToken(); + switch(token.value) + { + case TOK.at: + if (StorageClass _stc = parseAttribute(&udas)) + { + if (_stc == AST.STC.disable) + stc |= _stc; + else + { + OutBuffer buf; + AST.stcToBuffer(&buf, _stc); + error(attributeErrorMessage, buf.peekString()); + } + nextToken(); + } + break; + case TOK.deprecated_: + if (StorageClass _stc = parseDeprecatedAttribute(deprecationMessage)) + { + stc |= _stc; + nextToken(); + } + break; + case TOK.identifier: + Token* tp = peek(&token); + if (tp.value == TOK.assign || tp.value == TOK.comma || tp.value == TOK.rightCurly) + { + ident = token.ident; + type = null; + nextToken(); + } + else + { + goto default; + } + break; + default: + if (isAnonymousEnum) + { + type = parseType(&ident, null); + if (type == AST.Type.terror) + { + type = null; + nextToken(); + } + } + else + { + error(attributeErrorMessage, token.toChars()); + nextToken(); + } + break; + } } - else + + if (type && type != AST.Type.terror) { - type = parseType(&ident, null); if (!ident) error("no identifier for declarator `%s`", type.toChars()); - if (id || memtype) + if (!isAnonymousEnum) error("type only allowed if anonymous enum and no enum type"); } @@ -3017,11 +3130,22 @@ final class Parser(AST) : Lexer else { value = null; - if (type) + if (type && type != AST.Type.terror && isAnonymousEnum) error("if type, there must be an initializer"); } - auto em = new AST.EnumMember(loc, ident, value, type); + AST.UserAttributeDeclaration uad; + if (udas) + uad = new AST.UserAttributeDeclaration(udas, null); + + AST.DeprecatedDeclaration dd; + if (deprecationMessage) + { + dd = new AST.DeprecatedDeclaration(deprecationMessage, null); + stc |= AST.STC.deprecated_; + } + + auto em = new AST.EnumMember(loc, ident, value, type, stc, uad, dd); e.members.push(em); if (token.value == TOK.rightCurly) @@ -4049,8 +4173,19 @@ final class Parser(AST) : Lexer goto L1; case TOK.enum_: - stc = AST.STC.manifest; - goto L1; + { + Token* t = peek(&token); + if (t.value == TOK.leftCurly || t.value == TOK.colon) + break; + else if (t.value == TOK.identifier) + { + t = peek(t); + if (t.value == TOK.leftCurly || t.value == TOK.colon || t.value == TOK.semicolon) + break; + } + stc = AST.STC.manifest; + goto L1; + } case TOK.at: { @@ -4192,6 +4327,23 @@ final class Parser(AST) : Lexer tpl = parseTemplateParameterList(); check(TOK.assign); + bool hasParsedAttributes; + void parseAttributes() + { + if (hasParsedAttributes) // only parse once + return; + hasParsedAttributes = true; + udas = null; + storage_class = AST.STC.undefined_; + link = linkage; + setAlignment = false; + ealign = null; + parseStorageClasses(storage_class, link, setAlignment, ealign, udas); + } + + if (token.value == TOK.at) + parseAttributes; + AST.Declaration v; if (token.value == TOK.function_ || token.value == TOK.delegate_ || @@ -4210,21 +4362,30 @@ final class Parser(AST) : Lexer // identifier => expression AST.Dsymbol s = parseFunctionLiteral(); + + if (udas !is null) + { + if (storage_class != 0) + error("Cannot put a storage-class in an alias declaration."); + // parseAttributes shouldn't have set these variables + assert(link == linkage && !setAlignment && ealign is null); + auto tpl_ = cast(AST.TemplateDeclaration) s; + assert(tpl_ !is null && tpl_.members.dim == 1); + auto fd = cast(AST.FuncLiteralDeclaration) (*tpl_.members)[0]; + auto tf = cast(AST.TypeFunction) fd.type; + assert(tf.parameters.dim > 0); + auto as = new AST.Dsymbols(); + (*tf.parameters)[0].userAttribDecl = new AST.UserAttributeDeclaration(udas, as); + } + v = new AST.AliasDeclaration(loc, ident, s); } else { + parseAttributes(); // StorageClasses type - - storage_class = AST.STC.undefined_; - link = linkage; - setAlignment = false; - ealign = null; - udas = null; - parseStorageClasses(storage_class, link, setAlignment, ealign, udas); - if (udas) - error("user defined attributes not allowed for `%s` declarations", Token.toChars(tok)); + error("user-defined attributes not allowed for `%s` declarations", Token.toChars(tok)); t = parseType(); v = new AST.AliasDeclaration(loc, ident, t); @@ -4284,7 +4445,23 @@ final class Parser(AST) : Lexer parseStorageClasses(storage_class, link, setAlignment, ealign, udas); - if (token.value == TOK.struct_ || + if (token.value == TOK.enum_) + { + AST.Dsymbol d = parseEnum(); + auto a = new AST.Dsymbols(); + a.push(d); + + if (udas) + { + d = new AST.UserAttributeDeclaration(udas, a); + a = new AST.Dsymbols(); + a.push(d); + } + + addComment(d, comment); + return a; + } + else if (token.value == TOK.struct_ || token.value == TOK.union_ || token.value == TOK.class_ || token.value == TOK.interface_) @@ -4397,7 +4574,7 @@ final class Parser(AST) : Lexer */ if (udas) - error("user defined attributes not allowed for `%s` declarations", Token.toChars(tok)); + error("user-defined attributes not allowed for `%s` declarations", Token.toChars(tok)); if (token.value == TOK.assign) { @@ -4627,7 +4804,7 @@ final class Parser(AST) : Lexer parameters = new AST.Parameters(); Identifier id = Identifier.generateId("__T"); AST.Type t = new AST.TypeIdentifier(loc, id); - parameters.push(new AST.Parameter(0, t, token.ident, null)); + parameters.push(new AST.Parameter(0, t, token.ident, null, null)); tpl = new AST.TemplateParameters(); AST.TemplateParameter tp = new AST.TemplateTypeParameter(loc, id, null, null); @@ -4880,10 +5057,7 @@ final class Parser(AST) : Lexer const(char)* sp = !ident ? "" : " "; const(char)* s = !ident ? "" : ident.toChars(); - if (alt & 1) // contains C-style function pointer syntax - error(loc, "instead of C-style syntax, use D-style `%s%s%s`", t.toChars(), sp, s); - else - dmd.errors.deprecation(loc, "instead of C-style syntax, use D-style syntax `%s%s%s`", t.toChars(), sp, s); + error(loc, "instead of C-style syntax, use D-style `%s%s%s`", t.toChars(), sp, s); } /***************************************** @@ -5022,7 +5196,7 @@ final class Parser(AST) : Lexer if (!ai) error("no identifier for declarator `%s`", at.toChars()); Larg: - auto p = new AST.Parameter(storageClass, at, ai, null); + auto p = new AST.Parameter(storageClass, at, ai, null, null); parameters.push(p); if (token.value == TOK.comma) { @@ -5566,14 +5740,14 @@ final class Parser(AST) : Lexer AST.Type at = null; // infer parameter type nextToken(); check(TOK.assign); - param = new AST.Parameter(storageClass, at, ai, null); + param = new AST.Parameter(storageClass, at, ai, null, null); } else if (isDeclaration(&token, NeedDeclaratorId.must, TOK.assign, null)) { Identifier ai; AST.Type at = parseType(&ai); check(TOK.assign); - param = new AST.Parameter(storageClass, at, ai, null); + param = new AST.Parameter(storageClass, at, ai, null, null); } condition = parseExpression(); @@ -6060,20 +6234,6 @@ final class Parser(AST) : Lexer error("matching `}` expected, not end of file"); goto Lerror; - version (IN_GCC) - { - case TOK.leftParentheses: - case TOK.string_: - // If the first token is a string or '(', parse as extended asm. - if (!toklist) - { - s = parseExtAsm(stc); - statements.push(s); - continue; - } - goto default; - } - default: *ptoklist = Token.alloc(); memcpy(*ptoklist, &token, Token.sizeof); @@ -6137,228 +6297,6 @@ final class Parser(AST) : Lexer return s; } - version (IN_GCC) - { - /*********************************** - * Parse list of extended asm input or output operands. - * ExtAsmOperand: - * [Identifier] StringLiteral (Identifier), ... - */ - int parseExtAsmOperands(AST.Expressions* args, AST.Identifiers* names, AST.Expressions* constraints) - { - int numargs = 0; - - while (1) - { - AST.Expression arg; - Identifier name; - AST.Expression constraint; - - switch (token.value) - { - case TOK.semicolon: - case TOK.colon: - case TOK.endOfFile: - return numargs; - - case TOK.leftBracket: - nextToken(); - if (token.value == TOK.identifier) - { - name = token.ident; - nextToken(); - } - else - { - error("expected identifier after '['"); - return numargs; - } - check(TOK.rightBracket); - goto default; - - default: - constraint = parsePrimaryExp(); - if (constraint.op != TOK.string_) - { - error(constraint.loc, "expected constant string constraint for operand, not '%s'", constraint.toChars()); - goto Lerror; - } - arg = parseAssignExp(); - - args.push(arg); - names.push(name); - constraints.push(constraint); - numargs++; - - if (token.value == TOK.comma) - nextToken(); - break; - - } - } - Lerror: - while (token.value != TOK.rightCurly && - token.value != TOK.semicolon && - token.value != TOK.endOfFile) - nextToken(); - - return numargs; - } - - /*********************************** - * Parse list of extended asm clobbers. - * ExtAsmClobbers: - * StringLiteral, ... - */ - AST.Expressions *parseExtAsmClobbers() - { - AST.Expressions *clobbers; - - while (1) - { - AST.Expression clobber; - - switch (token.value) - { - case TOK.semicolon: - case TOK.colon: - case TOK.endOfFile: - return clobbers; - - case TOK.string_: - clobber = parseAssignExp(); - if (!clobbers) - clobbers = new AST.Expressions(); - clobbers.push(clobber); - - if (token.value == TOK.comma) - nextToken(); - break; - - default: - error("expected constant string constraint for clobber name, not '%s'", token.toChars()); - goto Lerror; - } - } - Lerror: - while (token.value != TOK.rightCurly && - token.value != TOK.semicolon && - token.value != TOK.endOfFile) - nextToken(); - - return clobbers; - } - - /*********************************** - * Parse list of extended asm goto labels. - * ExtAsmGotoLabels: - * Identifier, ... - */ - AST.Identifiers *parseExtAsmGotoLabels() - { - AST.Identifiers *labels; - - while (1) - { - switch (token.value) - { - case TOK.semicolon: - case TOK.endOfFile: - return labels; - - case TOK.identifier: - if (!labels) - labels = new AST.Identifiers(); - labels.push(token.ident); - - if (nextToken() == TOK.comma) - nextToken(); - break; - - default: - error("expected identifier for goto label name, not '%s'", token.toChars()); - goto Lerror; - } - } - Lerror: - while (token.value != TOK.rightCurly && - token.value != TOK.semicolon && - token.value != TOK.endOfFile) - nextToken(); - - return labels; - } - - /*********************************** - * Parse an extended asm statement. - * ExtAsmStatement: - * asm { StringLiterals [ : InputOperands [ : OutputOperands [ : Clobbers [ : GotoLabels ] ] ] ] } - */ - - AST.Statement parseExtAsm(StorageClass stc) - { - AST.Expressions *args; - AST.Identifiers *names; - AST.Expressions *constraints; - int outputargs = 0; - AST.Expressions *clobbers; - AST.Identifiers *labels; - Loc loc = token.loc; - - AST.Expression insn = parseExpression(); - if (token.value == TOK.semicolon) - goto Ldone; - - for (int section = 0; section < 4; ++section) - { - check(TOK.colon); - - final switch (section) - { - case 0: - if (!args) - { - args = new AST.Expressions(); - constraints = new AST.Expressions(); - names = new AST.Identifiers(); - } - outputargs = parseExtAsmOperands(args, names, constraints); - break; - - case 1: - parseExtAsmOperands(args, names, constraints); - break; - - case 2: - clobbers = parseExtAsmClobbers(); - break; - - case 3: - labels = parseExtAsmGotoLabels(); - break; - } - - switch (token.value) - { - case TOK.semicolon: - goto Ldone; - - case TOK.endOfFile: - error("matching '}' expected, not end of file"); - goto Ldone; - - default: - continue; - } - } - Ldone: - check(TOK.semicolon); - - return new AST.ExtAsmStatement(loc, stc, insn, args, names, - constraints, outputargs, clobbers, labels); - } - } - /***************************************** * Parse initializer for variable declaration. */ @@ -6578,7 +6516,9 @@ final class Parser(AST) : Lexer if (token.value == TOK.colon) { nextToken(); - e = AST.initializerToExpression(value); + AST.ExpInitializer expInit = value.isExpInitializer(); + assert(expInit); + e = expInit.exp; value = parseInitializer(); } else @@ -6680,7 +6620,7 @@ final class Parser(AST) : Lexer void checkParens(TOK value, AST.Expression e) { if (precedence[e.op] == PREC.rel && !e.parens) - error(e.loc, "`%s` must be parenthesized when next to operator `%s`", e.toChars(), Token.toChars(value)); + error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`", e.toChars(), Token.toChars(value)); } /// @@ -8054,7 +7994,7 @@ final class Parser(AST) : Lexer if (keys) e = new AST.AssocArrayLiteralExp(loc, keys, values); else - e = new AST.ArrayLiteralExp(loc, values); + e = new AST.ArrayLiteralExp(loc, null, values); break; } case TOK.leftCurly: @@ -8725,6 +8665,13 @@ final class Parser(AST) : Lexer AST.Expression parseAssignExp() { auto e = parseCondExp(); + // require parens for e.g. `t ? a = 1 : b = 2` + // Deprecated in 2018-05. + // @@@DEPRECATED_2.091@@@. + if (e.op == TOK.question && !e.parens && precedence[token.value] == PREC.assign) + dmd.errors.deprecation(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`", + e.toChars(), Token.toChars(token.value)); + const loc = token.loc; switch (token.value) { diff --git a/gcc/d/dmd/parsetimevisitor.d b/gcc/d/dmd/parsetimevisitor.d index a5a63054b..22741a6f7 100644 --- a/gcc/d/dmd/parsetimevisitor.d +++ b/gcc/d/dmd/parsetimevisitor.d @@ -119,10 +119,6 @@ public: void visit(AST.TryFinallyStatement s) { visit(cast(AST.Statement)s); } void visit(AST.ThrowStatement s) { visit(cast(AST.Statement)s); } void visit(AST.AsmStatement s) { visit(cast(AST.Statement)s); } - version (IN_GCC) - { - void visit(AST.ExtAsmStatement s) { visit(cast(AST.Statement)s); } - } void visit(AST.ExpStatement s) { visit(cast(AST.Statement)s); } void visit(AST.CompoundStatement s) { visit(cast(AST.Statement)s); } @@ -130,6 +126,10 @@ public: void visit(AST.CompoundDeclarationStatement s) { visit(cast(AST.CompoundStatement)s); } void visit(AST.CompoundAsmStatement s) { visit(cast(AST.CompoundStatement)s); } + // AsmStatements + void visit(AST.InlineAsmStatement s) { visit(cast(AST.AsmStatement)s); } + void visit(AST.GccAsmStatement s) { visit(cast(AST.AsmStatement)s); } + //========================================================================================= // Types void visit(AST.TypeBasic t) { visit(cast(AST.Type)t); } diff --git a/gcc/d/dmd/root/array.d b/gcc/d/dmd/root/array.d index 460578b4e..ac4979473 100644 --- a/gcc/d/dmd/root/array.d +++ b/gcc/d/dmd/root/array.d @@ -27,6 +27,16 @@ private: T[SMALLARRAYCAP] smallarray; // inline storage for small arrays public: + /******************* + * Params: + * dim = initial length of array + */ + this(size_t dim) + { + reserve(dim); + this.dim = dim; + } + @disable this(this); ~this() nothrow diff --git a/gcc/d/dmd/array.h b/gcc/d/dmd/root/array.h similarity index 100% rename from gcc/d/dmd/array.h rename to gcc/d/dmd/root/array.h diff --git a/gcc/d/dmd/ctfloat.h b/gcc/d/dmd/root/ctfloat.h similarity index 70% rename from gcc/d/dmd/ctfloat.h rename to gcc/d/dmd/root/ctfloat.h index d83ee6545..2ca8cd6e3 100644 --- a/gcc/d/dmd/ctfloat.h +++ b/gcc/d/dmd/root/ctfloat.h @@ -18,9 +18,6 @@ typedef longdouble real_t; // Compile-time floating-point helper struct CTFloat { - static bool yl2x_supported; - static bool yl2xp1_supported; - static void yl2x(const real_t *x, const real_t *y, real_t *res); static void yl2xp1(const real_t *x, const real_t *y, real_t *res); @@ -31,6 +28,23 @@ struct CTFloat static real_t fabs(real_t x); static real_t ldexp(real_t n, int exp); + static real_t round(real_t x); + static real_t floor(real_t x); + static real_t ceil(real_t x); + static real_t trunc(real_t x); + static real_t log(real_t x); + static real_t log2(real_t x); + static real_t log10(real_t x); + static real_t pow(real_t x, real_t y); + static real_t expm1(real_t x); + static real_t exp2(real_t x); + + static real_t fmin(real_t x, real_t y); + static real_t fmax(real_t x, real_t y); + static real_t copysign(real_t x, real_t s); + + static real_t fma(real_t x, real_t y, real_t z); + static bool isIdentical(real_t a, real_t b); static bool isNaN(real_t r); static bool isSNaN(real_t r); @@ -46,6 +60,8 @@ struct CTFloat static real_t one; static real_t minusone; static real_t half; + + static void initialize(); }; #endif diff --git a/gcc/d/dmd/root/dcompat.h b/gcc/d/dmd/root/dcompat.h new file mode 100644 index 000000000..e5026ead7 --- /dev/null +++ b/gcc/d/dmd/root/dcompat.h @@ -0,0 +1,25 @@ +/* Compiler implementation of the D programming language + * Copyright (C) 1999-2018 by The D Language Foundation, All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/dlang/dmd/blob/master/src/mars.h + */ + +#ifndef DMD_ROOT_DCOMPAT_H +#define DMD_ROOT_DCOMPAT_H + +#ifdef __DMC__ +#pragma once +#endif + +/// Represents a D [ ] array +template +struct DArray +{ + size_t length; + T *ptr; +}; + +#endif /* DMD_ROOT_DCOMPAT_H */ diff --git a/gcc/d/dmd/root/file.d b/gcc/d/dmd/root/file.d index 7a13792b5..c86cb3e94 100644 --- a/gcc/d/dmd/root/file.d +++ b/gcc/d/dmd/root/file.d @@ -66,11 +66,16 @@ nothrow: } } - extern (C++) const(char)* toChars() pure + extern (C++) const(char)* toChars() const pure nothrow @safe { return name.toChars(); } + const(char)[] toString() const nothrow pure @safe + { + return name.toString(); + } + /************************************* */ extern (C++) bool read() diff --git a/gcc/d/dmd/file.h b/gcc/d/dmd/root/file.h similarity index 97% rename from gcc/d/dmd/file.h rename to gcc/d/dmd/root/file.h index d3beda0ad..2fbb28334 100644 --- a/gcc/d/dmd/file.h +++ b/gcc/d/dmd/root/file.h @@ -33,7 +33,7 @@ struct File static File *create(const char *); ~File(); - const char *toChars(); + const char *toChars() const; /* Read file, return true if error */ diff --git a/gcc/d/dmd/root/filename.d b/gcc/d/dmd/root/filename.d index bdc3449cc..69b56e051 100644 --- a/gcc/d/dmd/root/filename.d +++ b/gcc/d/dmd/root/filename.d @@ -631,71 +631,77 @@ nothrow: } } + /** + Ensure that the provided path exists + + Accepts a path to either a file or a directory. + In the former case, the basepath (path to the containing directory) + will be checked for existence, and created if it does not exists. + In the later case, the directory pointed to will be checked for existence + and created if needed. + + Params: + path = a path to a file or a directory + + Returns: + `true` if the directory exists or was successfully created + */ extern (C++) static bool ensurePathExists(const(char)* path) { //printf("FileName::ensurePathExists(%s)\n", path ? path : ""); - if (path && *path) + if (!path || !(*path)) + return true; + if (exists(path)) + return true; + + // We were provided with a file name + // We need to call ourselves recursively to ensure parent dir exist + const(char)* p = FileName.path(path); + if (*p) { - if (!exists(path)) + version (Windows) { - const(char)* p = FileName.path(path); - if (*p) + const len = strlen(path); + const plen = strlen(p); + // Note: Windows filename comparison should be case-insensitive, + // however p is a subslice of path so we don't need it + if (len == plen || + (len > 2 && path[1] == ':' && path[2 .. len] == p[0 .. plen])) { - version (Windows) - { - size_t len = strlen(path); - if ((len > 2 && p[-1] == ':' && strcmp(path + 2, p) == 0) || len == strlen(p)) - { - mem.xfree(cast(void*)p); - return 0; - } - } - bool r = ensurePathExists(p); mem.xfree(cast(void*)p); - - if (r) - return r; - } - version (Windows) - { - char sep = '\\'; - } - else version (Posix) - { - char sep = '/'; - } - if (path[strlen(path) - 1] != sep) - { - version (Windows) - { - int r = _mkdir(path); - } - version (Posix) - { - int r = mkdir(path, (7 << 6) | (7 << 3) | 7); - } - if (r) - { - /* Don't error out if another instance of dmd just created - * this directory - */ - version (Windows) - { - // see core.sys.windows.winerror - the reason it's not imported here is because - // the autotester's dmd is too old and doesn't have that module - enum ERROR_ALREADY_EXISTS = 183; - - if (GetLastError() != ERROR_ALREADY_EXISTS) - return true; - } - version (Posix) - { - if (errno != EEXIST) - return true; - } - } + return true; } } + const r = ensurePathExists(p); + mem.xfree(cast(void*)p); + + if (!r) + return r; + } + + version (Windows) + const r = _mkdir(path); + version (Posix) + { + errno = 0; + const r = mkdir(path, (7 << 6) | (7 << 3) | 7); + } + + if (r == 0) + return true; + + // Don't error out if another instance of dmd just created + // this directory + version (Windows) + { + import core.sys.windows.winerror : ERROR_ALREADY_EXISTS; + if (GetLastError() == ERROR_ALREADY_EXISTS) + return true; + } + version (Posix) + { + if (errno == EEXIST) + return true; } return false; @@ -761,10 +767,15 @@ nothrow: mem.xfree(cast(void*)str); } - extern (C++) const(char)* toChars() const pure + extern (C++) const(char)* toChars() const pure nothrow @safe { return str; } + + const(char)[] toString() const pure nothrow @trusted + { + return str ? str[0 .. strlen(str)] : null; + } } version(Windows) diff --git a/gcc/d/dmd/filename.h b/gcc/d/dmd/root/filename.h similarity index 100% rename from gcc/d/dmd/filename.h rename to gcc/d/dmd/root/filename.h diff --git a/gcc/d/dmd/object.h b/gcc/d/dmd/root/object.h similarity index 87% rename from gcc/d/dmd/object.h rename to gcc/d/dmd/root/object.h index f20433cca..e3067e91d 100644 --- a/gcc/d/dmd/object.h +++ b/gcc/d/dmd/root/object.h @@ -16,6 +16,7 @@ #pragma once #endif +#include "dcompat.h" #include typedef size_t hash_t; @@ -32,6 +33,7 @@ enum DYNCAST DYNCAST_TUPLE, DYNCAST_PARAMETER, DYNCAST_STATEMENT, + DYNCAST_TEMPLATEPARAMETER, }; /* @@ -56,6 +58,9 @@ class RootObject virtual void print(); virtual const char *toChars(); + /// This function is `extern(D)` and should not be called from C++, + /// as the ABI does not match on some platforms + virtual DArray toString(); virtual void toBuffer(OutBuffer *buf); /** diff --git a/gcc/d/dmd/root/outbuffer.d b/gcc/d/dmd/root/outbuffer.d index 8bbe1e43d..1695f889e 100644 --- a/gcc/d/dmd/root/outbuffer.d +++ b/gcc/d/dmd/root/outbuffer.d @@ -288,7 +288,7 @@ struct OutBuffer offset += nbytes; } - extern (C++) void vprintf(const(char)* format, va_list args) /*nothrow*/ + extern (C++) void vprintf(const(char)* format, va_list args) nothrow { int count; if (doindent) @@ -334,7 +334,7 @@ struct OutBuffer offset += count; } - extern (C++) void printf(const(char)* format, ...) /*nothrow*/ + extern (C++) void printf(const(char)* format, ...) nothrow { va_list ap; va_start(ap, format); @@ -459,4 +459,3 @@ char[] unsignedToTempString(ulong value, char[] buf, uint radix = 10) @safe } while (value); return buf[i .. $]; } - diff --git a/gcc/d/dmd/outbuffer.h b/gcc/d/dmd/root/outbuffer.h similarity index 100% rename from gcc/d/dmd/outbuffer.h rename to gcc/d/dmd/root/outbuffer.h diff --git a/gcc/d/dmd/port.h b/gcc/d/dmd/root/port.h similarity index 100% rename from gcc/d/dmd/port.h rename to gcc/d/dmd/root/port.h diff --git a/gcc/d/dmd/root/rmem.d b/gcc/d/dmd/root/rmem.d index 9b4cd3dfc..6237c419d 100644 --- a/gcc/d/dmd/root/rmem.d +++ b/gcc/d/dmd/root/rmem.d @@ -242,18 +242,68 @@ else } } } +/** +Makes a null-terminated copy of the given string on newly allocated memory. +The null-terminator won't be part of the returned string slice. It will be +at position `n` where `n` is the length of the input string. + +Params: + s = string to copy -extern (D) static char[] xarraydup(const(char)[] s) nothrow +Returns: A null-terminated copy of the input array. +*/ +extern (D) char[] xarraydup(const(char)[] s) nothrow { - if (s) - { - auto p = cast(char*)mem.xmalloc(s.length + 1); - char[] a = p[0 .. s.length]; - a[] = s[0 .. s.length]; - p[s.length] = 0; // preserve 0 terminator semantics - return a; - } - return null; + if (!s) + return null; + + auto p = cast(char*)mem.xmalloc(s.length + 1); + char[] a = p[0 .. s.length]; + a[] = s[0 .. s.length]; + p[s.length] = 0; // preserve 0 terminator semantics + return a; } +/// +unittest +{ + auto s1 = "foo"; + auto s2 = s1.xarraydup; + s2[0] = 'b'; + assert(s1 == "foo"); + assert(s2 == "boo"); + assert(*(s2.ptr + s2.length) == '\0'); + string sEmpty; + assert(sEmpty.xarraydup is null); +} + +/** +Makes a copy of the given array on newly allocated memory. +Params: + s = array to copy + +Returns: A copy of the input array. +*/ +extern (D) T[] arraydup(T)(const scope T[] s) nothrow +{ + if (!s) + return null; + + const dim = s.length; + auto p = (cast(T*)mem.xmalloc(T.sizeof * dim))[0 .. dim]; + p[] = s; + return p; +} + +/// +unittest +{ + auto s1 = [0, 1, 2]; + auto s2 = s1.arraydup; + s2[0] = 4; + assert(s1 == [0, 1, 2]); + assert(s2 == [4, 1, 2]); + string sEmpty; + assert(sEmpty.arraydup is null); +} diff --git a/gcc/d/dmd/rmem.h b/gcc/d/dmd/root/rmem.h similarity index 100% rename from gcc/d/dmd/rmem.h rename to gcc/d/dmd/root/rmem.h diff --git a/gcc/d/dmd/root.h b/gcc/d/dmd/root/root.h similarity index 100% rename from gcc/d/dmd/root.h rename to gcc/d/dmd/root/root.h diff --git a/gcc/d/dmd/root/rootobject.d b/gcc/d/dmd/root/rootobject.d index 77a28db6d..7ece1b698 100644 --- a/gcc/d/dmd/root/rootobject.d +++ b/gcc/d/dmd/root/rootobject.d @@ -30,6 +30,7 @@ enum DYNCAST : int parameter, statement, condition, + templateparameter, } /*********************************************************** @@ -61,6 +62,14 @@ extern (C++) class RootObject assert(0); } + /// + extern(D) const(char)[] toString() + { + import core.stdc.string : strlen; + auto p = this.toChars(); + return p[0 .. strlen(p)]; + } + void toBuffer(OutBuffer* buf) nothrow pure @nogc @safe { assert(0); diff --git a/gcc/d/dmd/root/speller.d b/gcc/d/dmd/root/speller.d index db3ecc646..b66efeb6a 100644 --- a/gcc/d/dmd/root/speller.d +++ b/gcc/d/dmd/root/speller.d @@ -218,7 +218,7 @@ void* speller(const(char)* seed, scope dg_speller_t dg, const(char)* charset) unittest { - static __gshared const(char)*** cases = + __gshared const(char)*** cases = [ ["hello", "hell", "y"], ["hello", "hel", "y"], diff --git a/gcc/d/dmd/root/stringtable.d b/gcc/d/dmd/root/stringtable.d index 0452e791f..c18496ef0 100644 --- a/gcc/d/dmd/root/stringtable.d +++ b/gcc/d/dmd/root/stringtable.d @@ -103,6 +103,18 @@ public: pools = null; } + /** + Looks up the given string in the string table and returns its associated + value. + + Params: + s = the string to look up + length = the length of $(D_PARAM s) + str = the string to look up + + Returns: the string's associated value, or `null` if the string doesn't + exist in the string table + */ extern (C++) inout(StringValue)* lookup(const(char)* s, size_t length) inout nothrow pure { const(hash_t) hash = calcHash(s, length); @@ -111,6 +123,26 @@ public: return getValue(table[i].vptr); } + /// ditto + inout(StringValue)* lookup(const(char)[] str) inout nothrow pure + { + return lookup(str.ptr, str.length); + } + + /** + Inserts the given string and the given associated value into the string + table. + + Params: + s = the string to insert + length = the length of $(D_PARAM s) + ptrvalue = the value to associate with the inserted string + str = the string to insert + value = the value to associate with the inserted string + + Returns: the newly inserted value, or `null` if the string table already + contains the string + */ extern (C++) StringValue* insert(const(char)* s, size_t length, void* ptrvalue) nothrow { const(hash_t) hash = calcHash(s, length); @@ -128,6 +160,12 @@ public: return getValue(table[i].vptr); } + /// ditto + StringValue* insert(const(char)[] str, void* value) nothrow + { + return insert(str.ptr, str.length, value); + } + extern (C++) StringValue* update(const(char)* s, size_t length) nothrow { const(hash_t) hash = calcHash(s, length); @@ -146,6 +184,11 @@ public: return getValue(table[i].vptr); } + StringValue* update(const(char)[] name) nothrow + { + return update(name.ptr, name.length); + } + /******************************** * Walk the contents of the string table, * calling fp for each entry. @@ -168,11 +211,6 @@ public: return 0; } - StringValue* update(const(char)[] name) nothrow - { - return update(name.ptr, name.length); - } - private: nothrow: uint allocValue(const(char)* s, size_t length, void* ptrvalue) diff --git a/gcc/d/dmd/stringtable.h b/gcc/d/dmd/root/stringtable.h similarity index 100% rename from gcc/d/dmd/stringtable.h rename to gcc/d/dmd/root/stringtable.h diff --git a/gcc/d/dmd/semantic2.d b/gcc/d/dmd/semantic2.d index 38458ed17..768422dd4 100644 --- a/gcc/d/dmd/semantic2.d +++ b/gcc/d/dmd/semantic2.d @@ -353,7 +353,6 @@ private extern(C++) final class Semantic2Visitor : Visitor if (fd.semanticRun >= PASS.semantic2done) return; assert(fd.semanticRun <= PASS.semantic2); - fd.semanticRun = PASS.semantic2; //printf("FuncDeclaration::semantic2 [%s] fd0 = %s %s\n", loc.toChars(), toChars(), type.toChars()); @@ -446,13 +445,25 @@ private extern(C++) final class Semantic2Visitor : Visitor return 0; }); } - objc.setSelector(fd, sc); objc.validateSelector(fd); if (ClassDeclaration cd = fd.parent.isClassDeclaration()) { objc.checkLinkage(fd); } + if (!fd.type || fd.type.ty != Tfunction) + return; + TypeFunction f = cast(TypeFunction) fd.type; + if (!f.parameters) + return; + size_t nparams = Parameter.dim(f.parameters); + //semantic for parameters' UDAs + foreach (i; 0..nparams) + { + Parameter param = Parameter.getNth(f.parameters, i); + if (param && param.userAttribDecl) + param.userAttribDecl.semantic2(sc); + } } override void visit(Import i) diff --git a/gcc/d/dmd/semantic3.d b/gcc/d/dmd/semantic3.d index 6f9ea01c8..94cbfeca5 100644 --- a/gcc/d/dmd/semantic3.d +++ b/gcc/d/dmd/semantic3.d @@ -466,6 +466,8 @@ private extern(C++) final class Semantic3Visitor : Visitor funcdecl.parameters.push(v); funcdecl.localsymtab.insert(v); v.parent = funcdecl; + if (fparam.userAttribDecl) + v.userAttribDecl = fparam.userAttribDecl; } } @@ -482,8 +484,7 @@ private extern(C++) final class Semantic3Visitor : Visitor { TypeTuple t = cast(TypeTuple)fparam.type; size_t dim = Parameter.dim(t.arguments); - auto exps = new Objects(); - exps.setDim(dim); + auto exps = new Objects(dim); for (size_t j = 0; j < dim; j++) { Parameter narg = Parameter.getNth(t.arguments, j); @@ -673,7 +674,7 @@ private extern(C++) final class Semantic3Visitor : Visitor else { bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested()); - if (mustInit && !(sc2.ctorflow.fieldinit[i] & CSX.this_ctor)) + if (mustInit && !(sc2.ctorflow.fieldinit[i].csx & CSX.this_ctor)) { funcdecl.error("field `%s` must be initialized but skipped", v.toChars()); } @@ -1178,6 +1179,37 @@ private extern(C++) final class Semantic3Visitor : Visitor funcdecl.flags &= ~FUNCFLAG.inferScope; + // Eliminate maybescope's + { + // Create and fill array[] with maybe candidates from the `this` and the parameters + VarDeclaration[] array = void; + + VarDeclaration[10] tmp = void; + size_t dim = (funcdecl.vthis !is null) + (funcdecl.parameters ? funcdecl.parameters.dim : 0); + if (dim <= tmp.length) + array = tmp[0 .. dim]; + else + { + auto ptr = cast(VarDeclaration*)mem.xmalloc(dim * VarDeclaration.sizeof); + array = ptr[0 .. dim]; + } + size_t n = 0; + if (funcdecl.vthis) + array[n++] = funcdecl.vthis; + if (funcdecl.parameters) + { + foreach (v; *funcdecl.parameters) + { + array[n++] = v; + } + } + + eliminateMaybeScopes(array[0 .. n]); + + if (dim > tmp.length) + mem.xfree(array.ptr); + } + // Infer STC.scope_ if (funcdecl.parameters && !funcdecl.errors) { diff --git a/gcc/d/dmd/sideeffect.d b/gcc/d/dmd/sideeffect.d index 5fd061ef4..5cfdffad1 100644 --- a/gcc/d/dmd/sideeffect.d +++ b/gcc/d/dmd/sideeffect.d @@ -123,6 +123,9 @@ extern (C++) int callSideEffectLevel(Type t) assert(t.ty == Tfunction); tf = cast(TypeFunction)t; } + if (!tf.isnothrow) // function can throw + return 0; + tf.purityLevel(); PURE purity = tf.purity; if (t.ty == Tdelegate && purity > PURE.weak) @@ -132,13 +135,11 @@ extern (C++) int callSideEffectLevel(Type t) else if (!tf.isImmutable()) purity = PURE.const_; } - if (tf.isnothrow) - { - if (purity == PURE.strong) - return 2; - if (purity == PURE.const_) - return 1; - } + + if (purity == PURE.strong) + return 2; + if (purity == PURE.const_) + return 1; return 0; } @@ -364,13 +365,11 @@ extern (C++) bool discardValue(Expression e) */ VarDeclaration copyToTemp(StorageClass stc, const char* name, Expression e) { - assert(name && name[0] == '_' && name[1] == '_'); - auto id = Identifier.generateId(name); - auto ez = new ExpInitializer(e.loc, e); - auto vd = new VarDeclaration(e.loc, e.type, id, ez); - vd.storage_class = stc; - vd.storage_class |= STC.temp; - vd.storage_class |= STC.ctfe; // temporary is always CTFEable + assert(name[0] == '_' && name[1] == '_'); + auto vd = new VarDeclaration(e.loc, e.type, + Identifier.generateId(name), + new ExpInitializer(e.loc, e)); + vd.storage_class = stc | STC.temp | STC.ctfe; // temporary is always CTFEable return vd; } @@ -395,16 +394,11 @@ Expression extractSideEffect(Scope* sc, const char* name, return e; auto vd = copyToTemp(0, name, e); - if (e.isLvalue()) - vd.storage_class |= STC.ref_; - else - vd.storage_class |= STC.rvalue; + vd.storage_class |= e.isLvalue() ? STC.ref_ : STC.rvalue; - Expression de = new DeclarationExp(vd.loc, vd); - Expression ve = new VarExp(vd.loc, vd); - de = de.expressionSemantic(sc); - ve = ve.expressionSemantic(sc); + e0 = Expression.combine(e0, new DeclarationExp(vd.loc, vd) + .expressionSemantic(sc)); - e0 = Expression.combine(e0, de); - return ve; + return new VarExp(vd.loc, vd) + .expressionSemantic(sc); } diff --git a/gcc/d/dmd/statement.d b/gcc/d/dmd/statement.d index 0a07e2878..af4322093 100644 --- a/gcc/d/dmd/statement.d +++ b/gcc/d/dmd/statement.d @@ -240,14 +240,6 @@ extern (C++) abstract class Statement : RootObject { stop = true; } - - version (IN_GCC) - { - override void visit(ExtAsmStatement s) - { - stop = true; - } - } } scope ComeFrom cf = new ComeFrom(); @@ -940,8 +932,7 @@ extern (C++) final class CompoundDeclarationStatement : CompoundStatement override Statement syntaxCopy() { - auto a = new Statements(); - a.setDim(statements.dim); + auto a = new Statements(statements.dim); foreach (i, s; *statements) { (*a)[i] = s ? s.syntaxCopy() : null; @@ -971,8 +962,7 @@ extern (C++) final class UnrolledLoopStatement : Statement override Statement syntaxCopy() { - auto a = new Statements(); - a.setDim(statements.dim); + auto a = new Statements(statements.dim); foreach (i, s; *statements) { (*a)[i] = s ? s.syntaxCopy() : null; @@ -1102,8 +1092,7 @@ extern (C++) final class ForwardingStatement : Statement { return a; } - auto b = new Statements(); - b.setDim(a.dim); + auto b = new Statements(a.dim); foreach (i, s; *a) { (*b)[i] = s ? new ForwardingStatement(s.loc, sym, s) : null; @@ -1599,7 +1588,7 @@ extern (C++) final class SwitchStatement : Statement * Returns: * true if error */ - final bool checkLabel() + bool checkLabel() { /* * Checks the scope of a label for existing variable declaration. @@ -1974,8 +1963,7 @@ extern (C++) final class TryCatchStatement : Statement override Statement syntaxCopy() { - auto a = new Catches(); - a.setDim(catches.dim); + auto a = new Catches(catches.dim); foreach (i, c; *catches) { (*a)[i] = c.syntaxCopy(); @@ -2226,7 +2214,7 @@ extern (C++) final class GotoStatement : Statement return new GotoStatement(loc, ident); } - final bool checkLabel() + bool checkLabel() { if (!label.statement) { @@ -2391,9 +2379,32 @@ extern (C++) final class LabelDsymbol : Dsymbol /*********************************************************** */ -extern (C++) final class AsmStatement : Statement +extern (C++) class AsmStatement : Statement { Token* tokens; + + extern (D) this(const ref Loc loc, Token* tokens) + { + super(loc); + this.tokens = tokens; + } + + override Statement syntaxCopy() + { + return new AsmStatement(loc, tokens); + } + + override void accept(Visitor v) + { + v.visit(this); + } +} + +/*********************************************************** + * https://dlang.org/spec/iasm.html + */ +extern (C++) final class InlineAsmStatement : AsmStatement +{ code* asmcode; uint asmalign; // alignment of this statement uint regs; // mask of registers modified (must match regm_t in back end) @@ -2402,13 +2413,12 @@ extern (C++) final class AsmStatement : Statement extern (D) this(const ref Loc loc, Token* tokens) { - super(loc); - this.tokens = tokens; + super(loc, tokens); } override Statement syntaxCopy() { - return new AsmStatement(loc, tokens); + return new InlineAsmStatement(loc, tokens); } override void accept(Visitor v) @@ -2418,51 +2428,34 @@ extern (C++) final class AsmStatement : Statement } /*********************************************************** + * https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html + * Assembler instructions with D expression operands. */ -version (IN_GCC) +extern (C++) final class GccAsmStatement : AsmStatement { - extern (C++) final class ExtAsmStatement : Statement - { - StorageClass stc; - Expression insn; - Expressions *args; - Identifiers *names; - Expressions *constraints; // Array of StringExp's - uint outputargs; - Expressions *clobbers; // Array of StringExp's - Identifiers *labels; - GotoStatements *gotos; - - extern (D) this(Loc loc, StorageClass stc, Expression insn, - Expressions *args, Identifiers *names, - Expressions *constraints, int outputargs, - Expressions *clobbers, Identifiers *labels) - { - super(loc); - this.insn = insn; - this.args = args; - this.names = names; - this.constraints = constraints; - this.outputargs = outputargs; - this.clobbers = clobbers; - this.labels = labels; - this.gotos = null; - } + StorageClass stc; // attributes of the asm {} block + Expression insn; // string expression that is the template for assembler code + Expressions* args; // input and output operands of the statement + uint outputargs; // of the operands in 'args', the number of output operands + Identifiers* names; // list of symbolic names for the operands + Expressions* constraints; // list of string constants specifying constraints on operands + Expressions* clobbers; // list of string constants specifying clobbers and scratch registers + Identifiers* labels; // list of goto labels + GotoStatements* gotos; // of the goto labels, the equivalent statements they represent - override Statement syntaxCopy() - { - Expressions *c_args = Expression.arraySyntaxCopy(args); - Expressions *c_constraints = Expression.arraySyntaxCopy(constraints); - Expressions *c_clobbers = Expression.arraySyntaxCopy(clobbers); + extern (D) this(const ref Loc loc, Token* tokens) + { + super(loc, tokens); + } - return new ExtAsmStatement(loc, stc, insn.syntaxCopy(), c_args, names, - c_constraints, outputargs, c_clobbers, labels); - } + override Statement syntaxCopy() + { + return new GccAsmStatement(loc, tokens); + } - override void accept(Visitor v) - { - v.visit(this); - } + override void accept(Visitor v) + { + v.visit(this); } } @@ -2481,8 +2474,7 @@ extern (C++) final class CompoundAsmStatement : CompoundStatement override CompoundAsmStatement syntaxCopy() { - auto a = new Statements(); - a.setDim(statements.dim); + auto a = new Statements(statements.dim); foreach (i, s; *statements) { (*a)[i] = s ? s.syntaxCopy() : null; @@ -2515,8 +2507,7 @@ extern (C++) final class ImportStatement : Statement override Statement syntaxCopy() { - auto m = new Dsymbols(); - m.setDim(imports.dim); + auto m = new Dsymbols(imports.dim); foreach (i, s; *imports) { (*m)[i] = s.syntaxCopy(null); diff --git a/gcc/d/dmd/statement.h b/gcc/d/dmd/statement.h index 126e40f82..4f93eed8a 100644 --- a/gcc/d/dmd/statement.h +++ b/gcc/d/dmd/statement.h @@ -15,7 +15,7 @@ #pragma once #endif /* __DMC__ */ -#include "root.h" +#include "root/root.h" #include "arraytypes.h" #include "dsymbol.h" @@ -677,6 +677,14 @@ class AsmStatement : public Statement { public: Token *tokens; + + Statement *syntaxCopy(); + void accept(Visitor *v) { v->visit(this); } +}; + +class InlineAsmStatement : public AsmStatement +{ +public: code *asmcode; unsigned asmalign; // alignment of this statement unsigned regs; // mask of registers modified (must match regm_t in back end) @@ -684,7 +692,24 @@ class AsmStatement : public Statement bool naked; // true if function is to be naked Statement *syntaxCopy(); + void accept(Visitor *v) { v->visit(this); } +}; + +// A GCC asm statement - assembler instructions with D expression operands +class GccAsmStatement : public AsmStatement +{ +public: + StorageClass stc; // attributes of the asm {} block + Expression *insn; // string expression that is the template for assembler code + Expressions *args; // input and output operands of the statement + unsigned outputargs; // of the operands in 'args', the number of output operands + Identifiers *names; // list of symbolic names for the operands + Expressions *constraints; // list of string constants specifying constraints on operands + Expressions *clobbers; // list of string constants specifying clobbers and scratch registers + Identifiers *labels; // list of goto labels + GotoStatements *gotos; // of the goto labels, the equivalent statements they represent + Statement *syntaxCopy(); void accept(Visitor *v) { v->visit(this); } }; @@ -710,27 +735,4 @@ class ImportStatement : public Statement void accept(Visitor *v) { v->visit(this); } }; -#ifdef IN_GCC - -// Assembler instructions with D expression operands -class ExtAsmStatement : public Statement -{ -public: - StorageClass stc; - Expression *insn; - Expressions *args; - Identifiers *names; - Expressions *constraints; // Array of StringExp's - unsigned outputargs; - Expressions *clobbers; // Array of StringExp's - Identifiers *labels; - GotoStatements *gotos; - - Statement *syntaxCopy(); - - void accept(Visitor *v) { v->visit(this); } -}; - -#endif - #endif /* DMD_STATEMENT_H */ diff --git a/gcc/d/dmd/statementsem.d b/gcc/d/dmd/statementsem.d index aac2dd4cd..b834bc387 100644 --- a/gcc/d/dmd/statementsem.d +++ b/gcc/d/dmd/statementsem.d @@ -915,32 +915,35 @@ private extern (C++) final class StatementSemanticVisitor : Visitor return returnEarly(); } } - else if (!needExpansion) + else { - // Declare value - if (!declareVariable(p.storageClass, p.type, p.ident, e, t)) + if (!needExpansion) { - return returnEarly(); + // Declare value + if (!declareVariable(p.storageClass, p.type, p.ident, e, t)) + { + return returnEarly(); + } } - } - else - { // expand tuples into multiple `static foreach` variables. - assert(e && !t); - auto ident = Identifier.generateId("__value"); - declareVariable(0, e.type, ident, e, null); - import dmd.cond: StaticForeach; - auto field = Identifier.idPool(StaticForeach.tupleFieldName.ptr,StaticForeach.tupleFieldName.length); - Expression access = new DotIdExp(loc, e, field); - access = expressionSemantic(access, sc); - if (!tuple) return returnEarly(); - //printf("%s\n",tuple.toChars()); - foreach (l; 0 .. dim) - { - auto cp = (*fs.parameters)[l]; - Expression init_ = new IndexExp(loc, access, new IntegerExp(loc, l, Type.tsize_t)); - init_ = init_.expressionSemantic(sc); - assert(init_.type); - declareVariable(p.storageClass, init_.type, cp.ident, init_, null); + else + { // expand tuples into multiple `static foreach` variables. + assert(e && !t); + auto ident = Identifier.generateId("__value"); + declareVariable(0, e.type, ident, e, null); + import dmd.cond: StaticForeach; + auto field = Identifier.idPool(StaticForeach.tupleFieldName.ptr,StaticForeach.tupleFieldName.length); + Expression access = new DotIdExp(loc, e, field); + access = expressionSemantic(access, sc); + if (!tuple) return returnEarly(); + //printf("%s\n",tuple.toChars()); + foreach (l; 0 .. dim) + { + auto cp = (*fs.parameters)[l]; + Expression init_ = new IndexExp(loc, access, new IntegerExp(loc, l, Type.tsize_t)); + init_ = init_.expressionSemantic(sc); + assert(init_.type); + declareVariable(p.storageClass, init_.type, cp.ident, init_, null); + } } } @@ -1659,22 +1662,22 @@ private extern (C++) final class StatementSemanticVisitor : Visitor * extern(C) int _aaApply2(void*, in size_t, int delegate(void*, void*)) * _aaApply2(aggr, keysize, flde) */ - static __gshared const(char)** name = ["_aaApply", "_aaApply2"]; - static __gshared FuncDeclaration* fdapply = [null, null]; - static __gshared TypeDelegate* fldeTy = [null, null]; + __gshared const(char)** name = ["_aaApply", "_aaApply2"]; + __gshared FuncDeclaration* fdapply = [null, null]; + __gshared TypeDelegate* fldeTy = [null, null]; ubyte i = (dim == 2 ? 1 : 0); if (!fdapply[i]) { auto params = new Parameters(); - params.push(new Parameter(0, Type.tvoid.pointerTo(), null, null)); - params.push(new Parameter(STC.in_, Type.tsize_t, null, null)); + params.push(new Parameter(0, Type.tvoid.pointerTo(), null, null, null)); + params.push(new Parameter(STC.in_, Type.tsize_t, null, null, null)); auto dgparams = new Parameters(); - dgparams.push(new Parameter(0, Type.tvoidptr, null, null)); + dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); if (dim == 2) - dgparams.push(new Parameter(0, Type.tvoidptr, null, null)); + dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); fldeTy[i] = new TypeDelegate(new TypeFunction(dgparams, Type.tint32, 0, LINK.d)); - params.push(new Parameter(0, fldeTy[i], null, null)); + params.push(new Parameter(0, fldeTy[i], null, null, null)); fdapply[i] = FuncDeclaration.genCfunc(params, Type.tint32, name[i]); } @@ -1703,7 +1706,7 @@ private extern (C++) final class StatementSemanticVisitor : Visitor /* Call: * _aApply(aggr, flde) */ - static __gshared const(char)** fntab = + __gshared const(char)** fntab = [ "cc", "cw", "cd", "wc", "cc", "wd", @@ -1737,13 +1740,13 @@ private extern (C++) final class StatementSemanticVisitor : Visitor FuncDeclaration fdapply; TypeDelegate dgty; auto params = new Parameters(); - params.push(new Parameter(STC.in_, tn.arrayOf(), null, null)); + params.push(new Parameter(STC.in_, tn.arrayOf(), null, null, null)); auto dgparams = new Parameters(); - dgparams.push(new Parameter(0, Type.tvoidptr, null, null)); + dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); if (dim == 2) - dgparams.push(new Parameter(0, Type.tvoidptr, null, null)); + dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); dgty = new TypeDelegate(new TypeFunction(dgparams, Type.tint32, 0, LINK.d)); - params.push(new Parameter(0, dgty, null, null)); + params.push(new Parameter(0, dgty, null, null, null)); fdapply = FuncDeclaration.genCfunc(params, Type.tint32, fdname.ptr); if (tab.ty == Tsarray) @@ -1907,13 +1910,13 @@ else LcopyArg: id = Identifier.generateId("__applyArg", cast(int)i); - Initializer ie = new ExpInitializer(Loc.initial, new IdentifierExp(Loc.initial, id)); - auto v = new VarDeclaration(Loc.initial, p.type, p.ident, ie); + Initializer ie = new ExpInitializer(fs.loc, new IdentifierExp(fs.loc, id)); + auto v = new VarDeclaration(fs.loc, p.type, p.ident, ie); v.storage_class |= STC.temp; - Statement s = new ExpStatement(Loc.initial, v); + Statement s = new ExpStatement(fs.loc, v); fs._body = new CompoundStatement(fs.loc, s, fs._body); } - params.push(new Parameter(stc, p.type, id, null)); + params.push(new Parameter(stc, p.type, id, null, null)); } // https://issues.dlang.org/show_bug.cgi?id=13840 // Throwable nested function inside nothrow function is acceptable. @@ -1921,11 +1924,13 @@ else auto tf = new TypeFunction(params, Type.tint32, 0, LINK.d, stc); fs.cases = new Statements(); fs.gotos = new ScopeStatements(); - auto fld = new FuncLiteralDeclaration(fs.loc, Loc.initial, tf, TOK.delegate_, fs); + auto fld = new FuncLiteralDeclaration(fs.loc, fs.endloc, tf, TOK.delegate_, fs); fld.fbody = fs._body; Expression flde = new FuncExp(fs.loc, fld); flde = flde.expressionSemantic(sc); fld.tookAddressOf = 0; + if (flde.op == TOK.error) + return null; return cast(FuncExp)flde; } @@ -2361,7 +2366,7 @@ else fd.inlining = inlining; } } - else + else if (!global.params.ignoreUnsupportedPragmas) { ps.error("unrecognized `pragma(%s)`", ps.ident.toChars()); return setError(); @@ -3234,7 +3239,7 @@ else foreach (i, v; ad.fields) { bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested()); - if (mustInit && !(sc.ctorflow.fieldinit[i] & CSX.this_ctor)) + if (mustInit && !(sc.ctorflow.fieldinit[i].csx & CSX.this_ctor)) { rs.error("an earlier `return` statement skips field `%s` initialization", v.toChars()); errors = true; @@ -3519,7 +3524,7 @@ else cs.push(new ExpStatement(ss.loc, tmp)); auto args = new Parameters(); - args.push(new Parameter(0, ClassDeclaration.object.type, null, null)); + args.push(new Parameter(0, ClassDeclaration.object.type, null, null, null)); FuncDeclaration fdenter = FuncDeclaration.genCfunc(args, Type.tvoid, Id.monitorenter); Expression e = new CallExp(ss.loc, fdenter, new VarExp(ss.loc, tmp)); @@ -3561,7 +3566,7 @@ else cs.push(new ExpStatement(ss.loc, v)); auto args = new Parameters(); - args.push(new Parameter(0, t.pointerTo(), null, null)); + args.push(new Parameter(0, t.pointerTo(), null, null, null)); FuncDeclaration fdenter = FuncDeclaration.genCfunc(args, Type.tvoid, Id.criticalenter, STC.nothrow_); Expression int0 = new IntegerExp(ss.loc, dinteger_t(0), Type.tint8); @@ -4021,73 +4026,11 @@ else result = asmSemantic(s, sc); } - version (IN_GCC) - { - override void visit(ExtAsmStatement s) - { - // Fold the instruction template string. - s.insn = s.insn.expressionSemantic(sc); - s.insn.ctfeInterpret(); - - if (s.insn.op != TOK.string_ || (cast(StringExp) s.insn).sz != 1) - s.insn.error("instruction template must be a constant char string"); - - if (s.labels && s.outputargs) - s.error("extended asm statements with labels cannot have output constraints"); - - // Analyse all input and output operands. - if (s.args) - { - for (size_t i = 0; i < s.args.dim; i++) - { - Expression e = (*s.args)[i]; - e = e.expressionSemantic(sc); - // Check argument is a valid lvalue/rvalue. - if (i < s.outputargs) - e = e.modifiableLvalue(sc, null); - else if (e.checkValue()) - e = new ErrorExp(); - (*s.args)[i] = e; - - e = (*s.constraints)[i]; - e = e.expressionSemantic(sc); - assert(e.op == TOK.string_ && (cast(StringExp) e).sz == 1); - (*s.constraints)[i] = e; - } - } - - // Analyse all clobbers. - if (s.clobbers) - { - for (size_t i = 0; i < s.clobbers.dim; i++) - { - Expression e = (*s.clobbers)[i]; - e = e.expressionSemantic(sc); - assert(e.op == TOK.string_ && (cast(StringExp) e).sz == 1); - (*s.clobbers)[i] = e; - } - } - - // Analyse all goto labels. - if (s.labels) - { - for (size_t i = 0; i < s.labels.dim; i++) - { - Identifier ident = (*s.labels)[i]; - GotoStatement gs = new GotoStatement(s.loc, ident); - if (!s.gotos) - s.gotos = new GotoStatements(); - s.gotos.push(gs); - gs.statementSemantic(sc); - } - } - - result = s; - } - } - override void visit(CompoundAsmStatement cas) { + // Apply postfix attributes of the asm block to each statement. + sc = sc.push(); + sc.stc |= cas.stc; foreach (ref s; *cas.statements) { s = s ? s.statementSemantic(sc) : null; @@ -4103,6 +4046,7 @@ else if (!(cas.stc & (STC.trusted | STC.safe)) && sc.func.setUnsafe()) cas.error("`asm` statement is assumed to be `@system` - mark it with `@trusted` if it is not"); + sc.pop(); result = cas; } diff --git a/gcc/d/dmd/target.d b/gcc/d/dmd/target.d index 00af8f60a..3dd5c7461 100644 --- a/gcc/d/dmd/target.d +++ b/gcc/d/dmd/target.d @@ -57,8 +57,6 @@ struct Target // C++ ABI bool reverseCppOverloads; /// set if overloaded functions are grouped and in reverse order (such as in dmc and cl) 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 } diff --git a/gcc/d/dmd/target.h b/gcc/d/dmd/target.h index 3259d38c6..16ffffce5 100644 --- a/gcc/d/dmd/target.h +++ b/gcc/d/dmd/target.h @@ -43,8 +43,6 @@ struct Target // C++ ABI static bool reverseCppOverloads; // with dmc and cl, overloaded functions are grouped and in reverse order 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 template diff --git a/gcc/d/dmd/template.h b/gcc/d/dmd/template.h index 4f70bebb0..306e505fe 100644 --- a/gcc/d/dmd/template.h +++ b/gcc/d/dmd/template.h @@ -15,7 +15,7 @@ #pragma once #endif /* __DMC__ */ -#include "root.h" +#include "root/root.h" #include "arraytypes.h" #include "dsymbol.h" @@ -122,7 +122,7 @@ class TemplateDeclaration : public ScopeDsymbol * For this-parameter: * template Foo(this ident) */ -class TemplateParameter +class TemplateParameter : public RootObject { public: Loc loc; @@ -360,8 +360,9 @@ Dsymbol *isDsymbol(RootObject *o); Type *isType(RootObject *o); Tuple *isTuple(RootObject *o); Parameter *isParameter(RootObject *o); +TemplateParameter *isTemplateParameter(RootObject *o); bool arrayObjectIsError(const Objects *args); -bool isError(const RootObject *o); +bool isError(const RootObject *const o); Type *getType(RootObject *o); Dsymbol *getDsymbol(RootObject *o); diff --git a/gcc/d/dmd/tokens.d b/gcc/d/dmd/tokens.d index c380f975e..e3ae8d42d 100644 --- a/gcc/d/dmd/tokens.d +++ b/gcc/d/dmd/tokens.d @@ -283,6 +283,7 @@ enum TOK : int interval = 231, voidExpression, cantExpression, + showCtfeContext, objcClassReference, @@ -693,6 +694,7 @@ extern (C++) struct Token TOK.interval: "interval", TOK.voidExpression: "voidexp", TOK.cantExpression: "cantexp", + TOK.showCtfeContext : "showCtfeContext", TOK.objcClassReference: "class", ]; @@ -757,7 +759,7 @@ extern (C++) struct Token * ptr = pointer to string * length = length of string */ - final void setString(const(char)* ptr, size_t length) + void setString(const(char)* ptr, size_t length) { auto s = cast(char*)mem.xmalloc(length + 1); memcpy(s, ptr, length); @@ -772,7 +774,7 @@ extern (C++) struct Token * Params: * buf = string (not zero terminated) */ - final void setString(const ref OutBuffer buf) + void setString(const ref OutBuffer buf) { setString(cast(const(char)*)buf.data, buf.offset); } @@ -780,7 +782,7 @@ extern (C++) struct Token /**** * Set to empty string */ - final void setString() + void setString() { ustring = ""; len = 0; diff --git a/gcc/d/dmd/tokens.h b/gcc/d/dmd/tokens.h index ebe7e2a96..5e45014f0 100644 --- a/gcc/d/dmd/tokens.h +++ b/gcc/d/dmd/tokens.h @@ -15,7 +15,7 @@ #pragma once #endif /* __DMC__ */ -#include "port.h" +#include "root/port.h" #include "mars.h" class Identifier; @@ -182,6 +182,7 @@ enum TOK TOKinterval, TOKvoidexp, TOKcantexp, + TOKshowctfecontext, TOKobjc_class_reference, diff --git a/gcc/d/dmd/traits.d b/gcc/d/dmd/traits.d index 1b5c51fb2..e796a035e 100644 --- a/gcc/d/dmd/traits.d +++ b/gcc/d/dmd/traits.d @@ -17,9 +17,11 @@ import core.stdc.string; import dmd.aggregate; import dmd.arraytypes; +import dmd.attrib; import dmd.canthrow; import dmd.dclass; import dmd.declaration; +import dmd.denum; import dmd.dscope; import dmd.dsymbol; import dmd.dsymbolsem; @@ -409,8 +411,7 @@ extern (C++) Expression pointerBitmap(TraitsExp e) foreach (d_uns64 i; 0 .. data.dim) exps.push(new IntegerExp(e.loc, data[cast(size_t)i], Type.tsize_t)); - auto ale = new ArrayLiteralExp(e.loc, exps); - ale.type = Type.tsize_t.sarrayOf(data.dim + 1); + auto ale = new ArrayLiteralExp(e.loc, Type.tsize_t.sarrayOf(data.dim + 1), exps); return ale; } @@ -424,7 +425,8 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) if (e.ident != Id.compiles && e.ident != Id.isSame && e.ident != Id.identifier && - e.ident != Id.getProtection) + e.ident != Id.getProtection && + e.ident != Id.getAttributes) { if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 1)) return new ErrorExp(); @@ -510,6 +512,8 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) auto y = s.isDeclaration(); static if (is(T == FuncDeclaration)) auto y = s.isFuncDeclaration(); + static if (is(T == EnumMember)) + auto y = s.isEnumMember(); if (!y || !fp(y)) return False(); @@ -521,6 +525,7 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) alias isDsymX = isX!Dsymbol; alias isDeclX = isX!Declaration; alias isFuncX = isX!FuncDeclaration; + alias isEnumMemX = isX!EnumMember; if (e.ident == Id.isArithmetic) { @@ -634,7 +639,7 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) if (dim != 1) return dimError(1); - return isFuncX(f => f.isDisabled()); + return isDeclX(f => f.isDisabled()); } if (e.ident == Id.isAbstractFunction) { @@ -715,8 +720,12 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) Identifier id; if (auto po = isParameter(o)) { + if (!po.ident) + { + e.error("argument `%s` has no identifier", po.type.toChars()); + return new ErrorExp(); + } id = po.ident; - assert(id); } else { @@ -997,7 +1006,7 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) // if the parent is an interface declaration // we must check for functions with the same signature // in different inherited interfaces - if (sym.isInterfaceDeclaration()) + if (sym && sym.isInterfaceDeclaration()) insertInterfaceInheritedFunction(fd, e); else exps.push(e); @@ -1074,12 +1083,34 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) } if (e.ident == Id.getAttributes) { + /* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that + * a symbol should not be folded to a constant. + * Bit 1 means don't convert Parameter to Type if Parameter has an identifier + */ + if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 3)) + return new ErrorExp(); + if (dim != 1) return dimError(1); auto o = (*e.args)[0]; + auto po = isParameter(o); auto s = getDsymbolWithoutExpCtx(o); - if (!s) + UserAttributeDeclaration udad = null; + if (po) + { + udad = po.userAttribDecl; + } + else if (s) + { + if (s.isImport()) + { + s = s.isImport().mod; + } + //printf("getAttributes %s, attrs = %p, scope = %p\n", s.toChars(), s.userAttribDecl, s.scope); + udad = s.userAttribDecl; + } + else { version (none) { @@ -1093,13 +1124,7 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) e.error("first argument is not a symbol"); return new ErrorExp(); } - if (auto imp = s.isImport()) - { - s = imp.mod; - } - //printf("getAttributes %s, attrs = %p, scope = %p\n", s.toChars(), s.userAttribDecl, s.scope); - auto udad = s.userAttribDecl; auto exps = udad ? udad.getAttributes() : new Expressions(); auto tup = new TupleExp(e.loc, exps); return tup.expressionSemantic(sc); diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d index 75b655860..2be55343b 100644 --- a/gcc/d/dmd/typesem.d +++ b/gcc/d/dmd/typesem.d @@ -26,6 +26,7 @@ import dmd.dcast; import dmd.dclass; import dmd.declaration; import dmd.denum; +import dmd.dimport; import dmd.dmangle; import dmd.dscope; import dmd.dstruct; @@ -40,10 +41,12 @@ import dmd.globals; import dmd.hdrgen; import dmd.id; import dmd.identifier; +import dmd.imphint; import dmd.init; import dmd.initsem; import dmd.visitor; import dmd.mtype; +import dmd.objc; import dmd.opover; import dmd.root.ctfloat; import dmd.root.rmem; @@ -56,6 +59,367 @@ import dmd.target; import dmd.tokens; import dmd.typesem; +/************************** + * This evaluates exp while setting length to be the number + * of elements in the tuple t. + */ +private Expression semanticLength(Scope* sc, Type t, Expression exp) +{ + if (t.ty == Ttuple) + { + ScopeDsymbol sym = new ArrayScopeSymbol(sc, cast(TypeTuple)t); + sym.parent = sc.scopesym; + sc = sc.push(sym); + sc = sc.startCTFE(); + exp = exp.expressionSemantic(sc); + sc = sc.endCTFE(); + sc.pop(); + } + else + { + sc = sc.startCTFE(); + exp = exp.expressionSemantic(sc); + sc = sc.endCTFE(); + } + return exp; +} + +private Expression semanticLength(Scope* sc, TupleDeclaration tup, Expression exp) +{ + ScopeDsymbol sym = new ArrayScopeSymbol(sc, tup); + sym.parent = sc.scopesym; + + sc = sc.push(sym); + sc = sc.startCTFE(); + exp = exp.expressionSemantic(sc); + sc = sc.endCTFE(); + sc.pop(); + + return exp; +} + +/************************************* + * Resolve a tuple index. + */ +private void resolveTupleIndex(TypeQualified mt, const ref Loc loc, Scope* sc, Dsymbol s, Expression* pe, Type* pt, Dsymbol* ps, RootObject oindex) +{ + *pt = null; + *ps = null; + *pe = null; + + auto tup = s.isTupleDeclaration(); + + auto eindex = isExpression(oindex); + auto tindex = isType(oindex); + auto sindex = isDsymbol(oindex); + + if (!tup) + { + // It's really an index expression + if (tindex) + eindex = new TypeExp(loc, tindex); + else if (sindex) + eindex = dmd.expressionsem.resolve(loc, sc, sindex, false); + Expression e = new IndexExp(loc, dmd.expressionsem.resolve(loc, sc, s, false), eindex); + e = e.expressionSemantic(sc); + mt.resolveExp(e, pt, pe, ps); + return; + } + + // Convert oindex to Expression, then try to resolve to constant. + if (tindex) + tindex.resolve(loc, sc, &eindex, &tindex, &sindex); + if (sindex) + eindex = dmd.expressionsem.resolve(loc, sc, sindex, false); + if (!eindex) + { + .error(loc, "index `%s` is not an expression", oindex.toChars()); + *pt = Type.terror; + return; + } + + eindex = semanticLength(sc, tup, eindex); + eindex = eindex.ctfeInterpret(); + if (eindex.op == TOK.error) + { + *pt = Type.terror; + return; + } + const(uinteger_t) d = eindex.toUInteger(); + if (d >= tup.objects.dim) + { + .error(loc, "tuple index `%llu` exceeds length %u", d, tup.objects.dim); + *pt = Type.terror; + return; + } + + RootObject o = (*tup.objects)[cast(size_t)d]; + *pt = isType(o); + *ps = isDsymbol(o); + *pe = isExpression(o); + if (*pt) + *pt = (*pt).typeSemantic(loc, sc); + if (*pe) + mt.resolveExp(*pe, pt, pe, ps); +} + +/************************************* + * Takes an array of Identifiers and figures out if + * it represents a Type or an Expression. + * Output: + * if expression, *pe is set + * if type, *pt is set + */ +private void resolveHelper(TypeQualified mt, const ref Loc loc, Scope* sc, Dsymbol s, Dsymbol scopesym, + Expression* pe, Type* pt, Dsymbol* ps, bool intypeid = false) +{ + version (none) + { + printf("TypeQualified::resolveHelper(sc = %p, idents = '%s')\n", sc, mt.toChars()); + if (scopesym) + printf("\tscopesym = '%s'\n", scopesym.toChars()); + } + *pe = null; + *pt = null; + *ps = null; + if (s) + { + //printf("\t1: s = '%s' %p, kind = '%s'\n",s.toChars(), s, s.kind()); + Declaration d = s.isDeclaration(); + if (d && (d.storage_class & STC.templateparameter)) + s = s.toAlias(); + else + { + // check for deprecated or disabled aliases + s.checkDeprecated(loc, sc); + if (d) + d.checkDisabled(loc, sc, true); + } + s = s.toAlias(); + //printf("\t2: s = '%s' %p, kind = '%s'\n",s.toChars(), s, s.kind()); + for (size_t i = 0; i < mt.idents.dim; i++) + { + RootObject id = mt.idents[i]; + if (id.dyncast() == DYNCAST.expression || + id.dyncast() == DYNCAST.type) + { + Type tx; + Expression ex; + Dsymbol sx; + resolveTupleIndex(mt, loc, sc, s, &ex, &tx, &sx, id); + if (sx) + { + s = sx.toAlias(); + continue; + } + if (tx) + ex = new TypeExp(loc, tx); + assert(ex); + + ex = typeToExpressionHelper(mt, ex, i + 1); + ex = ex.expressionSemantic(sc); + mt.resolveExp(ex, pt, pe, ps); + return; + } + + Type t = s.getType(); // type symbol, type alias, or type tuple? + uint errorsave = global.errors; + int flags = t is null ? SearchLocalsOnly : IgnorePrivateImports; + + Dsymbol sm = s.searchX(loc, sc, id, flags); + if (sm && !(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc, sm)) + { + .deprecation(loc, "`%s` is not visible from module `%s`", sm.toPrettyChars(), sc._module.toChars()); + // sm = null; + } + if (global.errors != errorsave) + { + *pt = Type.terror; + return; + } + //printf("\t3: s = %p %s %s, sm = %p\n", s, s.kind(), s.toChars(), sm); + if (intypeid && !t && sm && sm.needThis()) + goto L3; + if (VarDeclaration v = s.isVarDeclaration()) + { + if (v.storage_class & (STC.const_ | STC.immutable_ | STC.manifest) || + v.type.isConst() || v.type.isImmutable()) + { + // https://issues.dlang.org/show_bug.cgi?id=13087 + // this.field is not constant always + if (!v.isThisDeclaration()) + goto L3; + } + } + if (!sm) + { + if (!t) + { + if (s.isDeclaration()) // var, func, or tuple declaration? + { + t = s.isDeclaration().type; + if (!t && s.isTupleDeclaration()) // expression tuple? + goto L3; + } + else if (s.isTemplateInstance() || + s.isImport() || s.isPackage() || s.isModule()) + { + goto L3; + } + } + if (t) + { + sm = t.toDsymbol(sc); + if (sm && id.dyncast() == DYNCAST.identifier) + { + sm = sm.search(loc, cast(Identifier)id /*, IgnorePrivateImports*/); + // Deprecated in 2018-01. + // Change to error by deleting the deprecation line and uncommenting + // the above parameter. The error will be issued in Type.getProperty. + // The deprecation is highlighted here to avoid a redundant call to + // ScopeDsymbol.search. + // @@@DEPRECATED_2019-01@@@. + if (sm) + { + .deprecation(loc, "`%s` is not visible from module `%s`", sm.toPrettyChars(), sc._module.toChars()); + goto L2; + } + } + L3: + Expression e; + VarDeclaration v = s.isVarDeclaration(); + FuncDeclaration f = s.isFuncDeclaration(); + if (intypeid || !v && !f) + e = dmd.expressionsem.resolve(loc, sc, s, true); + else + e = new VarExp(loc, s.isDeclaration(), true); + + e = typeToExpressionHelper(mt, e, i); + e = e.expressionSemantic(sc); + mt.resolveExp(e, pt, pe, ps); + return; + } + else + { + if (id.dyncast() == DYNCAST.dsymbol) + { + // searchX already handles errors for template instances + assert(global.errors); + } + else + { + assert(id.dyncast() == DYNCAST.identifier); + sm = s.search_correct(cast(Identifier)id); + if (sm) + error(loc, "identifier `%s` of `%s` is not defined, did you mean %s `%s`?", id.toChars(), mt.toChars(), sm.kind(), sm.toChars()); + else + error(loc, "identifier `%s` of `%s` is not defined", id.toChars(), mt.toChars()); + } + *pe = new ErrorExp(); + } + return; + } + L2: + s = sm.toAlias(); + } + + if (auto em = s.isEnumMember()) + { + // It's not a type, it's an expression + *pe = em.getVarExp(loc, sc); + return; + } + if (auto v = s.isVarDeclaration()) + { + /* This is mostly same with DsymbolExp::semantic(), but we cannot use it + * because some variables used in type context need to prevent lowering + * to a literal or contextful expression. For example: + * + * enum a = 1; alias b = a; + * template X(alias e){ alias v = e; } alias x = X!(1); + * struct S { int v; alias w = v; } + * // TypeIdentifier 'a', 'e', and 'v' should be TOK.variable, + * // because getDsymbol() need to work in AliasDeclaration::semantic(). + */ + if (!v.type || + !v.type.deco && v.inuse) + { + if (v.inuse) // https://issues.dlang.org/show_bug.cgi?id=9494 + error(loc, "circular reference to %s `%s`", v.kind(), v.toPrettyChars()); + else + error(loc, "forward reference to %s `%s`", v.kind(), v.toPrettyChars()); + *pt = Type.terror; + return; + } + if (v.type.ty == Terror) + *pt = Type.terror; + else + *pe = new VarExp(loc, v); + return; + } + if (auto fld = s.isFuncLiteralDeclaration()) + { + //printf("'%s' is a function literal\n", fld.toChars()); + *pe = new FuncExp(loc, fld); + *pe = (*pe).expressionSemantic(sc); + return; + } + version (none) + { + if (FuncDeclaration fd = s.isFuncDeclaration()) + { + *pe = new DsymbolExp(loc, fd); + return; + } + } + + L1: + Type t = s.getType(); + if (!t) + { + // If the symbol is an import, try looking inside the import + if (Import si = s.isImport()) + { + s = si.search(loc, s.ident); + if (s && s != si) + goto L1; + s = si; + } + *ps = s; + return; + } + if (t.ty == Tinstance && t != mt && !t.deco) + { + if (!(cast(TypeInstance)t).tempinst.errors) + error(loc, "forward reference to `%s`", t.toChars()); + *pt = Type.terror; + return; + } + + if (t.ty == Ttuple) + *pt = t; + else + *pt = t.merge(); + } + if (!s) + { + /* Look for what user might have intended + */ + const p = mt.mutableOf().unSharedOf().toChars(); + auto id = Identifier.idPool(p, cast(uint)strlen(p)); + if (const n = importHint(p)) + error(loc, "`%s` is not defined, perhaps `import %s;` ?", p, n); + else if (auto s2 = sc.search_correct(id)) + error(loc, "undefined identifier `%s`, did you mean %s `%s`?", p, s2.kind(), s2.toChars()); + else if (const q = Scope.search_correct_C(id)) + error(loc, "undefined identifier `%s`, did you mean `%s`?", p, q); + else + error(loc, "undefined identifier `%s`", p); + + *pt = Type.terror; + } +} + /************************************ * Transitively search a type for all function types. * If any function types with parameters are found that have parameter identifiers @@ -75,8 +439,8 @@ private Type stripDefaultArgs(Type t) 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) + return (t != p.type || p.defaultArg || p.ident || p.userAttribDecl) + ? new Parameter(p.storageClass, t, null, null, null) : null; } @@ -88,8 +452,7 @@ private Type stripDefaultArgs(Type t) if (ps) { // Replace params with a copy we can modify - Parameters* nparams = new Parameters(); - nparams.setDim(parameters.dim); + Parameters* nparams = new Parameters(parameters.dim); foreach (j, ref np; *nparams) { @@ -274,6 +637,7 @@ extern (C++) Expression typeToExpressionHelper(TypeQualified t, Expression e, si case DYNCAST.parameter: case DYNCAST.statement: case DYNCAST.condition: + case DYNCAST.templateparameter: assert(0); } } @@ -670,9 +1034,9 @@ private extern (C++) final class TypeSemanticVisitor : Visitor fatal(); } - static __gshared FuncDeclaration feq = null; - static __gshared FuncDeclaration fcmp = null; - static __gshared FuncDeclaration fhash = null; + __gshared FuncDeclaration feq = null; + __gshared FuncDeclaration fcmp = null; + __gshared FuncDeclaration fhash = null; if (!feq) feq = search_function(ClassDeclaration.object, Id.eq).isFuncDeclaration(); if (!fcmp) @@ -1060,8 +1424,7 @@ private extern (C++) final class TypeSemanticVisitor : Visitor * Make a copy, as original may be referenced elsewhere. */ size_t tdim = tt.arguments.dim; - auto newparams = new Parameters(); - newparams.setDim(tdim); + auto newparams = new Parameters(tdim); for (size_t j = 0; j < tdim; j++) { Parameter narg = (*tt.arguments)[j]; @@ -1084,7 +1447,7 @@ private extern (C++) final class TypeSemanticVisitor : Visitor } (*newparams)[j] = new Parameter( - stc, narg.type, narg.ident, narg.defaultArg); + stc, narg.type, narg.ident, narg.defaultArg, narg.userAttribDecl); } fparam.type = new TypeTuple(newparams); } @@ -2150,8 +2513,7 @@ private extern(C++) final class ResolveVisitor : Visitor * Do it this way because TemplateInstance::semanticTiargs() * can handle unresolved Objects this way. */ - auto objects = new Objects(); - objects.setDim(1); + auto objects = new Objects(1); (*objects)[0] = o; *ps = new TupleDeclaration(loc, tup.ident, objects); } @@ -2520,8 +2882,7 @@ private extern(C++) final class ResolveVisitor : Visitor /* Create a new TupleDeclaration which * is a slice [i1..i2] out of the old one. */ - auto objects = new Objects(); - objects.setDim(cast(size_t)(i2 - i1)); + auto objects = new Objects(cast(size_t)(i2 - i1)); for (size_t i = 0; i < objects.dim; i++) { (*objects)[i] = (*td.objects)[cast(size_t)i1 + i]; @@ -2606,6 +2967,7 @@ private extern(C++) final class DotExpVisitor : Visitor if (v.isField()) { auto ad = v.toParent().isAggregateDeclaration(); + objc.checkOffsetof(e, ad); ad.size(e.loc); if (ad.sizeok != Sizeok.done) { @@ -2837,7 +3199,7 @@ private extern(C++) final class DotExpVisitor : Visitor result = new ErrorExp(); return; } - else if (!(flag & DotExpFlag.noDeref) && sc.func && !sc.intypeof && sc.func.setUnsafe()) + else if (!(flag & DotExpFlag.noDeref) && sc.func && !sc.intypeof && sc.func.setUnsafe() && !(sc.flags & SCOPE.debug_)) { e.error("`%s.ptr` cannot be used in `@safe` code, use `&%s[0]` instead", e.toChars(), e.toChars()); result = new ErrorExp(); @@ -2892,7 +3254,7 @@ private extern(C++) final class DotExpVisitor : Visitor } else if (ident == Id.ptr) { - if (!(flag & DotExpFlag.noDeref) && sc.func && !sc.intypeof && sc.func.setUnsafe()) + if (!(flag & DotExpFlag.noDeref) && sc.func && !sc.intypeof && sc.func.setUnsafe() && !(sc.flags & SCOPE.debug_)) { e.error("`%s.ptr` cannot be used in `@safe` code, use `&%s[0]` instead", e.toChars(), e.toChars()); result = new ErrorExp(); @@ -2918,11 +3280,11 @@ private extern(C++) final class DotExpVisitor : Visitor } if (ident == Id.length) { - static __gshared FuncDeclaration fd_aaLen = null; + __gshared FuncDeclaration fd_aaLen = null; if (fd_aaLen is null) { auto fparams = new Parameters(); - fparams.push(new Parameter(STC.in_, mt, null, null)); + fparams.push(new Parameter(STC.in_, mt, null, null, null)); fd_aaLen = FuncDeclaration.genCfunc(fparams, Type.tsize_t, Id.aaLen); TypeFunction tf = fd_aaLen.type.toTypeFunction(); tf.purity = PURE.const_; @@ -2964,7 +3326,7 @@ private extern(C++) final class DotExpVisitor : Visitor } else if (ident == Id.funcptr) { - if (!(flag & DotExpFlag.noDeref) && sc.func && !sc.intypeof && sc.func.setUnsafe()) + if (!(flag & DotExpFlag.noDeref) && sc.func && !sc.intypeof && sc.func.setUnsafe() && !(sc.flags & SCOPE.debug_)) { e.error("`%s.funcptr` cannot be used in `@safe` code", e.toChars()); result = new ErrorExp(); @@ -2987,13 +3349,13 @@ private extern(C++) final class DotExpVisitor : Visitor * * If flag & 1, don't report "not a property" error and just return NULL. */ - final Expression noMember(Type mt, Scope* sc, Expression e, Identifier ident, int flag) + Expression noMember(Type mt, Scope* sc, Expression e, Identifier ident, int flag) { //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 + __gshared int nest; // https://issues.dlang.org/show_bug.cgi?id=17380 static Expression returnExp(Expression e) { @@ -3034,6 +3396,8 @@ private extern(C++) final class DotExpVisitor : Visitor * e.opDot().ident */ e = build_overload(e.loc, sc, e, null, fd); + // @@@DEPRECATED_2.087@@@. + e.deprecation("`opDot` is deprecated. Use `alias this`"); e = new DotIdExp(e.loc, e, ident); return returnExp(e.expressionSemantic(sc)); } @@ -3299,7 +3663,7 @@ private extern(C++) final class DotExpVisitor : Visitor if (s.isImport() || s.isModule() || s.isPackage()) { - e = dmd.expression.resolve(e.loc, sc, s, false); + e = dmd.expressionsem.resolve(e.loc, sc, s, false); result = e; return; } @@ -3460,6 +3824,8 @@ private extern(C++) final class DotExpVisitor : Visitor */ if (ident == Id._tupleof) { + objc.checkTupleof(e, mt); + /* Create a TupleExp */ e = e.expressionSemantic(sc); // do this before turning on noaccesscheck @@ -3789,7 +4155,7 @@ private extern(C++) final class DotExpVisitor : Visitor if (s.isImport() || s.isModule() || s.isPackage()) { - e = dmd.expression.resolve(e.loc, sc, s, false); + e = dmd.expressionsem.resolve(e.loc, sc, s, false); result = e; return; } @@ -3940,3 +4306,232 @@ private extern(C++) final class DotExpVisitor : Visitor result = e; } } + + +/************************ + * Get the the default initialization expression for a type. + * Params: + * mt = the type for which the init expression is returned + * loc = the location where the expression needs to be evaluated + * + * Returns: + * The initialization expression for the type. + */ +extern(C++) Expression defaultInit(Type mt, const ref Loc loc) +{ + scope v = new DefaultInitVisitor(loc); + mt.accept(v); + return v.result; +} + +private extern(C++) final class DefaultInitVisitor : Visitor +{ + alias visit = typeof(super).visit; + const Loc loc; + Expression result; + + this(const ref Loc loc) + { + this.loc = loc; + } + + override void visit(Type mt) + { + static if (LOGDEFAULTINIT) + { + printf("Type::defaultInit() '%s'\n", mt.toChars()); + } + result = null; + } + + override void visit(TypeError mt) + { + result = new ErrorExp(); + } + + override void visit(TypeBasic mt) + { + static if (LOGDEFAULTINIT) + { + printf("TypeBasic::defaultInit() '%s'\n", mt.toChars()); + } + dinteger_t value = 0; + + switch (mt.ty) + { + case Tchar: + value = 0xFF; + break; + + case Twchar: + case Tdchar: + value = 0xFFFF; + break; + + case Timaginary32: + case Timaginary64: + case Timaginary80: + case Tfloat32: + case Tfloat64: + case Tfloat80: + result = new RealExp(loc, Target.RealProperties.snan, mt); + return; + + case Tcomplex32: + case Tcomplex64: + case Tcomplex80: + { + // Can't use fvalue + I*fvalue (the im part becomes a quiet NaN). + const cvalue = complex_t(Target.RealProperties.snan, Target.RealProperties.snan); + result = new ComplexExp(loc, cvalue, mt); + return; + } + + case Tvoid: + error(loc, "`void` does not have a default initializer"); + result = new ErrorExp(); + return; + + default: + break; + } + result = new IntegerExp(loc, value, mt); + } + + override void visit(TypeVector mt) + { + //printf("TypeVector::defaultInit()\n"); + assert(mt.basetype.ty == Tsarray); + Expression e = mt.basetype.defaultInit(loc); + auto ve = new VectorExp(loc, e, mt); + ve.type = mt; + ve.dim = cast(int)(mt.basetype.size(loc) / mt.elementType().size(loc)); + result = ve; + } + + override void visit(TypeSArray mt) + { + static if (LOGDEFAULTINIT) + { + printf("TypeSArray::defaultInit() '%s'\n", mt.toChars()); + } + if (mt.next.ty == Tvoid) + result = mt.tuns8.defaultInit(loc); + else + result = mt.next.defaultInit(loc); + } + + override void visit(TypeDArray mt) + { + static if (LOGDEFAULTINIT) + { + printf("TypeDArray::defaultInit() '%s'\n", mt.toChars()); + } + result = new NullExp(loc, mt); + } + + override void visit(TypeAArray mt) + { + static if (LOGDEFAULTINIT) + { + printf("TypeAArray::defaultInit() '%s'\n", mt.toChars()); + } + result = new NullExp(loc, mt); + } + + override void visit(TypePointer mt) + { + static if (LOGDEFAULTINIT) + { + printf("TypePointer::defaultInit() '%s'\n", mt.toChars()); + } + result = new NullExp(loc, mt); + } + + override void visit(TypeReference mt) + { + static if (LOGDEFAULTINIT) + { + printf("TypeReference::defaultInit() '%s'\n", mt.toChars()); + } + result = new NullExp(loc, mt); + } + + override void visit(TypeFunction mt) + { + error(loc, "`function` does not have a default initializer"); + result = new ErrorExp(); + } + + override void visit(TypeDelegate mt) + { + static if (LOGDEFAULTINIT) + { + printf("TypeDelegate::defaultInit() '%s'\n", mt.toChars()); + } + result = new NullExp(loc, mt); + } + + override void visit(TypeStruct mt) + { + static if (LOGDEFAULTINIT) + { + printf("TypeStruct::defaultInit() '%s'\n", mt.toChars()); + } + Declaration d = new SymbolDeclaration(mt.sym.loc, mt.sym); + assert(d); + d.type = mt; + d.storage_class |= STC.rvalue; // https://issues.dlang.org/show_bug.cgi?id=14398 + result = new VarExp(mt.sym.loc, d); + } + + override void visit(TypeEnum mt) + { + static if (LOGDEFAULTINIT) + { + printf("TypeEnum::defaultInit() '%s'\n", mt.toChars()); + } + // Initialize to first member of enum + Expression e = mt.sym.getDefaultValue(loc); + e = e.copy(); + e.loc = loc; + e.type = mt; // to deal with const, immutable, etc., variants + result = e; + } + + override void visit(TypeClass mt) + { + static if (LOGDEFAULTINIT) + { + printf("TypeClass::defaultInit() '%s'\n", mt.toChars()); + } + result = new NullExp(loc, mt); + } + + override void visit(TypeTuple mt) + { + static if (LOGDEFAULTINIT) + { + printf("TypeTuple::defaultInit() '%s'\n", mt.toChars()); + } + auto exps = new Expressions(mt.arguments.dim); + for (size_t i = 0; i < mt.arguments.dim; i++) + { + Parameter p = (*mt.arguments)[i]; + assert(p.type); + Expression e = p.type.defaultInitLiteral(loc); + if (e.op == TOK.error) + { + result = e; + return; + } + (*exps)[i] = e; + } + result = new TupleExp(loc, exps); + } + + override void visit(TypeNull mt) + { + result = new NullExp(Loc.initial, Type.tnull); + } +} diff --git a/gcc/d/dmd/utils.d b/gcc/d/dmd/utils.d index d07ad29d2..bb6edab6a 100644 --- a/gcc/d/dmd/utils.d +++ b/gcc/d/dmd/utils.d @@ -95,7 +95,7 @@ extern (C++) void ensurePathToNameExists(Loc loc, const(char)* name) const(char)* pt = FileName.path(name); if (*pt) { - if (FileName.ensurePathExists(pt)) + if (!FileName.ensurePathExists(pt)) { error(loc, "cannot create directory %s", pt); fatal(); @@ -132,3 +132,41 @@ extern (C++) void escapePath(OutBuffer* buf, const(char)* fname) fname++; } } + +/// Slices a `\0`-terminated C-string, excluding the terminator +const(char)[] toDString (const(char)* s) pure nothrow @nogc +{ + return s ? s[0 .. strlen(s)] : null; +} + +/** +Compare two slices for equality, in a case-insensitive way + +Comparison is based on `char` and does not do decoding. +As a result, it's only really accurate for plain ASCII strings. + +Params: +s1 = string to compare +s2 = string to compare + +Returns: +`true` if `s1 == s2` regardless of case +*/ +extern(D) static bool iequals(const(char)[] s1, const(char)[] s2) +{ + import core.stdc.ctype : toupper; + + if (s1.length != s2.length) + return false; + + int result = 0; + foreach (idx, c1; s1) + { + // Since we did a length check, it is safe to bypass bounds checking + const c2 = s2.ptr[idx]; + if (c1 != c2) + if (toupper(c1) != toupper(c2)) + return false; + } + return true; +} diff --git a/gcc/d/dmd/visitor.h b/gcc/d/dmd/visitor.h index b5cf34c53..3818a1af7 100644 --- a/gcc/d/dmd/visitor.h +++ b/gcc/d/dmd/visitor.h @@ -53,9 +53,8 @@ class DebugStatement; class GotoStatement; class LabelStatement; class AsmStatement; -#ifdef IN_GCC -class ExtAsmStatement; -#endif +class InlineAsmStatement; +class GccAsmStatement; class CompoundAsmStatement; class ImportStatement; @@ -407,9 +406,6 @@ class ParseTimeVisitor virtual void visit(TryFinallyStatement *s) { visit((Statement *)s); } virtual void visit(ThrowStatement *s) { visit((Statement *)s); } virtual void visit(AsmStatement *s) { visit((Statement *)s); } -#ifdef IN_GCC - virtual void visit(ExtAsmStatement *s) { visit((Statement *)s); } -#endif virtual void visit(ExpStatement *s) { visit((Statement *)s); } virtual void visit(CompoundStatement *s) { visit((Statement *)s); } @@ -417,6 +413,10 @@ class ParseTimeVisitor virtual void visit(CompoundDeclarationStatement *s) { visit((CompoundStatement *)s); } virtual void visit(CompoundAsmStatement *s) { visit((CompoundStatement *)s); } + // AsmStatements + virtual void visit(InlineAsmStatement *s) { visit((AsmStatement *)s); } + virtual void visit(GccAsmStatement *s) { visit((AsmStatement *)s); } + // Types virtual void visit(TypeBasic *t) { visit((Type *)t); } virtual void visit(TypeError *t) { visit((Type *)t); } diff --git a/gcc/d/lang.opt b/gcc/d/lang.opt index 76883d45b..0729fdba4 100644 --- a/gcc/d/lang.opt +++ b/gcc/d/lang.opt @@ -273,10 +273,6 @@ fpreconditions D Var(flag_preconditions) Generate code for precondition contracts. -fproperty -D -This switch is deprecated; do not use. - frelease D Compile release version. diff --git a/gcc/d/toir.cc b/gcc/d/toir.cc index 027f4850c..a1c0f9f49 100644 --- a/gcc/d/toir.cc +++ b/gcc/d/toir.cc @@ -1197,7 +1197,7 @@ class IRVisitor : public Visitor /* D Inline Assembler is not implemented, as would require a writing an assembly parser for each supported target. Instead we leverage - GCC extended assembler using the ExtAsmStatement class. */ + GCC extended assembler using the GccAsmStatement class. */ void visit (AsmStatement *) { @@ -1207,7 +1207,7 @@ class IRVisitor : public Visitor /* Build a GCC extended assembler expression, whose components are an INSN string, some OUTPUTS, some INPUTS, and some CLOBBERS. */ - void visit (ExtAsmStatement *s) + void visit (GccAsmStatement *s) { StringExp *insn = (StringExp *)s->insn; tree outputs = NULL_TREE; diff --git a/gcc/d/typeinfo.cc b/gcc/d/typeinfo.cc index f7c2310a3..e0e61f655 100644 --- a/gcc/d/typeinfo.cc +++ b/gcc/d/typeinfo.cc @@ -813,7 +813,7 @@ class TypeInfoVisitor : public Visitor this->layout_field (inv); /* ClassFlags m_flags; */ - ClassFlags::Type flags = ClassFlags::hasOffTi; + int flags = ClassFlags::hasOffTi; if (cd->isCOMclass ()) flags |= ClassFlags::isCOMclass; @@ -907,7 +907,7 @@ class TypeInfoVisitor : public Visitor this->layout_field (null_pointer_node); /* ClassFlags m_flags; */ - ClassFlags::Type flags = ClassFlags::hasOffTi; + int flags = ClassFlags::hasOffTi; flags |= ClassFlags::hasTypeInfo; if (cd->isCOMinterface ()) flags |= ClassFlags::isCOMclass; @@ -1042,7 +1042,7 @@ class TypeInfoVisitor : public Visitor this->layout_field (null_pointer_node); /* StructFlags m_flags; */ - StructFlags::Type m_flags = 0; + int m_flags = StructFlags::none; if (ti->hasPointers ()) m_flags |= StructFlags::hasPointers; this->layout_field (size_int (m_flags)); diff --git a/gcc/testsuite/gdc.dg/runnable.d b/gcc/testsuite/gdc.dg/runnable.d index 1003b9eea..068fc9d49 100644 --- a/gcc/testsuite/gdc.dg/runnable.d +++ b/gcc/testsuite/gdc.dg/runnable.d @@ -604,7 +604,7 @@ void test66() // Bug 67 -__vector(float[4]) d[2]; // ICE +__vector(float[4])[2] d; // ICE /******************************************/ diff --git a/gcc/testsuite/gdc.test/compilable/betterCarray.d b/gcc/testsuite/gdc.test/compilable/betterCarray.d index 67fe42fb8..74c80be3b 100644 --- a/gcc/testsuite/gdc.test/compilable/betterCarray.d +++ b/gcc/testsuite/gdc.test/compilable/betterCarray.d @@ -15,12 +15,3 @@ int foo(int[] a, int i) { return a[i]; } - -// https://issues.dlang.org/show_bug.cgi?id=17787 -version (D_BetterC) -{ -} -else -{ - static assert(0); -} diff --git a/gcc/testsuite/gdc.test/compilable/betterc.d b/gcc/testsuite/gdc.test/compilable/betterc.d new file mode 100644 index 000000000..7df9bc22a --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/betterc.d @@ -0,0 +1,27 @@ +/* REQUIRED_ARGS: -betterC +*/ + +// https://issues.dlang.org/show_bug.cgi?id=17787 +version (D_BetterC) +{ +} +else +{ + static assert(0); +} + +// -betterC does not support `ModuleInfo`, `TypeInfo`, or exception handling +version (D_ModuleInfo) +{ + static assert(0); +} + +version (D_Exceptions) +{ + static assert(0); +} + +version (D_TypeInfo) +{ + static assert(0); +} diff --git a/gcc/testsuite/gdc.test/compilable/callconv.d b/gcc/testsuite/gdc.test/compilable/callconv.d index fbb78ed7c..ef66381e3 100644 --- a/gcc/testsuite/gdc.test/compilable/callconv.d +++ b/gcc/testsuite/gdc.test/compilable/callconv.d @@ -4,7 +4,7 @@ import core.stdc.stdarg; struct ABC { - int x[4]; + int[4] x; } ABC abc; diff --git a/gcc/testsuite/gdc.test/compilable/cppmangle.d b/gcc/testsuite/gdc.test/compilable/cppmangle.d index da2f05238..e744db088 100644 --- a/gcc/testsuite/gdc.test/compilable/cppmangle.d +++ b/gcc/testsuite/gdc.test/compilable/cppmangle.d @@ -744,3 +744,19 @@ version (Posix) static assert(test18957.mangleof == "_Z9test18957RKNSt9test18957E"); } + +/**************************************/ +// https://issues.dlang.org/show_bug.cgi?id=19043 +// Incorrect mangling for extern(C++) const template parameter on windows + +extern(C++) struct test19043(T) {} + +extern(C++) void test19043a(test19043!(const(char)) a) {} +extern(C++) void test19043b(T)(T a) {} +version(Windows) +{ + static assert(test19043a.mangleof == "?test19043a@@YAXU?$test19043@$$CBD@@@Z"); + static assert(test19043b!(test19043!(const(char))).mangleof == + "??$test19043b@U?$test19043@$$CBD@@@@YAXU?$test19043@$$CBD@@@Z"); +} + diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/header1.d b/gcc/testsuite/gdc.test/compilable/extra-files/header1.d index b0a73ff2c..720b4f0fe 100644 --- a/gcc/testsuite/gdc.test/compilable/extra-files/header1.d +++ b/gcc/testsuite/gdc.test/compilable/extra-files/header1.d @@ -581,3 +581,21 @@ struct SafeS int* p; } + +void test13x(@(10) int a, @(20) int, @(30) @(40) int[] arr...) {} + +enum Test14UDA1; +struct Test14UDA2 +{ + string str; +} + +Test14UDA2 test14uda3(string name) +{ + return Test14UDA2(name); +} +struct Test14UDA4(string v){} + +void test14x(@Test14UDA1 int, @Test14UDA2("1") int, @test14uda3("2") int, @Test14UDA4!"3" int) {} + +void test15x(@(20) void delegate(int) @safe dg){} diff --git a/gcc/testsuite/gdc.test/compilable/iasm_labeloperand.d b/gcc/testsuite/gdc.test/compilable/iasm_labeloperand.d index f88fd1651..0b7dce9e7 100644 --- a/gcc/testsuite/gdc.test/compilable/iasm_labeloperand.d +++ b/gcc/testsuite/gdc.test/compilable/iasm_labeloperand.d @@ -15,8 +15,8 @@ version (TestInlineAsm) version (GNU) { L1: - asm { ""; } - asm { "" : : : : L1, L2; } + asm { "" : : : : L1; } // Check back references + asm { "" : : : : L2; } // Check forward references L2: asm { ""; } } diff --git a/gcc/testsuite/gdc.test/compilable/imports/test19107a.d b/gcc/testsuite/gdc.test/compilable/imports/test19107a.d new file mode 100644 index 000000000..d270e3b49 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/imports/test19107a.d @@ -0,0 +1,3 @@ +module imports.test19107a.d; + +alias I(alias A) = A; diff --git a/gcc/testsuite/gdc.test/compilable/imports/test19107b.d b/gcc/testsuite/gdc.test/compilable/imports/test19107b.d new file mode 100644 index 000000000..8fd8087ef --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/imports/test19107b.d @@ -0,0 +1,3 @@ +module imports.test19107b; + +import imports.test19107a : I; diff --git a/gcc/testsuite/gdc.test/compilable/imports/test63a.d b/gcc/testsuite/gdc.test/compilable/imports/test63a.d index a8edbd8ae..d53b034a9 100644 --- a/gcc/testsuite/gdc.test/compilable/imports/test63a.d +++ b/gcc/testsuite/gdc.test/compilable/imports/test63a.d @@ -4,7 +4,7 @@ private import test63; struct s { - char a[ SIZE ]; + char[SIZE] a; } diff --git a/gcc/testsuite/gdc.test/compilable/interpret3.d b/gcc/testsuite/gdc.test/compilable/interpret3.d index 45393b2f4..476a8655e 100644 --- a/gcc/testsuite/gdc.test/compilable/interpret3.d +++ b/gcc/testsuite/gdc.test/compilable/interpret3.d @@ -4680,7 +4680,7 @@ static assert(bug6886()); struct Block10198 { - int val[3][4]; + int[4][3] val; } int bug10198() @@ -6431,7 +6431,7 @@ C73 test73 = func73(); struct S74 { - int n[1]; + int[1] n; static S74 test(){ S74 ret = void; ret.n[0] = 0; return ret; } } @@ -7266,7 +7266,7 @@ struct Matrix13827(T, uint N) { private static defaultMatrix() { - T arr[N]; + T[N] arr; return arr; } @@ -7742,6 +7742,15 @@ struct RBNode(T) static assert(!__traits(compiles, { alias bug18057 = RBNode!int; })); +/**************************************************/ +// https://issues.dlang.org/show_bug.cgi?id=19140 + +void test19140() +{ + real f19140(); + static if (__traits(compiles, (){ enum real r = f19140(); })) {} +} + /**************************************************/ // https://issues.dlang.org/show_bug.cgi?id=19074 diff --git a/gcc/testsuite/gdc.test/compilable/test16492.d b/gcc/testsuite/gdc.test/compilable/test16492.d index ca494c261..ecf75ceff 100644 --- a/gcc/testsuite/gdc.test/compilable/test16492.d +++ b/gcc/testsuite/gdc.test/compilable/test16492.d @@ -1,4 +1,4 @@ -// ARG_SETS: -debug; -o- +// ARG_SETS: -debug; -o-; -debug -dip1000 // https://issues.dlang.org/show_bug.cgi?id=16492 void mayCallGC(); @@ -13,3 +13,30 @@ void test() @nogc pure b ~= 4; } } + +void debugSafe() @safe +{ + debug unsafeSystem(); + debug unsafeTemplated(); +} + +void unsafeSystem() @system {} +void unsafeTemplated()() { + int[] arr; + auto b = arr.ptr; +} + +void debugSafe2() @safe +{ + char[] arr1, arr2; + debug unsafeDIP1000Lifetime(arr1, arr2); + + char* ptr; + char[] arr; + debug ptr = arr.ptr; +} + +void unsafeDIP1000Lifetime()(ref char[] p, scope char[] s) +{ + p = s; +} diff --git a/gcc/testsuite/gdc.test/compilable/test19081.d b/gcc/testsuite/gdc.test/compilable/test19081.d new file mode 100644 index 000000000..ba5fd9c77 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test19081.d @@ -0,0 +1,14 @@ +void main() { + @(1) enum { A } + /// comment + @(1) enum X { A } + @(2) enum Y; + @(1) @(2) enum Z { A } + + struct Test { int test; } + @Test(1) enum W { A } + @(1) enum V: int { A } + X a; + static assert(__traits(getAttributes, X).length == 1); + static assert(__traits(getAttributes, X)[0] == 1); +} diff --git a/gcc/testsuite/gdc.test/compilable/test19107.d b/gcc/testsuite/gdc.test/compilable/test19107.d new file mode 100644 index 000000000..3fca91f83 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test19107.d @@ -0,0 +1,21 @@ +// REQUIRED_ARGS: -dw +// EXTRA_FILES: imports/test19107a.d imports/test19107b.d +/* +TEST_OUTPUT: +--- +compilable/test19107.d(14): Deprecation: `imports.test19107b.I` is not visible from module `test19107` +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=19107 + +import imports.test19107b; + +void all(alias pred, T)(T t) + if (is(typeof(I!pred(t)))) +{ } + +void main(string[] args) +{ + args.all!(c => c); +} diff --git a/gcc/testsuite/gdc.test/compilable/test19108.d b/gcc/testsuite/gdc.test/compilable/test19108.d new file mode 100644 index 000000000..46207509d --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test19108.d @@ -0,0 +1,9 @@ +// REQUIRED_ARGS: -ignore + +// https://issues.dlang.org/show_bug.cgi?id=19108 + +pragma(unknown_global); +void main() +{ + pragma(unknown_local); // Error: unrecognized pragma +} diff --git a/gcc/testsuite/gdc.test/compilable/test66.d b/gcc/testsuite/gdc.test/compilable/test66.d index 1213884f7..fb496732f 100644 --- a/gcc/testsuite/gdc.test/compilable/test66.d +++ b/gcc/testsuite/gdc.test/compilable/test66.d @@ -11,7 +11,7 @@ enum struct Token { - static char[] tochars[TOKmax]; + static char[][TOKmax] tochars; } class Lexer diff --git a/gcc/testsuite/gdc.test/compilable/test9701.d b/gcc/testsuite/gdc.test/compilable/test9701.d new file mode 100644 index 000000000..8f822add5 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test9701.d @@ -0,0 +1,58 @@ +// https://issues.dlang.org/show_bug.cgi?id=9701 + +template AliasSeq(TList...) +{ + alias AliasSeq = TList; +} + +enum +{ + uda4, + uda5, + uda6, + uda8, + uda9 +} + +enum Enum +{ + value0, + @("uda1") value1, + @("uda2", "uda3", 42) value2, + @uda4 value3, + @uda5 @uda6 value4, + @("uda7") @uda8 value5, + @uda9 @("uda10") value6, + deprecated value7, + deprecated("message") value8, +} + +@("uda0") +enum +{ + value0, + @("uda1") value1, + @("uda2") @("uda3") value2, + @uda4 value3, + @uda5 @uda6 value4, + @("uda7") @uda8 value5, + @uda9 @("uda10") value6 +} + +static assert(__traits(getAttributes, Enum.value0).length == 0); +static assert(__traits(getAttributes, Enum.value1) == AliasSeq!("uda1")); +static assert(__traits(getAttributes, Enum.value2) == AliasSeq!("uda2", "uda3", 42)); +static assert(__traits(getAttributes, Enum.value3) == AliasSeq!(uda4)); +static assert(__traits(getAttributes, Enum.value4) == AliasSeq!(uda5, uda6)); +static assert(__traits(getAttributes, Enum.value5) == AliasSeq!("uda7", uda8)); +static assert(__traits(getAttributes, Enum.value6) == AliasSeq!(uda9, "uda10")); +static assert(__traits(isDeprecated, Enum.value7)); +static assert(__traits(isDeprecated, Enum.value8)); + +static assert(__traits(getAttributes, value0) == AliasSeq!("uda0")); +static assert(__traits(getAttributes, value1) == AliasSeq!("uda0", "uda1")); +static assert(__traits(getAttributes, value2) == AliasSeq!("uda0", "uda2", "uda3")); +static assert(__traits(getAttributes, value3) == AliasSeq!("uda0", uda4)); +static assert(__traits(getAttributes, value4) == AliasSeq!("uda0", uda5, uda6)); +static assert(__traits(getAttributes, value5) == AliasSeq!("uda0", "uda7", uda8)); +static assert(__traits(getAttributes, value6) == AliasSeq!("uda0", uda9, "uda10")); diff --git a/gcc/testsuite/gdc.test/compilable/testheaderudamodule.d b/gcc/testsuite/gdc.test/compilable/testheaderudamodule.d index a62f365b3..b0d4515f9 100644 --- a/gcc/testsuite/gdc.test/compilable/testheaderudamodule.d +++ b/gcc/testsuite/gdc.test/compilable/testheaderudamodule.d @@ -11,3 +11,5 @@ struct UDA } void main() {} + +void foo(@(1) int bar, @UDA(2) string bebe) {} diff --git a/gcc/testsuite/gdc.test/compilable/traits.d b/gcc/testsuite/gdc.test/compilable/traits.d new file mode 100644 index 000000000..5dc90fb71 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/traits.d @@ -0,0 +1,13 @@ +// This file is intended to contain all compilable traits-related tests in an +// effort to keep the number of files in the `compilable` folder to a minimum. + +// https://issues.dlang.org/show_bug.cgi?id=19152 + +class C19152 +{ + int OnExecute() + { + auto name = __traits(getOverloads, this, "OnExecute").stringof; + return 0; + } +} diff --git a/gcc/testsuite/gdc.test/compilable/version.d b/gcc/testsuite/gdc.test/compilable/version.d new file mode 100644 index 000000000..00c5c8084 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/version.d @@ -0,0 +1,23 @@ +/* REQUIRED_ARGS: +*/ + +version (D_ModuleInfo) +{ } +else +{ + static assert(0); +} + +version (D_Exceptions) +{ } +else +{ + static assert(0); +} + +version (D_TypeInfo) +{ } +else +{ + static assert(0); +} \ No newline at end of file diff --git a/gcc/testsuite/gdc.test/d_do_test.exp b/gcc/testsuite/gdc.test/d_do_test.exp index 3c03f0ac9..ae637ec9e 100644 --- a/gcc/testsuite/gdc.test/d_do_test.exp +++ b/gcc/testsuite/gdc.test/d_do_test.exp @@ -83,6 +83,9 @@ proc gdc-convert-args { args } { || [string match "-gc" $arg] } { lappend out "-g" + } elseif [string match "-ignore" $arg] { + lappend out "-fignore-unknown-pragmas" + } elseif [string match "-inline" $arg] { lappend out "-finline-functions" @@ -95,9 +98,6 @@ proc gdc-convert-args { args } { } elseif [string match "-O" $arg] { lappend out "-O2" - } elseif [string match "-property" $arg] { - lappend out "-fproperty" - } elseif [string match "-release" $arg] { lappend out "-frelease" diff --git a/gcc/testsuite/gdc.test/fail_compilation/bug18743.d b/gcc/testsuite/gdc.test/fail_compilation/bug18743.d new file mode 100644 index 000000000..27f546e84 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/bug18743.d @@ -0,0 +1,22 @@ +// REQUIRED_ARGS: -de +/* +TEST_OUTPUT: +--- +fail_compilation/bug18743.d(18): Deprecation: `a ? a = 4 : a` must be surrounded by parentheses when next to operator `=` +fail_compilation/bug18743.d(19): Deprecation: `a ? --a : a` must be surrounded by parentheses when next to operator `+=` +--- +*/ + +void main() +{ + int a; + + // ok + (a ? a = 4 : a) = 5; + a ? a = 4 : (a = 5); + + a ? a = 4 : a = 5; + a ? --a : a += 1; + + a ? a = 4 : a++; // ok +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/deprecate12979a.d b/gcc/testsuite/gdc.test/fail_compilation/deprecate12979a.d index 37f8fd5ba..ecb94c09b 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/deprecate12979a.d +++ b/gcc/testsuite/gdc.test/fail_compilation/deprecate12979a.d @@ -11,8 +11,15 @@ fail_compilation/deprecate12979a.d(14): Deprecation: `asm` statement is assumed void foo() nothrow { - asm + version(GNU) { - ret; + asm { ""; } + } + else + { + asm + { + ret; + } } } diff --git a/gcc/testsuite/gdc.test/fail_compilation/deprecate12979b.d b/gcc/testsuite/gdc.test/fail_compilation/deprecate12979b.d index f20af0f41..e4843c399 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/deprecate12979b.d +++ b/gcc/testsuite/gdc.test/fail_compilation/deprecate12979b.d @@ -10,8 +10,15 @@ fail_compilation/deprecate12979b.d(13): Deprecation: `asm` statement is assumed void foo() pure { - asm + version(GNU) { - ret; + asm { ""; } + } + else + { + asm + { + ret; + } } } diff --git a/gcc/testsuite/gdc.test/fail_compilation/deprecate12979c.d b/gcc/testsuite/gdc.test/fail_compilation/deprecate12979c.d index a0e4a1db9..aa655de49 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/deprecate12979c.d +++ b/gcc/testsuite/gdc.test/fail_compilation/deprecate12979c.d @@ -10,8 +10,15 @@ fail_compilation/deprecate12979c.d(13): Deprecation: `asm` statement is assumed void foo() @nogc { - asm + version(GNU) { - ret; + asm { ""; } + } + else + { + asm + { + ret; + } } } diff --git a/gcc/testsuite/gdc.test/fail_compilation/deprecate12979d.d b/gcc/testsuite/gdc.test/fail_compilation/deprecate12979d.d index 5ef6fcd45..abbb78558 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/deprecate12979d.d +++ b/gcc/testsuite/gdc.test/fail_compilation/deprecate12979d.d @@ -9,8 +9,15 @@ fail_compilation/deprecate12979d.d(12): Error: `asm` statement is assumed to be void foo() @safe { - asm + version(GNU) { - ret; + asm { ""; } + } + else + { + asm + { + ret; + } } } diff --git a/gcc/testsuite/gdc.test/fail_compilation/deprecateopdot.d b/gcc/testsuite/gdc.test/fail_compilation/deprecateopdot.d new file mode 100644 index 000000000..a03b8a18a --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/deprecateopdot.d @@ -0,0 +1,30 @@ +/* +REQUIRED_ARGS: -de +TEST_OUTPUT: +--- +fail_compilation/deprecateopdot.d(27): Deprecation: `opDot` is deprecated. Use `alias this` +fail_compilation/deprecateopdot.d(28): Deprecation: `opDot` is deprecated. Use `alias this` +fail_compilation/deprecateopdot.d(29): Deprecation: `opDot` is deprecated. Use `alias this` +--- +*/ +struct S6 +{ + int a, b; +} +struct T6 +{ + S6 s; + + S6* opDot() + { + return &s; + } +} + +void test6() +{ + T6 t; + t.a = 4; + assert(t.a == 4); + t.b = 5; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag10319.d b/gcc/testsuite/gdc.test/fail_compilation/diag10319.d index ec1f8abe5..4a01c5435 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag10319.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag10319.d @@ -1,13 +1,15 @@ /* TEST_OUTPUT: --- -fail_compilation/diag10319.d(25): Error: `pure` function `D main` cannot call impure function `diag10319.foo` -fail_compilation/diag10319.d(25): Error: `@safe` function `D main` cannot call `@system` function `diag10319.foo` -fail_compilation/diag10319.d(26): Error: `pure` function `D main` cannot call impure function `diag10319.bar!int.bar` -fail_compilation/diag10319.d(26): Error: `@safe` function `D main` cannot call `@system` function `diag10319.bar!int.bar` -fail_compilation/diag10319.d(25): Error: function `diag10319.foo` is not `nothrow` -fail_compilation/diag10319.d(26): Error: function `diag10319.bar!int.bar` is not `nothrow` -fail_compilation/diag10319.d(23): Error: `nothrow` function `D main` may throw +fail_compilation/diag10319.d(27): Error: `pure` function `D main` cannot call impure function `diag10319.foo` +fail_compilation/diag10319.d(27): Error: `@safe` function `D main` cannot call `@system` function `diag10319.foo` +fail_compilation/diag10319.d(16): `diag10319.foo` is declared here +fail_compilation/diag10319.d(28): Error: `pure` function `D main` cannot call impure function `diag10319.bar!int.bar` +fail_compilation/diag10319.d(28): Error: `@safe` function `D main` cannot call `@system` function `diag10319.bar!int.bar` +fail_compilation/diag10319.d(18): `diag10319.bar!int.bar` is declared here +fail_compilation/diag10319.d(27): Error: function `diag10319.foo` is not `nothrow` +fail_compilation/diag10319.d(28): Error: function `diag10319.bar!int.bar` is not `nothrow` +fail_compilation/diag10319.d(25): Error: `nothrow` function `D main` may throw --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag12678.d b/gcc/testsuite/gdc.test/fail_compilation/diag12678.d index bfbfbd58d..8b17968fd 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag12678.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag12678.d @@ -1,9 +1,11 @@ /* TEST_OUTPUT: --- -fail_compilation/diag12678.d(19): Error: const field `cf1` initialized multiple times -fail_compilation/diag12678.d(22): Error: immutable field `if1` initialized multiple times -fail_compilation/diag12678.d(25): Error: const field `cf2` initialization is not allowed in loops or after labels +fail_compilation/diag12678.d(21): Error: const field `cf1` initialized multiple times +fail_compilation/diag12678.d(20): Previous initialization is here. +fail_compilation/diag12678.d(24): Error: immutable field `if1` initialized multiple times +fail_compilation/diag12678.d(23): Previous initialization is here. +fail_compilation/diag12678.d(27): Error: const field `cf2` initialization is not allowed in loops or after labels --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag13320.d b/gcc/testsuite/gdc.test/fail_compilation/diag13320.d index 0f04998c3..2808606bd 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag13320.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag13320.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/diag13320.d(13): Error: `f += 1` is not a scalar, it is a `Foo` +fail_compilation/diag13320.d(13): Error: `f` is not a scalar, it is a `Foo` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag19022.d b/gcc/testsuite/gdc.test/fail_compilation/diag19022.d new file mode 100644 index 000000000..0aa26f537 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag19022.d @@ -0,0 +1,18 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag19022.d(16): Error: immutable field `b` initialized multiple times +fail_compilation/diag19022.d(15): Previous initialization is here. +--- +*/ +// https://issues.dlang.org/show_bug.cgi?id=19022 + +struct Foo +{ + immutable int b; + this(int a) + { + b = 2; + b = 2; + } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag6717.d b/gcc/testsuite/gdc.test/fail_compilation/diag6717.d index eaf1018f2..e6ff2417a 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag6717.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag6717.d @@ -7,8 +7,15 @@ fail_compilation/diag6717.d(12): Error: end of instruction expected, not `h` void main() { - asm + version(GNU) { - mov AX, 12h ; + asm { ""h; } + } + else + { + asm + { + mov AX, 12h ; + } } } diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag7050a.d b/gcc/testsuite/gdc.test/fail_compilation/diag7050a.d index 2cec7e098..abd0d1825 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag7050a.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag7050a.d @@ -1,7 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/diag7050a.d(14): Error: `@safe` function `diag7050a.foo` cannot call `@system` constructor `diag7050a.Foo.this` +fail_compilation/diag7050a.d(15): Error: `@safe` function `diag7050a.foo` cannot call `@system` constructor `diag7050a.Foo.this` +fail_compilation/diag7050a.d(11): `diag7050a.Foo.this` is declared here --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag7050c.d b/gcc/testsuite/gdc.test/fail_compilation/diag7050c.d index 298bd85c3..3b366df7a 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag7050c.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag7050c.d @@ -1,7 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/diag7050c.d(13): Error: `@safe` destructor `diag7050c.B.~this` cannot call `@system` destructor `diag7050c.A.~this` +fail_compilation/diag7050c.d(14): Error: `@safe` destructor `diag7050c.B.~this` cannot call `@system` destructor `diag7050c.A.~this` +fail_compilation/diag7050c.d(11): `diag7050c.A.~this` is declared here --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag_cstyle.d b/gcc/testsuite/gdc.test/fail_compilation/diag_cstyle.d index 1b1cd0c45..1870b58ce 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag_cstyle.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag_cstyle.d @@ -6,8 +6,8 @@ fail_compilation/diag_cstyle.d(14): Error: instead of C-style syntax, use D-styl fail_compilation/diag_cstyle.d(15): Error: instead of C-style syntax, use D-style `int function(int)* fp3` fail_compilation/diag_cstyle.d(17): Error: instead of C-style syntax, use D-style `int function(int) FP` fail_compilation/diag_cstyle.d(19): Error: instead of C-style syntax, use D-style `int function() fp` -fail_compilation/diag_cstyle.d(19): Deprecation: instead of C-style syntax, use D-style syntax `int[] arr` -fail_compilation/diag_cstyle.d(21): Deprecation: instead of C-style syntax, use D-style syntax `string[] result` +fail_compilation/diag_cstyle.d(19): Error: instead of C-style syntax, use D-style `int[] arr` +fail_compilation/diag_cstyle.d(21): Error: instead of C-style syntax, use D-style `string[] result` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/disable.d b/gcc/testsuite/gdc.test/fail_compilation/disable.d index 4f0a139c5..7e7d9c2d4 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/disable.d +++ b/gcc/testsuite/gdc.test/fail_compilation/disable.d @@ -1,15 +1,16 @@ /* TEST_OUTPUT: --- -fail_compilation/disable.d(50): Error: function `disable.DisabledOpAssign.opAssign` is not callable because it is annotated with `@disable` -fail_compilation/disable.d(53): Error: function `disable.DisabledPostblit.opAssign` is not callable because it is annotated with `@disable` -fail_compilation/disable.d(56): Error: function `disable.HasDtor.opAssign` is not callable because it is annotated with `@disable` -fail_compilation/disable.d(60): Error: generated function `disable.Nested!(DisabledOpAssign).Nested.opAssign` is not callable because it is annotated with `@disable` -fail_compilation/disable.d(63): Error: generated function `disable.Nested!(DisabledPostblit).Nested.opAssign` is not callable because it is annotated with `@disable` -fail_compilation/disable.d(66): Error: generated function `disable.Nested!(HasDtor).Nested.opAssign` is not callable because it is annotated with `@disable` -fail_compilation/disable.d(70): Error: generated function `disable.NestedDtor!(DisabledOpAssign).NestedDtor.opAssign` is not callable because it is annotated with `@disable` -fail_compilation/disable.d(73): Error: generated function `disable.NestedDtor!(DisabledPostblit).NestedDtor.opAssign` is not callable because it is annotated with `@disable` -fail_compilation/disable.d(76): Error: generated function `disable.NestedDtor!(HasDtor).NestedDtor.opAssign` is not callable because it is annotated with `@disable` +fail_compilation/disable.d(56): Error: function `disable.DisabledOpAssign.opAssign` cannot be used because it is annotated with `@disable` +fail_compilation/disable.d(59): Error: function `disable.DisabledPostblit.opAssign` cannot be used because it is annotated with `@disable` +fail_compilation/disable.d(62): Error: function `disable.HasDtor.opAssign` cannot be used because it is annotated with `@disable` +fail_compilation/disable.d(66): Error: generated function `disable.Nested!(DisabledOpAssign).Nested.opAssign` cannot be used because it is annotated with `@disable` +fail_compilation/disable.d(69): Error: generated function `disable.Nested!(DisabledPostblit).Nested.opAssign` cannot be used because it is annotated with `@disable` +fail_compilation/disable.d(72): Error: generated function `disable.Nested!(HasDtor).Nested.opAssign` cannot be used because it is annotated with `@disable` +fail_compilation/disable.d(76): Error: generated function `disable.NestedDtor!(DisabledOpAssign).NestedDtor.opAssign` cannot be used because it is annotated with `@disable` +fail_compilation/disable.d(79): Error: generated function `disable.NestedDtor!(DisabledPostblit).NestedDtor.opAssign` cannot be used because it is annotated with `@disable` +fail_compilation/disable.d(82): Error: generated function `disable.NestedDtor!(HasDtor).NestedDtor.opAssign` cannot be used because it is annotated with `@disable` +fail_compilation/disable.d(84): Error: enum member `disable.Enum1.value` cannot be used because it is annotated with `@disable` --- */ struct DisabledOpAssign { @@ -44,6 +45,11 @@ struct NestedDtor (T) ~this() {} } +enum Enum1 +{ + @disable value +} + void main () { DisabledOpAssign o; @@ -74,4 +80,6 @@ void main () NestedDtor!(HasDtor) ndd; ndd = NestedDtor!(HasDtor)(); + + auto v1 = Enum1.value; } diff --git a/gcc/testsuite/gdc.test/fail_compilation/disable_new.d b/gcc/testsuite/gdc.test/fail_compilation/disable_new.d index fe1524234..d5f1ecf07 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/disable_new.d +++ b/gcc/testsuite/gdc.test/fail_compilation/disable_new.d @@ -1,8 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/disable_new.d(23): Error: allocator `disable_new.C.new` is not callable because it is annotated with `@disable` -fail_compilation/disable_new.d(24): Error: allocator `disable_new.S.new` is not callable because it is annotated with `@disable` +fail_compilation/disable_new.d(23): Error: allocator `disable_new.C.new` cannot be used because it is annotated with `@disable` +fail_compilation/disable_new.d(24): Error: allocator `disable_new.S.new` cannot be used because it is annotated with `@disable` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10207.d b/gcc/testsuite/gdc.test/fail_compilation/fail10207.d index ac8b4eef1..3f09a2754 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail10207.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail10207.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail10207.d(7): Error: user defined attributes not allowed for `alias` declarations +fail_compilation/fail10207.d(7): Error: user-defined attributes not allowed for `alias` declarations --- */ alias @Safe int __externC; diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10254.d b/gcc/testsuite/gdc.test/fail_compilation/fail10254.d index b4e538546..8ad8fded5 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail10254.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail10254.d @@ -1,10 +1,12 @@ /* TEST_OUTPUT: --- -fail_compilation/fail10254.d(18): Error: `pure` function `fail10254.foo` cannot call impure constructor `fail10254.C.this` -fail_compilation/fail10254.d(18): Error: `@safe` function `fail10254.foo` cannot call `@system` constructor `fail10254.C.this` -fail_compilation/fail10254.d(19): Error: `pure` function `fail10254.foo` cannot call impure constructor `fail10254.S.this` -fail_compilation/fail10254.d(19): Error: `@safe` function `fail10254.foo` cannot call `@system` constructor `fail10254.S.this` +fail_compilation/fail10254.d(20): Error: `pure` function `fail10254.foo` cannot call impure constructor `fail10254.C.this` +fail_compilation/fail10254.d(20): Error: `@safe` function `fail10254.foo` cannot call `@system` constructor `fail10254.C.this` +fail_compilation/fail10254.d(15): `fail10254.C.this` is declared here +fail_compilation/fail10254.d(21): Error: `pure` function `fail10254.foo` cannot call impure constructor `fail10254.S.this` +fail_compilation/fail10254.d(21): Error: `@safe` function `fail10254.foo` cannot call `@system` constructor `fail10254.S.this` +fail_compilation/fail10254.d(16): `fail10254.S.this` is declared here --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10968.d b/gcc/testsuite/gdc.test/fail_compilation/fail10968.d index fe09e26c9..a24bec160 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail10968.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail10968.d @@ -1,18 +1,24 @@ /* TEST_OUTPUT: --- -fail_compilation/fail10968.d(33): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(33): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(34): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(34): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(35): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(35): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(38): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(38): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` fail_compilation/fail10968.d(39): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` fail_compilation/fail10968.d(39): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` +fail_compilation/fail10968.d(27): `fail10968.SA.__postblit` is declared here fail_compilation/fail10968.d(40): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` fail_compilation/fail10968.d(40): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` +fail_compilation/fail10968.d(27): `fail10968.SA.__postblit` is declared here +fail_compilation/fail10968.d(41): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` +fail_compilation/fail10968.d(41): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` +fail_compilation/fail10968.d(27): `fail10968.SA.__postblit` is declared here +fail_compilation/fail10968.d(44): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` +fail_compilation/fail10968.d(44): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` +fail_compilation/fail10968.d(27): `fail10968.SA.__postblit` is declared here +fail_compilation/fail10968.d(45): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` +fail_compilation/fail10968.d(45): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` +fail_compilation/fail10968.d(27): `fail10968.SA.__postblit` is declared here +fail_compilation/fail10968.d(46): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` +fail_compilation/fail10968.d(46): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` +fail_compilation/fail10968.d(27): `fail10968.SA.__postblit` is declared here --- */ @@ -43,12 +49,12 @@ void bar() pure @safe /* TEST_OUTPUT: --- -fail_compilation/fail10968.d(66): Error: struct `fail10968.SD` is not copyable because it is annotated with `@disable` -fail_compilation/fail10968.d(67): Error: struct `fail10968.SD` is not copyable because it is annotated with `@disable` -fail_compilation/fail10968.d(68): Error: struct `fail10968.SD` is not copyable because it is annotated with `@disable` -fail_compilation/fail10968.d(71): Error: struct `fail10968.SD` is not copyable because it is annotated with `@disable` fail_compilation/fail10968.d(72): Error: struct `fail10968.SD` is not copyable because it is annotated with `@disable` fail_compilation/fail10968.d(73): Error: struct `fail10968.SD` is not copyable because it is annotated with `@disable` +fail_compilation/fail10968.d(74): Error: struct `fail10968.SD` is not copyable because it is annotated with `@disable` +fail_compilation/fail10968.d(77): Error: struct `fail10968.SD` is not copyable because it is annotated with `@disable` +fail_compilation/fail10968.d(78): Error: struct `fail10968.SD` is not copyable because it is annotated with `@disable` +fail_compilation/fail10968.d(79): Error: struct `fail10968.SD` is not copyable because it is annotated with `@disable` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12622.d b/gcc/testsuite/gdc.test/fail_compilation/fail12622.d index 6d81468d4..1a8b18511 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail12622.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail12622.d @@ -1,15 +1,16 @@ /* TEST_OUTPUT: --- -fail_compilation/fail12622.d(25): Error: `pure` function `fail12622.foo` cannot call impure function pointer `fp` -fail_compilation/fail12622.d(25): Error: `@nogc` function `fail12622.foo` cannot call non-@nogc function pointer `fp` -fail_compilation/fail12622.d(25): Error: `@safe` function `fail12622.foo` cannot call `@system` function pointer `fp` -fail_compilation/fail12622.d(27): Error: `pure` function `fail12622.foo` cannot call impure function pointer `fp` -fail_compilation/fail12622.d(27): Error: `@nogc` function `fail12622.foo` cannot call non-@nogc function pointer `fp` -fail_compilation/fail12622.d(27): Error: `@safe` function `fail12622.foo` cannot call `@system` function pointer `fp` -fail_compilation/fail12622.d(29): Error: `pure` function `fail12622.foo` cannot call impure function `fail12622.bar` -fail_compilation/fail12622.d(29): Error: `@safe` function `fail12622.foo` cannot call `@system` function `fail12622.bar` -fail_compilation/fail12622.d(29): Error: `@nogc` function `fail12622.foo` cannot call non-@nogc function `fail12622.bar` +fail_compilation/fail12622.d(26): Error: `pure` function `fail12622.foo` cannot call impure function pointer `fp` +fail_compilation/fail12622.d(26): Error: `@nogc` function `fail12622.foo` cannot call non-@nogc function pointer `fp` +fail_compilation/fail12622.d(26): Error: `@safe` function `fail12622.foo` cannot call `@system` function pointer `fp` +fail_compilation/fail12622.d(28): Error: `pure` function `fail12622.foo` cannot call impure function pointer `fp` +fail_compilation/fail12622.d(28): Error: `@nogc` function `fail12622.foo` cannot call non-@nogc function pointer `fp` +fail_compilation/fail12622.d(28): Error: `@safe` function `fail12622.foo` cannot call `@system` function pointer `fp` +fail_compilation/fail12622.d(30): Error: `pure` function `fail12622.foo` cannot call impure function `fail12622.bar` +fail_compilation/fail12622.d(30): Error: `@safe` function `fail12622.foo` cannot call `@system` function `fail12622.bar` +fail_compilation/fail12622.d(20): `fail12622.bar` is declared here +fail_compilation/fail12622.d(30): Error: `@nogc` function `fail12622.foo` cannot call non-@nogc function `fail12622.bar` --- */ // Note that, today nothrow violation errors are accidentally hidden. diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12635.d b/gcc/testsuite/gdc.test/fail_compilation/fail12635.d deleted file mode 100644 index 62000371a..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail12635.d +++ /dev/null @@ -1,15 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail12635.d(13): Error: Cannot generate a segment prefix for a branching instruction ---- -*/ - -void foo() -{ - asm - { - L1: - jmp DS:L1; - } -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13120.d b/gcc/testsuite/gdc.test/fail_compilation/fail13120.d index ec892f4b8..f1cf340b6 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail13120.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail13120.d @@ -16,9 +16,10 @@ void g1(char[] s) pure @nogc /* TEST_OUTPUT: --- -fail_compilation/fail13120.d(34): Error: `pure` function `fail13120.h2` cannot call impure function `fail13120.g2!().g2` -fail_compilation/fail13120.d(34): Error: `@safe` function `fail13120.h2` cannot call `@system` function `fail13120.g2!().g2` -fail_compilation/fail13120.d(34): Error: `@nogc` function `fail13120.h2` cannot call non-@nogc function `fail13120.g2!().g2` +fail_compilation/fail13120.d(35): Error: `pure` function `fail13120.h2` cannot call impure function `fail13120.g2!().g2` +fail_compilation/fail13120.d(35): Error: `@safe` function `fail13120.h2` cannot call `@system` function `fail13120.g2!().g2` +fail_compilation/fail13120.d(27): `fail13120.g2!().g2` is declared here +fail_compilation/fail13120.d(35): Error: `@nogc` function `fail13120.h2` cannot call non-@nogc function `fail13120.g2!().g2` --- */ void f2() {} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13938.d b/gcc/testsuite/gdc.test/fail_compilation/fail13938.d deleted file mode 100644 index ed301dccc..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail13938.d +++ /dev/null @@ -1,16 +0,0 @@ -// REQUIRED_ARGS: -o- -/* -TEST_OUTPUT: ---- -fail_compilation/fail13938.d(14): Error: cannot directly load TLS variable `val` ---- -*/ - -void test1() -{ - static int val; - asm - { - mov EAX, val; - } -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13939.d b/gcc/testsuite/gdc.test/fail_compilation/fail13939.d deleted file mode 100644 index 03fdaf396..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail13939.d +++ /dev/null @@ -1,17 +0,0 @@ -// REQUIRED_ARGS: -o- -fPIC -// DISABLED: win32 win64 -/* -TEST_OUTPUT: ---- -fail_compilation/fail13939.d(15): Error: cannot directly load global variable `val` with PIC code ---- -*/ -version(Windows) static assert(0); -void test1() -{ - __gshared int val; - asm - { - mov EAX, val; - } -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail14009.d b/gcc/testsuite/gdc.test/fail_compilation/fail14009.d index 8a5bfc556..de62d5f09 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail14009.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail14009.d @@ -7,8 +7,18 @@ fail_compilation/fail14009.d(12): Error: expression expected not `:` void main() { + version(GNU) + { + asm { + "" : : "r" 1 ? 2 : 3; // accepted + "" : : "r" 1 ? 2 : : 3; // rejected + } + } + else + { asm { mov EAX, FS: 1 ? 2 : 3; // accepted mov EAX, FS: 1 ? 2 : : 3; // rejected } + } } diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail14407.d b/gcc/testsuite/gdc.test/fail_compilation/fail14407.d index b1ab94c14..3bdeea591 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail14407.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail14407.d @@ -7,15 +7,15 @@ fail_compilation/fail14407.d(23): Deprecation: class `imports.a14407.C` is depre fail_compilation/fail14407.d(23): Deprecation: allocator `imports.a14407.C.new` is deprecated fail_compilation/fail14407.d(23): Error: `pure` function `fail14407.testC` cannot call impure allocator `imports.a14407.C.new` fail_compilation/fail14407.d(23): Error: `@safe` function `fail14407.testC` cannot call `@system` allocator `imports.a14407.C.new` +fail_compilation/imports/a14407.d(5): `imports.a14407.C.new` is declared here fail_compilation/fail14407.d(23): Error: `@nogc` function `fail14407.testC` cannot call non-@nogc allocator `imports.a14407.C.new` fail_compilation/fail14407.d(23): Error: class `imports.a14407.C` member `new` is not accessible fail_compilation/fail14407.d(23): Error: `pure` function `fail14407.testC` cannot call impure constructor `imports.a14407.C.this` fail_compilation/fail14407.d(23): Error: `@safe` function `fail14407.testC` cannot call `@system` constructor `imports.a14407.C.this` +fail_compilation/imports/a14407.d(9): `imports.a14407.C.this` is declared here fail_compilation/fail14407.d(23): Error: `@nogc` function `fail14407.testC` cannot call non-@nogc constructor `imports.a14407.C.this` fail_compilation/fail14407.d(23): Error: class `imports.a14407.C` member `this` is not accessible fail_compilation/fail14407.d(23): Error: allocator `imports.a14407.C.new` is not `nothrow` -fail_compilation/fail14407.d(23): Error: constructor `imports.a14407.C.this` is not `nothrow` -fail_compilation/fail14407.d(21): Error: `nothrow` function `fail14407.testC` may throw --- */ void testC() pure nothrow @safe @nogc @@ -26,19 +26,23 @@ void testC() pure nothrow @safe @nogc /* TEST_OUTPUT: --- -fail_compilation/fail14407.d(46): Deprecation: struct `imports.a14407.S` is deprecated -fail_compilation/fail14407.d(46): Deprecation: allocator `imports.a14407.S.new` is deprecated -fail_compilation/fail14407.d(46): Error: `pure` function `fail14407.testS` cannot call impure allocator `imports.a14407.S.new` -fail_compilation/fail14407.d(46): Error: `@safe` function `fail14407.testS` cannot call `@system` allocator `imports.a14407.S.new` -fail_compilation/fail14407.d(46): Error: `@nogc` function `fail14407.testS` cannot call non-@nogc allocator `imports.a14407.S.new` -fail_compilation/fail14407.d(46): Error: struct `imports.a14407.S` member `new` is not accessible -fail_compilation/fail14407.d(46): Error: `pure` function `fail14407.testS` cannot call impure constructor `imports.a14407.S.this` -fail_compilation/fail14407.d(46): Error: `@safe` function `fail14407.testS` cannot call `@system` constructor `imports.a14407.S.this` -fail_compilation/fail14407.d(46): Error: `@nogc` function `fail14407.testS` cannot call non-@nogc constructor `imports.a14407.S.this` -fail_compilation/fail14407.d(46): Error: struct `imports.a14407.S` member `this` is not accessible -fail_compilation/fail14407.d(46): Error: allocator `imports.a14407.S.new` is not `nothrow` -fail_compilation/fail14407.d(46): Error: constructor `imports.a14407.S.this` is not `nothrow` -fail_compilation/fail14407.d(44): Error: `nothrow` function `fail14407.testS` may throw +fail_compilation/fail14407.d(23): Error: constructor `imports.a14407.C.this` is not `nothrow` +fail_compilation/fail14407.d(21): Error: `nothrow` function `fail14407.testC` may throw +fail_compilation/fail14407.d(50): Deprecation: struct `imports.a14407.S` is deprecated +fail_compilation/fail14407.d(50): Deprecation: allocator `imports.a14407.S.new` is deprecated +fail_compilation/fail14407.d(50): Error: `pure` function `fail14407.testS` cannot call impure allocator `imports.a14407.S.new` +fail_compilation/fail14407.d(50): Error: `@safe` function `fail14407.testS` cannot call `@system` allocator `imports.a14407.S.new` +fail_compilation/imports/a14407.d(14): `imports.a14407.S.new` is declared here +fail_compilation/fail14407.d(50): Error: `@nogc` function `fail14407.testS` cannot call non-@nogc allocator `imports.a14407.S.new` +fail_compilation/fail14407.d(50): Error: struct `imports.a14407.S` member `new` is not accessible +fail_compilation/fail14407.d(50): Error: `pure` function `fail14407.testS` cannot call impure constructor `imports.a14407.S.this` +fail_compilation/fail14407.d(50): Error: `@safe` function `fail14407.testS` cannot call `@system` constructor `imports.a14407.S.this` +fail_compilation/imports/a14407.d(18): `imports.a14407.S.this` is declared here +fail_compilation/fail14407.d(50): Error: `@nogc` function `fail14407.testS` cannot call non-@nogc constructor `imports.a14407.S.this` +fail_compilation/fail14407.d(50): Error: struct `imports.a14407.S` member `this` is not accessible +fail_compilation/fail14407.d(50): Error: allocator `imports.a14407.S.new` is not `nothrow` +fail_compilation/fail14407.d(50): Error: constructor `imports.a14407.S.this` is not `nothrow` +fail_compilation/fail14407.d(48): Error: `nothrow` function `fail14407.testS` may throw --- */ void testS() pure nothrow @safe @nogc diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail14486.d b/gcc/testsuite/gdc.test/fail_compilation/fail14486.d index 28e78ffd3..c5bb941d8 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail14486.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail14486.d @@ -3,18 +3,97 @@ /* TEST_OUTPUT: --- -fail_compilation/fail14486.d(23): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class -fail_compilation/fail14486.d(24): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class -fail_compilation/fail14486.d(25): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class -fail_compilation/fail14486.d(29): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class -fail_compilation/fail14486.d(30): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class -fail_compilation/fail14486.d(31): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class -fail_compilation/fail14486.d(35): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class -fail_compilation/fail14486.d(36): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class -fail_compilation/fail14486.d(37): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class -fail_compilation/fail14486.d(41): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class -fail_compilation/fail14486.d(42): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class -fail_compilation/fail14486.d(43): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class +fail_compilation/fail14486.d(102): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class +fail_compilation/fail14486.d(103): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class +fail_compilation/fail14486.d(104): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class +fail_compilation/fail14486.d(108): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class +fail_compilation/fail14486.d(109): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class +fail_compilation/fail14486.d(110): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class +fail_compilation/fail14486.d(114): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class +fail_compilation/fail14486.d(115): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class +fail_compilation/fail14486.d(116): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class +fail_compilation/fail14486.d(120): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class +fail_compilation/fail14486.d(121): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class +fail_compilation/fail14486.d(122): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class +fail_compilation/fail14486.d(126): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(126): Error: `delete c0` is not `@safe` but is used in `@safe` function `test1a` +fail_compilation/fail14486.d(127): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(127): Error: `pure` function `fail14486.test1a` cannot call impure destructor `fail14486.C1a.~this` +fail_compilation/fail14486.d(127): Error: `@safe` function `fail14486.test1a` cannot call `@system` destructor `fail14486.C1a.~this` +fail_compilation/fail14486.d(101): `fail14486.C1a.~this` is declared here +fail_compilation/fail14486.d(127): Error: `@nogc` function `fail14486.test1a` cannot call non-@nogc destructor `fail14486.C1a.~this` +fail_compilation/fail14486.d(128): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(128): Error: `pure` function `fail14486.test1a` cannot call impure destructor `fail14486.C2a.~this` +fail_compilation/fail14486.d(128): Error: `@safe` function `fail14486.test1a` cannot call `@system` destructor `fail14486.C2a.~this` +fail_compilation/fail14486.d(102): `fail14486.C2a.~this` is declared here +fail_compilation/fail14486.d(128): Error: `@nogc` function `fail14486.test1a` cannot call non-@nogc destructor `fail14486.C2a.~this` +fail_compilation/fail14486.d(129): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(129): Error: `pure` function `fail14486.test1a` cannot call impure deallocator `fail14486.C3a.delete` +fail_compilation/fail14486.d(129): Error: `@safe` function `fail14486.test1a` cannot call `@system` deallocator `fail14486.C3a.delete` +fail_compilation/fail14486.d(103): `fail14486.C3a.delete` is declared here +fail_compilation/fail14486.d(129): Error: `@nogc` function `fail14486.test1a` cannot call non-@nogc deallocator `fail14486.C3a.delete` +fail_compilation/fail14486.d(130): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(130): Error: `delete c4` is not `@safe` but is used in `@safe` function `test1a` +fail_compilation/fail14486.d(135): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(136): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(137): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(138): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(139): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(136): Error: destructor `fail14486.C1b.~this` is not `nothrow` +fail_compilation/fail14486.d(137): Error: destructor `fail14486.C2b.~this` is not `nothrow` +fail_compilation/fail14486.d(138): Error: deallocator `fail14486.C3b.delete` is not `nothrow` +fail_compilation/fail14486.d(133): Error: `nothrow` function `fail14486.test1b` may throw +fail_compilation/fail14486.d(144): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(144): Error: `delete s0` is not `@safe` but is used in `@safe` function `test2a` +fail_compilation/fail14486.d(145): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(145): Error: `pure` function `fail14486.test2a` cannot call impure destructor `fail14486.S1a.~this` +fail_compilation/fail14486.d(145): Error: `@safe` function `fail14486.test2a` cannot call `@system` destructor `fail14486.S1a.~this` +fail_compilation/fail14486.d(113): `fail14486.S1a.~this` is declared here +fail_compilation/fail14486.d(145): Error: `@nogc` function `fail14486.test2a` cannot call non-@nogc destructor `fail14486.S1a.~this` +fail_compilation/fail14486.d(146): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(146): Error: `pure` function `fail14486.test2a` cannot call impure destructor `fail14486.S2a.~this` +fail_compilation/fail14486.d(146): Error: `@safe` function `fail14486.test2a` cannot call `@system` destructor `fail14486.S2a.~this` +fail_compilation/fail14486.d(114): `fail14486.S2a.~this` is declared here +fail_compilation/fail14486.d(146): Error: `@nogc` function `fail14486.test2a` cannot call non-@nogc destructor `fail14486.S2a.~this` +fail_compilation/fail14486.d(147): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(147): Error: `pure` function `fail14486.test2a` cannot call impure deallocator `fail14486.S3a.delete` +fail_compilation/fail14486.d(147): Error: `@safe` function `fail14486.test2a` cannot call `@system` deallocator `fail14486.S3a.delete` +fail_compilation/fail14486.d(115): `fail14486.S3a.delete` is declared here +fail_compilation/fail14486.d(147): Error: `@nogc` function `fail14486.test2a` cannot call non-@nogc deallocator `fail14486.S3a.delete` +fail_compilation/fail14486.d(148): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(153): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(154): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(155): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(156): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(157): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(154): Error: destructor `fail14486.S1b.~this` is not `nothrow` +fail_compilation/fail14486.d(155): Error: destructor `fail14486.S2b.~this` is not `nothrow` +fail_compilation/fail14486.d(156): Error: deallocator `fail14486.S3b.delete` is not `nothrow` +fail_compilation/fail14486.d(151): Error: `nothrow` function `fail14486.test2b` may throw +fail_compilation/fail14486.d(162): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(162): Error: `delete a0` is not `@safe` but is used in `@safe` function `test3a` +fail_compilation/fail14486.d(163): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(163): Error: `pure` function `fail14486.test3a` cannot call impure destructor `fail14486.S1a.~this` +fail_compilation/fail14486.d(163): Error: `@safe` function `fail14486.test3a` cannot call `@system` destructor `fail14486.S1a.~this` +fail_compilation/fail14486.d(113): `fail14486.S1a.~this` is declared here +fail_compilation/fail14486.d(163): Error: `@nogc` function `fail14486.test3a` cannot call non-@nogc destructor `fail14486.S1a.~this` +fail_compilation/fail14486.d(164): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(164): Error: `pure` function `fail14486.test3a` cannot call impure destructor `fail14486.S2a.~this` +fail_compilation/fail14486.d(164): Error: `@safe` function `fail14486.test3a` cannot call `@system` destructor `fail14486.S2a.~this` +fail_compilation/fail14486.d(114): `fail14486.S2a.~this` is declared here +fail_compilation/fail14486.d(164): Error: `@nogc` function `fail14486.test3a` cannot call non-@nogc destructor `fail14486.S2a.~this` +fail_compilation/fail14486.d(165): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(165): Error: `delete a3` is not `@safe` but is used in `@safe` function `test3a` +fail_compilation/fail14486.d(166): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(166): Error: `delete a4` is not `@safe` but is used in `@safe` function `test3a` +fail_compilation/fail14486.d(171): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(172): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(173): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(174): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(175): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. +fail_compilation/fail14486.d(172): Error: destructor `fail14486.S1b.~this` is not `nothrow` +fail_compilation/fail14486.d(173): Error: destructor `fail14486.S2b.~this` is not `nothrow` +fail_compilation/fail14486.d(169): Error: `nothrow` function `fail14486.test3b` may throw --- */ @@ -42,27 +121,6 @@ struct S2b { ~this() {} nothrow delete(void* p) {} } struct S3b { nothrow ~this() {} delete(void* p) {} } struct S4b { nothrow ~this() {} nothrow delete(void* p) {} } -/* -TEST_OUTPUT: ---- -fail_compilation/fail14486.d(68): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(68): Error: `delete c0` is not `@safe` but is used in `@safe` function `test1a` -fail_compilation/fail14486.d(69): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(69): Error: `pure` function `fail14486.test1a` cannot call impure destructor `fail14486.C1a.~this` -fail_compilation/fail14486.d(69): Error: `@safe` function `fail14486.test1a` cannot call `@system` destructor `fail14486.C1a.~this` -fail_compilation/fail14486.d(69): Error: `@nogc` function `fail14486.test1a` cannot call non-@nogc destructor `fail14486.C1a.~this` -fail_compilation/fail14486.d(70): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(70): Error: `pure` function `fail14486.test1a` cannot call impure destructor `fail14486.C2a.~this` -fail_compilation/fail14486.d(70): Error: `@safe` function `fail14486.test1a` cannot call `@system` destructor `fail14486.C2a.~this` -fail_compilation/fail14486.d(70): Error: `@nogc` function `fail14486.test1a` cannot call non-@nogc destructor `fail14486.C2a.~this` -fail_compilation/fail14486.d(71): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(71): Error: `pure` function `fail14486.test1a` cannot call impure deallocator `fail14486.C3a.delete` -fail_compilation/fail14486.d(71): Error: `@safe` function `fail14486.test1a` cannot call `@system` deallocator `fail14486.C3a.delete` -fail_compilation/fail14486.d(71): Error: `@nogc` function `fail14486.test1a` cannot call non-@nogc deallocator `fail14486.C3a.delete` -fail_compilation/fail14486.d(72): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(72): Error: `delete c4` is not `@safe` but is used in `@safe` function `test1a` ---- -*/ void test1a() @nogc pure @safe { C0a c0; delete c0; // error @@ -72,20 +130,6 @@ void test1a() @nogc pure @safe C4a c4; delete c4; // no error } -/* -TEST_OUTPUT: ---- -fail_compilation/fail14486.d(91): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(92): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(93): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(94): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(95): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(92): Error: destructor `fail14486.C1b.~this` is not `nothrow` -fail_compilation/fail14486.d(93): Error: destructor `fail14486.C2b.~this` is not `nothrow` -fail_compilation/fail14486.d(94): Error: deallocator `fail14486.C3b.delete` is not `nothrow` -fail_compilation/fail14486.d(89): Error: `nothrow` function `fail14486.test1b` may throw ---- -*/ void test1b() nothrow { C0b c0; delete c0; // no error @@ -95,26 +139,6 @@ void test1b() nothrow C4b c4; delete c4; // no error } -/* -TEST_OUTPUT: ---- -fail_compilation/fail14486.d(120): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(120): Error: `delete s0` is not `@safe` but is used in `@safe` function `test2a` -fail_compilation/fail14486.d(121): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(121): Error: `pure` function `fail14486.test2a` cannot call impure destructor `fail14486.S1a.~this` -fail_compilation/fail14486.d(121): Error: `@safe` function `fail14486.test2a` cannot call `@system` destructor `fail14486.S1a.~this` -fail_compilation/fail14486.d(121): Error: `@nogc` function `fail14486.test2a` cannot call non-@nogc destructor `fail14486.S1a.~this` -fail_compilation/fail14486.d(122): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(122): Error: `pure` function `fail14486.test2a` cannot call impure destructor `fail14486.S2a.~this` -fail_compilation/fail14486.d(122): Error: `@safe` function `fail14486.test2a` cannot call `@system` destructor `fail14486.S2a.~this` -fail_compilation/fail14486.d(122): Error: `@nogc` function `fail14486.test2a` cannot call non-@nogc destructor `fail14486.S2a.~this` -fail_compilation/fail14486.d(123): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(123): Error: `pure` function `fail14486.test2a` cannot call impure deallocator `fail14486.S3a.delete` -fail_compilation/fail14486.d(123): Error: `@safe` function `fail14486.test2a` cannot call `@system` deallocator `fail14486.S3a.delete` -fail_compilation/fail14486.d(123): Error: `@nogc` function `fail14486.test2a` cannot call non-@nogc deallocator `fail14486.S3a.delete` -fail_compilation/fail14486.d(124): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. ---- -*/ void test2a() @nogc pure @safe { S0a* s0; delete s0; // error @@ -124,20 +148,6 @@ void test2a() @nogc pure @safe S4a* s4; delete s4; // no error } -/* -TEST_OUTPUT: ---- -fail_compilation/fail14486.d(143): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(144): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(145): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(146): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(147): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(144): Error: destructor `fail14486.S1b.~this` is not `nothrow` -fail_compilation/fail14486.d(145): Error: destructor `fail14486.S2b.~this` is not `nothrow` -fail_compilation/fail14486.d(146): Error: deallocator `fail14486.S3b.delete` is not `nothrow` -fail_compilation/fail14486.d(141): Error: `nothrow` function `fail14486.test2b` may throw ---- -*/ void test2b() nothrow { S0b* s0; delete s0; // no error @@ -147,25 +157,6 @@ void test2b() nothrow S4b* s4; delete s4; // no error } -/* -TEST_OUTPUT: ---- -fail_compilation/fail14486.d(171): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(171): Error: `delete a0` is not `@safe` but is used in `@safe` function `test3a` -fail_compilation/fail14486.d(172): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(172): Error: `pure` function `fail14486.test3a` cannot call impure destructor `fail14486.S1a.~this` -fail_compilation/fail14486.d(172): Error: `@safe` function `fail14486.test3a` cannot call `@system` destructor `fail14486.S1a.~this` -fail_compilation/fail14486.d(172): Error: `@nogc` function `fail14486.test3a` cannot call non-@nogc destructor `fail14486.S1a.~this` -fail_compilation/fail14486.d(173): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(173): Error: `pure` function `fail14486.test3a` cannot call impure destructor `fail14486.S2a.~this` -fail_compilation/fail14486.d(173): Error: `@safe` function `fail14486.test3a` cannot call `@system` destructor `fail14486.S2a.~this` -fail_compilation/fail14486.d(173): Error: `@nogc` function `fail14486.test3a` cannot call non-@nogc destructor `fail14486.S2a.~this` -fail_compilation/fail14486.d(174): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(174): Error: `delete a3` is not `@safe` but is used in `@safe` function `test3a` -fail_compilation/fail14486.d(175): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(175): Error: `delete a4` is not `@safe` but is used in `@safe` function `test3a` ---- -*/ void test3a() @nogc pure @safe { S0a[] a0; delete a0; // error @@ -175,19 +166,6 @@ void test3a() @nogc pure @safe S4a[] a4; delete a4; // error } -/* -TEST_OUTPUT: ---- -fail_compilation/fail14486.d(193): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(194): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(195): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(196): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(197): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead. -fail_compilation/fail14486.d(194): Error: destructor `fail14486.S1b.~this` is not `nothrow` -fail_compilation/fail14486.d(195): Error: destructor `fail14486.S2b.~this` is not `nothrow` -fail_compilation/fail14486.d(191): Error: `nothrow` function `fail14486.test3b` may throw ---- -*/ void test3b() nothrow { S0b[] a0; delete a0; // no error diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail15044.d b/gcc/testsuite/gdc.test/fail_compilation/fail15044.d index 075777dad..964dcf913 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail15044.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail15044.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail15044.d(30): Error: generated function `fail15044.V.opAssign` is not callable because it is annotated with `@disable` +fail_compilation/fail15044.d(30): Error: generated function `fail15044.V.opAssign` cannot be used because it is annotated with `@disable` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail152.d b/gcc/testsuite/gdc.test/fail_compilation/fail152.d index 7448937f6..ef85562cd 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail152.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail152.d @@ -12,13 +12,17 @@ void a(X...)(X expr) alias X[0] var1; version(GNU) { - version(X86) asm {"fstpd %0;" : "=m" (var1) : : ;} - else version(X86_64) asm {"fstpd %0;" : "=m" (var1) : : ;} - else static assert(false, "ASM code not implemented for this architecture"); + asm { + "" : "=m" (X[0]); + "" : "=m" (var1); + } } - else asm { - //fld double ptr X[0]; // (1) segfaults - fstp double ptr var1; // (2) ICE + else + { + asm { + //fld double ptr X[0]; // (1) segfaults + fstp double ptr var1; // (2) ICE + } } } diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail17602.d b/gcc/testsuite/gdc.test/fail_compilation/fail17602.d new file mode 100644 index 000000000..418f79d3f --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail17602.d @@ -0,0 +1,17 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail17602.d(16): Error: cannot implicitly convert expression `cast(Status)0` of type `imports.imp17602.Status` to `fail17602.Status` +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=17602 + +import imports.imp17602; + +enum Status { off } + +void main() +{ + Status status = imports.imp17602.Status.on; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail18620.d b/gcc/testsuite/gdc.test/fail_compilation/fail18620.d new file mode 100644 index 000000000..ce202b884 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail18620.d @@ -0,0 +1,21 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail18620.d(14): Error: `strlen` cannot be interpreted at compile time, because it has no available source code +fail_compilation/fail18620.d(19): compile time context created here +fail_compilation/fail18620.d(14): Error: `strlen` cannot be interpreted at compile time, because it has no available source code +fail_compilation/fail18620.d(20): compile time context created here +--- +*/ +class A{ + this(const(char)* s) + { + import core.stdc.string; + auto a=strlen(s); + } +} + +void main(){ + static a = new A("a"); + __gshared b = new A("b"); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail18719.d b/gcc/testsuite/gdc.test/fail_compilation/fail18719.d index 9976d4462..7bf7e3b5f 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail18719.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail18719.d @@ -4,7 +4,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail18719.d(29): Deprecation: immutable field `x` initialized multiple times +fail_compilation/fail18719.d(29): Deprecation: immutable field `x` was initialized in a previous constructor call --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail18985.d b/gcc/testsuite/gdc.test/fail_compilation/fail18985.d new file mode 100644 index 000000000..830a6792a --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail18985.d @@ -0,0 +1,18 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail18985.d(16): Error: `foo` is not a scalar, it is a `object.Object` +fail_compilation/fail18985.d(17): Error: `bar` is not a scalar, it is a `shared(Object)` +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=18985 + +Object foo; +shared Object bar; + +void main() +{ + foo += 1; + bar += 1; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail18994.d b/gcc/testsuite/gdc.test/fail_compilation/fail18994.d new file mode 100644 index 000000000..d40081b2a --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail18994.d @@ -0,0 +1,20 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail18994.d(19): Error: struct `fail18994.Type1` is not copyable because it is annotated with `@disable` +--- +*/ +struct Type2 +{ + int opApply(int delegate(ref Type1)) { return 0; } +} + +struct Type1 +{ + @disable this(this); +} + +void test() +{ + foreach(b; Type2()) {} +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19038.d b/gcc/testsuite/gdc.test/fail_compilation/fail19038.d new file mode 100644 index 000000000..ef1a8b767 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail19038.d @@ -0,0 +1,29 @@ +/* TEST_OUTPUT: +--- +fail_compilation/fail19038.d(21): Error: cannot implicitly convert expression `a` of type `string[][]` to `const(string)[][]` +fail_compilation/fail19038.d(23): Error: cannot modify `const` expression `c[0]` +--- + * Credit: yshui + * https://github.com/dlang/dmd/pull/8413#issuecomment-401104961 + * https://issues.dlang.org/show_bug.cgi?id=19038 + */ + + +void test() +{ + /* string[][] is not implicitly converible to const(string)[][], + * and there is good reason why: + * + * https://stackoverflow.com/questions/5055655/double-pointer-const-correctness-warnings-in-c + */ + + string[][] a = [["Lord"]]; + const(string)[][] b = a; // assume this works (and it should not) + const(string)[] c = ["Sauron"]; + c[0] = "Mordor"; // invalid, because c[0] is const(string) + + b[0] = c; // valid, b[0] is const(string)[] + // But now, a[0] has become c + a[0][0] = "Nazgul"; // valid, because a[0][0] is string + // But this also changes c[0], which shouldn't be possible +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19181.d b/gcc/testsuite/gdc.test/fail_compilation/fail19181.d new file mode 100644 index 000000000..873e2929e --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail19181.d @@ -0,0 +1,16 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail19181.d(15): Error: undefined identifier `LanguageError` +--- +*/ +struct S +{ + void opDispatch(string name, T)(T arg) { } +} + +void main() +{ + S s; + s.foo(LanguageError); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail2350.d b/gcc/testsuite/gdc.test/fail_compilation/fail2350.d deleted file mode 100644 index 83208b434..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail2350.d +++ /dev/null @@ -1,15 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail2350.d(8): Error: function `fail2350.test2350` naked assembly functions with contracts are not supported ---- -*/ - -void test2350() -in -{ -} -body -{ - asm { naked; } -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail274.d b/gcc/testsuite/gdc.test/fail_compilation/fail274.d index 8699f51fd..c3be59074 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail274.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail274.d @@ -7,5 +7,6 @@ fail_compilation/fail274.d(10): Error: expression expected not `;` void main() { - asm { inc [; } + version(GNU) asm { "" : [; } + else asm { inc [; } } diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail327.d b/gcc/testsuite/gdc.test/fail_compilation/fail327.d index 4402bb120..140b8c7d5 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail327.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail327.d @@ -7,11 +7,6 @@ fail_compilation/fail327.d(10): Error: `asm` statement is assumed to be `@system @safe void foo() { - version(GNU) - { - version(X86) asm {"xor %%EAX,%%EAX" : : : ;} - else version(X86_64) asm {"xor %%EAX,%%EAX" : : : ;} - else static assert(false, "ASM code not implemented for this architecture"); - } + version(GNU) asm { ""; } else asm { xor EAX,EAX; } } diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail328.d b/gcc/testsuite/gdc.test/fail_compilation/fail328.d index 677c6cfe7..12347e579 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail328.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail328.d @@ -1,7 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/fail328.d(12): Error: `@safe` function `fail328.foo` cannot call `@system` function `fail328.bar` +fail_compilation/fail328.d(13): Error: `@safe` function `fail328.foo` cannot call `@system` function `fail328.bar` +fail_compilation/fail328.d(9): `fail328.bar` is declared here --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail3354.d b/gcc/testsuite/gdc.test/fail_compilation/fail3354.d deleted file mode 100644 index bcf6368c4..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail3354.d +++ /dev/null @@ -1,12 +0,0 @@ - -void main() -{ - version(D_InlineAsm_X86) {} - else version(D_InlineAsm_X64) {} - else static assert(0); - - asm { - fldz ST(0), ST(1), ST(2), ST(3); - fld ST(0), ST(1), ST(2), ST(3); - } -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail341.d b/gcc/testsuite/gdc.test/fail_compilation/fail341.d index 05632aa65..a883e6e70 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail341.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail341.d @@ -2,7 +2,7 @@ TEST_OUTPUT: --- fail_compilation/fail341.d(26): Error: struct `fail341.S` is not copyable because it is annotated with `@disable` -fail_compilation/fail341.d(27): Error: function `fail341.foo` is not callable because it is annotated with `@disable` +fail_compilation/fail341.d(27): Error: function `fail341.foo` cannot be used because it is annotated with `@disable` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail353.d b/gcc/testsuite/gdc.test/fail_compilation/fail353.d deleted file mode 100644 index 56cda779d..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail353.d +++ /dev/null @@ -1,42 +0,0 @@ -/* -TEST_OUTPUT: ---- -block displacement of -130 exceeds the maximum offset of -128 to 127. ---- -*/ - -void foo() -{ - enum NOP = 0x9090_9090_9090_9090; - - version(GNU) - { - version(X86) asm { - "L1:" - "dq %0,%0,%0,%0;" - "dq %0,%0,%0,%0;" - "dq %0,%0,%0,%0;" - "dq %0,%0,%0,%0;" - "loop L1;" : "n" (NOP) : : ; - } - else version(X86_64) asm { - "L1:" - "dq %0,%0,%0,%0;" - "dq %0,%0,%0,%0;" - "dq %0,%0,%0,%0;" - "dq %0,%0,%0,%0;" - "loop L1;" : "n" (NOP) : : ; - } - else static assert(false, "ASM code not implemented for this architecture"); - } - else asm - { - L1: - dq NOP,NOP,NOP,NOP; // 32 - dq NOP,NOP,NOP,NOP; // 64 - dq NOP,NOP,NOP,NOP; // 96 - dq NOP,NOP,NOP,NOP; // 128 - // unnoticed signed underflow of rel8 with DMD2.056 - loop L1; - } -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail3672.d b/gcc/testsuite/gdc.test/fail_compilation/fail3672.d index a7321448e..e03c664b8 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail3672.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail3672.d @@ -2,7 +2,7 @@ TEST_OUTPUT: --- fail_compilation/fail3672.d(27): Error: read-modify-write operations are not allowed for `shared` variables. Use `core.atomic.atomicOp!"+="(*p, 1)` instead. -fail_compilation/fail3672.d(31): Error: read-modify-write operations are not allowed for `shared` variables. Use `core.atomic.atomicOp!"+="(*sfp, 1)` instead. +fail_compilation/fail3672.d(31): Error: none of the `opOpAssign` overloads of `SF` are callable for `*sfp` of type `shared(SF)` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail7848.d b/gcc/testsuite/gdc.test/fail_compilation/fail7848.d index b5445cadd..ec90899f2 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail7848.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail7848.d @@ -3,28 +3,32 @@ /* TEST_OUTPUT: --- -fail_compilation/fail7848.d(45): Deprecation: class allocators have been deprecated, consider moving the allocation strategy outside of the class -fail_compilation/fail7848.d(51): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class -fail_compilation/fail7848.d(37): Error: `pure` function `fail7848.C.__unittest_L35_C30` cannot call impure function `fail7848.func` -fail_compilation/fail7848.d(37): Error: `@safe` function `fail7848.C.__unittest_L35_C30` cannot call `@system` function `fail7848.func` -fail_compilation/fail7848.d(37): Error: `@nogc` function `fail7848.C.__unittest_L35_C30` cannot call non-@nogc function `fail7848.func` -fail_compilation/fail7848.d(37): Error: function `fail7848.func` is not `nothrow` -fail_compilation/fail7848.d(35): Error: `nothrow` function `fail7848.C.__unittest_L35_C30` may throw -fail_compilation/fail7848.d(42): Error: `pure` function `fail7848.C.__invariant1` cannot call impure function `fail7848.func` -fail_compilation/fail7848.d(42): Error: `@safe` function `fail7848.C.__invariant1` cannot call `@system` function `fail7848.func` -fail_compilation/fail7848.d(42): Error: `@nogc` function `fail7848.C.__invariant1` cannot call non-@nogc function `fail7848.func` -fail_compilation/fail7848.d(42): Error: function `fail7848.func` is not `nothrow` -fail_compilation/fail7848.d(40): Error: `nothrow` function `fail7848.C.__invariant1` may throw -fail_compilation/fail7848.d(47): Error: `pure` allocator `fail7848.C.new` cannot call impure function `fail7848.func` -fail_compilation/fail7848.d(47): Error: `@safe` allocator `fail7848.C.new` cannot call `@system` function `fail7848.func` -fail_compilation/fail7848.d(47): Error: `@nogc` allocator `fail7848.C.new` cannot call non-@nogc function `fail7848.func` -fail_compilation/fail7848.d(47): Error: function `fail7848.func` is not `nothrow` -fail_compilation/fail7848.d(45): Error: `nothrow` allocator `fail7848.C.new` may throw -fail_compilation/fail7848.d(53): Error: `pure` deallocator `fail7848.C.delete` cannot call impure function `fail7848.func` -fail_compilation/fail7848.d(53): Error: `@safe` deallocator `fail7848.C.delete` cannot call `@system` function `fail7848.func` -fail_compilation/fail7848.d(53): Error: `@nogc` deallocator `fail7848.C.delete` cannot call non-@nogc function `fail7848.func` -fail_compilation/fail7848.d(53): Error: function `fail7848.func` is not `nothrow` -fail_compilation/fail7848.d(51): Error: `nothrow` deallocator `fail7848.C.delete` may throw +fail_compilation/fail7848.d(49): Deprecation: class allocators have been deprecated, consider moving the allocation strategy outside of the class +fail_compilation/fail7848.d(55): Deprecation: class deallocators have been deprecated, consider moving the deallocation strategy outside of the class +fail_compilation/fail7848.d(41): Error: `pure` function `fail7848.C.__unittest_L39_C30` cannot call impure function `fail7848.func` +fail_compilation/fail7848.d(41): Error: `@safe` function `fail7848.C.__unittest_L39_C30` cannot call `@system` function `fail7848.func` +fail_compilation/fail7848.d(35): `fail7848.func` is declared here +fail_compilation/fail7848.d(41): Error: `@nogc` function `fail7848.C.__unittest_L39_C30` cannot call non-@nogc function `fail7848.func` +fail_compilation/fail7848.d(41): Error: function `fail7848.func` is not `nothrow` +fail_compilation/fail7848.d(39): Error: `nothrow` function `fail7848.C.__unittest_L39_C30` may throw +fail_compilation/fail7848.d(46): Error: `pure` function `fail7848.C.__invariant1` cannot call impure function `fail7848.func` +fail_compilation/fail7848.d(46): Error: `@safe` function `fail7848.C.__invariant1` cannot call `@system` function `fail7848.func` +fail_compilation/fail7848.d(35): `fail7848.func` is declared here +fail_compilation/fail7848.d(46): Error: `@nogc` function `fail7848.C.__invariant1` cannot call non-@nogc function `fail7848.func` +fail_compilation/fail7848.d(46): Error: function `fail7848.func` is not `nothrow` +fail_compilation/fail7848.d(44): Error: `nothrow` function `fail7848.C.__invariant1` may throw +fail_compilation/fail7848.d(51): Error: `pure` allocator `fail7848.C.new` cannot call impure function `fail7848.func` +fail_compilation/fail7848.d(51): Error: `@safe` allocator `fail7848.C.new` cannot call `@system` function `fail7848.func` +fail_compilation/fail7848.d(35): `fail7848.func` is declared here +fail_compilation/fail7848.d(51): Error: `@nogc` allocator `fail7848.C.new` cannot call non-@nogc function `fail7848.func` +fail_compilation/fail7848.d(51): Error: function `fail7848.func` is not `nothrow` +fail_compilation/fail7848.d(49): Error: `nothrow` allocator `fail7848.C.new` may throw +fail_compilation/fail7848.d(57): Error: `pure` deallocator `fail7848.C.delete` cannot call impure function `fail7848.func` +fail_compilation/fail7848.d(57): Error: `@safe` deallocator `fail7848.C.delete` cannot call `@system` function `fail7848.func` +fail_compilation/fail7848.d(35): `fail7848.func` is declared here +fail_compilation/fail7848.d(57): Error: `@nogc` deallocator `fail7848.C.delete` cannot call non-@nogc function `fail7848.func` +fail_compilation/fail7848.d(57): Error: function `fail7848.func` is not `nothrow` +fail_compilation/fail7848.d(55): Error: `nothrow` deallocator `fail7848.C.delete` may throw --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9665a.d b/gcc/testsuite/gdc.test/fail_compilation/fail9665a.d index 54ff6779a..074d7e58b 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail9665a.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail9665a.d @@ -1,15 +1,41 @@ // REQUIRED_ARGS: // PERMUTE_ARGS: - -/***************************************************/ -// immutable field - /+ TEST_OUTPUT: --- -fail_compilation/fail9665a.d(19): Error: immutable field `v` initialized multiple times +fail_compilation/fail9665a.d(45): Error: immutable field `v` initialized multiple times +fail_compilation/fail9665a.d(44): Previous initialization is here. +fail_compilation/fail9665a.d(55): Error: immutable field `v` initialized multiple times +fail_compilation/fail9665a.d(54): Previous initialization is here. +fail_compilation/fail9665a.d(60): Error: immutable field `v` initialized multiple times +fail_compilation/fail9665a.d(59): Previous initialization is here. +fail_compilation/fail9665a.d(65): Error: immutable field `v` initialized multiple times +fail_compilation/fail9665a.d(64): Previous initialization is here. +fail_compilation/fail9665a.d(75): Error: immutable field `v` initialized multiple times +fail_compilation/fail9665a.d(74): Previous initialization is here. +fail_compilation/fail9665a.d(80): Error: immutable field `v` initialized multiple times +fail_compilation/fail9665a.d(79): Previous initialization is here. +fail_compilation/fail9665a.d(85): Error: immutable field `v` initialized multiple times +fail_compilation/fail9665a.d(84): Previous initialization is here. +fail_compilation/fail9665a.d(98): Error: immutable field `v` initialization is not allowed in loops or after labels +fail_compilation/fail9665a.d(103): Error: immutable field `v` initialization is not allowed in loops or after labels +fail_compilation/fail9665a.d(108): Error: immutable field `v` initialized multiple times +fail_compilation/fail9665a.d(107): Previous initialization is here. +fail_compilation/fail9665a.d(113): Error: immutable field `v` initialized multiple times +fail_compilation/fail9665a.d(112): Previous initialization is here. +fail_compilation/fail9665a.d(118): Error: immutable field `v` initialized multiple times +fail_compilation/fail9665a.d(117): Previous initialization is here. +fail_compilation/fail9665a.d(132): Error: immutable field `v` initialized multiple times +fail_compilation/fail9665a.d(131): Previous initialization is here. +fail_compilation/fail9665a.d(136): Error: immutable field `w` initialized multiple times +fail_compilation/fail9665a.d(135): Previous initialization is here. +fail_compilation/fail9665a.d(150): Error: static assert: `__traits(compiles, this.v = 1)` is false --- +/ + +/***************************************************/ +// immutable field + struct S1A { immutable int v; @@ -20,14 +46,6 @@ struct S1A } } -/+ -TEST_OUTPUT: ---- -fail_compilation/fail9665a.d(37): Error: immutable field `v` initialized multiple times -fail_compilation/fail9665a.d(42): Error: immutable field `v` initialized multiple times -fail_compilation/fail9665a.d(47): Error: immutable field `v` initialized multiple times ---- -+/ struct S1B { immutable int v; @@ -48,14 +66,6 @@ struct S1B } } -/+ -TEST_OUTPUT: ---- -fail_compilation/fail9665a.d(65): Error: immutable field `v` initialized multiple times -fail_compilation/fail9665a.d(70): Error: immutable field `v` initialized multiple times -fail_compilation/fail9665a.d(75): Error: immutable field `v` initialized multiple times ---- -+/ struct S1C { immutable int v; @@ -79,16 +89,6 @@ struct S1C /***************************************************/ // with control flow -/+ -TEST_OUTPUT: ---- -fail_compilation/fail9665a.d(98): Error: immutable field `v` initialization is not allowed in loops or after labels -fail_compilation/fail9665a.d(103): Error: immutable field `v` initialization is not allowed in loops or after labels -fail_compilation/fail9665a.d(108): Error: immutable field `v` initialized multiple times -fail_compilation/fail9665a.d(113): Error: immutable field `v` initialized multiple times -fail_compilation/fail9665a.d(118): Error: immutable field `v` initialized multiple times ---- -+/ struct S2 { immutable int v; @@ -122,13 +122,6 @@ struct S2 /***************************************************/ // with immutable constructor -/+ -TEST_OUTPUT: ---- -fail_compilation/fail9665a.d(139): Error: immutable field `v` initialized multiple times -fail_compilation/fail9665a.d(143): Error: immutable field `w` initialized multiple times ---- -+/ struct S3 { int v; @@ -147,12 +140,6 @@ struct S3 /***************************************************/ // in __traits(compiles) -/+ -TEST_OUTPUT: ---- -fail_compilation/fail9665a.d(163): Error: static assert: `__traits(compiles, this.v = 1)` is false ---- -+/ struct S4 { immutable int v; diff --git a/gcc/testsuite/gdc.test/fail_compilation/fix19018.d b/gcc/testsuite/gdc.test/fail_compilation/fix19018.d new file mode 100644 index 000000000..fc1b71024 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fix19018.d @@ -0,0 +1,22 @@ +/* REQUIRED_ARGS: -de + * PERMUTE_ARGS: + * TEST_OUTPUT: +--- +fail_compilation/fix19018.d(17): Deprecation: `0b` isn't a valid integer literal, use `0b0` instead +fail_compilation/fix19018.d(18): Deprecation: `0B` isn't a valid integer literal, use `0B0` instead +fail_compilation/fix19018.d(19): Deprecation: `0x` isn't a valid integer literal, use `0x0` instead +fail_compilation/fix19018.d(20): Deprecation: `0X` isn't a valid integer literal, use `0X0` instead +fail_compilation/fix19018.d(21): Deprecation: `0x_` isn't a valid integer literal, use `0x0` instead +--- + */ + +// https://issues.dlang.org/show_bug.cgi?id=19018 + +void foo() +{ + auto a = 0b; + auto b = 0B; + auto c = 0x; + auto d = 0X; + auto e = 0x_; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fix19059.d b/gcc/testsuite/gdc.test/fail_compilation/fix19059.d new file mode 100644 index 000000000..5d952c74c --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fix19059.d @@ -0,0 +1,18 @@ +/* REQUIRED_ARGS: + * PERMUTE_ARGS: + * TEST_OUTPUT: +--- +fail_compilation/fix19059.d(16): Error: radix 8 digit expected, not `8` +fail_compilation/fix19059.d(16): Error: octal literals `010` are no longer supported, use `std.conv.octal!10` instead +fail_compilation/fix19059.d(17): Error: radix 8 digit expected, not `9` +fail_compilation/fix19059.d(17): Error: octal literals `011` are no longer supported, use `std.conv.octal!11` instead +--- + */ + +// https://issues.dlang.org/show_bug.cgi?id=19059 + +void foo() +{ + auto a = 08; + auto b = 09; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice14929.d b/gcc/testsuite/gdc.test/fail_compilation/ice14929.d index b7724cbec..349e32809 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice14929.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice14929.d @@ -75,7 +75,7 @@ template isComponentStorage(CS, C) { CS cs = CS.init; ulong eid; - cs.add(eid, c); + cs.add(eid, C.init); })); } diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice15239.d b/gcc/testsuite/gdc.test/fail_compilation/ice15239.d index dfae4b71c..c77c562e3 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice15239.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice15239.d @@ -16,8 +16,18 @@ struct T void main() { - asm + version(GNU) { - call T.foo; + asm + { + "" : : "r" T.foo; + } + } + else + { + asm + { + call T.foo; + } } } diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice15441.d b/gcc/testsuite/gdc.test/fail_compilation/ice15441.d index fd1da40c1..f24a09562 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice15441.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice15441.d @@ -1,8 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/ice15441.d(24): Error: variable `ice15441.main.__front$n$` type `void` is inferred from initializer `__r$n$.front()`, and variables cannot be of type `void` -fail_compilation/ice15441.d(24): Error: expression `__r$n$.front()` is `void` and has no value +fail_compilation/ice15441.d(24): Error: variable `ice15441.main.__front3` type `void` is inferred from initializer `__r2.front()`, and variables cannot be of type `void` +fail_compilation/ice15441.d(24): Error: expression `__r2.front()` is `void` and has no value fail_compilation/ice15441.d(24): Error: `s1.front` is `void` and has no value fail_compilation/ice15441.d(27): Error: cannot infer argument types, expected 1 argument, not 2 --- diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/imp17602.d b/gcc/testsuite/gdc.test/fail_compilation/imports/imp17602.d new file mode 100644 index 000000000..c1e679028 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/imp17602.d @@ -0,0 +1,3 @@ +module imports.imp17602; + +enum Status { on } diff --git a/gcc/testsuite/gdc.test/fail_compilation/retscope6.d b/gcc/testsuite/gdc.test/fail_compilation/retscope6.d index 678471094..eafd67bcf 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/retscope6.d +++ b/gcc/testsuite/gdc.test/fail_compilation/retscope6.d @@ -21,3 +21,120 @@ int* test() @safe arr[0] ~= &i; return arr[0][0]; } + +/* TEST_OUTPUT: +--- +fail_compilation/retscope6.d(7034): Error: reference to local variable `i` assigned to non-scope parameter `_param_1` calling retscope6.S.emplace!(int*).emplace +fail_compilation/retscope6.d(7035): Error: reference to local variable `i` assigned to non-scope parameter `_param_0` calling retscope6.S.emplace2!(int*).emplace2 +fail_compilation/retscope6.d(7024): Error: scope variable `_param_2` assigned to `s` with longer lifetime +fail_compilation/retscope6.d(7025): Error: scope variable `_param_2` assigned to `t` with longer lifetime +fail_compilation/retscope6.d(7037): Error: template instance `retscope6.S.emplace4!(int*)` error instantiating +--- +*/ + +#line 7000 + +alias T = int*; + +struct S +{ + T payload; + + static void emplace(Args...)(ref S s, Args args) @safe + { + s.payload = args[0]; + } + + void emplace2(Args...)(Args args) @safe + { + payload = args[0]; + } + + static void emplace3(Args...)(S s, Args args) @safe + { + s.payload = args[0]; + } + + static void emplace4(Args...)(scope ref S s, scope out S t, scope Args args) @safe + { + s.payload = args[0]; + t.payload = args[0]; + } + +} + +void foo() @safe +{ + S s; + int i; + s.emplace(s, &i); + s.emplace2(&i); + s.emplace3(s, &i); + s.emplace4(s, s, &i); +} + + +/* TEST_OUTPUT: +--- +fail_compilation/retscope6.d(8016): Error: reference to local variable `i` assigned to non-scope parameter `s` calling retscope6.frank!().frank +fail_compilation/retscope6.d(8031): Error: reference to local variable `i` assigned to non-scope parameter `p` calling retscope6.betty!().betty +fail_compilation/retscope6.d(8031): Error: reference to local variable `j` assigned to non-scope parameter `q` calling retscope6.betty!().betty +fail_compilation/retscope6.d(8048): Error: reference to local variable `j` assigned to non-scope parameter `q` calling retscope6.archie!().archie +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=19035 + +#line 8000 +@safe +{ + +void escape(int*); + +/**********************/ + +void frank()(ref scope int* p, int* s) +{ + p = s; // should error here +} + +void testfrankly() +{ + int* p; + int i; + frank(p, &i); +} + +/**********************/ + +void betty()(int* p, int* q) +{ + p = q; + escape(p); +} + +void testbetty() +{ + int i; + int j; + betty(&i, &j); // should error on i and j +} + +/**********************/ + +void archie()(int* p, int* q, int* r) +{ + p = q; + r = p; + escape(q); +} + +void testarchie() +{ + int i; + int j; + int k; + archie(&i, &j, &k); // should error on j +} + +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test12979.d b/gcc/testsuite/gdc.test/fail_compilation/test12979.d index adb3028bc..f5482790d 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test12979.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test12979.d @@ -9,8 +9,18 @@ fail_compilation/test12979.d(13): Error: `const`/`immutable`/`shared`/`inout` at void foo() { - asm const shared + version(GNU) { - ret; + asm const shared + { + ""; + } + } + else + { + asm const shared + { + ret; + } } } diff --git a/gcc/testsuite/gdc.test/fail_compilation/test15373.d b/gcc/testsuite/gdc.test/fail_compilation/test15373.d new file mode 100644 index 000000000..1b3cecd2d --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test15373.d @@ -0,0 +1,22 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/test15373.d(21): Error: Runtime type information is not supported for `extern(C++)` classes +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=15373 + +// Using `typeid` on an `extern(C++) class` type is ok as it is evaluated at compile-time +// See test/runnable/test15373.d + +// Using `typeid` on an `extern(C++) class` instance is not ok because `extern(C++) class` +// instances are not rooted in `Object` + +extern(C++) class C { } + +void foo() +{ + C c = new C(); + auto ti = typeid(c); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test17908a.d b/gcc/testsuite/gdc.test/fail_compilation/test17908a.d index 5cc5c0a8c..907239734 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test17908a.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test17908a.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/test17908a.d(10): Error: function `test17908a.foo` is not callable because it is annotated with `@disable` +fail_compilation/test17908a.d(10): Error: function `test17908a.foo` cannot be used because it is annotated with `@disable` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/test17908b.d b/gcc/testsuite/gdc.test/fail_compilation/test17908b.d index 5000f9796..e2c4d84ab 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test17908b.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test17908b.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/test17908b.d(13): Error: function `test17908b.foobar` is not callable because it is annotated with `@disable` +fail_compilation/test17908b.d(13): Error: function `test17908b.foobar` cannot be used because it is annotated with `@disable` --- */ void foobar() {} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test19176.d b/gcc/testsuite/gdc.test/fail_compilation/test19176.d new file mode 100644 index 000000000..bb38d9593 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test19176.d @@ -0,0 +1,27 @@ +/* +REQUIRED_ARGS: -unittest +TEST_OUTPUT: +--- +fail_compilation/test19176.d(19): Error: `static if` conditional cannot be at global scope +fail_compilation/test19176.d(14): Error: `tuple()` has no effect +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=19176 + +void main() +{ + __traits(getUnitTests, foo); +} + +template foo() +{ + static if(true) + { + enum bar; + } + else + { + enum bar; + } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test9701.d b/gcc/testsuite/gdc.test/fail_compilation/test9701.d new file mode 100644 index 000000000..384c51444 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test9701.d @@ -0,0 +1,63 @@ +/* +TEST_OUTPUT +--- +fail_compilation/test9701.d(38): Error: `@safe` is not a valid attribute for enum members +fail_compilation/test9701.d(39): Error: `@system` is not a valid attribute for enum members +fail_compilation/test9701.d(40): Error: `@trusted` is not a valid attribute for enum members +fail_compilation/test9701.d(41): Error: `@nogc` is not a valid attribute for enum members +fail_compilation/test9701.d(42): Error: `pure` is not a valid attribute for enum members +fail_compilation/test9701.d(43): Error: `shared` is not a valid attribute for enum members +fail_compilation/test9701.d(44): Error: `inout` is not a valid attribute for enum members +fail_compilation/test9701.d(45): Error: `immutable` is not a valid attribute for enum members +fail_compilation/test9701.d(46): Error: `const` is not a valid attribute for enum members +fail_compilation/test9701.d(47): Error: `synchronized` is not a valid attribute for enum members +fail_compilation/test9701.d(48): Error: `scope` is not a valid attribute for enum members +fail_compilation/test9701.d(49): Error: `auto` is not a valid attribute for enum members +fail_compilation/test9701.d(50): Error: `ref` is not a valid attribute for enum members +fail_compilation/test9701.d(51): Error: `__gshared` is not a valid attribute for enum members +fail_compilation/test9701.d(52): Error: `final` is not a valid attribute for enum members +fail_compilation/test9701.d(53): Error: `extern` is not a valid attribute for enum members +fail_compilation/test9701.d(54): Error: `export` is not a valid attribute for enum members +fail_compilation/test9701.d(55): Error: `nothrow` is not a valid attribute for enum members +fail_compilation/test9701.d(56): Error: `public` is not a valid attribute for enum members +fail_compilation/test9701.d(57): Error: `private` is not a valid attribute for enum members +fail_compilation/test9701.d(58): Error: `package` is not a valid attribute for enum members +fail_compilation/test9701.d(59): Error: `static` is not a valid attribute for enum members +fail_compilation/test9701.d(60): Error: `static` is not a valid attribute for enum members +fail_compilation/test9701.d(61): Error: `static` is not a valid attribute for enum members +fail_compilation/test9701.d(62): Error: `static` is not a valid attribute for enum members +--- +*/ + +// This test exists to verify that parsing of enum member attributes rejects invalid attributes + +// https://issues.dlang.org/show_bug.cgi?id=9701 + +enum Enum +{ + @safe safe, + @system system, + @trusted trusted, + @nogc nogc, + pure pure_, + shared shared_, + inout inout_, + immutable immutable_, + const const_, + synchronized synchronized_, + scope scope_, + auto auto_, + ref ref_, + __gshared __gshared_, + final final_, + extern extern_, + export export_, + nothrow nothrow_, + public public_, + private private_, + package package_, + static static1, + @("a") static static2, + static @("a") static3, + @("a") static @("b") static3, +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test9701b.d b/gcc/testsuite/gdc.test/fail_compilation/test9701b.d new file mode 100644 index 000000000..16c25413e --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test9701b.d @@ -0,0 +1,22 @@ +/* +REQUIRED_ARGS: -de +TEST_OUTPUT +--- +fail_compilation/test9701b.d(20): Deprecation: enum member `test9701b.Enum.e0` is deprecated +fail_compilation/test9701b.d(21): Deprecation: enum member `test9701b.Enum.e1` is deprecated - message +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=9701 + +enum Enum +{ + deprecated e0, + deprecated("message") e1, +} + +void main() +{ + auto value = Enum.e0; + auto value2 = Enum.e1; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/udaparams.d b/gcc/testsuite/gdc.test/fail_compilation/udaparams.d new file mode 100644 index 000000000..ec760bd60 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/udaparams.d @@ -0,0 +1,57 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/udaparams.d(31): Error: variadic parameter cannot have user-defined attributes +fail_compilation/udaparams.d(32): Error: variadic parameter cannot have user-defined attributes +fail_compilation/udaparams.d(34): Error: user-defined attributes cannot appear as postfixes +fail_compilation/udaparams.d(35): Error: user-defined attributes cannot appear as postfixes +fail_compilation/udaparams.d(36): Error: user-defined attributes cannot appear as postfixes +fail_compilation/udaparams.d(38): Error: `@safe` attribute for function parameter is not supported +fail_compilation/udaparams.d(39): Error: `@safe` attribute for function parameter is not supported +fail_compilation/udaparams.d(40): Error: `@safe` attribute for function parameter is not supported +fail_compilation/udaparams.d(43): Error: `@system` attribute for function parameter is not supported +fail_compilation/udaparams.d(44): Error: `@trusted` attribute for function parameter is not supported +fail_compilation/udaparams.d(45): Error: `@nogc` attribute for function parameter is not supported +fail_compilation/udaparams.d(51): Error: Cannot put a storage-class in an alias declaration. +fail_compilation/udaparams.d(52): Error: Cannot put a storage-class in an alias declaration. +fail_compilation/udaparams.d(53): Error: semicolon expected to close `alias` declaration +fail_compilation/udaparams.d(53): Error: declaration expected, not `=>` +fail_compilation/udaparams.d(54): Error: semicolon expected to close `alias` declaration +fail_compilation/udaparams.d(54): Error: declaration expected, not `=>` +fail_compilation/udaparams.d(57): Error: basic type expected, not `@` +fail_compilation/udaparams.d(57): Error: identifier expected for template value parameter +fail_compilation/udaparams.d(57): Error: found `@` when expecting `)` +fail_compilation/udaparams.d(57): Error: basic type expected, not `3` +fail_compilation/udaparams.d(57): Error: found `3` when expecting `)` +fail_compilation/udaparams.d(57): Error: semicolon expected following function declaration +fail_compilation/udaparams.d(57): Error: declaration expected, not `)` +--- +*/ + +void vararg1(int a, @(10) ...); +extern(C) void vararg2(int a, @(10) ...); + +void rhsuda(int a @(10)); +void rhsuda2(int @(10)); +void rhsuda3(int[] arr @(10) ...); + +void wrongAttr1(@safe int); +void wrongAttr2(@safe void function()); +void wrongAttr3(@safe void delegate()); + + +void test16(A)(A a @system); +void test16(A)(A a @trusted); +void test16(A)(A a @nogc); + +// lambdas without parentheses +alias test19a = @safe b => 1 + 2; +alias test19b = @system b => 1 + 2; +alias test19c = @nogc b => 1 + 2; +alias test19d = @(2) @system b => 1 + 2; +alias test19e = @safe @(2) b => 1 + 2; +alias test19f = extern(C++) b => 1 + 2; +alias test19g = align(2) b => 1 + 2; + +// UDAs on Template parameter aren't supported +void test21(@(3) T)(T t) {} diff --git a/gcc/testsuite/gdc.test/runnable/betterc.d b/gcc/testsuite/gdc.test/runnable/betterc.d index b312dac09..d4655a24d 100644 --- a/gcc/testsuite/gdc.test/runnable/betterc.d +++ b/gcc/testsuite/gdc.test/runnable/betterc.d @@ -81,12 +81,22 @@ mixin template initArray() { T[6] a1 = [true, false, true, true, false, true]; } + else static if (is(T == Sint)) + { + T[6] a1 = [Sint(1), Sint(2), Sint(3), Sint(1), Sint(2), Sint(3)]; + } else { T[6] a1 = [1,2,3,1,2,3]; } } +struct Sint +{ + int x; + this(int v) { x = v;} +} + void testRuntimeLowerings() { // test call to `object.__equals` @@ -96,7 +106,7 @@ void testRuntimeLowerings() assert(a1[0..3] == a1[3..$]); } - + test__equals!int; test__equals!uint; test__equals!long; @@ -110,6 +120,7 @@ void testRuntimeLowerings() test__equals!char; test__equals!(const char); test__equals!bool; + test__equals!Sint; // test call to `object.__cmp` void test__cmp(T)() @@ -129,22 +140,19 @@ void testRuntimeLowerings() test__cmp!byte; test__cmp!dchar; test__cmp!wchar; - - - // __cmp currently requires runtime support from `core.internal.string : dstrcmp`. - // If that runtime dependency can be removed, the following code might work. - //--------------------------------------------------------------------------------- - // test__cmp!ubyte; - // test__cmp!char; - // test__cmp!(const char); - // test__cmp!bool; - - // auto s = "abc"; - // switch(s) // _switch - // { - // case "abc": - // break; - // default: - // break; - // } + test__cmp!ubyte; + test__cmp!char; + test__cmp!(const char); + test__cmp!bool; + test__cmp!Sint; + + // test call to `object.__switch`` + auto s = "abc"; + switch(s) + { + case "abc": + break; + default: + break; + } } diff --git a/gcc/testsuite/gdc.test/runnable/bitops.d b/gcc/testsuite/gdc.test/runnable/bitops.d index eb51f0f47..27b2108c4 100644 --- a/gcc/testsuite/gdc.test/runnable/bitops.d +++ b/gcc/testsuite/gdc.test/runnable/bitops.d @@ -7,7 +7,7 @@ import core.bitop; void test1() { - size_t array[2]; + size_t[2] array; uint x; version (D_LP64) size_t bitToUse = 67; @@ -95,7 +95,7 @@ void test4() void test5() { - size_t array[2]; + size_t[2] array; array[0] = 2; array[1] = 0x100; diff --git a/gcc/testsuite/gdc.test/runnable/cppa.d b/gcc/testsuite/gdc.test/runnable/cppa.d index 0a467b2d7..d4a2b66a0 100644 --- a/gcc/testsuite/gdc.test/runnable/cppa.d +++ b/gcc/testsuite/gdc.test/runnable/cppa.d @@ -5,6 +5,7 @@ import core.stdc.stdio; import core.stdc.stdarg; import core.stdc.config; +import core.stdc.stdint; extern (C++) int foob(int i, int j, int k); @@ -888,13 +889,13 @@ void testVtable() /****************************************/ /* problems detected by fuzzer */ -extern(C++) void fuzz1_cppvararg(long arg10, long arg11, bool arg12); -extern(C++) void fuzz1_dvararg(long arg10, long arg11, bool arg12) +extern(C++) void fuzz1_cppvararg(int64_t arg10, int64_t arg11, bool arg12); +extern(C++) void fuzz1_dvararg(int64_t arg10, int64_t arg11, bool arg12) { fuzz1_checkValues(arg10, arg11, arg12); } -extern(C++) void fuzz1_checkValues(long arg10, long arg11, bool arg12) +extern(C++) void fuzz1_checkValues(int64_t arg10, int64_t arg11, bool arg12) { assert(arg10 == 103); assert(arg11 == 104); @@ -911,13 +912,13 @@ void fuzz1() } //////// -extern(C++) void fuzz2_cppvararg(ulong arg10, ulong arg11, bool arg12); -extern(C++) void fuzz2_dvararg(ulong arg10, ulong arg11, bool arg12) +extern(C++) void fuzz2_cppvararg(uint64_t arg10, uint64_t arg11, bool arg12); +extern(C++) void fuzz2_dvararg(uint64_t arg10, uint64_t arg11, bool arg12) { fuzz2_checkValues(arg10, arg11, arg12); } -extern(C++) void fuzz2_checkValues(ulong arg10, ulong arg11, bool arg12) +extern(C++) void fuzz2_checkValues(uint64_t arg10, uint64_t arg11, bool arg12) { assert(arg10 == 103); assert(arg11 == 104); @@ -1230,7 +1231,7 @@ void test15455() /****************************************/ // 15372 -extern(C++) int foo15372(T)(T v); +extern(C++) int foo15372(T)(int v); void test15372() { @@ -1258,7 +1259,7 @@ void test15802() /****************************************/ // 16536 - mangling mismatch on OSX -version(OSX) extern(C++) ulong pass16536(ulong); +version(OSX) extern(C++) uint64_t pass16536(uint64_t); void test16536() { @@ -1385,7 +1386,7 @@ mixin template scopeAllocCpp(C) enum cppCtorReturnsThis = false; else enum cppCtorReturnsThis = true; - + static if (cppCtorReturnsThis) scope C ptr = new C; else @@ -1446,7 +1447,7 @@ void test18928() // https://issues.dlang.org/show_bug.cgi?id=18953 // Win32: extern(C++) struct destructor not called correctly through runtime -extern(C++) +extern(C++) struct S18953 { char x; diff --git a/gcc/testsuite/gdc.test/runnable/delegate.d b/gcc/testsuite/gdc.test/runnable/delegate.d index a371e1cee..fb8bf5c82 100644 --- a/gcc/testsuite/gdc.test/runnable/delegate.d +++ b/gcc/testsuite/gdc.test/runnable/delegate.d @@ -227,8 +227,8 @@ void test10() class A12 { public: - int delegate(int, int) dgs[4]; - int function(int, int) fps[4]; + int delegate(int, int)[4] dgs; + int function(int, int)[4] fps; int delegate(int, int) dg; int function(int, int) fp; int f(int x, int y) { diff --git a/gcc/testsuite/gdc.test/runnable/dhry.d b/gcc/testsuite/gdc.test/runnable/dhry.d index 812d7d279..c61e609e5 100644 --- a/gcc/testsuite/gdc.test/runnable/dhry.d +++ b/gcc/testsuite/gdc.test/runnable/dhry.d @@ -355,13 +355,13 @@ alias int Enumeration; const int StrLen = 30; -alias int One_Thirty; -alias int One_Fifty; -alias char Capital_Letter; -alias bool Boolean; -alias char Str_30 [StrLen]; -alias int Arr_1_Dim [50]; -alias int Arr_2_Dim [50] [50]; +alias int One_Thirty; +alias int One_Fifty; +alias char Capital_Letter; +alias bool Boolean; +alias char[StrLen] Str_30; +alias int[50] Arr_1_Dim; +alias int[50][50] Arr_2_Dim; struct record { @@ -369,19 +369,19 @@ struct record Enumeration Discr; union V { struct V1 { - Enumeration Enum_Comp; - int Int_Comp; - char Str_Comp [StrLen]; + Enumeration Enum_Comp; + int Int_Comp; + char[StrLen] Str_Comp; } V1 var_1; struct V2 { - Enumeration E_Comp_2; - char Str_2_Comp [StrLen]; + Enumeration E_Comp_2; + char [StrLen] Str_2_Comp; } V2 var_2; struct V3 { - char Ch_1_Comp; - char Ch_2_Comp; + char Ch_1_Comp; + char Ch_2_Comp; } V3 var_3; } @@ -400,8 +400,8 @@ int Int_Glob; Boolean Bool_Glob; char Ch_1_Glob, Ch_2_Glob; -int Arr_1_Glob [50]; -int Arr_2_Glob [50] [50]; +int[50] Arr_1_Glob; +int[50][50] Arr_2_Glob; char[StrLen] Reg_Define = "Register option selected."; diff --git a/gcc/testsuite/gdc.test/runnable/externmangle.d b/gcc/testsuite/gdc.test/runnable/externmangle.d index 1d7476326..811ba5146 100644 --- a/gcc/testsuite/gdc.test/runnable/externmangle.d +++ b/gcc/testsuite/gdc.test/runnable/externmangle.d @@ -1,5 +1,8 @@ // EXTRA_CPP_SOURCES: extra-files/externmangle.cpp +import core.stdc.config; +import core.stdc.stdint; + extern(C++): struct Foo(X) @@ -122,12 +125,16 @@ interface Module public static int dim(Array!Module*); }; -ulong testlongmangle(int a, uint b, long c, ulong d); - -import core.stdc.config; +uint64_t testlongmangle(int a, uint b, int64_t c, uint64_t d); cpp_ulong testCppLongMangle(cpp_long a, cpp_ulong b); cpp_ulonglong testCppLongLongMangle(cpp_longlong a, cpp_ulonglong b); -cpp_size_t testCppSizeTMangle(cpp_ptrdiff_t a, cpp_size_t b); + +// direct size_t/ptrdiff_t interop is fine except on 32-bit OS X +version (OSX) { version (D_LP64) {} else version = OSX_32; } +version (OSX_32) + cpp_size_t testCppSizeTMangle(cpp_ptrdiff_t a, cpp_size_t b); +else + size_t testCppSizeTMangle(ptrdiff_t a, size_t b); __gshared extern int[2][2][2] test31; __gshared extern int* test32; diff --git a/gcc/testsuite/gdc.test/runnable/foreach3.d b/gcc/testsuite/gdc.test/runnable/foreach3.d index e167217e7..a0ff2d42f 100644 --- a/gcc/testsuite/gdc.test/runnable/foreach3.d +++ b/gcc/testsuite/gdc.test/runnable/foreach3.d @@ -3,7 +3,7 @@ import core.stdc.stdio; struct Foo { - uint array[2]; + uint[2] array; int opApply(int delegate(ref uint) dg) { diff --git a/gcc/testsuite/gdc.test/runnable/foreach4.d b/gcc/testsuite/gdc.test/runnable/foreach4.d index 32ac4663f..faad12853 100644 --- a/gcc/testsuite/gdc.test/runnable/foreach4.d +++ b/gcc/testsuite/gdc.test/runnable/foreach4.d @@ -8,7 +8,7 @@ alias bool bit; class Foo { - uint array[2]; + uint[2] array; int opApply(int delegate(ref uint) dg) { @@ -679,7 +679,7 @@ void test22() class Foo23 { - int array[2]; + int[2] array; int opApply(int delegate(ref int) dg) { @@ -790,7 +790,7 @@ void test25() struct Foo26 { - uint array[2]; + uint[2] array; int forward(int delegate(ref uint) dg) { diff --git a/gcc/testsuite/gdc.test/runnable/imports/inline2a.d b/gcc/testsuite/gdc.test/runnable/imports/inline2a.d index c0f4d173e..0c9aa0bd1 100644 --- a/gcc/testsuite/gdc.test/runnable/imports/inline2a.d +++ b/gcc/testsuite/gdc.test/runnable/imports/inline2a.d @@ -39,7 +39,7 @@ module imports.inline2a; class Primes { private - static const short primes[] = + static const short[] primes = [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, diff --git a/gcc/testsuite/gdc.test/runnable/imports/test11931d.d b/gcc/testsuite/gdc.test/runnable/imports/test11931d.d index 7a3ed39ed..ef2b5f1e6 100644 --- a/gcc/testsuite/gdc.test/runnable/imports/test11931d.d +++ b/gcc/testsuite/gdc.test/runnable/imports/test11931d.d @@ -26,5 +26,5 @@ struct Signal(T, A...) private: alias D = T delegate(A); - D _arr[]; + D[] _arr; } diff --git a/gcc/testsuite/gdc.test/runnable/interpret.d b/gcc/testsuite/gdc.test/runnable/interpret.d index 1dc902322..5b22c1988 100644 --- a/gcc/testsuite/gdc.test/runnable/interpret.d +++ b/gcc/testsuite/gdc.test/runnable/interpret.d @@ -945,7 +945,7 @@ void test45() /************************************************/ -const int foo46[5] = [0,1,2,3,4]; +const int[5] foo46 = [0,1,2,3,4]; void test46() { @@ -1108,7 +1108,7 @@ bool equals54(string a, string b) /************************************************/ -const string foo55[2] = ["a", "b"]; +const string[2] foo55 = ["a", "b"]; string retsth55(int i) { return foo55[i]; } void test55() @@ -1121,7 +1121,7 @@ void test55() string retsth56(int i) { - static const string foo[2] = ["a", "b"]; + static const string[2] foo = ["a", "b"]; return foo[i]; } @@ -2602,7 +2602,7 @@ int delegtest6() { DelegStruct s; s.a = 5; - FoolishStruct k[3]; + FoolishStruct[3] k; DelegType u = &s.bar; k[1].z = u; return k[1].z(3); @@ -3278,7 +3278,7 @@ void test9954() struct Bug10483 { - int val[3][4]; + int[3][4] val; } struct Outer10483 diff --git a/gcc/testsuite/gdc.test/runnable/opover2.d b/gcc/testsuite/gdc.test/runnable/opover2.d index 78fa04c4b..8bd64e24c 100644 --- a/gcc/testsuite/gdc.test/runnable/opover2.d +++ b/gcc/testsuite/gdc.test/runnable/opover2.d @@ -1,4 +1,4 @@ -// PERMUTE_ARGS: -inline -O -property +// PERMUTE_ARGS: -inline -O // REQUIRED_ARGS: -dip25 // Test operator overloading diff --git a/gcc/testsuite/gdc.test/runnable/printargs.d b/gcc/testsuite/gdc.test/runnable/printargs.d index f4fc70316..545f20808 100644 --- a/gcc/testsuite/gdc.test/runnable/printargs.d +++ b/gcc/testsuite/gdc.test/runnable/printargs.d @@ -3,7 +3,7 @@ extern(C) int printf(const char*, ...); -int main(char args[][]) +int main(char[][] args) { int i; diff --git a/gcc/testsuite/gdc.test/runnable/property2.d b/gcc/testsuite/gdc.test/runnable/property2.d index 0735b9350..af62b44a3 100644 --- a/gcc/testsuite/gdc.test/runnable/property2.d +++ b/gcc/testsuite/gdc.test/runnable/property2.d @@ -1,21 +1,10 @@ -// PERMUTE_ARGS: -property - extern (C) int printf(const char* fmt, ...); -// Is -property option specified? -enum enforceProperty = !__traits(compiles, { - int prop(){ return 1; } - int n = prop; -}); - /*******************************************/ template select(alias v1, alias v2) { - static if (enforceProperty) - enum select = v1; - else - enum select = v2; + enum select = v2; } struct Test(int N) @@ -28,32 +17,28 @@ struct Test(int N) ref foo(){ getset = 1; return value; } enum result = select!(0, 1); - // -property test.d(xx): Error: not a property foo - // (no option) prints "getter" + // prints "getter" } static if (N == 1) { ref foo(int x){ getset = 2; value = x; return value; } enum result = select!(0, 2); - // -property test.d(xx): Error: not a property foo - // (no option) prints "setter" + // prints "setter" } static if (N == 2) { @property ref foo(){ getset = 1; return value; } enum result = select!(1, 1); - // -property prints "getter" - // (no option) prints "getter" + // prints "getter" } static if (N == 3) { @property ref foo(int x){ getset = 2; value = x; return value; } enum result = select!(2, 2); - // -property prints "setter" - // (no option) prints "setter" + // prints "setter" } @@ -63,8 +48,7 @@ struct Test(int N) ref foo(int x){ getset = 2; value = x; return value; } enum result = select!(0, 2); - // -property test.d(xx): Error: not a property foo - // (no option) prints "setter" + // prints "setter" } static if (N == 5) { @@ -72,8 +56,7 @@ struct Test(int N) ref foo(int x){ getset = 2; value = x; return value; } enum result = select!(0, 0); - // -property test.d(xx): Error: cannot overload both property and non-property functions - // (no option) test.d(xx): Error: cannot overload both property and non-property functions + // test.d(xx): Error: cannot overload both property and non-property functions } static if (N == 6) { @@ -81,8 +64,7 @@ struct Test(int N) @property ref foo(int x){ getset = 2; value = x; return value; } enum result = select!(0, 0); - // -property test.d(xx): Error: cannot overload both property and non-property functions - // (no option) test.d(xx): Error: cannot overload both property and non-property functions + // test.d(xx): Error: cannot overload both property and non-property functions } static if (N == 7) { @@ -90,8 +72,7 @@ struct Test(int N) @property ref foo(int x){ getset = 2; value = x; return value; } enum result = select!(2, 2); - // -property prints "setter" - // (no option) prints "setter" + // prints "setter" } } @@ -141,10 +122,7 @@ void spam7722(Foo7722 f) {} void test7722() { auto f = new Foo7722; - static if (enforceProperty) - static assert(!__traits(compiles, f.spam7722)); - else - f.spam7722; + f.spam7722; } /*******************************************/ @@ -159,10 +137,7 @@ void test7722() assert(dg(0) == v); } - static if (enforceProperty) - checkImpl!(v1)(); - else - checkImpl!(v2)(); + checkImpl!(v2)(); } struct S {} diff --git a/gcc/testsuite/gdc.test/runnable/template1.d b/gcc/testsuite/gdc.test/runnable/template1.d index 51f96bf2a..120c6db4b 100644 --- a/gcc/testsuite/gdc.test/runnable/template1.d +++ b/gcc/testsuite/gdc.test/runnable/template1.d @@ -1620,7 +1620,7 @@ void test67() /******************************************/ template T68(int a) { - int vec[a]; + int[a] vec; } void test68() diff --git a/gcc/testsuite/gdc.test/runnable/template2.d b/gcc/testsuite/gdc.test/runnable/template2.d index ba5ad13eb..710290a2e 100644 --- a/gcc/testsuite/gdc.test/runnable/template2.d +++ b/gcc/testsuite/gdc.test/runnable/template2.d @@ -10,7 +10,7 @@ template VecTemplate(tfloat, int dim:3) { struct Vector { - tfloat d[dim]; + tfloat[dim] d; version(none) { diff --git a/gcc/testsuite/gdc.test/runnable/template9.d b/gcc/testsuite/gdc.test/runnable/template9.d index d5fd5ac9e..b51a0377e 100644 --- a/gcc/testsuite/gdc.test/runnable/template9.d +++ b/gcc/testsuite/gdc.test/runnable/template9.d @@ -3201,7 +3201,7 @@ void test11843() { struct Foo { - int x[string]; + int[string] x; } struct Bar(alias foo) {} diff --git a/gcc/testsuite/gdc.test/runnable/test11.d b/gcc/testsuite/gdc.test/runnable/test11.d index 5b21ba0fa..1995a330b 100644 --- a/gcc/testsuite/gdc.test/runnable/test11.d +++ b/gcc/testsuite/gdc.test/runnable/test11.d @@ -530,14 +530,14 @@ struct NODE27 { shared(NODE27) *next; } -static shared NODE27 nodetbl[3] = +static shared NODE27[3] nodetbl = [ { 0,cast(shared(NODE27)*)nodetbl + 1}, { 0,cast(shared(NODE27)*)nodetbl + 2}, { 0,null} ]; -static shared NODE27 nodetbl2[3] = [ +static shared NODE27[3] nodetbl2 = [ { 0,&nodetbl2[1]}, { 0,&nodetbl2[2]}, { 0,null} diff --git a/gcc/testsuite/gdc.test/runnable/test15.d b/gcc/testsuite/gdc.test/runnable/test15.d index 81778f3a5..d063d8de4 100644 --- a/gcc/testsuite/gdc.test/runnable/test15.d +++ b/gcc/testsuite/gdc.test/runnable/test15.d @@ -205,8 +205,8 @@ class A15 List2.rehash; } private: - int delegate(in int arg1) List1[char[]]; - int List2[char []]; + int delegate(in int arg1)[char[]] List1; + int[char []] List2; } void test15() @@ -1043,9 +1043,9 @@ void test56() /************************************/ -void det(float mat[][]) +void det(float[][] mat) { - float newmat[][]; + float[][] newmat; size_t i = newmat[0 .. (mat.length - 1)].length; } diff --git a/gcc/testsuite/gdc.test/runnable/test15373.d b/gcc/testsuite/gdc.test/runnable/test15373.d new file mode 100644 index 000000000..13144b75e --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/test15373.d @@ -0,0 +1,15 @@ +// https://issues.dlang.org/show_bug.cgi?id=15373 + +// Using `typeid` on an `extern(C++) class` type is fine as it is evaluated at compile-time + +// Using `typeid` on an `extern(C++) class` instance is not ok because `extern(C++) class` +// instances are not rooted in `Object`. See test/fail_compilation/test15373.d + +extern(C++) class C +{ } + +void main() +{ + auto Cti = typeid(C); + assert(Cti.name == "test15373.C"); +} diff --git a/gcc/testsuite/gdc.test/runnable/test17.d b/gcc/testsuite/gdc.test/runnable/test17.d index 58873a095..4de9ded1a 100644 --- a/gcc/testsuite/gdc.test/runnable/test17.d +++ b/gcc/testsuite/gdc.test/runnable/test17.d @@ -12,8 +12,8 @@ void ulog(string s) int open() { char *s; - char abs[2000]; - char qu[100]; + char[2000] abs; + char[100] qu; int a; ulog("reaches this only 9 times of 10!\n"); return 0; @@ -22,7 +22,7 @@ int open() int yhenda() { - char MEM[2200]; + char[2200] MEM; int a; ulog("point(2.1) \n"); open(); diff --git a/gcc/testsuite/gdc.test/runnable/test19.d b/gcc/testsuite/gdc.test/runnable/test19.d index b9360eeea..c8e04663d 100644 --- a/gcc/testsuite/gdc.test/runnable/test19.d +++ b/gcc/testsuite/gdc.test/runnable/test19.d @@ -128,8 +128,8 @@ int[] test6_1(int[] a) void test6() { printf("test6()\n"); - int b[3]; - int a[]; + int[3] b; + int[] a; b[0] = 0; b[1] = 1; @@ -144,7 +144,7 @@ void test6() class OutBuffer7 { - char data[]; + char[] data; uint offset; void write(const(char) *p, uint nbytes) diff --git a/gcc/testsuite/gdc.test/runnable/test22.d b/gcc/testsuite/gdc.test/runnable/test22.d index 0525df3eb..33410a356 100644 --- a/gcc/testsuite/gdc.test/runnable/test22.d +++ b/gcc/testsuite/gdc.test/runnable/test22.d @@ -665,7 +665,7 @@ struct particle float yg; /* Y Gravity */ } -particle particles[10000]; +particle[10000] particles; void test32() { @@ -1100,7 +1100,7 @@ body void test47() { real x = 3.1; - static real pp[] = [56.1, 32.7, 6]; + static real[] pp = [56.1, 32.7, 6]; real r; printf("The result should be %Lf\n",(56.1L + (32.7L + 6L * x) * x)); diff --git a/gcc/testsuite/gdc.test/runnable/test34.d b/gcc/testsuite/gdc.test/runnable/test34.d index 003024db9..4ad01c998 100644 --- a/gcc/testsuite/gdc.test/runnable/test34.d +++ b/gcc/testsuite/gdc.test/runnable/test34.d @@ -709,19 +709,8 @@ void foo35() xxx = cast(typeof(xxx))(a + b); version(GNU) { - version(X86) asm - { - "int $3;" : : : ; - } - else version(X86_64) asm - { - "int $3;" : : : ; - } - else - { - import gcc.builtins; - __builtin_trap(); - } + import gcc.builtins; + __builtin_trap(); } else asm { int 3; } @@ -1071,7 +1060,7 @@ void func53(TestStruct[2] testarg) assert(testarg[1].dummy2 == 2); } -TestStruct m53[2]; +TestStruct[2] m53; void test53() { diff --git a/gcc/testsuite/gdc.test/runnable/test4.d b/gcc/testsuite/gdc.test/runnable/test4.d index b5263e7a9..ac25cfe88 100644 --- a/gcc/testsuite/gdc.test/runnable/test4.d +++ b/gcc/testsuite/gdc.test/runnable/test4.d @@ -48,17 +48,17 @@ void test1() void test2() { - byte foo1[5]; - ubyte foo2[6]; - short foo3[7]; - ushort foo4[8]; - int foo5[9]; - uint foo6[10]; - long foo7[11]; - ulong foo8[12]; - float foo9[13]; - double foo10[14]; - real foo11[15]; + byte[5] foo1; + ubyte[6] foo2; + short[7] foo3; + ushort[8] foo4; + int[9] foo5; + uint[10] foo6; + long[11] foo7; + ulong[12] foo8; + float[13] foo9; + double[14] foo10; + real[15] foo11; int i; @@ -96,17 +96,17 @@ void test2() void test3() { - byte foo1[5] = 20; - ubyte foo2[6] = 21; - short foo3[7] = 22; - ushort foo4[8] = 23; - int foo5[9] = 24; - uint foo6[10] = 25; - long foo7[11] = 26; - ulong foo8[12] = 27; - float foo9[13] = 28; - double foo10[14] = 29; - real foo11[15] = 30; + byte[5] foo1 = 20; + ubyte[6] foo2 = 21; + short[7] foo3 = 22; + ushort[8] foo4 = 23; + int[9] foo5 = 24; + uint[10] foo6 = 25; + long[11] foo7 = 26; + ulong[12] foo8 = 27; + float[13] foo9 = 28; + double[14] foo10 = 29; + real[15] foo11 = 30; int i; @@ -257,13 +257,13 @@ struct TestVectors string replace; }; -TestVectors tva[] = +TestVectors[] tva = [ { pattern:"(a)\\1", input:"abaab", result:"y", format:"&", replace:"aa" }, { pattern:"abc", input:"abc", result:"y", format:"&", replace:"abc" }, ]; -TestVectors tvs[2] = +TestVectors[2] tvs = [ { pattern:"(a)\\1", input:"abaab", result:"y", format:"&", replace:"aa" }, { pattern:"abc", input:"abc", result:"y", format:"&", replace:"abc" }, @@ -360,8 +360,8 @@ const uint MAX_PATH1 = 260; enum { MAX_PATH2 = 261 } struct WIN32_FIND_DATA { - char cFileName1[MAX_PATH1]; - char cFileName2[MAX_PATH2]; + char[MAX_PATH1] cFileName1; + char[MAX_PATH2] cFileName2; } void test11() @@ -502,10 +502,10 @@ void test15() struct GUID { // size is 16 align(1): - uint Data1; - ushort Data2; - ushort Data3; - ubyte Data4[8]; + uint Data1; + ushort Data2; + ushort Data3; + ubyte[8] Data4; } @@ -562,7 +562,7 @@ void test17() /* ================================ */ -private const uint crc_table[256] = [ +private const uint[256] crc_table = [ 0x00000000u, 0x77073096u, 0xee0e612cu, 0x990951bau, 0x076dc419u, 0x2d02ef8du ]; diff --git a/gcc/testsuite/gdc.test/runnable/test42.d b/gcc/testsuite/gdc.test/runnable/test42.d index 9babc4e6e..6febbc0c3 100644 --- a/gcc/testsuite/gdc.test/runnable/test42.d +++ b/gcc/testsuite/gdc.test/runnable/test42.d @@ -1704,7 +1704,7 @@ class C103 { va_list ap; va_start(ap, a); - auto internal = va_arg!int(ap); + auto internal = va_arg!int(ap); printf("internal: %d\n", internal); x103 = internal; va_end(ap); @@ -1831,7 +1831,7 @@ void test108() void test109() { - double x[] = new double[1]; + double[] x = new double[1]; assert(x[0] != 0); } @@ -1898,7 +1898,7 @@ struct VariantN void bar(int value, int i) { - int args[2] = [ VariantN(value), VariantN(i) ]; + int[2] args = [ VariantN(value), VariantN(i) ]; } } @@ -2264,7 +2264,7 @@ void test135() void test136() { - struct S { int i[3]; } + struct S { int[3] i; } enum S s = S(8); const int i = s.i[2]; assert(i == 8); @@ -2472,19 +2472,8 @@ void crash(int x) version(GNU) { - version(X86) asm - { - "int $3;" : : :; - } - else version(X86_64) asm - { - "int $3;" : : :; - } - else - { - import gcc.builtins; - __builtin_trap(); - } + import gcc.builtins; + __builtin_trap(); } else { @@ -3301,7 +3290,7 @@ void test200() // Bugzilla 2931 struct Bug2931 { - int val[3][4]; + int[4][3] val; } struct Outer2931 { @@ -4051,10 +4040,10 @@ void test229() { /***************************************************/ -static immutable real negtab[14] = +static immutable real[14] negtab = [ 1e-4096L,1e-2048L,1e-1024L,1e-512L,1e-256L,1e-128L,1e-64L,1e-32L, 1e-16L,1e-8L,1e-4L,1e-2L,1e-1L,1.0L ]; -static immutable real postab[13] = +static immutable real[13] postab = [ 1e+4096L,1e+2048L,1e+1024L,1e+512L,1e+256L,1e+128L,1e+64L,1e+32L, 1e+16L,1e+8L,1e+4L,1e+2L,1e+1L ]; @@ -4241,14 +4230,7 @@ int bug3809() { version(GNU) { - version(X86) - asm { "nop"; } - else version(X86_64) - asm { "nop"; } - else version(ARM) - asm { "nop"; } - else - static assert(false, "ASM code not implemented for this architecture"); + asm { ""; } } else { diff --git a/gcc/testsuite/gdc.test/runnable/test48.d b/gcc/testsuite/gdc.test/runnable/test48.d index 827d68125..bfc767ebf 100644 --- a/gcc/testsuite/gdc.test/runnable/test48.d +++ b/gcc/testsuite/gdc.test/runnable/test48.d @@ -38,6 +38,6 @@ void main() printf("offset2 = %d\n", offset2); assert(offset2 == 8); - int t1[S.tupleof.offsetof[1]]; + int[S.tupleof.offsetof[1]] t1; assert(t1.length == 4); } diff --git a/gcc/testsuite/gdc.test/runnable/test8.d b/gcc/testsuite/gdc.test/runnable/test8.d index afd01c805..d397f6f48 100644 --- a/gcc/testsuite/gdc.test/runnable/test8.d +++ b/gcc/testsuite/gdc.test/runnable/test8.d @@ -668,7 +668,7 @@ void test37() void test38() { int n = atoi("1"); - static char flags[8192 + 1]; + static char[8192 + 1] flags; long i, k; int count = 0; diff --git a/gcc/testsuite/gdc.test/runnable/testaa.d b/gcc/testsuite/gdc.test/runnable/testaa.d index 7ba707fd9..11b803b59 100644 --- a/gcc/testsuite/gdc.test/runnable/testaa.d +++ b/gcc/testsuite/gdc.test/runnable/testaa.d @@ -10,7 +10,7 @@ import std.random; // for uniform random numbers /************************************************/ -int nametable[char[]]; +int[char[]] nametable; void insert(string name, int value) { @@ -315,7 +315,7 @@ void test13() string[] key = array.keys; assert(key.length==3); - bool have[3]; + bool[3] have; assert(!have[0]); assert(!have[1]); @@ -493,7 +493,7 @@ void dummy17() { } -int bb17[string]; +int[string] bb17; int foo17() { @@ -1328,6 +1328,20 @@ void test14321() assert(Bar.op == "d"); } +/************************************************/ +// 19112 + +void test19112() +{ + int[int[1]] aa; + aa[[2]] = 1; + assert([2] in aa); + + int[int[]] aa2 = [[1, 2, 3]: 4]; + int[3] k = [1, 2, 3]; + assert(*(k in aa2) == 4); +} + /************************************************/ int main() @@ -1379,6 +1393,7 @@ int main() test11730(); test14089(); test14321(); + test19112(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/testaa2.d b/gcc/testsuite/gdc.test/runnable/testaa2.d index 2299afe80..849b9c403 100644 --- a/gcc/testsuite/gdc.test/runnable/testaa2.d +++ b/gcc/testsuite/gdc.test/runnable/testaa2.d @@ -4,12 +4,12 @@ extern(C) int printf(const char*, ...); /************************************************/ -int a[string]; +int[string] a; size_t foo(immutable char [3] s) { printf("foo()\n"); - int b[string]; + int[string] b; string[] key; int[] value; printf("foo() 2\n"); @@ -22,7 +22,7 @@ size_t foo(immutable char [3] s) void foo2() { - int c[string]; + int[string] c; string[] key; int[] value; int i; diff --git a/gcc/testsuite/gdc.test/runnable/testdt.d b/gcc/testsuite/gdc.test/runnable/testdt.d index 8224abc13..da27f8e7b 100644 --- a/gcc/testsuite/gdc.test/runnable/testdt.d +++ b/gcc/testsuite/gdc.test/runnable/testdt.d @@ -2,7 +2,7 @@ /******************************************/ -static int bigarray[100][100]; +static int[100][100] bigarray; void test1() { diff --git a/gcc/testsuite/gdc.test/runnable/testthread.d b/gcc/testsuite/gdc.test/runnable/testthread.d index 4ae439508..139a89107 100644 --- a/gcc/testsuite/gdc.test/runnable/testthread.d +++ b/gcc/testsuite/gdc.test/runnable/testthread.d @@ -49,7 +49,7 @@ int main() assert(f.x == 3); int i; - Thread tx[5]; + Thread[5] tx; for (i = 0; i < tx.length; i++) tx[i] = new Thread(&f.bar); for (i = 0; i < tx.length; i++) diff --git a/gcc/testsuite/gdc.test/runnable/traits_getPointerBitmap.d b/gcc/testsuite/gdc.test/runnable/traits_getPointerBitmap.d index 512bcea79..bbf38b1fb 100644 --- a/gcc/testsuite/gdc.test/runnable/traits_getPointerBitmap.d +++ b/gcc/testsuite/gdc.test/runnable/traits_getPointerBitmap.d @@ -190,7 +190,7 @@ class N union U { - size_t data[4]; + size_t[4] data; Large*[] arr; // { length, ptr } struct diff --git a/gcc/testsuite/gdc.test/runnable/uda.d b/gcc/testsuite/gdc.test/runnable/uda.d index c1782c90a..4c8c6ede2 100644 --- a/gcc/testsuite/gdc.test/runnable/uda.d +++ b/gcc/testsuite/gdc.test/runnable/uda.d @@ -467,6 +467,192 @@ static assert(__traits(getAttributes, FileData11844.member)[0](new FileData11844 /************************************************/ +template AliasSeq(T...) +{ + alias AliasSeq = T; +} + +template ParameterUDA(size_t p_num, func...) +if (func.length == 1 && is(typeof(func[0]) PT == __parameters) && PT.length > p_num) +{ + static if (is(typeof(func[0]) PT == __parameters)) + { + template Get(size_t i) + { + static if (//!isFunctionPointer!func && !isDelegate!func + // Parameters without UDA may yield CT error. + /*&&*/ is(typeof(__traits(getAttributes, PT[i..i+1]))x)) + { + alias Get = AliasSeq!(__traits(getAttributes, PT[i..i+1])); + } + else + { + alias Get = AliasSeq!(); + } + } + } + else + { + static assert(0, func[0].stringof ~ "is not a function"); + + // Define dummy entities to avoid pointless errors + template Get(size_t i) { alias Get = AliasSeq!(); } + alias PT = AliasSeq!(); + } + + alias ParameterUDA = Get!p_num; +} + +void test13x(@(10) int a, @(20) int, @(30) @(40) int[] arr...) {} + +void test13() +{ + static assert([ParameterUDA!(0, test13x)] == [10]); + static assert([ParameterUDA!(1, test13x)] == [20]); + static assert([ParameterUDA!(2, test13x)] == [30, 40]); +} + +template Test13t(F) +{ + static assert(!__traits(compiles, ParameterUDA!(0, F))); + static assert(!__traits(compiles, ParameterUDA!(1, F))); + static assert(!__traits(compiles, ParameterUDA!(2, F))); + enum Test13t = true; +} + +alias test13t = Test13t!(typeof(test13x)); + +enum Test14UDA1; + +struct Test14UDA2 +{ + string str; +} + +Test14UDA2 test14uda3(string name) +{ + return Test14UDA2(name); +} + +struct Test14UDA4(string v) +{ +} + +void test14x(@Test14UDA1 int, @Test14UDA2("1") int, @test14uda3("2") int, @Test14UDA4!"3" int) {} +void test14() +{ + static assert(is(ParameterUDA!(0, test14x)[0] == Test14UDA1)); + static assert(ParameterUDA!(1, test14x)[0] == Test14UDA2("1")); + static assert(ParameterUDA!(2, test14x)[0] == test14uda3("2")); + static assert(is(ParameterUDA!(3, test14x)[0] == Test14UDA4!"3")); +} + +void test15x(@(20) void delegate(int) @safe dg) +{ + static assert([__traits(getAttributes, dg)] == [20]); + static assert(is(typeof(dg) == void delegate(int) @safe)); +} + +template MinimalFunctionTypeOf(func...) +if (func.length == 1) +{ + static if (is(func[0] T) || is(typeof(func[0]) T) && is(T Fdlg == delegate)) + alias MinimalFunctionTypeOf = Fdlg; // HIT: delegate + else + static assert(0); +} + +template Parameters(func...) +if (func.length == 1) +{ + static if (is(MinimalFunctionTypeOf!func P == function)) + alias Parameters = P; + else + static assert(0, "argument has no parameters"); +} + +void test15y(@(20) void delegate(@Test14UDA2("2") @("test15yUDA") int) @safe dg) +{ + static assert([__traits(getAttributes, dg)] == [20]); + static assert(is(typeof(dg) == void delegate(int) @safe)); + auto foo = (@("myUDA") int x){ + static assert([__traits(getAttributes, x)] == ["myUDA"]); + }; + static assert(__traits(getAttributes, Parameters!dg)[0] == Test14UDA2("2")); + static assert(__traits(getAttributes, Parameters!dg)[1] == "test15yUDA"); +} + +void test15z() +{ + test15y((@(15) @(16) int x){ + static assert([__traits(getAttributes, x)] == [15, 16]); + }); +} + +void test16x(A)(@(22) A a) +{ + static assert([__traits(getAttributes, a)] == [22]); +} + +void test16() +{ + static assert([ParameterUDA!(0, test16x!int)] == [22]); +} + +void test17() +{ + void test17x(A)(@(23) A a) + { + static assert([__traits(getAttributes, a)] == [23]); + } + static assert([ParameterUDA!(0, test17x!int)] == [23]); +} + +void test18() +{ + void test18a(@(Tuple!(18, "a")) int a) + { + static assert(__traits(getAttributes, a) == Tuple!(18, "a")); + } + void test18b(@Tuple!(18, "b") int a) + { + static assert(__traits(getAttributes, a) == Tuple!(18, "b")); + } + + static assert(ParameterUDA!(0, test18a) == Tuple!(18, "a")); + static assert(ParameterUDA!(0, test18b) == Tuple!(18, "b")); +} + +// Lambdas with parentheses: +void test19() +{ + // lambdas without parentheses + alias test19a = @(3) b => 1 + 2; + alias test19b = @(3) @(4) b => 1 + 2; + + alias test19c = (@(3) c, @(5) d) => 1 + 2; + alias test19d = (@(3) @(4) c, @(5) d) => 1 + 2; + auto test19e = (@(3) int c, @(5) int d) => 1 + 2; + + // still allow alias function declarations + alias FuncType = @safe void function(); + alias FuncType2 = @safe nothrow void function(); + alias FuncType3 = nothrow void function(); + alias FuncType4 = nothrow @safe void function(); +} + +void test20() +{ + // Using a delegate with inferred parameter type + void test20a(int delegate(int) t){ t(1); } + test20a((@(19) a) { + static assert([__traits(getAttributes, a)] == [19]); + return a + 2; + }); +} + +/************************************************/ + int main() { test1(); @@ -482,6 +668,13 @@ int main() test11(); test12(); test9178(); + test13(); + test14(); + test16(); + test17(); + test18(); + test19(); + test20(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/untag.d b/gcc/testsuite/gdc.test/runnable/untag.d index 44f6d6806..6031fffb6 100644 --- a/gcc/testsuite/gdc.test/runnable/untag.d +++ b/gcc/testsuite/gdc.test/runnable/untag.d @@ -29,7 +29,7 @@ bool startsWithConsume(alias pred = "a == b", R1, R2)(ref R1 r1, R2 r2) uint bug = 1; -int main(string args[]) { +int main(string[] args) { getopt(args, "bug", &bug); enforce(bug <= 2); auto txt = readText("runnable/extra-files/untag.html"); diff --git a/libphobos/libdruntime/Makefile.am b/libphobos/libdruntime/Makefile.am index 9d675a85f..78295a8c8 100644 --- a/libphobos/libdruntime/Makefile.am +++ b/libphobos/libdruntime/Makefile.am @@ -154,7 +154,7 @@ DRUNTIME_DSOURCES_GENERATED = gcc/config.d gcc/libbacktrace.d # https://www.gnu.org/software/automake/manual/html_node/Wildcards.html DRUNTIME_SSOURCES = core/threadasm.S -DRUNTIME_CSOURCES = core/stdc/errno_.c rt/bss_section.c +DRUNTIME_CSOURCES = core/stdc/errno_.c DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \ core/checkedint.d core/cpuid.d core/demangle.d core/exception.d \ @@ -180,14 +180,15 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \ rt/critical_.d rt/deh.d rt/dmain2.d rt/ehalloc.d rt/invariant.d \ rt/lifetime.d rt/memory.d rt/minfo.d rt/monitor_.d rt/obj.d rt/qsort.d \ rt/sections.d rt/sections_android.d rt/sections_elf_shared.d \ - rt/sections_osx.d rt/sections_solaris.d rt/sections_win32.d \ - rt/sections_win64.d rt/switch_.d rt/tlsgc.d rt/typeinfo/ti_Acdouble.d \ - rt/typeinfo/ti_Acfloat.d rt/typeinfo/ti_Acreal.d \ - rt/typeinfo/ti_Adouble.d rt/typeinfo/ti_Afloat.d rt/typeinfo/ti_Ag.d \ - rt/typeinfo/ti_Aint.d rt/typeinfo/ti_Along.d rt/typeinfo/ti_Areal.d \ - rt/typeinfo/ti_Ashort.d rt/typeinfo/ti_C.d rt/typeinfo/ti_byte.d \ - rt/typeinfo/ti_cdouble.d rt/typeinfo/ti_cent.d rt/typeinfo/ti_cfloat.d \ - rt/typeinfo/ti_char.d rt/typeinfo/ti_creal.d rt/typeinfo/ti_dchar.d \ + rt/sections_osx_x86.d rt/sections_osx_x86_64.d rt/sections_solaris.d \ + rt/sections_win32.d rt/sections_win64.d rt/tlsgc.d \ + rt/typeinfo/ti_Acdouble.d rt/typeinfo/ti_Acfloat.d \ + rt/typeinfo/ti_Acreal.d rt/typeinfo/ti_Adouble.d \ + rt/typeinfo/ti_Afloat.d rt/typeinfo/ti_Ag.d rt/typeinfo/ti_Aint.d \ + rt/typeinfo/ti_Along.d rt/typeinfo/ti_Areal.d rt/typeinfo/ti_Ashort.d \ + rt/typeinfo/ti_C.d rt/typeinfo/ti_byte.d rt/typeinfo/ti_cdouble.d \ + rt/typeinfo/ti_cent.d rt/typeinfo/ti_cfloat.d rt/typeinfo/ti_char.d \ + rt/typeinfo/ti_creal.d rt/typeinfo/ti_dchar.d \ rt/typeinfo/ti_delegate.d rt/typeinfo/ti_double.d \ rt/typeinfo/ti_float.d rt/typeinfo/ti_idouble.d \ rt/typeinfo/ti_ifloat.d rt/typeinfo/ti_int.d rt/typeinfo/ti_ireal.d \ @@ -196,8 +197,8 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \ rt/typeinfo/ti_ucent.d rt/typeinfo/ti_uint.d rt/typeinfo/ti_ulong.d \ rt/typeinfo/ti_ushort.d rt/typeinfo/ti_void.d rt/typeinfo/ti_wchar.d \ rt/util/array.d rt/util/container/array.d rt/util/container/common.d \ - rt/util/container/hashtab.d rt/util/container/treap.d rt/util/hash.d \ - rt/util/random.d rt/util/typeinfo.d rt/util/utf.d + rt/util/container/hashtab.d rt/util/container/treap.d rt/util/random.d \ + rt/util/typeinfo.d rt/util/utf.d DRUNTIME_DSOURCES_STDCXX = core/stdcpp/exception.d \ core/stdcpp/typeinfo.d @@ -235,7 +236,8 @@ DRUNTIME_DSOURCES_FREEBSD = core/sys/freebsd/dlfcn.d \ core/sys/freebsd/sys/elf.d core/sys/freebsd/sys/elf32.d \ core/sys/freebsd/sys/elf64.d core/sys/freebsd/sys/elf_common.d \ core/sys/freebsd/sys/event.d core/sys/freebsd/sys/link_elf.d \ - core/sys/freebsd/sys/mman.d core/sys/freebsd/time.d + core/sys/freebsd/sys/mman.d core/sys/freebsd/sys/mount.d \ + core/sys/freebsd/time.d DRUNTIME_DSOURCES_LINUX = core/sys/linux/config.d \ core/sys/linux/dlfcn.d core/sys/linux/elf.d core/sys/linux/epoll.d \ diff --git a/libphobos/libdruntime/Makefile.in b/libphobos/libdruntime/Makefile.in index 0170a968a..79df88814 100644 --- a/libphobos/libdruntime/Makefile.in +++ b/libphobos/libdruntime/Makefile.in @@ -174,9 +174,9 @@ am__objects_1 = core/atomic.lo core/attribute.lo core/bitop.lo \ rt/ehalloc.lo rt/invariant.lo rt/lifetime.lo rt/memory.lo \ rt/minfo.lo rt/monitor_.lo rt/obj.lo rt/qsort.lo \ rt/sections.lo rt/sections_android.lo \ - rt/sections_elf_shared.lo rt/sections_osx.lo \ - rt/sections_solaris.lo rt/sections_win32.lo \ - rt/sections_win64.lo rt/switch_.lo rt/tlsgc.lo \ + rt/sections_elf_shared.lo rt/sections_osx_x86.lo \ + rt/sections_osx_x86_64.lo rt/sections_solaris.lo \ + rt/sections_win32.lo rt/sections_win64.lo rt/tlsgc.lo \ rt/typeinfo/ti_Acdouble.lo rt/typeinfo/ti_Acfloat.lo \ rt/typeinfo/ti_Acreal.lo rt/typeinfo/ti_Adouble.lo \ rt/typeinfo/ti_Afloat.lo rt/typeinfo/ti_Ag.lo \ @@ -198,8 +198,7 @@ am__objects_1 = core/atomic.lo core/attribute.lo core/bitop.lo \ rt/typeinfo/ti_wchar.lo rt/util/array.lo \ rt/util/container/array.lo rt/util/container/common.lo \ rt/util/container/hashtab.lo rt/util/container/treap.lo \ - rt/util/hash.lo rt/util/random.lo rt/util/typeinfo.lo \ - rt/util/utf.lo + rt/util/random.lo rt/util/typeinfo.lo rt/util/utf.lo am__objects_2 = gc/bits.lo gc/config.lo gc/gcinterface.lo \ gc/impl/conservative/gc.lo gc/impl/manual/gc.lo \ gc/impl/proto/gc.lo gc/os.lo gc/pooltable.lo gc/proxy.lo @@ -258,7 +257,8 @@ am__objects_12 = core/sys/freebsd/dlfcn.lo \ core/sys/freebsd/sys/elf64.lo \ core/sys/freebsd/sys/elf_common.lo \ core/sys/freebsd/sys/event.lo core/sys/freebsd/sys/link_elf.lo \ - core/sys/freebsd/sys/mman.lo core/sys/freebsd/time.lo + core/sys/freebsd/sys/mman.lo core/sys/freebsd/sys/mount.lo \ + core/sys/freebsd/time.lo @DRUNTIME_OS_FREEBSD_TRUE@am__objects_13 = $(am__objects_12) am__objects_14 = core/sys/openbsd/dlfcn.lo @DRUNTIME_OS_OPENBSD_TRUE@am__objects_15 = $(am__objects_14) @@ -396,16 +396,14 @@ am__objects_25 = $(am__objects_1) $(am__objects_3) $(am__objects_4) \ $(am__objects_13) $(am__objects_15) $(am__objects_17) \ $(am__objects_19) $(am__objects_21) $(am__objects_23) \ $(am__objects_24) -am__objects_26 = libgdruntime_la-errno_.lo \ - libgdruntime_la-bss_section.lo +am__objects_26 = libgdruntime_la-errno_.lo am__objects_27 = libgdruntime_la-threadasm.lo am__objects_28 = $(am__objects_25) $(am__objects_26) $(am__objects_27) am_libgdruntime_la_OBJECTS = $(am__objects_28) libgdruntime_la_OBJECTS = $(am_libgdruntime_la_OBJECTS) libgdruntime_t_la_DEPENDENCIES = $(DRUNTIME_TEST_LOBJECTS) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) -am__objects_29 = libgdruntime_t_la-errno_.lo \ - libgdruntime_t_la-bss_section.lo +am__objects_29 = libgdruntime_t_la-errno_.lo am__objects_30 = libgdruntime_t_la-threadasm.lo am_libgdruntime_t_la_OBJECTS = $(am__objects_29) $(am__objects_30) libgdruntime_t_la_OBJECTS = $(am_libgdruntime_t_la_OBJECTS) @@ -415,7 +413,7 @@ libgdruntime_t_la_OBJECTS = $(am_libgdruntime_t_la_OBJECTS) am_unittest_OBJECTS = ../testsuite/test_runner.$(OBJEXT) unittest_OBJECTS = $(am_unittest_OBJECTS) unittest_DEPENDENCIES = libgdruntime_t.la -am__objects_31 = errno_.$(OBJEXT) bss_section.$(OBJEXT) +am__objects_31 = errno_.$(OBJEXT) am__objects_32 = threadasm.$(OBJEXT) am_unittest_static_OBJECTS = ../testsuite/test_runner.$(OBJEXT) \ $(am__objects_31) $(am__objects_32) @@ -708,7 +706,7 @@ DRUNTIME_DSOURCES_GENERATED = gcc/config.d gcc/libbacktrace.d # Can't use wildcards here: # https://www.gnu.org/software/automake/manual/html_node/Wildcards.html DRUNTIME_SSOURCES = core/threadasm.S -DRUNTIME_CSOURCES = core/stdc/errno_.c rt/bss_section.c +DRUNTIME_CSOURCES = core/stdc/errno_.c DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \ core/checkedint.d core/cpuid.d core/demangle.d core/exception.d \ core/internal/abort.d core/internal/arrayop.d core/internal/convert.d \ @@ -733,14 +731,15 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \ rt/critical_.d rt/deh.d rt/dmain2.d rt/ehalloc.d rt/invariant.d \ rt/lifetime.d rt/memory.d rt/minfo.d rt/monitor_.d rt/obj.d rt/qsort.d \ rt/sections.d rt/sections_android.d rt/sections_elf_shared.d \ - rt/sections_osx.d rt/sections_solaris.d rt/sections_win32.d \ - rt/sections_win64.d rt/switch_.d rt/tlsgc.d rt/typeinfo/ti_Acdouble.d \ - rt/typeinfo/ti_Acfloat.d rt/typeinfo/ti_Acreal.d \ - rt/typeinfo/ti_Adouble.d rt/typeinfo/ti_Afloat.d rt/typeinfo/ti_Ag.d \ - rt/typeinfo/ti_Aint.d rt/typeinfo/ti_Along.d rt/typeinfo/ti_Areal.d \ - rt/typeinfo/ti_Ashort.d rt/typeinfo/ti_C.d rt/typeinfo/ti_byte.d \ - rt/typeinfo/ti_cdouble.d rt/typeinfo/ti_cent.d rt/typeinfo/ti_cfloat.d \ - rt/typeinfo/ti_char.d rt/typeinfo/ti_creal.d rt/typeinfo/ti_dchar.d \ + rt/sections_osx_x86.d rt/sections_osx_x86_64.d rt/sections_solaris.d \ + rt/sections_win32.d rt/sections_win64.d rt/tlsgc.d \ + rt/typeinfo/ti_Acdouble.d rt/typeinfo/ti_Acfloat.d \ + rt/typeinfo/ti_Acreal.d rt/typeinfo/ti_Adouble.d \ + rt/typeinfo/ti_Afloat.d rt/typeinfo/ti_Ag.d rt/typeinfo/ti_Aint.d \ + rt/typeinfo/ti_Along.d rt/typeinfo/ti_Areal.d rt/typeinfo/ti_Ashort.d \ + rt/typeinfo/ti_C.d rt/typeinfo/ti_byte.d rt/typeinfo/ti_cdouble.d \ + rt/typeinfo/ti_cent.d rt/typeinfo/ti_cfloat.d rt/typeinfo/ti_char.d \ + rt/typeinfo/ti_creal.d rt/typeinfo/ti_dchar.d \ rt/typeinfo/ti_delegate.d rt/typeinfo/ti_double.d \ rt/typeinfo/ti_float.d rt/typeinfo/ti_idouble.d \ rt/typeinfo/ti_ifloat.d rt/typeinfo/ti_int.d rt/typeinfo/ti_ireal.d \ @@ -749,8 +748,8 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \ rt/typeinfo/ti_ucent.d rt/typeinfo/ti_uint.d rt/typeinfo/ti_ulong.d \ rt/typeinfo/ti_ushort.d rt/typeinfo/ti_void.d rt/typeinfo/ti_wchar.d \ rt/util/array.d rt/util/container/array.d rt/util/container/common.d \ - rt/util/container/hashtab.d rt/util/container/treap.d rt/util/hash.d \ - rt/util/random.d rt/util/typeinfo.d rt/util/utf.d + rt/util/container/hashtab.d rt/util/container/treap.d rt/util/random.d \ + rt/util/typeinfo.d rt/util/utf.d DRUNTIME_DSOURCES_STDCXX = core/stdcpp/exception.d \ core/stdcpp/typeinfo.d @@ -788,7 +787,8 @@ DRUNTIME_DSOURCES_FREEBSD = core/sys/freebsd/dlfcn.d \ core/sys/freebsd/sys/elf.d core/sys/freebsd/sys/elf32.d \ core/sys/freebsd/sys/elf64.d core/sys/freebsd/sys/elf_common.d \ core/sys/freebsd/sys/event.d core/sys/freebsd/sys/link_elf.d \ - core/sys/freebsd/sys/mman.d core/sys/freebsd/time.d + core/sys/freebsd/sys/mman.d core/sys/freebsd/sys/mount.d \ + core/sys/freebsd/time.d DRUNTIME_DSOURCES_LINUX = core/sys/linux/config.d \ core/sys/linux/dlfcn.d core/sys/linux/elf.d core/sys/linux/epoll.d \ @@ -1125,11 +1125,11 @@ rt/qsort.lo: rt/$(am__dirstamp) rt/sections.lo: rt/$(am__dirstamp) rt/sections_android.lo: rt/$(am__dirstamp) rt/sections_elf_shared.lo: rt/$(am__dirstamp) -rt/sections_osx.lo: rt/$(am__dirstamp) +rt/sections_osx_x86.lo: rt/$(am__dirstamp) +rt/sections_osx_x86_64.lo: rt/$(am__dirstamp) rt/sections_solaris.lo: rt/$(am__dirstamp) rt/sections_win32.lo: rt/$(am__dirstamp) rt/sections_win64.lo: rt/$(am__dirstamp) -rt/switch_.lo: rt/$(am__dirstamp) rt/tlsgc.lo: rt/$(am__dirstamp) rt/typeinfo/$(am__dirstamp): @$(MKDIR_P) rt/typeinfo @@ -1182,7 +1182,6 @@ rt/util/container/array.lo: rt/util/container/$(am__dirstamp) rt/util/container/common.lo: rt/util/container/$(am__dirstamp) rt/util/container/hashtab.lo: rt/util/container/$(am__dirstamp) rt/util/container/treap.lo: rt/util/container/$(am__dirstamp) -rt/util/hash.lo: rt/util/$(am__dirstamp) rt/util/random.lo: rt/util/$(am__dirstamp) rt/util/typeinfo.lo: rt/util/$(am__dirstamp) rt/util/utf.lo: rt/util/$(am__dirstamp) @@ -1353,6 +1352,7 @@ core/sys/freebsd/sys/event.lo: core/sys/freebsd/sys/$(am__dirstamp) core/sys/freebsd/sys/link_elf.lo: \ core/sys/freebsd/sys/$(am__dirstamp) core/sys/freebsd/sys/mman.lo: core/sys/freebsd/sys/$(am__dirstamp) +core/sys/freebsd/sys/mount.lo: core/sys/freebsd/sys/$(am__dirstamp) core/sys/freebsd/time.lo: core/sys/freebsd/$(am__dirstamp) core/sys/openbsd/$(am__dirstamp): @$(MKDIR_P) core/sys/openbsd @@ -1848,6 +1848,8 @@ mostlyclean-compile: -rm -f core/sys/freebsd/sys/link_elf.lo -rm -f core/sys/freebsd/sys/mman.$(OBJEXT) -rm -f core/sys/freebsd/sys/mman.lo + -rm -f core/sys/freebsd/sys/mount.$(OBJEXT) + -rm -f core/sys/freebsd/sys/mount.lo -rm -f core/sys/freebsd/time.$(OBJEXT) -rm -f core/sys/freebsd/time.lo -rm -f core/sys/linux/config.$(OBJEXT) @@ -2492,16 +2494,16 @@ mostlyclean-compile: -rm -f rt/sections_android.lo -rm -f rt/sections_elf_shared.$(OBJEXT) -rm -f rt/sections_elf_shared.lo - -rm -f rt/sections_osx.$(OBJEXT) - -rm -f rt/sections_osx.lo + -rm -f rt/sections_osx_x86.$(OBJEXT) + -rm -f rt/sections_osx_x86.lo + -rm -f rt/sections_osx_x86_64.$(OBJEXT) + -rm -f rt/sections_osx_x86_64.lo -rm -f rt/sections_solaris.$(OBJEXT) -rm -f rt/sections_solaris.lo -rm -f rt/sections_win32.$(OBJEXT) -rm -f rt/sections_win32.lo -rm -f rt/sections_win64.$(OBJEXT) -rm -f rt/sections_win64.lo - -rm -f rt/switch_.$(OBJEXT) - -rm -f rt/switch_.lo -rm -f rt/tlsgc.$(OBJEXT) -rm -f rt/tlsgc.lo -rm -f rt/typeinfo/ti_Acdouble.$(OBJEXT) @@ -2588,8 +2590,6 @@ mostlyclean-compile: -rm -f rt/util/container/hashtab.lo -rm -f rt/util/container/treap.$(OBJEXT) -rm -f rt/util/container/treap.lo - -rm -f rt/util/hash.$(OBJEXT) - -rm -f rt/util/hash.lo -rm -f rt/util/random.$(OBJEXT) -rm -f rt/util/random.lo -rm -f rt/util/typeinfo.$(OBJEXT) @@ -2633,27 +2633,15 @@ threadasm.obj: core/threadasm.S libgdruntime_la-errno_.lo: core/stdc/errno_.c $(LIBTOOL) --tag=CC $(libgdruntime_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgdruntime_la-errno_.lo `test -f 'core/stdc/errno_.c' || echo '$(srcdir)/'`core/stdc/errno_.c -libgdruntime_la-bss_section.lo: rt/bss_section.c - $(LIBTOOL) --tag=CC $(libgdruntime_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgdruntime_la-bss_section.lo `test -f 'rt/bss_section.c' || echo '$(srcdir)/'`rt/bss_section.c - libgdruntime_t_la-errno_.lo: core/stdc/errno_.c $(LIBTOOL) --tag=CC $(libgdruntime_t_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgdruntime_t_la-errno_.lo `test -f 'core/stdc/errno_.c' || echo '$(srcdir)/'`core/stdc/errno_.c -libgdruntime_t_la-bss_section.lo: rt/bss_section.c - $(LIBTOOL) --tag=CC $(libgdruntime_t_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgdruntime_t_la-bss_section.lo `test -f 'rt/bss_section.c' || echo '$(srcdir)/'`rt/bss_section.c - errno_.o: core/stdc/errno_.c $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o errno_.o `test -f 'core/stdc/errno_.c' || echo '$(srcdir)/'`core/stdc/errno_.c errno_.obj: core/stdc/errno_.c $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o errno_.obj `if test -f 'core/stdc/errno_.c'; then $(CYGPATH_W) 'core/stdc/errno_.c'; else $(CYGPATH_W) '$(srcdir)/core/stdc/errno_.c'; fi` -bss_section.o: rt/bss_section.c - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bss_section.o `test -f 'rt/bss_section.c' || echo '$(srcdir)/'`rt/bss_section.c - -bss_section.obj: rt/bss_section.c - $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bss_section.obj `if test -f 'rt/bss_section.c'; then $(CYGPATH_W) 'rt/bss_section.c'; else $(CYGPATH_W) '$(srcdir)/rt/bss_section.c'; fi` - mostlyclean-libtool: -rm -f *.lo diff --git a/libphobos/libdruntime/core/internal/arrayop.d b/libphobos/libdruntime/core/internal/arrayop.d index c53552e7a..620ce06e7 100644 --- a/libphobos/libdruntime/core/internal/arrayop.d +++ b/libphobos/libdruntime/core/internal/arrayop.d @@ -230,12 +230,12 @@ else } } -bool isUnaryOp(string op) +bool isUnaryOp(scope string op) pure nothrow @safe @nogc { return op[0] == 'u'; } -bool isBinaryOp(string op) +bool isBinaryOp(scope string op) pure nothrow @safe @nogc { if (op == "^^") return true; diff --git a/libphobos/libdruntime/core/internal/convert.d b/libphobos/libdruntime/core/internal/convert.d index 9cf8798b3..adf76aaa5 100644 --- a/libphobos/libdruntime/core/internal/convert.d +++ b/libphobos/libdruntime/core/internal/convert.d @@ -10,13 +10,36 @@ module core.internal.convert; import core.internal.traits : Unqual; -@trusted pure nothrow -const(ubyte)[] toUbyte(T)(ref T val) if(is(Unqual!T == float) || is(Unqual!T == double) || is(Unqual!T == real) || +/+ +A @nogc function can allocate memory during CTFE. ++/ +@nogc nothrow pure @trusted +private ubyte[] ctfe_alloc()(size_t n) +{ + if (!__ctfe) + { + assert(0, "CTFE only"); + } + else + { + static ubyte[] alloc(size_t x) nothrow pure + { + if (__ctfe) // Needed to prevent _d_newarray from appearing in compiled prorgam. + return new ubyte[x]; + else + assert(0); + } + return (cast(ubyte[] function(size_t) @nogc nothrow pure) &alloc)(n); + } +} + +@trusted pure nothrow @nogc +const(ubyte)[] toUbyte(T)(const ref T val) if(is(Unqual!T == float) || is(Unqual!T == double) || is(Unqual!T == real) || is(Unqual!T == ifloat) || is(Unqual!T == idouble) || is(Unqual!T == ireal)) { static const(ubyte)[] reverse_(const(ubyte)[] arr) { - ubyte[] buff = new ubyte[arr.length]; + ubyte[] buff = ctfe_alloc(arr.length); foreach(k, v; arr) { buff[$-k-1] = v; @@ -31,7 +54,7 @@ const(ubyte)[] toUbyte(T)(ref T val) if(is(Unqual!T == float) || is(Unqual!T == uint exp = parsed.exponent; uint sign = parsed.sign; - ubyte[T.sizeof] buff; + ubyte[] buff = ctfe_alloc(T.sizeof); size_t off_bytes = 0; size_t off_bits = 0; @@ -60,7 +83,7 @@ const(ubyte)[] toUbyte(T)(ref T val) if(is(Unqual!T == float) || is(Unqual!T == version(LittleEndian) { - return buff.dup; + return buff; } else { @@ -73,13 +96,13 @@ const(ubyte)[] toUbyte(T)(ref T val) if(is(Unqual!T == float) || is(Unqual!T == } } -@safe pure nothrow +@safe pure nothrow @nogc private Float parse(bool is_denormalized = false, T)(T x) if(is(Unqual!T == ifloat) || is(Unqual!T == idouble) || is(Unqual!T == ireal)) { return parse(x.im); } -@safe pure nothrow +@safe pure nothrow @nogc private Float parse(bool is_denormalized = false, T:real)(T x_) if(floatFormat!T != FloatFormat.Real80) { Unqual!T x = x_; @@ -116,7 +139,7 @@ private Float parse(bool is_denormalized = false, T:real)(T x_) if(floatFormat!T return Float(mant, exp, sign); } -@safe pure nothrow +@safe pure nothrow @nogc private Float parse(bool _ = false, T:real)(T x_) if(floatFormat!T == FloatFormat.Real80) { Unqual!T x = x_; @@ -228,10 +251,10 @@ private template FloatTraits(T) if(floatFormat!T == FloatFormat.Quadruple) //Uns } -@safe pure nothrow +@safe pure nothrow @nogc private real binPow2(int pow) { - static real binPosPow2(int pow) @safe pure nothrow + static real binPosPow2(int pow) @safe pure nothrow @nogc { assert(pow > 0); @@ -256,14 +279,14 @@ private real binPow2(int pow) //Need in CTFE, because CTFE float and double expressions computed more precisely that run-time expressions. -@safe pure nothrow +@safe pure nothrow @nogc private ulong shiftrRound(ulong x) { return (x >> 1) + (x & 1); } -@safe pure nothrow -private uint binLog2(T)(T x) +@safe pure nothrow @nogc +private uint binLog2(T)(const T x) { assert(x > 0); int max = 2 ^^ (FloatTraits!T.EXPONENT-1)-1; @@ -290,7 +313,7 @@ private uint binLog2(T)(T x) return max; } -@safe pure nothrow +@safe pure nothrow @nogc private ulong denormalizedMantissa(T)(T x) if(floatFormat!T == FloatFormat.Real80) { x *= 2.0L^^FloatTraits!T.MANTISSA; @@ -299,7 +322,7 @@ private ulong denormalizedMantissa(T)(T x) if(floatFormat!T == FloatFormat.Real8 return fl.mantissa >> pow; } -@safe pure nothrow +@safe pure nothrow @nogc private ulong denormalizedMantissa(T)(T x) if(floatFormat!T != FloatFormat.Real80) { x *= 2.0L^^FloatTraits!T.MANTISSA; @@ -475,21 +498,23 @@ template floatFormat(T) if(is(T:real) || is(T:ireal)) } // all toUbyte functions must be evaluable at compile time -@trusted pure nothrow -const(ubyte)[] toUbyte(T)(T[] arr) if (T.sizeof == 1) +@trusted pure nothrow @nogc +const(ubyte)[] toUbyte(T)(const T[] arr) if (T.sizeof == 1) { return cast(const(ubyte)[])arr; } -@trusted pure nothrow -const(ubyte)[] toUbyte(T)(T[] arr) if ((is(typeof(toUbyte(arr[0])) == const(ubyte)[])) && (T.sizeof > 1)) +@trusted pure nothrow @nogc +const(ubyte)[] toUbyte(T)(const T[] arr) if ((is(typeof(toUbyte(arr[0])) == const(ubyte)[])) && (T.sizeof > 1)) { if (__ctfe) { - const(ubyte)[] ret; + ubyte[] ret = ctfe_alloc(T.sizeof * arr.length); + size_t offset = 0; foreach (cur; arr) { - ret ~= toUbyte(cur); + ret[offset .. offset + T.sizeof] = toUbyte(cur)[0 .. T.sizeof]; + offset += T.sizeof; } return ret; } @@ -499,14 +524,16 @@ const(ubyte)[] toUbyte(T)(T[] arr) if ((is(typeof(toUbyte(arr[0])) == const(ubyt } } -@trusted pure nothrow -const(ubyte)[] toUbyte(T)(ref T val) if (__traits(isIntegral, T) && !is(T == enum)) +@trusted pure nothrow @nogc +const(ubyte)[] toUbyte(T)(const ref T val) if (__traits(isIntegral, T) && !is(T == enum)) { static if (T.sizeof == 1) { if (__ctfe) { - return cast(const(ubyte)[])[val]; + ubyte[] result = ctfe_alloc(1); + result[0] = cast(ubyte) val; + return result; } else { @@ -515,7 +542,7 @@ const(ubyte)[] toUbyte(T)(ref T val) if (__traits(isIntegral, T) && !is(T == enu } else if (__ctfe) { - ubyte[T.sizeof] tmp; + ubyte[] tmp = ctfe_alloc(T.sizeof); Unqual!T val_ = val; for (size_t i = 0; i < T.sizeof; ++i) { @@ -525,7 +552,7 @@ const(ubyte)[] toUbyte(T)(ref T val) if (__traits(isIntegral, T) && !is(T == enu tmp[idx] = cast(ubyte)(val_&0xff); val_ >>= 8; } - return tmp[].dup; + return tmp; } else { @@ -533,14 +560,19 @@ const(ubyte)[] toUbyte(T)(ref T val) if (__traits(isIntegral, T) && !is(T == enu } } -@trusted pure nothrow -const(ubyte)[] toUbyte(T)(ref T val) if (is(Unqual!T == cfloat) || is(Unqual!T == cdouble) ||is(Unqual!T == creal)) +@trusted pure nothrow @nogc +const(ubyte)[] toUbyte(T)(const ref T val) if (is(Unqual!T == cfloat) || is(Unqual!T == cdouble) ||is(Unqual!T == creal)) { if (__ctfe) { auto re = val.re; auto im = val.im; - return (re.toUbyte() ~ im.toUbyte()); + auto a = re.toUbyte(); + auto b = im.toUbyte(); + ubyte[] result = ctfe_alloc(a.length + b.length); + result[0 .. a.length] = a[0 .. a.length]; + result[a.length .. $] = b[0 .. b.length]; + return result; } else { @@ -548,13 +580,13 @@ const(ubyte)[] toUbyte(T)(ref T val) if (is(Unqual!T == cfloat) || is(Unqual!T = } } -@trusted pure nothrow -const(ubyte)[] toUbyte(T)(ref T val) if (is(T V == enum) && is(typeof(toUbyte(cast(V)val)) == const(ubyte)[])) +@trusted pure nothrow @nogc +const(ubyte)[] toUbyte(T)(const ref T val) if (is(T V == enum) && is(typeof(toUbyte(cast(const V)val)) == const(ubyte)[])) { if (__ctfe) { static if (is(T V == enum)){} - return toUbyte(cast(V) val); + return toUbyte(cast(const V) val); } else { @@ -571,7 +603,7 @@ nothrow pure @safe unittest enum ctfe_works = (() => { Month x = Month.jan; return toUbyte(x).length > 0; })(); } -private bool isNonReference(T)() +package(core.internal) bool isNonReference(T)() { static if (is(T == struct) || is(T == union)) { @@ -579,7 +611,10 @@ private bool isNonReference(T)() } else static if (__traits(isStaticArray, T)) { - return isNonReference!(typeof(T.init[0]))(); + static if (T.length > 0) + return isNonReference!(typeof(T.init[0]))(); + else + return true; } else static if (is(T E == enum)) { @@ -605,20 +640,20 @@ private bool isNonReference(T)() private bool isNonReferenceStruct(T)() if (is(T == struct) || is(T == union)) { - foreach (cur; T.init.tupleof) + static foreach (cur; T.tupleof) { - static if (!isNonReference!(typeof(cur))()) return false; + if (!isNonReference!(typeof(cur))()) return false; } return true; } -@trusted pure nothrow -const(ubyte)[] toUbyte(T)(ref T val) if (is(T == struct) || is(T == union)) +@trusted pure nothrow @nogc +const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == struct) || is(T == union)) { if (__ctfe) { - ubyte[T.sizeof] bytes; + ubyte[] bytes = ctfe_alloc(T.sizeof); foreach (key, cur; val.tupleof) { alias CUR_TYPE = typeof(cur); @@ -637,7 +672,7 @@ const(ubyte)[] toUbyte(T)(ref T val) if (is(T == struct) || is(T == union)) assert(0, "Unable to compute byte representation of "~typeof(CUR_TYPE).stringof~" field at compile time"); } } - return bytes[].dup; + return bytes; } else { diff --git a/libphobos/libdruntime/core/internal/hash.d b/libphobos/libdruntime/core/internal/hash.d index 7472f94d3..7d8ba9bc0 100644 --- a/libphobos/libdruntime/core/internal/hash.d +++ b/libphobos/libdruntime/core/internal/hash.d @@ -10,9 +10,217 @@ module core.internal.hash; import core.internal.convert; +import core.internal.traits : allSatisfy; + +// If true ensure that positive zero and negative zero have the same hash. +// Historically typeid(float).getHash did this but hashOf(float) did not. +private enum floatCoalesceZeroes = true; +// If true ensure that all NaNs of the same floating point type have the same hash. +// Historically typeid(float).getHash didn't do this but hashOf(float) did. +private enum floatCoalesceNaNs = true; + +// If either of the above are true then no struct or array that contains the +// representation of a floating point number may be hashed with `bytesHash`. + +@nogc nothrow pure @safe unittest +{ + static if (floatCoalesceZeroes) + assert(hashOf(+0.0) == hashOf(-0.0)); // Same hash for +0.0 and -0.0. + static if (floatCoalesceNaNs) + assert(hashOf(double.nan) == hashOf(-double.nan)); // Same hash for different NaN. +} + +private enum hasCallableToHash(T) = __traits(compiles, + { + size_t hash = ((T* x) => (*x).toHash())(null); + }); + +@nogc nothrow pure @safe unittest +{ + static struct S { size_t toHash() { return 4; } } + assert(hasCallableToHash!S); + assert(!hasCallableToHash!(shared const S)); +} + +private enum isFinalClassWithAddressBasedHash(T) = __traits(isFinalClass, T) + // Use __traits(compiles, ...) in case there are multiple overloads of `toHash`. + && __traits(compiles, {static assert(&Object.toHash is &T.toHash);}); + +@nogc nothrow pure @safe unittest +{ + static class C1 {} + final static class C2 : C1 {} + final static class C3 : C1 { override size_t toHash() const nothrow { return 1; }} + static assert(!isFinalClassWithAddressBasedHash!Object); + static assert(!isFinalClassWithAddressBasedHash!C1); + static assert(isFinalClassWithAddressBasedHash!C2); + static assert(!isFinalClassWithAddressBasedHash!C3); +} + +/+ +Is it valid to calculate a hash code for T based on the bits of its +representation? Always false for interfaces, dynamic arrays, and +associative arrays. False for all classes except final classes that do +not override `toHash`. + +Note: according to the spec as of +https://github.com/dlang/dlang.org/commit/d66eff16491b0664c0fc00ba80a7aa291703f1f2 +the contents of unnamed paddings between fields is undefined. Currently +this hashing implementation assumes that the padding contents (if any) +for all instances of `T` are the same. The correctness of this +assumption is yet to be verified. ++/ +private template canBitwiseHash(T) +{ + static if (is(T EType == enum)) + enum canBitwiseHash = .canBitwiseHash!EType; + else static if (__traits(isFloating, T)) + enum canBitwiseHash = !(floatCoalesceZeroes || floatCoalesceNaNs); + else static if (__traits(isScalar, T)) + enum canBitwiseHash = true; + else static if (is(T == class)) + { + enum canBitwiseHash = isFinalClassWithAddressBasedHash!T; + } + else static if (is(T == interface)) + { + enum canBitwiseHash = false; + } + else static if (is(T == struct)) + { + static if (hasCallableToHash!T || __traits(isNested, T)) + enum canBitwiseHash = false; + else + enum canBitwiseHash = allSatisfy!(.canBitwiseHash, typeof(T.tupleof)); + } + else static if (is(T == union)) + { + // Right now we always bytewise hash unions that lack callable `toHash`. + enum canBitwiseHash = !hasCallableToHash!T; + } + else static if (is(T E : E[])) + { + static if (__traits(isStaticArray, T)) + enum canBitwiseHash = (T.length == 0) || .canBitwiseHash!E; + else + enum canBitwiseHash = false; + } + else static if (__traits(isAssociativeArray, T)) + { + enum canBitwiseHash = false; + } + else + { + static assert(is(T == delegate) || is(T : void) || is(T : typeof(null)), + "Internal error: unanticipated type "~T.stringof); + enum canBitwiseHash = true; + } +} + +private template UnqualUnsigned(T) if (__traits(isIntegral, T)) +{ + static if (T.sizeof == ubyte.sizeof) alias UnqualUnsigned = ubyte; + else static if (T.sizeof == ushort.sizeof) alias UnqualUnsigned = ushort; + else static if (T.sizeof == uint.sizeof) alias UnqualUnsigned = uint; + else static if (T.sizeof == ulong.sizeof) alias UnqualUnsigned = ulong; + else static if (T.sizeof == ulong.sizeof * 2) + { + static assert(T.sizeof == ucent.sizeof); + alias UnqualUnsigned = ucent; + } + else + { + static assert(0, "No known unsigned equivalent of " ~ T.stringof); + } + + static assert(UnqualUnsigned.sizeof == T.sizeof && __traits(isUnsigned, UnqualUnsigned)); +} + +// Overly restrictive for simplicity: has false negatives but no false positives. +private template useScopeConstPassByValue(T) +{ + static if (__traits(isScalar, T)) + enum useScopeConstPassByValue = true; + else static if (is(T == class) || is(T == interface)) + // Overly restrictive for simplicity. + enum useScopeConstPassByValue = isFinalClassWithAddressBasedHash!T; + else static if (is(T == struct) || is(T == union)) + { + // Overly restrictive for simplicity. + enum useScopeConstPassByValue = T.sizeof <= (int[]).sizeof && + __traits(isPOD, T) && // "isPOD" just to check there's no dtor or postblit. + canBitwiseHash!T; // We can't verify toHash doesn't leak. + } + else static if (is(T : E[], E)) + { + static if (!__traits(isStaticArray, T)) + // Overly restrictive for simplicity. + enum useScopeConstPassByValue = .useScopeConstPassByValue!E; + else static if (T.length == 0) + enum useScopeConstPassByValue = true; + else + enum useScopeConstPassByValue = T.sizeof <= (uint[]).sizeof + && .useScopeConstPassByValue!(typeof(T.init[0])); + } + else static if (is(T : V[K], K, V)) + { + // Overly restrictive for simplicity. + enum useScopeConstPassByValue = .useScopeConstPassByValue!K + && .useScopeConstPassByValue!V; + } + else + { + static assert(is(T == delegate) || is(T : void) || is(T : typeof(null)), + "Internal error: unanticipated type "~T.stringof); + enum useScopeConstPassByValue = true; + } +} + +@safe unittest +{ + static assert(useScopeConstPassByValue!int); + static assert(useScopeConstPassByValue!string); + + static int ctr; + static struct S1 { ~this() { ctr++; } } + static struct S2 { this(this) { ctr++; } } + static assert(!useScopeConstPassByValue!S1, + "Don't default pass by value a struct with a non-vacuous destructor."); + static assert(!useScopeConstPassByValue!S2, + "Don't default pass by value a struct with a non-vacuous postblit."); +} + +//enum hash. CTFE depends on base type +size_t hashOf(T)(scope const T val) +if (is(T EType == enum) && useScopeConstPassByValue!EType) +{ + static if (is(T EType == enum)) //for EType + { + return hashOf(cast(const EType) val); + } + else + { + static assert(0); + } +} + +//enum hash. CTFE depends on base type +size_t hashOf(T)(scope const T val, size_t seed) +if (is(T EType == enum) && useScopeConstPassByValue!EType) +{ + static if (is(T EType == enum)) //for EType + { + return hashOf(cast(const EType) val, seed); + } + else + { + static assert(0); + } +} //enum hash. CTFE depends on base type -size_t hashOf(T)(auto ref T val, size_t seed = 0) if (is(T == enum)) +size_t hashOf(T)(auto ref T val, size_t seed = 0) +if (is(T EType == enum) && !useScopeConstPassByValue!EType) { static if (is(T EType == enum)) //for EType { @@ -25,84 +233,235 @@ size_t hashOf(T)(auto ref T val, size_t seed = 0) if (is(T == enum)) } } -//CTFE ready (depends on base type). Can be merged with dynamic array hash -size_t hashOf(T)(auto ref T val, size_t seed = 0) if (!is(T == enum) && __traits(isStaticArray, T)) +//CTFE ready (depends on base type). +size_t hashOf(T)(scope const auto ref T val, size_t seed = 0) +if (!is(T == enum) && __traits(isStaticArray, T) && canBitwiseHash!T) { - size_t cur_hash = seed; - foreach (ref cur; val) + // FIXME: + // We would like to to do this: + // + //static if (T.length == 0) + // return seed; + //else static if (T.length == 1) + // return hashOf(val[0], seed); + //else + // /+ hash like a dynamic array +/ + // + // ... but that's inefficient when using a runtime TypeInfo (introduces a branch) + // and PR #2243 wants typeid(T).getHash(&val) to produce the same result as + // hashOf(val). + static if (T.length == 0) { - cur_hash = hashOf(cur, cur_hash); + return bytesHashAlignedBy!size_t((ubyte[]).init, seed); + } + static if (is(typeof(toUbyte(val)) == const(ubyte)[])) + { + return bytesHashAlignedBy!T(toUbyte(val), seed); + } + else //Other types. CTFE unsupported + { + assert(!__ctfe, "unable to compute hash of "~T.stringof~" at compile time"); + return bytesHashAlignedBy!T((cast(const(ubyte)*) &val)[0 .. T.sizeof], seed); } - return cur_hash; } -//dynamic array hash +//CTFE ready (depends on base type). size_t hashOf(T)(auto ref T val, size_t seed = 0) +if (!is(T == enum) && __traits(isStaticArray, T) && !canBitwiseHash!T) +{ + // FIXME: + // We would like to to do this: + // + //static if (T.length == 0) + // return seed; + //else static if (T.length == 1) + // return hashOf(val[0], seed); + //else + // /+ hash like a dynamic array +/ + // + // ... but that's inefficient when using a runtime TypeInfo (introduces a branch) + // and PR #2243 wants typeid(T).getHash(&val) to produce the same result as + // hashOf(val). + return hashOf(val[], seed); +} + +//dynamic array hash +size_t hashOf(T)(scope const T val, size_t seed = 0) if (!is(T == enum) && !is(T : typeof(null)) && is(T S: S[]) && !__traits(isStaticArray, T) - && !is(T == struct) && !is(T == class) && !is(T == union)) + && !is(T == struct) && !is(T == class) && !is(T == union) + && (__traits(isScalar, S) || canBitwiseHash!S)) { alias ElementType = typeof(val[0]); - static if (is(ElementType == interface) || is(ElementType == class) || - ((is(ElementType == struct) || is(ElementType == union)) - && is(typeof(val[0].toHash()) == size_t))) - //class or interface array or struct array with toHash(); CTFE depend on toHash() method + static if (!canBitwiseHash!ElementType) { size_t hash = seed; - foreach (o; val) + foreach (ref o; val) { - hash = hashOf(o, hash); + hash = hashOf(hashOf(o), hash); // double hashing to match TypeInfo.getHash } return hash; } else static if (is(typeof(toUbyte(val)) == const(ubyte)[])) //ubyteble array (arithmetic types and structs without toHash) CTFE ready for arithmetic types and structs without reference fields { - return bytesHash(toUbyte(val), seed); + return bytesHashAlignedBy!ElementType(toUbyte(val), seed); } else //Other types. CTFE unsupported { - assert(!__ctfe, "unable to compute hash of "~T.stringof); - return bytesHash(val.ptr, ElementType.sizeof*val.length, seed); + assert(!__ctfe, "unable to compute hash of "~T.stringof~" at compile time"); + return bytesHashAlignedBy!ElementType((cast(const(ubyte)*) val.ptr)[0 .. ElementType.sizeof*val.length], seed); } } +//dynamic array hash +size_t hashOf(T)(T val, size_t seed = 0) +if (!is(T == enum) && !is(T : typeof(null)) && is(T S: S[]) && !__traits(isStaticArray, T) + && !is(T == struct) && !is(T == class) && !is(T == union) + && !(__traits(isScalar, S) || canBitwiseHash!S)) +{ + size_t hash = seed; + foreach (ref o; val) + { + hash = hashOf(hashOf(o), hash); // double hashing because TypeInfo.getHash doesn't allow to pass seed value + } + return hash; +} + @nogc nothrow pure @safe unittest // issue 18918 { // Check hashOf dynamic array of scalars is usable in @safe code. const _ = hashOf("abc"); } -nothrow pure @system unittest +@nogc nothrow pure @system unittest { void*[] val; const _ = hashOf(val); // Check a PR doesn't break this. } //arithmetic type hash -@trusted nothrow pure -size_t hashOf(T)(auto ref T val, size_t seed = 0) if (!is(T == enum) && __traits(isArithmetic, T)) +@trusted @nogc nothrow pure +size_t hashOf(T)(scope const T val) if (!is(T == enum) && __traits(isArithmetic, T) + && __traits(isIntegral, T) && T.sizeof <= size_t.sizeof) +{ + return cast(UnqualUnsigned!T) val; +} + +//arithmetic type hash +@trusted @nogc nothrow pure +size_t hashOf(T)(scope const T val, size_t seed) if (!is(T == enum) && __traits(isArithmetic, T) + && __traits(isIntegral, T) && T.sizeof <= size_t.sizeof) +{ + static if (size_t.sizeof < ulong.sizeof) + { + //MurmurHash3 32-bit single round + enum uint c1 = 0xcc9e2d51; + enum uint c2 = 0x1b873593; + enum uint c3 = 0xe6546b64; + enum uint r1 = 15; + enum uint r2 = 13; + } + else + { + //Half of MurmurHash3 64-bit single round + //(omits second interleaved update) + enum ulong c1 = 0x87c37b91114253d5; + enum ulong c2 = 0x4cf5ad432745937f; + enum ulong c3 = 0x52dce729; + enum uint r1 = 31; + enum uint r2 = 27; + } + auto h = c1 * cast(UnqualUnsigned!T) val; + h = (h << r1) | (h >>> (typeof(h).sizeof * 8 - r1)); + h = (h * c2) ^ seed; + h = (h << r2) | (h >>> (typeof(h).sizeof * 8 - r2)); + return h * 5 + c3; +} + +//arithmetic type hash +@trusted @nogc nothrow pure +size_t hashOf(T)(scope const T val, size_t seed = 0) if (!is(T == enum) && __traits(isArithmetic, T) + && (!__traits(isIntegral, T) || T.sizeof > size_t.sizeof)) { static if(__traits(isFloating, val)) { - T data = (val != val) ? T.nan : val; - return bytesHash(toUbyte(data), seed); + static if (floatCoalesceZeroes || floatCoalesceNaNs) + { + import core.internal.traits : Unqual; + Unqual!T data = val; + // +0.0 and -0.0 become the same. + static if (floatCoalesceZeroes && is(typeof(data = 0))) + if (data == 0) data = 0; + static if (floatCoalesceZeroes && is(typeof(data = 0.0i))) + if (data == 0.0i) data = 0.0i; + static if (floatCoalesceZeroes && is(typeof(data = 0.0 + 0.0i))) + { + if (data.re == 0.0) data = 0.0 + (data.im * 1.0i); + if (data.im == 0.0i) data = data.re + 0.0i; + } + static if (floatCoalesceNaNs) + if (data != data) data = T.nan; // All NaN patterns become the same. + } + else + { + alias data = val; + } + + static if (T.mant_dig == float.mant_dig && T.sizeof == uint.sizeof) + return hashOf(*cast(const uint*) &data, seed); + else static if (T.mant_dig == double.mant_dig && T.sizeof == ulong.sizeof) + return hashOf(*cast(const ulong*) &data, seed); + else + return bytesHashAlignedBy!T(toUbyte(data), seed); } else { - return bytesHash(toUbyte(val), seed); + static assert(T.sizeof > size_t.sizeof && __traits(isIntegral, T)); + static foreach (i; 0 .. T.sizeof / size_t.sizeof) + seed = hashOf(cast(size_t) (val >>> (size_t.sizeof * 8 * i)), seed); + return seed; } } //typeof(null) hash. CTFE supported -@trusted nothrow pure -size_t hashOf(T)(auto ref T val, size_t seed = 0) if (!is(T == enum) && is(T : typeof(null))) +@trusted @nogc nothrow pure +size_t hashOf(T)(scope const T val) if (!is(T == enum) && is(T : typeof(null))) { - return hashOf(cast(void*)null, seed); + return 0; +} + +//typeof(null) hash. CTFE supported +@trusted @nogc nothrow pure +size_t hashOf(T)(scope const T val, size_t seed) if (!is(T == enum) && is(T : typeof(null))) +{ + return hashOf(size_t(0), seed); } //Pointers hash. CTFE unsupported if not null -@trusted nothrow pure -size_t hashOf(T)(auto ref T val, size_t seed = 0) +@trusted @nogc nothrow pure +size_t hashOf(T)(scope const T val) +if (!is(T == enum) && is(T V : V*) && !is(T : typeof(null)) + && !is(T == struct) && !is(T == class) && !is(T == union)) +{ + if(__ctfe) + { + if(val is null) + { + return 0; + } + else + { + assert(0, "Unable to calculate hash of non-null pointer at compile time"); + } + + } + auto addr = cast(size_t) val; + return addr ^ (addr >>> 4); +} + +//Pointers hash. CTFE unsupported if not null +@trusted @nogc nothrow pure +size_t hashOf(T)(scope const T val, size_t seed) if (!is(T == enum) && is(T V : V*) && !is(T : typeof(null)) && !is(T == struct) && !is(T == class) && !is(T == union)) { @@ -121,12 +480,16 @@ if (!is(T == enum) && is(T V : V*) && !is(T : typeof(null)) return hashOf(cast(size_t)val, seed); } -//struct or union hash -size_t hashOf(T)(auto ref T val, size_t seed = 0) if (!is(T == enum) && (is(T == struct) || is(T == union))) -{ - static if (is(typeof(val.toHash()) == size_t)) //CTFE depends on toHash() +private enum _hashOfStruct = +q{ + enum bool isChained = is(typeof(seed) : size_t); + static if (!isChained) enum size_t seed = 0; + static if (hasCallableToHash!T) //CTFE depends on toHash() { - return hashOf(val.toHash(), seed); + static if (isChained) + return hashOf(cast(size_t) val.toHash(), seed); + else + return val.toHash(); } else { @@ -135,17 +498,58 @@ size_t hashOf(T)(auto ref T val, size_t seed = 0) if (!is(T == enum) && (is(T == pragma(msg, "Warning: struct "~__traits(identifier, T)~" has method toHash, however it cannot be called with "~T.stringof~" this."); } - static if (is(typeof(toUbyte(val)) == const(ubyte)[]))//CTFE ready for structs without reference fields + static if (T.tupleof.length == 0) { - return bytesHash(toUbyte(val), seed); + return seed; + } + else static if ((is(T == struct) && !canBitwiseHash!T) || T.tupleof.length == 1) + { + static foreach (i, F; typeof(val.tupleof)) + { + static if (i != 0) + h = hashOf(val.tupleof[i], h); + else static if (isChained) + size_t h = hashOf(val.tupleof[i], seed); + else + size_t h = hashOf(val.tupleof[i]); + } + return h; + } + else static if (is(typeof(toUbyte(val)) == const(ubyte)[]))//CTFE ready for structs without reference fields + { + return bytesHashAlignedBy!T(toUbyte(val), seed); } else // CTFE unsupported { assert(!__ctfe, "unable to compute hash of "~T.stringof); const(ubyte)[] bytes = (() @trusted => (cast(const(ubyte)*)&val)[0 .. T.sizeof])(); - return bytesHash(bytes, seed); + return bytesHashAlignedBy!T(bytes, seed); } } +}; + +//struct or union hash +size_t hashOf(T)(scope const auto ref T val, size_t seed = 0) +if (!is(T == enum) && (is(T == struct) || is(T == union)) + && canBitwiseHash!T) +{ + mixin(_hashOfStruct); +} + +//struct or union hash +size_t hashOf(T)(auto ref T val) +if (!is(T == enum) && (is(T == struct) || is(T == union)) + && !canBitwiseHash!T) +{ + mixin(_hashOfStruct); +} + +//struct or union hash +size_t hashOf(T)(auto ref T val, size_t seed) +if (!is(T == enum) && (is(T == struct) || is(T == union)) + && !canBitwiseHash!T) +{ + mixin(_hashOfStruct); } nothrow pure @safe unittest // issue 18925 @@ -172,24 +576,60 @@ nothrow pure @safe unittest // issue 19005 } //delegate hash. CTFE unsupported -@trusted nothrow pure -size_t hashOf(T)(auto ref T val, size_t seed = 0) if (!is(T == enum) && is(T == delegate)) +@trusted @nogc nothrow pure +size_t hashOf(T)(scope const T val, size_t seed = 0) if (!is(T == enum) && is(T == delegate)) { assert(!__ctfe, "unable to compute hash of "~T.stringof); const(ubyte)[] bytes = (cast(const(ubyte)*)&val)[0 .. T.sizeof]; - return bytesHash(bytes, seed); + return bytesHashAlignedBy!T(bytes, seed); +} + +//address-based class hash. CTFE only if null. +@nogc nothrow pure @trusted +size_t hashOf(T)(scope const T val) +if (!is(T == enum) && (is(T == interface) || is(T == class)) + && isFinalClassWithAddressBasedHash!T) +{ + if (__ctfe) if (val is null) return 0; + return hashOf(cast(const void*) val); +} + +//address-based class hash. CTFE only if null. +@nogc nothrow pure @trusted +size_t hashOf(T)(scope const T val, size_t seed) +if (!is(T == enum) && (is(T == interface) || is(T == class)) + && isFinalClassWithAddressBasedHash!T) +{ + if (__ctfe) if (val is null) return hashOf(size_t(0), seed); + return hashOf(cast(const void*) val, seed); +} + +//class or interface hash. CTFE depends on toHash +size_t hashOf(T)(T val) +if (!is(T == enum) && (is(T == interface) || is(T == class)) + && !isFinalClassWithAddressBasedHash!T) +{ + static if (__traits(compiles, {size_t h = val.toHash();})) + return val ? val.toHash() : 0; + else + return val ? (cast(Object)val).toHash() : 0; } //class or interface hash. CTFE depends on toHash -size_t hashOf(T)(auto ref T val, size_t seed = 0) if (!is(T == enum) && is(T == interface) || is(T == class)) +size_t hashOf(T)(T val, size_t seed) +if (!is(T == enum) && (is(T == interface) || is(T == class)) + && !isFinalClassWithAddressBasedHash!T) { - return hashOf(val ? (cast(Object)val).toHash() : 0, seed); + static if (__traits(compiles, {size_t h = val.toHash();})) + return hashOf(val ? cast(size_t) val.toHash() : size_t(0), seed); + else + return hashOf(val ? (cast(Object)val).toHash() : 0, seed); } //associative array hash. CTFE depends on base types -size_t hashOf(T)(auto ref T aa, size_t seed = 0) if (!is(T == enum) && __traits(isAssociativeArray, T)) +size_t hashOf(T)(T aa) if (!is(T == enum) && __traits(isAssociativeArray, T)) { - if (!aa.length) return hashOf(0, seed); + if (!aa.length) return 0; size_t h = 0; // The computed hash is independent of the foreach traversal order. @@ -200,7 +640,13 @@ size_t hashOf(T)(auto ref T aa, size_t seed = 0) if (!is(T == enum) && __traits( hpair[1] = val.hashOf(); h += hpair.hashOf(); } - return h.hashOf(seed); + return h; +} + +//associative array hash. CTFE depends on base types +size_t hashOf(T)(T aa, size_t seed) if (!is(T == enum) && __traits(isAssociativeArray, T)) +{ + return hashOf(hashOf(aa), seed); } unittest @@ -229,6 +675,12 @@ unittest int* a = null; } + static struct Plain + { + int a = 1; + int b = 2; + } + interface IBoo { void boo(); @@ -276,9 +728,10 @@ unittest enum Foo[] staexpr = [Foo(), Foo(), Foo()]; enum Bar[] vsaexpr = [Bar(), Bar(), Bar()]; enum realexpr = 7.88; - enum raexpr = [8.99L+86i, 3.12L+99i, 5.66L+12i]; + enum raexpr = [8.99+86i, 3.12+99i, 5.66+12i]; enum nullexpr = null; - + enum plstr = Plain(); + enum plarrstr = [Plain(), Plain(), Plain()]; //No CTFE: Boom rstructexpr = Boom(); Boom[] rstrarrexpr = [Boom(), Boom(), Boom()]; @@ -322,6 +775,9 @@ unittest enum h28 = realexpr.hashOf(); enum h29 = raexpr.hashOf(); enum h30 = nullexpr.hashOf(); + enum h31 = plstr.hashOf(); + enum h32 = plarrstr.hashOf(); + enum h33 = hashOf(cast(Plain[3])plarrstr); auto v1 = dexpr; auto v2 = fexpr; @@ -347,14 +803,17 @@ unittest auto v22 = cast(IBoo[3])[cast(IBoo)new Boo, cast(IBoo)new Boo, cast(IBoo)new Boo]; auto v23 = cast(Bar[3])vsaexpr; auto v30 = null; + auto v31 = plstr; + auto v32 = plarrstr; + auto v33 = cast(Plain[3])plarrstr; //NO CTFE: - /*auto v24 = rstructexpr; + auto v24 = rstructexpr; auto v25 = rstrarrexpr; auto v26 = dgexpr; auto v27 = ptrexpr; auto v28 = realexpr; - auto v29 = raexpr;*/ + auto v29 = raexpr; //runtime hashes auto rth1 = hashOf(v1); @@ -381,13 +840,17 @@ unittest auto rth22 = hashOf(v22); auto rth23 = hashOf(v23); auto rth30 = hashOf(v30); - /*//NO CTFE: + //NO CTFE: auto rth24 = hashOf(v24); auto rth25 = hashOf(v25); auto rth26 = hashOf(v26); auto rth27 = hashOf(v27); auto rth28 = hashOf(v28); - auto rth29 = hashOf(v29);*/ + auto rth29 = hashOf(v29); + + auto rth31 = hashOf(v31); + auto rth32 = hashOf(v32); + auto rth33 = hashOf(v33); assert(h1 == rth1); assert(h2 == rth2); @@ -419,8 +882,84 @@ unittest assert(h28 == rth28); assert(h29 == rth29);*/ assert(h30 == rth30); + assert(h31 == rth31); + assert(h32 == rth32); + assert(h33 == rth33); assert(hashOf(null, 0) != hashOf(null, 123456789)); // issue 18932 + + static size_t tiHashOf(T)(T var) + { + return typeid(T).getHash(&var); + } + + auto tih1 = tiHashOf(v1); + auto tih2 = tiHashOf(v2); + auto tih3 = tiHashOf(v3); + auto tih4 = tiHashOf(v4); + auto tih5 = tiHashOf(v5); + auto tih6 = tiHashOf(v6); + auto tih7 = tiHashOf(v7); + auto tih8 = tiHashOf(v8); + auto tih9 = tiHashOf(v9); + auto tih10 = tiHashOf(v10); + auto tih11 = tiHashOf(v11); + auto tih12 = tiHashOf(v12); + auto tih13 = tiHashOf(v13); + auto tih14 = tiHashOf(v14); + auto tih15 = tiHashOf(v15); + auto tih16 = tiHashOf(v16); + auto tih17 = tiHashOf(v17); + auto tih18 = tiHashOf(v18); + auto tih19 = tiHashOf(v19); + auto tih20 = tiHashOf(v20); + auto tih21 = tiHashOf(v21); + auto tih22 = tiHashOf(v22); + auto tih23 = tiHashOf(v23); + auto tih24 = tiHashOf(v24); + auto tih25 = tiHashOf(v25); + auto tih26 = tiHashOf(v26); + auto tih27 = tiHashOf(v27); + auto tih28 = tiHashOf(v28); + auto tih29 = tiHashOf(v29); + auto tih30 = tiHashOf(v30); + auto tih31 = tiHashOf(v31); + auto tih32 = tiHashOf(v32); + auto tih33 = tiHashOf(v33); + + assert(tih1 == rth1); + assert(tih2 == rth2); + assert(tih3 == rth3); + assert(tih4 == rth4); + assert(tih5 == rth5); + assert(tih6 == rth6); + assert(tih7 == rth7); + assert(tih8 == rth8); + assert(tih9 == rth9); + //assert(tih10 == rth10); // need compiler-generated __xtoHash changes + assert(tih11 == rth11); + assert(tih12 == rth12); + assert(tih13 == rth13); + assert(tih14 == rth14); + assert(tih15 == rth15); + assert(tih16 == rth16); + assert(tih17 == rth17); + assert(tih18 == rth18); + //assert(tih19 == rth19); // need compiler-generated __xtoHash changes + assert(tih20 == rth20); + assert(tih21 == rth21); + assert(tih22 == rth22); + //assert(tih23 == rth23); // need compiler-generated __xtoHash changes + //assert(tih24 == rth24); + //assert(tih25 == rth25); + assert(tih26 == rth26); + assert(tih27 == rth27); + assert(tih28 == rth28); + assert(tih29 == rth29); + assert(tih30 == rth30); + assert(tih31 == rth31); + assert(tih32 == rth32); + assert(tih33 == rth33); } @@ -446,53 +985,53 @@ unittest // issue 15111 testAlias!(int[8]); } +nothrow pure @system unittest // issue 18918 +{ + static struct S { string array; } + auto s1 = S("abc"); + auto s2 = S(s1.array.idup); + assert(hashOf(s1) == hashOf(s2)); + enum e = hashOf(S("abc")); + assert(hashOf(s1) == e); +} + // MurmurHash3 was written by Austin Appleby, and is placed in the public // domain. The author hereby disclaims copyright to this source code. // This overload is for backwards compatibility. @system pure nothrow @nogc -size_t bytesHash(const(void)* buf, size_t len, size_t seed) +size_t bytesHash()(scope const(void)* buf, size_t len, size_t seed) { - return bytesHash((cast(const(ubyte)*) buf)[0 .. len], seed); + return bytesHashAlignedBy!ubyte((cast(const(ubyte)*) buf)[0 .. len], seed); } -private @nogc nothrow pure @trusted -size_t bytesHash(scope const(ubyte)[] bytes, size_t seed) +private template bytesHashAlignedBy(AlignType) { - static uint rotl32(uint n)(in uint x) pure nothrow @safe @nogc - { - return (x << n) | (x >> (32 - n)); - } + alias bytesHashAlignedBy = bytesHash!(AlignType.alignof >= uint.alignof); +} - //----------------------------------------------------------------------------- - // Block read - if your platform needs to do endian-swapping or can only - // handle aligned reads, do the conversion here - static uint get32bits(const (ubyte)* x) pure nothrow @nogc +//----------------------------------------------------------------------------- +// Block read - if your platform needs to do endian-swapping or can only +// handle aligned reads, do the conversion here +private uint get32bits()(scope const(ubyte)* x) @nogc nothrow pure @system +{ + version(BigEndian) { - //Compiler can optimize this code to simple *cast(uint*)x if it possible. - version(BigEndian) - { - return ((cast(uint) x[0]) << 24) | ((cast(uint) x[1]) << 16) | ((cast(uint) x[2]) << 8) | (cast(uint) x[3]); - } - else - { - return ((cast(uint) x[3]) << 24) | ((cast(uint) x[2]) << 16) | ((cast(uint) x[1]) << 8) | (cast(uint) x[0]); - } + return ((cast(uint) x[0]) << 24) | ((cast(uint) x[1]) << 16) | ((cast(uint) x[2]) << 8) | (cast(uint) x[3]); } - - //----------------------------------------------------------------------------- - // Finalization mix - force all bits of a hash block to avalanche - static uint fmix32(uint h) pure nothrow @safe @nogc + else { - h ^= h >> 16; - h *= 0x85ebca6b; - h ^= h >> 13; - h *= 0xc2b2ae35; - h ^= h >> 16; - - return h; + return ((cast(uint) x[3]) << 24) | ((cast(uint) x[2]) << 16) | ((cast(uint) x[1]) << 8) | (cast(uint) x[0]); } +} +/+ +Params: + dataKnownToBeAligned = whether the data is known at compile time to be uint-aligned. ++/ +@nogc nothrow pure @trusted +private size_t bytesHash(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes, size_t seed) +{ auto len = bytes.length; auto data = bytes.ptr; auto nblocks = len / 4; @@ -508,13 +1047,16 @@ size_t bytesHash(scope const(ubyte)[] bytes, size_t seed) auto end_data = data+nblocks*uint.sizeof; for(; data!=end_data; data += uint.sizeof) { - uint k1 = get32bits(data); + static if (dataKnownToBeAligned) + uint k1 = __ctfe ? get32bits(data) : *(cast(const uint*) data); + else + uint k1 = get32bits(data); k1 *= c1; - k1 = rotl32!15(k1); + k1 = (k1 << 15) | (k1 >> (32 - 15)); k1 *= c2; h1 ^= k1; - h1 = rotl32!13(h1); + h1 = (h1 << 13) | (h1 >> (32 - 13)); h1 = h1*5+c3; } @@ -527,7 +1069,7 @@ size_t bytesHash(scope const(ubyte)[] bytes, size_t seed) case 3: k1 ^= data[2] << 16; goto case; case 2: k1 ^= data[1] << 8; goto case; case 1: k1 ^= data[0]; - k1 *= c1; k1 = rotl32!15(k1); k1 *= c2; h1 ^= k1; + k1 *= c1; k1 = (k1 << 15) | (k1 >> (32 - 15)); k1 *= c2; h1 ^= k1; goto default; default: } @@ -535,7 +1077,10 @@ size_t bytesHash(scope const(ubyte)[] bytes, size_t seed) //---------- // finalization h1 ^= len; - h1 = fmix32(h1); + // Force all bits of the hash block to avalanche. + h1 = (h1 ^ (h1 >> 16)) * 0x85ebca6b; + h1 = (h1 ^ (h1 >> 13)) * 0xc2b2ae35; + h1 ^= h1 >> 16; return h1; } @@ -566,4 +1111,5 @@ pure nothrow @system @nogc unittest // that you expect to change the result of bytesHash. assert(bytesHash(&a[1], a.length - 2, 0) == 2727459272); assert(bytesHash(&b, 5, 0) == 2727459272); + assert(bytesHashAlignedBy!uint((cast(const ubyte*) &b)[0 .. 5], 0) == 2727459272); } diff --git a/libphobos/libdruntime/core/internal/parseoptions.d b/libphobos/libdruntime/core/internal/parseoptions.d index d3f59400f..5ecfb0f76 100644 --- a/libphobos/libdruntime/core/internal/parseoptions.d +++ b/libphobos/libdruntime/core/internal/parseoptions.d @@ -14,6 +14,8 @@ import core.stdc.stdio; import core.stdc.ctype; import core.stdc.string; import core.vararg; +import core.internal.traits : externDFunc; + @nogc nothrow: extern extern(C) string[] rt_args(); @@ -22,6 +24,10 @@ extern extern(C) __gshared bool rt_envvars_enabled; extern extern(C) __gshared bool rt_cmdline_enabled; extern extern(C) __gshared string[] rt_options; +alias rt_configCallBack = string delegate(string) @nogc nothrow; +alias fn_configOption = string function(string opt, scope rt_configCallBack dg, bool reverse) @nogc nothrow; +alias rt_configOption = externDFunc!("rt.config.rt_configOption", fn_configOption); + /** * initialize members of struct CFG from rt_config options * @@ -32,13 +38,6 @@ extern extern(C) __gshared string[] rt_options; */ bool initConfigOptions(CFG)(ref CFG cfg, string cfgname) { - import core.internal.traits : externDFunc; - - alias rt_configCallBack = string delegate(string) @nogc nothrow; - alias fn_configOption = string function(string opt, scope rt_configCallBack dg, bool reverse) @nogc nothrow; - - alias rt_configOption = externDFunc!("rt.config.rt_configOption", fn_configOption); - string parse(string opt) @nogc nothrow { if (!parseOptions(cfg, opt)) @@ -110,6 +109,28 @@ bool parseOptions(CFG)(ref CFG cfg, string opt) return true; } +/** +Parses an individual option `optname` value from a provided string `str`. +The option type is given by the type `T` of the field `res` to which the parsed +value will be written too. +In case of an error, `errName` will be used to display an error message and +the failure of the parsing will be indicated by a falsy return value. + +For boolean values, '0/n/N' (false) or '1/y/Y' (true) are accepted. + +Params: + optname = name of the option to parse + str = raw string to parse the option value from + res = reference to the resulting data field that the option should be parsed too + errName = full-text name of the option which should be displayed in case of errors + +Returns: `false` if a parsing error happened. +*/ +bool rt_parseOption(T)(const(char)[] optname, ref inout(char)[] str, ref T res, const(char)[] errName) +{ + return parse(optname, str, res, errName); +} + private: bool optError(in char[] msg, in char[] name, const(char)[] errName) diff --git a/libphobos/libdruntime/core/internal/string.d b/libphobos/libdruntime/core/internal/string.d index aca822832..497a7e88e 100644 --- a/libphobos/libdruntime/core/internal/string.d +++ b/libphobos/libdruntime/core/internal/string.d @@ -15,7 +15,20 @@ nothrow: alias UnsignedStringBuf = char[20]; -char[] unsignedToTempString(ulong value, return char[] buf, uint radix = 10) @safe +/** +Converts an unsigned integer value to a string of characters. + +This implementation is a template so it can be used when compiling with -betterC. + +Params: + value = the unsigned integer value to convert + buf = the pre-allocated buffer used to store the result + radix = the numeric base to use in the conversion (defaults to 10) + +Returns: + The unsigned integer value as a string of characters +*/ +char[] unsignedToTempString()(ulong value, return scope char[] buf, uint radix = 10) @safe { if (radix < 2) // not a valid radix, just return an empty string @@ -52,7 +65,19 @@ private struct TempStringNoAlloc alias get this; } -auto unsignedToTempString(ulong value, uint radix = 10) @safe +/** +Converts an unsigned integer value to a string of characters. + +This implementation is a template so it can be used when compiling with -betterC. + +Params: + value = the unsigned integer value to convert + radix = the numeric base to use in the conversion (defaults to 10) + +Returns: + The unsigned integer value as a string of characters +*/ +auto unsignedToTempString()(ulong value, uint radix = 10) @safe { TempStringNoAlloc result = void; result._len = unsignedToTempString(value, result._buf, radix).length & 0xff; @@ -86,7 +111,7 @@ unittest alias SignedStringBuf = char[20]; -char[] signedToTempString(long value, return char[] buf, uint radix = 10) @safe +char[] signedToTempString(long value, return scope char[] buf, uint radix = 10) @safe { bool neg = value < 0; if(neg) @@ -212,7 +237,7 @@ unittest static assert(!__traits(compiles, 100.numDigits!37())); } -int dstrcmp( scope const char[] s1, scope const char[] s2 ) @trusted +int dstrcmp()( scope const char[] s1, scope const char[] s2 ) @trusted { immutable len = s1.length <= s2.length ? s1.length : s2.length; if (__ctfe) diff --git a/libphobos/libdruntime/core/internal/traits.d b/libphobos/libdruntime/core/internal/traits.d index bff5d2531..33787344e 100644 --- a/libphobos/libdruntime/core/internal/traits.d +++ b/libphobos/libdruntime/core/internal/traits.d @@ -128,6 +128,30 @@ template dtorIsNothrow(T) enum dtorIsNothrow = is(typeof(function{T t=void;}) : void function() nothrow); } +/* +Tests whether all given items satisfy a template predicate, i.e. evaluates to +$(D F!(T[0]) && F!(T[1]) && ... && F!(T[$ - 1])). +*/ +package(core.internal) +template allSatisfy(alias F, T...) +{ + static if (T.length == 0) + { + enum allSatisfy = true; + } + else static if (T.length == 1) + { + enum allSatisfy = F!(T[0]); + } + else + { + static if (allSatisfy!(F, T[0 .. $/2])) + enum allSatisfy = allSatisfy!(F, T[$/2 .. $]); + else + enum allSatisfy = false; + } +} + template anySatisfy(alias F, T...) { static if (T.length == 0) diff --git a/libphobos/libdruntime/core/runtime.d b/libphobos/libdruntime/core/runtime.d index ba012243a..a44cb78a4 100644 --- a/libphobos/libdruntime/core/runtime.d +++ b/libphobos/libdruntime/core/runtime.d @@ -703,10 +703,7 @@ extern (C) UnitTestResult runModuleUnitTests() } } - import core.internal.traits : externDFunc; - alias rt_configCallBack = string delegate(string) @nogc nothrow; - alias fn_configOption = string function(string opt, scope rt_configCallBack dg, bool reverse) @nogc nothrow; - alias rt_configOption = externDFunc!("rt.config.rt_configOption", fn_configOption); + import core.internal.parseoptions : rt_configOption; if (results.passed != results.executed) { diff --git a/libphobos/libdruntime/core/simd.d b/libphobos/libdruntime/core/simd.d index e19c797b5..4efdcb741 100644 --- a/libphobos/libdruntime/core/simd.d +++ b/libphobos/libdruntime/core/simd.d @@ -396,14 +396,58 @@ version ( D_SIMD ) * Returns: * result of opcode */ - pure @safe void16 __simd(XMM opcode, void16 op1, void16 op2); + pure @safe V1 simd(XMM opcode, V1, V2)(V1 op1, V2 op2) + if (is(V1 == __vector) && is(V2 == __vector)) + { + pragma(inline, true); + return cast(V1)__simd(opcode, op1, op2); + } + + pure @safe void16 __simd(XMM opcode, void16 op1, void16 op2); // intrinsic + + /// + unittest + { + float4 a; + a = simd!(XMM.PXOR)(a, a); + } /** * Unary SIMD instructions. */ - pure @safe void16 __simd(XMM opcode, void16 op1); - pure @safe void16 __simd(XMM opcode, double d); /// - pure @safe void16 __simd(XMM opcode, float f); /// + pure @safe V1 simd(XMM opcode, V1)(V1 op1) + if (is(V1 == __vector)) + { + pragma(inline, true); + return cast(V1)__simd(opcode, op1); + } + + /// + pure @safe V1 simd(XMM opcode, V1)(double d) + if (is(V1 == __vector)) + { + pragma(inline, true); + return cast(V1)__simd(opcode, d); + } + + /// + pure @safe V1 simd(XMM opcode, V1)(float f) + if (is(V1 == __vector)) + { + pragma(inline, true); + return cast(V1)__simd(opcode, f); + } + + pure @safe void16 __simd(XMM opcode, void16 op1); // intrinsic + pure @safe void16 __simd(XMM opcode, double d); // intrinsic + pure @safe void16 __simd(XMM opcode, float f); // intrinsic + + /// + unittest + { + float4 a; + a = simd!(XMM.LODSS)(a); + } /**** * For instructions: @@ -420,7 +464,21 @@ version ( D_SIMD ) * Returns: * result of opcode */ - pure @safe void16 __simd(XMM opcode, void16 op1, void16 op2, ubyte imm8); + pure @safe V1 simd(XMM opcode, ubyte imm8, V1, V2)(V1 op1, V2 op2) + if (is(V1 == __vector) && is(V2 == __vector)) + { + pragma(inline, true); + return cast(V1)__simd(opcode, op1, op2, imm8); + } + + pure @safe void16 __simd(XMM opcode, void16 op1, void16 op2, ubyte imm8); // intrinsic + + /// + unittest + { + float4 a; + a = simd!(XMM.CMPPD, 0x7A)(a, a); + } /*** * For instructions with the imm8 version: @@ -433,7 +491,21 @@ version ( D_SIMD ) * Returns: * result of opcode */ - pure @safe void16 __simd_ib(XMM opcode, void16 op1, ubyte imm8); + pure @safe V1 simd(XMM opcode, ubyte imm8, V1)(V1 op1) + if (is(V1 == __vector)) + { + pragma(inline, true); + return cast(V1)__simd_ib(opcode, op1, imm8); + } + + pure @safe void16 __simd_ib(XMM opcode, void16 op1, ubyte imm8); // intrinsic + + /// + unittest + { + float4 a; + a = simd!(XMM.PSRLQ, 0x7A)(a); + } /***** * For "store" operations of the form: @@ -442,9 +514,44 @@ version ( D_SIMD ) * op2 * These cannot be marked as pure, as semantic() doesn't check them. */ - @safe void16 __simd_sto(XMM opcode, void16 op1, void16 op2); - @safe void16 __simd_sto(XMM opcode, double op1, void16 op2); /// - @safe void16 __simd_sto(XMM opcode, float op1, void16 op2); /// + @safe V1 simd_sto(XMM opcode, V1, V2)(V1 op1, V2 op2) + if (is(V1 == __vector) && is(V2 == __vector)) + { + pragma(inline, true); + return cast(V1)__simd_sto(opcode, op1, op2); + } + + /// + @safe V1 simd_stod(XMM opcode, V1, V2)(double op1, V1 op2) + if (is(V1 == __vector)) + { + pragma(inline, true); + return cast(V1)__simd_sto(opcode, op1, op2); + } + + /// + @safe V1 simd_stof(XMM opcode, V1)(float op1, V1 op2) + if (is(V1 == __vector)) + { + pragma(inline, true); + return cast(V1)__simd_sto(opcode, op1, op2); + } + + @safe void16 __simd_sto(XMM opcode, void16 op1, void16 op2); // intrinsic + @safe void16 __simd_sto(XMM opcode, double op1, void16 op2); // intrinsic + @safe void16 __simd_sto(XMM opcode, float op1, void16 op2); // intrinsic + + /// + unittest + { + void16 a; + float f = 1; + double d = 1; + + cast(void)simd_sto!(XMM.STOUPS)(a, a); + //simd_sto!(XMM.STOUPS)(f, a); + //simd_sto!(XMM.STOUPS)(d, a); + } /* The following use overloading to ensure correct typing. * Compile with inlining on for best performance. @@ -452,12 +559,12 @@ version ( D_SIMD ) pure @safe short8 pcmpeq()(short8 v1, short8 v2) { - return __simd(XMM.PCMPEQW, v1, v2); + return cast(short8)__simd(XMM.PCMPEQW, v1, v2); } pure @safe ushort8 pcmpeq()(ushort8 v1, ushort8 v2) { - return __simd(XMM.PCMPEQW, v1, v2); + return cast(ushort8)__simd(XMM.PCMPEQW, v1, v2); } /********************* diff --git a/libphobos/libdruntime/core/stdc/config.d b/libphobos/libdruntime/core/stdc/config.d index 06be6f40d..042b31ce8 100644 --- a/libphobos/libdruntime/core/stdc/config.d +++ b/libphobos/libdruntime/core/stdc/config.d @@ -106,18 +106,18 @@ version( GNU ) { import gcc.builtins; - enum __c_long : __builtin_clong; - enum __c_ulong : __builtin_culong; - - enum __c_longlong : __builtin_clonglong; - enum __c_ulonglong : __builtin_culonglong; - alias __builtin_clong c_long; alias __builtin_culong c_ulong; + enum __c_long : __builtin_clong; + enum __c_ulong : __builtin_culong; + alias __c_long cpp_long; alias __c_ulong cpp_ulong; + enum __c_longlong : __builtin_clonglong; + enum __c_ulonglong : __builtin_culonglong; + alias __c_longlong cpp_longlong; alias __c_ulonglong cpp_ulonglong; } @@ -126,12 +126,12 @@ else version( Windows ) enum __c_long : int; enum __c_ulong : uint; - alias __c_long cpp_long; - alias __c_ulong cpp_ulong; - alias int c_long; alias uint c_ulong; + alias __c_long cpp_long; + alias __c_ulong cpp_ulong; + alias long cpp_longlong; alias ulong cpp_ulonglong; } @@ -139,18 +139,15 @@ else version( Posix ) { static if( (void*).sizeof > int.sizeof ) { - enum __c_long : long; - enum __c_ulong : ulong; - enum __c_longlong : long; enum __c_ulonglong : ulong; - alias __c_long cpp_long; - alias __c_ulong cpp_ulong; - alias long c_long; alias ulong c_ulong; + alias long cpp_long; + alias ulong cpp_ulong; + alias __c_longlong cpp_longlong; alias __c_ulonglong cpp_ulonglong; } @@ -159,12 +156,12 @@ else version( Posix ) enum __c_long : int; enum __c_ulong : uint; - alias __c_long cpp_long; - alias __c_ulong cpp_ulong; - alias int c_long; alias uint c_ulong; + alias __c_long cpp_long; + alias __c_ulong cpp_ulong; + alias long cpp_longlong; alias ulong cpp_ulonglong; } diff --git a/libphobos/libdruntime/core/stdc/stdlib.d b/libphobos/libdruntime/core/stdc/stdlib.d index 9b1633865..86da1df7c 100644 --- a/libphobos/libdruntime/core/stdc/stdlib.d +++ b/libphobos/libdruntime/core/stdc/stdlib.d @@ -135,17 +135,9 @@ else version (MinGW) /// alias __mingw_strtold strtold; } -else version (CRuntime_Bionic) -{ - /// - real strtold(scope inout(char)* nptr, scope inout(char)** endptr) - { // Fake it again till we make it - return strtod(nptr, endptr); - } -} else { - /// + /// Added to Bionic since Lollipop. real strtold(scope inout(char)* nptr, scope inout(char)** endptr); } diff --git a/libphobos/libdruntime/core/stdc/time.d b/libphobos/libdruntime/core/stdc/time.d index ee8d7d780..baccf6bff 100644 --- a/libphobos/libdruntime/core/stdc/time.d +++ b/libphobos/libdruntime/core/stdc/time.d @@ -86,7 +86,7 @@ version( Windows ) } else version( OSX ) { - enum clock_t CLOCKS_PER_SEC = 100; + enum clock_t CLOCKS_PER_SEC = 1_000_000; // was 100 until OSX 10.4/10.5 version (X86) extern (C) pragma(mangle, "clock$UNIX2003") clock_t clock(); else diff --git a/libphobos/libdruntime/core/sync/semaphore.d b/libphobos/libdruntime/core/sync/semaphore.d index dccdc3b46..eadb09b0d 100644 --- a/libphobos/libdruntime/core/sync/semaphore.d +++ b/libphobos/libdruntime/core/sync/semaphore.d @@ -337,19 +337,17 @@ class Semaphore } -private: - version( Windows ) - { - HANDLE m_hndl; - } - else version( Darwin ) - { - semaphore_t m_hndl; - } - else version( Posix ) - { - sem_t m_hndl; - } +protected: + + /// Aliases the operating-system-specific semaphore type. + version(Windows) alias Handle = HANDLE; + /// ditto + else version(Darwin) alias Handle = semaphore_t; + /// ditto + else version(Posix) alias Handle = sem_t; + + /// Handle to the system-specific semaphore. + Handle m_hndl; } diff --git a/libphobos/libdruntime/core/sys/dragonflybsd/sys/elf_common.d b/libphobos/libdruntime/core/sys/dragonflybsd/sys/elf_common.d index d4ac93d18..eef6a89e7 100644 --- a/libphobos/libdruntime/core/sys/dragonflybsd/sys/elf_common.d +++ b/libphobos/libdruntime/core/sys/dragonflybsd/sys/elf_common.d @@ -389,6 +389,7 @@ enum SHF_LINK_ORDER = (1 << 7); enum SHF_OS_NONCONFORMING = (1 << 8); enum SHF_GROUP = (1 << 9); enum SHF_TLS = (1 << 10); +enum SHF_COMPRESSED = (1 << 11); enum SHF_MASKOS = 0x0ff00000; enum SHF_MASKPROC = 0xf0000000; diff --git a/libphobos/libdruntime/core/sys/freebsd/sys/elf_common.d b/libphobos/libdruntime/core/sys/freebsd/sys/elf_common.d index fb8fcba1a..6188d0ef7 100644 --- a/libphobos/libdruntime/core/sys/freebsd/sys/elf_common.d +++ b/libphobos/libdruntime/core/sys/freebsd/sys/elf_common.d @@ -265,6 +265,7 @@ enum SHF_LINK_ORDER = (1 << 7); enum SHF_OS_NONCONFORMING = (1 << 8); enum SHF_GROUP = (1 << 9); enum SHF_TLS = (1 << 10); +enum SHF_COMPRESSED = (1 << 11); enum SHF_MASKOS = 0x0ff00000; enum SHF_MASKPROC = 0xf0000000; diff --git a/libphobos/libdruntime/core/sys/freebsd/sys/mount.d b/libphobos/libdruntime/core/sys/freebsd/sys/mount.d new file mode 100644 index 000000000..203834215 --- /dev/null +++ b/libphobos/libdruntime/core/sys/freebsd/sys/mount.d @@ -0,0 +1,304 @@ +//Written in the D programming language + +/++ + D header file for FreeBSD's sys/mount.h. + + Copyright: Copyright 2018 - + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + Authors: $(HTTP jmdavisprog.com, Jonathan M Davis) + +/ +module core.sys.freebsd.sys.mount; + +version(FreeBSD): + +import core.stdc.config : c_long; +import core.sys.posix.sys.stat : stat_t; +import core.sys.posix.sys.types : uid_t; + +extern(C) @nogc nothrow: + +struct fsid_t +{ + int[2] val; +} + +enum MAXFIDSZ = 16; + +struct fid +{ + ushort fid_len; + ushort fid_data0; + char[MAXFIDSZ] fid_data; +} + +enum MFSNAMELEN = 16; +enum MNAMELEN = 88; +enum STATFS_VERSION = 0x20030518; + +struct statfs_t +{ + uint f_version; + uint f_type; + ulong f_flags; + ulong f_bsize; + ulong f_iosize; + ulong f_blocks; + ulong f_bfree; + long f_bavail; + ulong f_files; + long f_ffree; + ulong f_syncwrites; + ulong f_asyncwrites; + ulong f_syncreads; + ulong f_asyncreads; + ulong[10] f_spare; + uint f_namemax; + uid_t f_owner; + fsid_t f_fsid; + char[80] f_charspare; + char[MFSNAMELEN] f_fstypename; + char[MNAMELEN] f_mntfromname; + char[MNAMELEN] f_mntonname; +} + + +enum ulong MNT_RDONLY = 0x0000000000000001; +enum ulong MNT_SYNCHRONOUS = 0x0000000000000002; +enum ulong MNT_NOEXEC = 0x0000000000000004; +enum ulong MNT_NOSUID = 0x0000000000000008; +enum ulong MNT_NFS4ACLS = 0x0000000000000010; +enum ulong MNT_UNION = 0x0000000000000020; +enum ulong MNT_ASYNC = 0x0000000000000040; +enum ulong MNT_SUIDDIR = 0x0000000000100000; +enum ulong MNT_SOFTDEP = 0x0000000000200000; +enum ulong MNT_NOSYMFOLLOW = 0x0000000000400000; +enum ulong MNT_GJOURNAL = 0x0000000002000000; +enum ulong MNT_MULTILABEL = 0x0000000004000000; +enum ulong MNT_ACLS = 0x0000000008000000; +enum ulong MNT_NOATIME = 0x0000000010000000; +enum ulong MNT_NOCLUSTERR = 0x0000000040000000; +enum ulong MNT_NOCLUSTERW = 0x0000000080000000; +enum ulong MNT_SUJ = 0x0000000100000000; +enum ulong MNT_AUTOMOUNTED = 0x0000000200000000; + +enum ulong MNT_EXRDONLY = 0x0000000000000080; +enum ulong MNT_EXPORTED = 0x0000000000000100; +enum ulong MNT_DEFEXPORTED = 0x0000000000000200; +enum ulong MNT_EXPORTANON = 0x0000000000000400; +enum ulong MNT_EXKERB = 0x0000000000000800; +enum ulong MNT_EXPUBLIC = 0x0000000020000000; + +enum ulong MNT_LOCAL = 0x0000000000001000; +enum ulong MNT_QUOTA = 0x0000000000002000; +enum ulong MNT_ROOTFS = 0x0000000000004000; +enum ulong MNT_USER = 0x0000000000008000; +enum ulong MNT_IGNORE = 0x0000000000800000; + +enum MNT_VISFLAGMASK = MNT_RDONLY | MNT_SYNCHRONOUS | MNT_NOEXEC | + MNT_NOSUID | MNT_UNION | MNT_SUJ | + MNT_ASYNC | MNT_EXRDONLY | MNT_EXPORTED | + MNT_DEFEXPORTED | MNT_EXPORTANON| MNT_EXKERB | + MNT_LOCAL | MNT_USER | MNT_QUOTA | + MNT_ROOTFS | MNT_NOATIME | MNT_NOCLUSTERR | + MNT_NOCLUSTERW | MNT_SUIDDIR | MNT_SOFTDEP | + MNT_IGNORE | MNT_EXPUBLIC | MNT_NOSYMFOLLOW | + MNT_GJOURNAL | MNT_MULTILABEL | MNT_ACLS | + MNT_NFS4ACLS | MNT_AUTOMOUNTED; + +enum MNT_UPDATEMASK = MNT_NOSUID | MNT_NOEXEC | + MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | + MNT_NOATIME | + MNT_NOSYMFOLLOW | MNT_IGNORE | + MNT_NOCLUSTERR | MNT_NOCLUSTERW | MNT_SUIDDIR | + MNT_ACLS | MNT_USER | MNT_NFS4ACLS | + MNT_AUTOMOUNTED; + +enum ulong MNT_UPDATE = 0x0000000000010000; +enum ulong MNT_DELEXPORT = 0x0000000000020000; +enum ulong MNT_RELOAD = 0x0000000000040000; +enum ulong MNT_FORCE = 0x0000000000080000; +enum ulong MNT_SNAPSHOT = 0x0000000001000000; +enum ulong MNT_NONBUSY = 0x0000000004000000; +enum ulong MNT_BYFSID = 0x0000000008000000; +enum ulong MNT_CMDFLAGS = MNT_UPDATE | MNT_DELEXPORT | MNT_RELOAD | + MNT_FORCE | MNT_SNAPSHOT | MNT_NONBUSY | + MNT_BYFSID; + +enum uint MNTK_UNMOUNTF = 0x00000001; +enum uint MNTK_ASYNC = 0x00000002; +enum uint MNTK_SOFTDEP = 0x00000004; +enum uint MNTK_NOINSMNTQ = 0x00000008; +enum uint MNTK_DRAINING = 0x00000010; +enum uint MNTK_REFEXPIRE = 0x00000020; +enum uint MNTK_EXTENDED_SHARED = 0x00000040; +enum uint MNTK_SHARED_WRITES = 0x00000080; +enum uint MNTK_NO_IOPF = 0x00000100; +enum uint MNTK_VGONE_UPPER = 0x00000200; +enum uint MNTK_VGONE_WAITER = 0x00000400; +enum uint MNTK_LOOKUP_EXCL_DOTDOT = 0x00000800; +enum uint MNTK_MARKER = 0x00001000; +enum uint MNTK_UNMAPPED_BUFS = 0x00002000; +enum uint MNTK_USES_BCACHE = 0x00004000; +enum uint MNTK_NOASYNC = 0x00800000; +enum uint MNTK_UNMOUNT = 0x01000000; +enum uint MNTK_MWAIT = 0x02000000; +enum uint MNTK_SUSPEND = 0x08000000; +enum uint MNTK_SUSPEND2 = 0x04000000; +enum uint MNTK_SUSPENDED = 0x10000000; +enum uint MNTK_NULL_NOCACHE = 0x20000000; +enum uint MNTK_LOOKUP_SHARED = 0x40000000; +enum uint MNTK_NOKNOTE = 0x80000000; + +enum VFS_VFSCONF = 0; +enum VFS_GENERIC = 0; + +enum VFS_MAXTYPENUM = 1; +enum VFS_CONF = 2; + +enum MNT_WAIT = 1; +enum MNT_NOWAIT = 2; +enum MNT_LAZY = 3; +enum MNT_SUSPEND = 4; + +struct fhandle_t +{ + fsid_t fh_fsid; + fid fh_fid; +} + +// TODO - requires declarations from elsewhere that we don't currently have bindings for. +/+ +struct oexport_args +{ + int ex_flags; + uid_t ex_root; + xucred ex_anon; + sockaddr* ex_addr; + ubyte ex_addrlen; + sockaddr* ex_mask; + ubyte ex_masklen; + char* ex_indexfile; +} + +enum MAXSECFLAVORS = 5; + +struct export_args +{ + int ex_flags; + uid_t ex_root; + xucred ex_anon; + sockaddr* ex_addr; + ubyte ex_addrlen; + sockaddr* ex_mask; + ubyte ex_masklen; + char* ex_indexfile; + int ex_numsecflavors; + int[MAXSECFLAVORS] ex_secflavors; +} + +struct nfs_public +{ + int np_valid; + fhandle_t np_handle; + mount_t* np_mount; + char* np_index; +} + +struct vfsconf +{ + uint vfc_version; + char[MFSNAMELEN] vfc_name; + vfsops* vfc_vfsops; + int vfc_typenum; + int vfc_refcount; + int vfc_flags; + vfsoptdecl* vfc_opts; + TAILQ_ENTRY(vfsconf) vfc_list; +} + +struct xvfsconf +{ + vfsops* vfc_vfsops; + char[MFSNAMELEN] vfc_name; + int vfc_typenum; + int vfc_refcount; + int vfc_flags; + vfsconf* vfc_next; +} ++/ + +struct ovfsconf +{ + void* vfc_vfsops; + char[32] vfc_name; + int vfc_index; + int vfc_refcount; + int vfc_flags; +} + +enum uint VFCF_STATIC = 0x00010000; +enum uint VFCF_NETWORK = 0x00020000; +enum uint VFCF_READONLY = 0x00040000; +enum uint VFCF_SYNTHETIC = 0x00080000; +enum uint VFCF_LOOPBACK = 0x00100000; +enum uint VFCF_UNICODE = 0x00200000; +enum uint VFCF_JAIL = 0x00400000; +enum uint VFCF_DELEGADMIN = 0x00800000; +enum uint VFCF_SBDRY = 0x01000000; + +alias fsctlop_t = uint; + +struct vfsidctl +{ + int vc_vers; + fsid_t vc_fsid; + char[MFSNAMELEN] vc_fstypename; + fsctlop_t vc_op; + void* vc_ptr; + size_t vc_len; + uint[12] vc_spare; +} + +enum uint VFS_CTL_VERS1 = 0x01; + +enum uint VFS_CTL_QUERY = 0x00010001; +enum uint VFS_CTL_TIMEO = 0x00010002; +enum uint VFS_CTL_NOLOCKS = 0x00010003; + +struct vfsquery +{ + uint vq_flags; + uint[31] vq_spare; +} + +enum uint VQ_NOTRESP = 0x0001; +enum uint VQ_NEEDAUTH = 0x0002; +enum uint VQ_LOWDISK = 0x0004; +enum uint VQ_MOUNT = 0x0008; +enum uint VQ_UNMOUNT = 0x0010; +enum uint VQ_DEAD = 0x0020; +enum uint VQ_ASSIST = 0x0040; +enum uint VQ_NOTRESPLOCK = 0x0080; +enum uint VQ_FLAG0100 = 0x0100; +enum uint VQ_FLAG0200 = 0x0200; +enum uint VQ_FLAG0400 = 0x0400; +enum uint VQ_FLAG0800 = 0x0800; +enum uint VQ_FLAG1000 = 0x1000; +enum uint VQ_FLAG2000 = 0x2000; +enum uint VQ_FLAG4000 = 0x4000; +enum uint VQ_FLAG8000 = 0x8000; + +int fhopen(const fhandle_t*, int); +int fhstat(const fhandle_t*, stat_t*); +int fhstatfs(const fhandle_t*, statfs_t*); +int fstatfs(int, statfs_t*); +int getfh(const char*, fhandle_t*); +int getfsstat(statfs_t*, c_long, int); +int getmntinfo(statfs_t**, int); +int lgetfh(const char*, fhandle_t*); +int mount(const char*, const char*, int, void*); +//int nmount(iovec*, uint, int); +int statfs(const char*, statfs_t*); +int unmount(const char*, int); + +//int getvfsbyname(const char*, xvfsconf*); diff --git a/libphobos/libdruntime/core/sys/linux/elf.d b/libphobos/libdruntime/core/sys/linux/elf.d index 19f7aa420..45e89eae6 100644 --- a/libphobos/libdruntime/core/sys/linux/elf.d +++ b/libphobos/libdruntime/core/sys/linux/elf.d @@ -314,6 +314,7 @@ enum SHF_LINK_ORDER = (1 << 7); enum SHF_OS_NONCONFORMING = (1 << 8); enum SHF_GROUP = (1 << 9); enum SHF_TLS = (1 << 10); +enum SHF_COMPRESSED = (1 << 11); enum SHF_MASKOS = 0x0ff00000; enum SHF_MASKPROC = 0xf0000000; enum SHF_ORDERED = (1 << 30); diff --git a/libphobos/libdruntime/core/sys/posix/setjmp.d b/libphobos/libdruntime/core/sys/posix/setjmp.d index d795db076..ad6b0cdb4 100644 --- a/libphobos/libdruntime/core/sys/posix/setjmp.d +++ b/libphobos/libdruntime/core/sys/posix/setjmp.d @@ -167,6 +167,12 @@ else version( FreeBSD ) enum _JBLEN = 5; struct _jmp_buf { c_long[_JBLEN + 1] _jb; } } + else version( AArch64 ) + { + enum _JBLEN = 31; + // __int128_t + struct _jmp_buf { long[2][_JBLEN + 1] _jb; }; + } else static assert(0); alias _jmp_buf[1] jmp_buf; @@ -365,6 +371,11 @@ else version( FreeBSD ) enum _JB_SIGFLAG = 5; struct _sigjmp_buf { c_long[_JBLEN + 1] _sjb; } } + else version( AArch64 ) + { + // __int128_t + struct _sigjmp_buf { long[2][_JBLEN + 1] _jb; }; + } else static assert(0); alias _sigjmp_buf[1] sigjmp_buf; @@ -469,4 +480,4 @@ else version( CRuntime_UClibc ) { int _setjmp(ref jmp_buf); void _longjmp(ref jmp_buf, int); -} \ No newline at end of file +} diff --git a/libphobos/libdruntime/core/sys/posix/signal.d b/libphobos/libdruntime/core/sys/posix/signal.d index 50e8008d4..eb500ff9b 100644 --- a/libphobos/libdruntime/core/sys/posix/signal.d +++ b/libphobos/libdruntime/core/sys/posix/signal.d @@ -1740,7 +1740,7 @@ else version( CRuntime_UClibc ) private enum __SI_MAX_SIZE = 128; - static if( false /* __WORDSIZE == 64 */ ) + static if( __WORDSIZE == 64 ) { private enum __SI_PAD_SIZE = ((__SI_MAX_SIZE / int.sizeof) - 4); } @@ -3611,7 +3611,7 @@ else version( CRuntime_UClibc ) { private enum __SIGEV_MAX_SIZE = 64; - static if( false /* __WORDSIZE == 64 */ ) + static if( __WORDSIZE == 64 ) { private enum __SIGEV_PAD_SIZE = ((__SIGEV_MAX_SIZE / int.sizeof) - 4); } diff --git a/libphobos/libdruntime/core/sys/posix/sys/socket.d b/libphobos/libdruntime/core/sys/posix/sys/socket.d index e00bc59d9..2f9f4cc1d 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/socket.d +++ b/libphobos/libdruntime/core/sys/posix/sys/socket.d @@ -474,6 +474,7 @@ version( CRuntime_Glibc ) SO_RCVLOWAT = 18, SO_RCVTIMEO = 20, SO_REUSEADDR = 2, + SO_REUSEPORT = 15, SO_SNDBUF = 7, SO_SNDLOWAT = 19, SO_SNDTIMEO = 21, @@ -508,6 +509,7 @@ version( CRuntime_Glibc ) SO_RCVLOWAT = 18, SO_RCVTIMEO = 20, SO_REUSEADDR = 2, + SO_REUSEPORT = 15, SO_SNDBUF = 7, SO_SNDLOWAT = 19, SO_SNDTIMEO = 21, diff --git a/libphobos/libdruntime/core/sys/posix/sys/statvfs.d b/libphobos/libdruntime/core/sys/posix/sys/statvfs.d index a9d39d7b6..b6c50432e 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/statvfs.d +++ b/libphobos/libdruntime/core/sys/posix/sys/statvfs.d @@ -1,12 +1,12 @@ -/** - * D header file for POSIX. - * - * Copyright: Copyright Robert Klotzner 2012 - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Robert Klotzner - * Standards: The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004 Edition - */ +/++ + D header file correspoding to sys/statvfs.h. + Copyright: Copyright 2012 - + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + Authors: Robert Klotzner and $(HTTP jmdavisprog.com, Jonathan M Davis) + Standards: $(HTTP http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_statvfs.h.html, + The Open Group Base Specifications Issue 7 IEEE Std 1003.1, 2018 Edition) + +/ module core.sys.posix.sys.statvfs; private import core.stdc.config; private import core.sys.posix.config; @@ -135,64 +135,127 @@ else version(NetBSD) } else version (FreeBSD) { - enum MFSNAMELEN = 16; - enum MNAMELEN = 88; + import core.sys.freebsd.sys.mount; - struct fsid_t - { - int[2] __fsid_val; - } + // @@@DEPRECATED_2.091@@@ + deprecated("Moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") + alias MFSNAMELEN = core.sys.freebsd.sys.mount.MFSNAMELEN; - struct statfs_t - { - uint f_version; /* structure version number */ - uint f_type; /* type of filesystem */ - ulong f_flags; /* copy of mount exported flags */ - ulong f_bsize; /* filesystem fragment size */ - ulong f_iosize; /* optimal transfer block size */ - ulong f_blocks; /* total data blocks in filesystem */ - ulong f_bfree; /* free blocks in filesystem */ - long f_bavail; /* free blocks avail to non-superuser */ - ulong f_files; /* total file nodes in filesystem */ - long f_ffree; /* free nodes avail to non-superuser */ - ulong f_syncwrites; /* count of sync writes since mount */ - ulong f_asyncwrites; /* count of async writes since mount */ - ulong f_syncreads; /* count of sync reads since mount */ - ulong f_asyncreads; /* count of async reads since mount */ - ulong[10] f_spare; /* unused spare */ - uint f_namemax; /* maximum filename length */ - uint f_owner; /* user that mounted the filesystem */ - fsid_t f_fsid; /* filesystem id */ - char[80] f_charspare; /* spare string space */ - char[MFSNAMELEN] f_fstypename; /* filesystem type name */ - char[MNAMELEN] f_mntfromname; /* mounted filesystem */ - char[MNAMELEN] f_mntonname; /* directory on which mounted */ - } + // @@@DEPRECATED_2.091@@@ + deprecated("Moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") + alias MNAMELEN = core.sys.freebsd.sys.mount.MNAMELEN; + + // @@@DEPRECATED_2.091@@@ + deprecated("Moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") + alias fsid_t = core.sys.freebsd.sys.mount.fsid_t; + + // @@@DEPRECATED_2.091@@@ + deprecated("Moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") + alias statfs_t = core.sys.freebsd.sys.mount.statfs_t; + // @@@DEPRECATED_2.091@@@ + deprecated("Values moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") enum FFlag { + // @@@DEPRECATED_2.091@@@ + deprecated("Moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") MNT_RDONLY = 1, /* read only filesystem */ + + // @@@DEPRECATED_2.091@@@ + deprecated("Moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") MNT_SYNCHRONOUS = 2, /* fs written synchronously */ + + // @@@DEPRECATED_2.091@@@ + deprecated("Moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") MNT_NOEXEC = 4, /* can't exec from filesystem */ + + // @@@DEPRECATED_2.091@@@ + deprecated("Moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") MNT_NOSUID = 8, /* don't honor setuid fs bits */ + + // @@@DEPRECATED_2.091@@@ + deprecated("Moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") MNT_NFS4ACLS = 16, /* enable NFS version 4 ACLs */ + + // @@@DEPRECATED_2.091@@@ + deprecated("Moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") MNT_UNION = 32, /* union with underlying fs */ + + // @@@DEPRECATED_2.091@@@ + deprecated("Moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") MNT_ASYNC = 64, /* fs written asynchronously */ + + // @@@DEPRECATED_2.091@@@ + deprecated("Moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") MNT_SUIDDIR = 128, /* special SUID dir handling */ + + // @@@DEPRECATED_2.091@@@ + deprecated("Moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") MNT_SOFTDEP = 256, /* using soft updates */ + + // @@@DEPRECATED_2.091@@@ + deprecated("Moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") MNT_NOSYMFOLLOW = 512, /* do not follow symlinks */ + + // @@@DEPRECATED_2.091@@@ + deprecated("Moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") MNT_GJOURNAL = 1024, /* GEOM journal support enabled */ + + // @@@DEPRECATED_2.091@@@ + deprecated("Moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") MNT_MULTILABEL = 2048, /* MAC support for objects */ + + // @@@DEPRECATED_2.091@@@ + deprecated("Moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") MNT_ACLS = 4096, /* ACL support enabled */ + + // @@@DEPRECATED_2.091@@@ + deprecated("Moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") MNT_NOATIME = 8192, /* dont update file access time */ + + // @@@DEPRECATED_2.091@@@ + deprecated("Moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") MNT_NOCLUSTERR = 16384, /* disable cluster read */ + + // @@@DEPRECATED_2.091@@@ + deprecated("Moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") MNT_NOCLUSTERW = 32768, /* disable cluster write */ + + // @@@DEPRECATED_2.091@@@ + deprecated("Moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") MNT_SUJ = 65536, /* using journaled soft updates */ + + // @@@DEPRECATED_2.091@@@ + deprecated("Moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") MNT_AUTOMOUNTED = 131072 /* mounted by automountd(8) */ } - int statfs(const char* file, statfs_t* buf); - int fstatfs(int fildes, statfs_t* buf) @trusted; + deprecated("Moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") + alias statfs = core.sys.freebsd.sys.mount.statfs; + + deprecated("Moved to core.sys.freebsd.sys.mount to correspond to C header file sys/mount.h") + alias fstatfs = core.sys.freebsd.sys.mount.fstatfs; + + struct statvfs_t + { + fsblkcnt_t f_bavail; + fsblkcnt_t f_bfree; + fsblkcnt_t f_blocks; + fsfilcnt_t f_favail; + fsfilcnt_t f_ffree; + fsfilcnt_t f_files; + ulong f_bsize; + ulong f_flag; + ulong f_frsize; + ulong f_fsid; + ulong f_namemax; + } + + enum uint ST_RDONLY = 0x1; + enum uint ST_NOSUID = 0x2; + + int fstatvfs(int, statvfs_t*); + int statvfs(const char*, statvfs_t*); } else { diff --git a/libphobos/libdruntime/core/sys/posix/sys/types.d b/libphobos/libdruntime/core/sys/posix/sys/types.d index 416f4064c..6d21d83ea 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/types.d +++ b/libphobos/libdruntime/core/sys/posix/sys/types.d @@ -1056,12 +1056,15 @@ else version( CRuntime_Bionic ) size_t guard_size; int sched_policy; int sched_priority; - version(AArch64) char[16] __reserved; + version(D_LP64) char[16] __reserved; } struct pthread_cond_t { - int value; //volatile + version(D_LP64) + int[12] __private; + else + int[1] __private; } alias c_long pthread_condattr_t; @@ -1069,24 +1072,24 @@ else version( CRuntime_Bionic ) struct pthread_mutex_t { - int value; //volatile + version(D_LP64) + int[10] __private; + else + int[1] __private; } alias c_long pthread_mutexattr_t; - alias int pthread_once_t; //volatile + alias int pthread_once_t; struct pthread_rwlock_t { - pthread_mutex_t lock; - pthread_cond_t cond; - int numLocks; - int writerThreadId; - int pendingReaders; - int pendingWriters; - void*[4] reserved; + version(D_LP64) + int[14] __private; + else + int[10] __private; } - alias int pthread_rwlockattr_t; + alias c_long pthread_rwlockattr_t; alias c_long pthread_t; } else version ( CRuntime_UClibc ) diff --git a/libphobos/libdruntime/core/sys/posix/sys/utsname.d b/libphobos/libdruntime/core/sys/posix/sys/utsname.d index 30d51a09e..fa14328a6 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/utsname.d +++ b/libphobos/libdruntime/core/sys/posix/sys/utsname.d @@ -26,8 +26,9 @@ version(CRuntime_Glibc) char[utsNameLength] sysname; char[utsNameLength] nodename; char[utsNameLength] release; - // The field name is version but version is a keyword in D. - char[utsNameLength] update; + char[utsNameLength] version_; + // TODO Deprecate after version_ has been in an official release. + alias update = version_; char[utsNameLength] machine; char[utsNameLength] __domainname; @@ -44,8 +45,9 @@ else version(Darwin) char[utsNameLength] sysname; char[utsNameLength] nodename; char[utsNameLength] release; - // The field name is version but version is a keyword in D. - char[utsNameLength] update; + char[utsNameLength] version_; + // TODO Deprecate after version_ has been in an official release. + alias update = version_; char[utsNameLength] machine; } @@ -61,8 +63,9 @@ else version(FreeBSD) char[SYS_NMLN] sysname; char[SYS_NMLN] nodename; char[SYS_NMLN] release; - // The field name is version but version is a keyword in D. - char[SYS_NMLN] update; + char[SYS_NMLN] version_; + // TODO Deprecate after version_ has been in an official release. + alias update = version_; char[SYS_NMLN] machine; } @@ -78,8 +81,9 @@ else version(NetBSD) char[utsNameLength] sysname; char[utsNameLength] nodename; char[utsNameLength] release; - // The field name is version but version is a keyword in D. - char[utsNameLength] update; + char[utsNameLength] version_; + // TODO Deprecate after version_ has been in an official release. + alias update = version_; char[utsNameLength] machine; } @@ -94,8 +98,9 @@ else version(DragonFlyBSD) char[utsNameLength] sysname; char[utsNameLength] nodename; char[utsNameLength] release; - // The field name is version but version is a keyword in D. - char[utsNameLength] update; + char[utsNameLength] version_; + // TODO Deprecate after version_ has been in an official release. + alias update = version_; char[utsNameLength] machine; } diff --git a/libphobos/libdruntime/core/sys/posix/ucontext.d b/libphobos/libdruntime/core/sys/posix/ucontext.d index 235239c0f..3e0e92b62 100644 --- a/libphobos/libdruntime/core/sys/posix/ucontext.d +++ b/libphobos/libdruntime/core/sys/posix/ucontext.d @@ -765,6 +765,38 @@ else version( FreeBSD ) int[6] mc_spare2; } } + else version( AArch64 ) + { + alias __register_t = long; + + struct gpregs + { + __register_t[30] gp_x; + __register_t gp_lr; + __register_t gp_sp; + __register_t gp_elr; + uint gp_spsr; + int gp_pad; + } + + struct fpregs + { + ulong[2][32] fp_q; // __uint128_t + uint fp_sr; + uint fp_cr; + int fp_flags; + int fp_pad; + } + + struct mcontext_t + { + gpregs mc_gpregs; + fpregs mc_fpregs; + int mc_flags; + int mc_pad; + ulong[8] mc_spare; + } + } // enum UCF_SWAPPED = 0x00000001; diff --git a/libphobos/libdruntime/object.d b/libphobos/libdruntime/object.d index 47f19835f..809d1ba3f 100644 --- a/libphobos/libdruntime/object.d +++ b/libphobos/libdruntime/object.d @@ -871,7 +871,15 @@ class Object size_t toHash() @trusted nothrow { // BUG: this prevents a compacting GC from working, needs to be fixed - return cast(size_t)cast(void*)this; + size_t addr = cast(size_t) cast(void*) this; + // The bottom log2((void*).alignof) bits of the address will always + // be 0. Moreover it is likely that each Object is allocated with a + // separate call to malloc. The alignment of malloc differs from + // platform to platform, but rather than having special cases for + // each platform it is safe to use a shift of 4. To minimize + // collisions in the low bits it is more important for the shift to + // not be too small than for the shift to not be too big. + return addr ^ (addr >>> 4); } /** @@ -1017,10 +1025,7 @@ class TypeInfo override size_t toHash() @trusted const nothrow { - import core.internal.traits : externDFunc; - alias hashOf = externDFunc!("rt.util.hash.hashOf", - size_t function(const(void)[], size_t) @trusted pure nothrow @nogc); - return hashOf(this.toString(), 0); + return hashOf(this.toString()); } override int opCmp(Object o) @@ -1056,7 +1061,10 @@ class TypeInfo * Bugs: * fix https://issues.dlang.org/show_bug.cgi?id=12516 e.g. by changing this to a truly safe interface. */ - size_t getHash(scope const void* p) @trusted nothrow const { return cast(size_t)p; } + size_t getHash(scope const void* p) @trusted nothrow const + { + return hashOf(p); + } /// Compares two instances for equality. bool equals(in void* p1, in void* p2) const { return p1 == p2; } @@ -1183,7 +1191,8 @@ class TypeInfo_Pointer : TypeInfo override size_t getHash(scope const void* p) @trusted const { - return cast(size_t)*cast(void**)p; + size_t addr = cast(size_t) *cast(const void**)p; + return addr ^ (addr >> 4); } override bool equals(in void* p1, in void* p2) const @@ -1857,6 +1866,10 @@ class TypeInfo_Interface : TypeInfo override size_t getHash(scope const void* p) @trusted const { + if (!*cast(void**)p) + { + return 0; + } Interface* pi = **cast(Interface ***)*cast(void**)p; Object o = cast(Object)(*cast(void**)p - pi.offset); assert(o); @@ -1934,10 +1947,7 @@ class TypeInfo_Struct : TypeInfo } else { - import core.internal.traits : externDFunc; - alias hashOf = externDFunc!("rt.util.hash.hashOf", - size_t function(const(void)[], size_t) @trusted pure nothrow @nogc); - return hashOf(p[0 .. initializer().length], 0); + return hashOf(p[0 .. initializer().length]); } } @@ -2828,6 +2838,7 @@ extern (C) // size_t _aaLen(in void* p) pure nothrow @nogc; private void* _aaGetY(void** paa, const TypeInfo_AssociativeArray ti, in size_t valuesize, in void* pkey) pure nothrow; + private void* _aaGetX(void** paa, const TypeInfo_AssociativeArray ti, in size_t valuesize, in void* pkey, out bool found) pure nothrow; // inout(void)* _aaGetRvalueX(inout void* p, in TypeInfo keyti, in size_t valuesize, in void* pkey); inout(void)[] _aaValues(inout void* p, in size_t keysize, in size_t valuesize, const TypeInfo tiValArray) pure nothrow; inout(void)[] _aaKeys(inout void* p, in size_t keysize, const TypeInfo tiKeyArray) pure nothrow; @@ -2866,40 +2877,63 @@ void* aaLiteral(Key, Value)(Key[] keys, Value[] values) @trusted pure alias AssociativeArray(Key, Value) = Value[Key]; +/*********************************** + * Removes all remaining keys and values from an associative array. + * Params: + * aa = The associative array. + */ void clear(T : Value[Key], Value, Key)(T aa) { _aaClear(*cast(void **) &aa); } +/* ditto */ void clear(T : Value[Key], Value, Key)(T* aa) { _aaClear(*cast(void **) aa); } +/*********************************** + * Reorganizes the associative array in place so that lookups are more + * efficient. + * Params: + * aa = The associative array. + * Returns: + * The rehashed associative array. + */ T rehash(T : Value[Key], Value, Key)(T aa) { _aaRehash(cast(void**)&aa, typeid(Value[Key])); return aa; } +/* ditto */ T rehash(T : Value[Key], Value, Key)(T* aa) { _aaRehash(cast(void**)aa, typeid(Value[Key])); return *aa; } +/* ditto */ T rehash(T : shared Value[Key], Value, Key)(T aa) { _aaRehash(cast(void**)&aa, typeid(Value[Key])); return aa; } +/* ditto */ T rehash(T : shared Value[Key], Value, Key)(T* aa) { _aaRehash(cast(void**)aa, typeid(Value[Key])); return *aa; } +/*********************************** + * Create a new associative array of the same size and copy the contents of the + * associative array into it. + * Params: + * aa = The associative array. + */ V[K] dup(T : V[K], K, V)(T aa) { //pragma(msg, "K = ", K, ", V = ", V); @@ -2936,6 +2970,7 @@ V[K] dup(T : V[K], K, V)(T aa) return result; } +/* ditto */ V[K] dup(T : V[K], K, V)(T* aa) { return (*aa).dup; @@ -2952,6 +2987,13 @@ private AARange _aaToRange(T: V[K], K, V)(ref T aa) pure nothrow @nogc @safe return _aaRange(() @trusted { return cast(void*)realAA; } ()); } +/*********************************** + * Returns a forward range over the keys of the associative array. + * Params: + * aa = The associative array. + * Returns: + * A forward range. + */ auto byKey(T : V[K], K, V)(T aa) pure nothrow @nogc @safe { import core.internal.traits : substInout; @@ -2974,11 +3016,19 @@ auto byKey(T : V[K], K, V)(T aa) pure nothrow @nogc @safe return Result(_aaToRange(aa)); } +/* ditto */ auto byKey(T : V[K], K, V)(T* aa) pure nothrow @nogc { return (*aa).byKey(); } +/*********************************** + * Returns a forward range over the values of the associative array. + * Params: + * aa = The associative array. + * Returns: + * A forward range. + */ auto byValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe { import core.internal.traits : substInout; @@ -3001,11 +3051,19 @@ auto byValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe return Result(_aaToRange(aa)); } +/* ditto */ auto byValue(T : V[K], K, V)(T* aa) pure nothrow @nogc { return (*aa).byValue(); } +/*********************************** + * Returns a forward range over the key value pairs of the associative array. + * Params: + * aa = The associative array. + * Returns: + * A forward range. + */ auto byKeyValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe { import core.internal.traits : substInout; @@ -3046,11 +3104,20 @@ auto byKeyValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe return Result(_aaToRange(aa)); } +/* ditto */ auto byKeyValue(T : V[K], K, V)(T* aa) pure nothrow @nogc { return (*aa).byKeyValue(); } +/*********************************** + * Returns a dynamic array, the elements of which are the keys in the + * associative array. + * Params: + * aa = The associative array. + * Returns: + * A dynamic array. + */ Key[] keys(T : Value[Key], Value, Key)(T aa) @property { auto a = cast(void[])_aaKeys(cast(inout(void)*)aa, Key.sizeof, typeid(Key[])); @@ -3059,11 +3126,20 @@ Key[] keys(T : Value[Key], Value, Key)(T aa) @property return res; } +/* ditto */ Key[] keys(T : Value[Key], Value, Key)(T *aa) @property { return (*aa).keys; } +/*********************************** + * Returns a dynamic array, the elements of which are the values in the + * associative array. + * Params: + * aa = The associative array. + * Returns: + * A dynamic array. + */ Value[] values(T : Value[Key], Value, Key)(T aa) @property { auto a = cast(void[])_aaValues(cast(inout(void)*)aa, Key.sizeof, Value.sizeof, typeid(Value[])); @@ -3072,6 +3148,7 @@ Value[] values(T : Value[Key], Value, Key)(T aa) @property return res; } +/* ditto */ Value[] values(T : Value[Key], Value, Key)(T *aa) @property { return (*aa).values; @@ -3105,12 +3182,23 @@ unittest assert(T.count == 2); } +/*********************************** + * Looks up key; if it exists returns corresponding value else evaluates and + * returns defaultValue. + * Params: + * aa = The associative array. + * key = The key. + * defaultValue = The default value. + * Returns: + * The value. + */ inout(V) get(K, V)(inout(V[K]) aa, K key, lazy inout(V) defaultValue) { auto p = key in aa; return p ? *p : defaultValue; } +/* ditto */ inout(V) get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue) { return (*aa).get(key, defaultValue); @@ -3127,6 +3215,229 @@ inout(V) get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue) } } +unittest +{ + static class T + { + static size_t count; + this() { ++count; } + } + + T[string] aa; + + auto a = new T; + aa["foo"] = a; + assert(T.count == 1); + auto b = aa.get("foo", new T); + assert(T.count == 1); + assert(b is a); + auto c = aa.get("bar", new T); + assert(T.count == 2); + assert(c !is a); + + //Obviously get doesn't add. + assert("bar" !in aa); +} + +/*********************************** + * Looks up key; if it exists returns corresponding value else evaluates + * value, adds it to the associative array and returns it. + * Params: + * aa = The associative array. + * key = The key. + * value = The required value. + * Returns: + * The value. + */ +ref V require(K, V)(ref V[K] aa, K key, lazy V value = V.init) +{ + bool found; + auto p = cast(V*) _aaGetX(cast(void**)&aa, typeid(V[K]), V.sizeof, &key, found); + return found ? *p : (*p = value); +} + +unittest +{ + static class T + { + static size_t count; + this() { ++count; } + } + + T[string] aa; + + auto a = new T; + aa["foo"] = a; + assert(T.count == 1); + auto b = aa.require("foo", new T); + assert(T.count == 1); + assert(b is a); + auto c = aa.require("bar", null); + assert(T.count == 1); + assert(c is null); + assert("bar" in aa); + auto d = aa.require("bar", new T); + assert(d is null); + auto e = aa.require("baz", new T); + assert(T.count == 2); + assert(e !is a); + + assert("baz" in aa); + + bool created = false; + auto f = aa.require("qux", { created = true; return new T; }()); + assert(created == true); + + T g; + auto h = aa.require("qux", { g = new T; return g; }()); + assert(g !is h); +} + +unittest +{ + static struct S + { + int value; + } + + S[string] aa; + + aa.require("foo").value = 1; + assert(aa == ["foo" : S(1)]); + + aa["bar"] = S(2); + auto a = aa.require("bar", S(3)); + assert(a == S(2)); + + auto b = aa["bar"]; + assert(b == S(2)); + + S* c = &aa.require("baz", S(4)); + assert(c is &aa["baz"]); + assert(*c == S(4)); + + assert("baz" in aa); + + auto d = aa["baz"]; + assert(d == S(4)); +} + +pure unittest +{ + string[string] aa; + + auto a = aa.require("foo", "bar"); + assert("foo" in aa); +} + +// Constraints for aa update. Delegates, Functions or Functors (classes that +// provide opCall) are allowed. See unittest for an example. +private +{ + template isCreateOperation(C, V) + { + static if (is(C : V delegate()) || is(C : V function())) + enum bool isCreateOperation = true; + else static if (isCreateOperation!(typeof(&C.opCall), V)) + enum bool isCreateOperation = true; + else + enum bool isCreateOperation = false; + } + + template isUpdateOperation(U, V) + { + static if (is(U : V delegate(ref V)) || is(U : V function(ref V))) + enum bool isUpdateOperation = true; + else static if (isUpdateOperation!(typeof(&U.opCall), V)) + enum bool isUpdateOperation = true; + else + enum bool isUpdateOperation = false; + } +} + +/*********************************** + * Looks up key; if it exists applies the update delegate else evaluates the + * create delegate and adds it to the associative array + * Params: + * aa = The associative array. + * key = The key. + * create = The delegate to apply on create. + * update = The delegate to apply on update. + */ +void update(K, V, C, U)(ref V[K] aa, K key, scope C create, scope U update) +if (isCreateOperation!(C, V) && isUpdateOperation!(U, V)) +{ + bool found; + auto p = cast(V*) _aaGetX(cast(void**)&aa, typeid(V[K]), V.sizeof, &key, found); + if (!found) + *p = create(); + else + *p = update(*p); +} + +unittest +{ + static class C {} + C[string] aa; + + C orig = new C; + aa["foo"] = orig; + + C newer; + C older; + + void test(string key) + { + aa.update(key, { + newer = new C; + return newer; + }, (ref C c) { + older = c; + newer = new C; + return newer; + }); + } + + test("foo"); + assert(older is orig); + assert(newer is aa["foo"]); + + test("bar"); + assert(newer is aa["bar"]); +} + +unittest +{ + static class C {} + C[string] aa; + + auto created = false; + auto updated = false; + + class Creator + { + C opCall() + { + created = true; + return new C(); + } + } + + class Updater + { + C opCall(ref C) + { + updated = true; + return new C(); + } + } + + aa.update("foo", new Creator, new Updater); + assert(created); + aa.update("foo", new Creator, new Updater); + assert(updated); +} + unittest { static assert(!__traits(compiles, @@ -3939,20 +4250,35 @@ version (none) } } -/** -Calculates the hash value of $(D arg) with $(D seed) initial value. -The result may not be equal to `typeid(T).getHash(&arg)`. +version (D_Ddoc) +{ + // This lets DDoc produce better documentation. -Params: - arg = argument to calculate the hash value of - seed = the $(D seed) value (may be used for hash chaining) + /** + Calculates the hash value of `arg` with an optional `seed` initial value. + The result might not be equal to `typeid(T).getHash(&arg)`. -Return: calculated hash value of $(D arg) -*/ -size_t hashOf(T)(auto ref T arg, size_t seed = 0) + Params: + arg = argument to calculate the hash value of + seed = optional `seed` value (may be used for hash chaining) + + Return: calculated hash value of `arg` + */ + size_t hashOf(T)(auto ref T arg, size_t seed) + { + static import core.internal.hash; + return core.internal.hash.hashOf(arg, seed); + } + /// ditto + size_t hashOf(T)(auto ref T arg) + { + static import core.internal.hash; + return core.internal.hash.hashOf(arg); + } +} +else { - import core.internal.hash; - return core.internal.hash.hashOf(arg, seed); + public import core.internal.hash : hashOf; } /// @@ -4238,14 +4564,12 @@ private size_t getArrayHash(in TypeInfo element, in void* ptr, in size_t count) } import core.internal.traits : externDFunc; - alias hashOf = externDFunc!("rt.util.hash.hashOf", - size_t function(const(void)[], size_t) @trusted pure nothrow @nogc); if(!hasCustomToHash(element)) - return hashOf(ptr[0 .. elementSize * count], 0); + return hashOf(ptr[0 .. elementSize * count]); size_t hash = 0; foreach(size_t i; 0 .. count) - hash = hash * 33 + element.getHash(ptr + i * elementSize); + hash = hashOf(element.getHash(ptr + i * elementSize), hash); return hash; } @@ -4579,3 +4903,101 @@ void __ArrayDtor(T)(T[] a) foreach_reverse (ref T e; a) e.__xdtor(); } + +/** +Used by `__ArrayCast` to emit a descriptive error message. + +It is a template so it can be used by `__ArrayCast` in -betterC +builds. It is separate from `__ArrayCast` to minimize code +bloat. + +Params: + fromType = name of the type being cast from + fromSize = total size in bytes of the array being cast from + toType = name of the type being cast o + toSize = total size in bytes of the array being cast to + */ +private void onArrayCastError()(string fromType, size_t fromSize, string toType, size_t toSize) @trusted +{ + import core.internal.string : unsignedToTempString; + import core.stdc.stdlib : alloca; + + const(char)[][8] msgComponents = + [ + "Cannot cast `" + , fromType + , "` to `" + , toType + , "`; an array of size " + , unsignedToTempString(fromSize) + , " does not align on an array of size " + , unsignedToTempString(toSize) + ]; + + // convert discontiguous `msgComponents` to contiguous string on the stack + size_t length = 0; + foreach (m ; msgComponents) + length += m.length; + + auto msg = (cast(char*)alloca(length))[0 .. length]; + + size_t index = 0; + foreach (m ; msgComponents) + foreach (c; m) + msg[index++] = c; + + // first argument must evaluate to `false` at compile-time to maintain memory safety in release builds + assert(false, msg); +} + +/** +The compiler lowers expressions of `cast(TTo[])TFrom[]` to +this implementation. + +Params: + from = the array to reinterpret-cast + +Returns: + `from` reinterpreted as `TTo[]` + */ +TTo[] __ArrayCast(TFrom, TTo)(TFrom[] from) @nogc pure @trusted +{ + const fromSize = from.length * TFrom.sizeof; + const toLength = fromSize / TTo.sizeof; + + if ((fromSize % TTo.sizeof) != 0) + { + onArrayCastError(TFrom.stringof, fromSize, TTo.stringof, toLength * TTo.sizeof); + } + + struct Array + { + size_t length; + void* ptr; + } + auto a = cast(Array*)&from; + a.length = toLength; // jam new length + return *cast(TTo[]*)a; +} + +@safe @nogc pure nothrow unittest +{ + byte[int.sizeof * 3] b = cast(byte) 0xab; + int[] i; + short[] s; + + i = __ArrayCast!(byte, int)(b); + assert(i.length == 3); + foreach (v; i) + assert(v == cast(int) 0xabab_abab); + + s = __ArrayCast!(byte, short)(b); + assert(s.length == 6); + foreach (v; s) + assert(v == cast(short) 0xabab); + + s = __ArrayCast!(int, short)(i); + assert(s.length == 6); + foreach (v; s) + assert(v == cast(short) 0xabab); +} diff --git a/libphobos/libdruntime/rt/aaA.d b/libphobos/libdruntime/rt/aaA.d index cfe37de7d..7c085734f 100644 --- a/libphobos/libdruntime/rt/aaA.d +++ b/libphobos/libdruntime/rt/aaA.d @@ -365,8 +365,29 @@ extern (C) size_t _aaLen(in AA aa) pure nothrow @nogc * If key was not in the aa, a mutable pointer to newly inserted value which * is set to all zeros */ -extern (C) void* _aaGetY(AA* aa, const TypeInfo_AssociativeArray ti, in size_t valsz, - in void* pkey) +extern (C) void* _aaGetY(AA* aa, const TypeInfo_AssociativeArray ti, + in size_t valsz, in void* pkey) +{ + bool found; + return _aaGetX(aa, ti, valsz, pkey, found); +} + +/****************************** + * Lookup *pkey in aa. + * Called only from implementation of require + * Params: + * aa = associative array opaque pointer + * ti = TypeInfo for the associative array + * valsz = ignored + * pkey = pointer to the key value + * found = true if the value was found + * Returns: + * if key was in the aa, a mutable pointer to the existing value. + * If key was not in the aa, a mutable pointer to newly inserted value which + * is set to all zeros + */ +extern (C) void* _aaGetX(AA* aa, const TypeInfo_AssociativeArray ti, + in size_t valsz, in void* pkey, out bool found) { // lazily alloc implementation if (aa.impl is null) @@ -377,7 +398,10 @@ extern (C) void* _aaGetY(AA* aa, const TypeInfo_AssociativeArray ti, in size_t v // found a value => return it if (auto p = aa.findSlotLookup(hash, pkey, ti.key)) + { + found = true; return p.entry + aa.valoff; + } auto p = aa.findSlotInsert(hash); if (p.deleted) @@ -655,17 +679,20 @@ extern (C) hash_t _aaGetHash(in AA* aa, in TypeInfo tiRaw) nothrow auto uti = unqualify(tiRaw); auto ti = *cast(TypeInfo_AssociativeArray*)&uti; immutable off = aa.valoff; + auto keyHash = &ti.key.getHash; auto valHash = &ti.value.getHash; size_t h; + import core.stdc.stdio; foreach (b; aa.buckets) { if (!b.filled) continue; - size_t[2] h2 = [b.hash, valHash(b.entry + off)]; + size_t[2] h2 = [keyHash(b.entry), valHash(b.entry + off)]; // use addition here, so that hash is independent of element order h += hashOf(h2); } + return h; } diff --git a/libphobos/libdruntime/rt/bss_section.c b/libphobos/libdruntime/rt/bss_section.c deleted file mode 100644 index b00f40d51..000000000 --- a/libphobos/libdruntime/rt/bss_section.c +++ /dev/null @@ -1,21 +0,0 @@ -/** - * This module is used to detect copy relocated ModuleInfos (located in .bss section). - * - * Copyright: Copyright Martin Nowak 2014-. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Martin Nowak - * Source: $(DRUNTIMESRC src/rt/_bss_section.c) - */ - -/* These symbols are defined in the linker script and bracket the - * .bss, .lbss, .lrodata and .ldata sections. - */ -#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) -// Need to use weak linkage to workaround a bug in ld.bfd (Bugzilla 13025). -extern int __attribute__((weak)) __bss_start, _end; - -__attribute__ ((visibility ("hidden"))) void* rt_get_bss_start(); -__attribute__ ((visibility ("hidden"))) void* rt_get_end(); -void* rt_get_bss_start() { return (void*)&__bss_start; } -void* rt_get_end() { return (void*)&_end; } -#endif diff --git a/libphobos/libdruntime/rt/config.d b/libphobos/libdruntime/rt/config.d index 2e42b2fca..f7344e50e 100644 --- a/libphobos/libdruntime/rt/config.d +++ b/libphobos/libdruntime/rt/config.d @@ -1,41 +1,47 @@ /** -* Configuration options for druntime -* -* Copyright: Copyright Digital Mars 2014. -* License: Distributed under the -* $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). -* (See accompanying file LICENSE) -* Authors: Rainer Schuetze -* Source: $(DRUNTIMESRC rt/_config.d) +Configuration options for druntime. + +The default way to configure the runtime is by passing command line arguments +starting with `--DRT-` and followed by the option name, e.g. `--DRT-gcopt` to +configure the GC. +Command line options starting with `--DRT-` are filtered out before calling main, +so the program will not see them. They are still available via `rt_args()`. + +Configuration via the command line can be disabled by declaring a variable for the +linker to pick up before using it's default from the runtime: + +--- +extern(C) __gshared bool rt_cmdline_enabled = false; +--- + +Likewise, declare a boolean rt_envvars_enabled to enable configuration via the +environment variable `DRT_` followed by the option name, e.g. `DRT_GCOPT`: + +--- +extern(C) __gshared bool rt_envvars_enabled = true; +--- + +Setting default configuration properties in the executable can be done by specifying an +array of options named `rt_options`: + +--- +extern(C) __gshared string[] rt_options = [ "gcopt=precise:1 profile:1"]; +--- + +Evaluation order of options is `rt_options`, then environment variables, then command +line arguments, i.e. if command line arguments are not disabled, they can override +options specified through the environment or embedded in the executable. + +Copyright: Copyright Digital Mars 2014. +License: Distributed under the + $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + (See accompanying file LICENSE) +Authors: Rainer Schuetze +Source: $(DRUNTIMESRC rt/_config.d) */ module rt.config; -// The default way to configure the runtime is by passing command line arguments -// starting with "--DRT-" and followed by the option name, e.g. "--DRT-gcopt" to -// configure the GC. -// Command line options starting with "--DRT-" are filtered out before calling main, -// so the program will not see them. They are still available via rt_args(). -// -// Configuration via the command line can be disabled by declaring a variable for the -// linker to pick up before using it's default from the runtime: -// -// extern(C) __gshared bool rt_cmdline_enabled = false; -// -// Likewise, declare a boolean rt_envvars_enabled to enable configuration via the -// environment variable "DRT_" followed by the option name, e.g. "DRT_GCOPT": -// -// extern(C) __gshared bool rt_envvars_enabled = true; -// -// Setting default configuration properties in the executable can be done by specifying an -// array of options named rt_options: -// -// extern(C) __gshared string[] rt_options = [ "gcopt=precise:1 profile:1"]; -// -// Evaluation order of options is rt_options, then environment variables, then command -// line arguments, i.e. if command line arguments are not disabled, they can override -// options specified through the environment or embedded in the executable. - // put each variable in its own COMDAT by making them template instances template rt_envvars_enabled() { @@ -60,7 +66,7 @@ alias rt_configCallBack = string delegate(string) @nogc nothrow; /** * get a druntime config option using standard configuration options -* opt name of the option to retreive +* opt name of the option to retrieve * dg if non-null, passes the option through this * delegate and only returns its return value if non-null * reverse reverse the default processing order cmdline/envvar/rt_options diff --git a/libphobos/libdruntime/rt/dmain2.d b/libphobos/libdruntime/rt/dmain2.d index b749da537..12e68d147 100644 --- a/libphobos/libdruntime/rt/dmain2.d +++ b/libphobos/libdruntime/rt/dmain2.d @@ -456,23 +456,17 @@ extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc) args = argsCopy[0..j]; } - bool trapExceptions = rt_trapExceptions; + auto useExceptionTrap = parseExceptionOptions(); version (Windows) { if (IsDebuggerPresent()) - trapExceptions = false; - version (GNU) - { - /* IsDebuggerPresent doesn't detect GDC. Would be nice to have - some way of detecting valid console output */ - trapExceptions = true; - } + useExceptionTrap = false; } void tryExec(scope void delegate() dg) { - if (trapExceptions) + if (useExceptionTrap) { try { @@ -568,6 +562,18 @@ private void formatThrowable(Throwable t, scope void delegate(in char[] s) nothr } } +private auto parseExceptionOptions() +{ + import rt.config : rt_configOption; + import core.internal.parseoptions : rt_parseOption; + const optName = "trapExceptions"; + auto option = rt_configOption(optName); + auto trap = rt_trapExceptions; + if (option.length) + rt_parseOption(optName, option, trap, ""); + return trap; +} + extern (C) void _d_print_throwable(Throwable t) { // On Windows, a console may not be present to print the output to. diff --git a/libphobos/libdruntime/rt/lifetime.d b/libphobos/libdruntime/rt/lifetime.d index 6fc18da90..63766c458 100644 --- a/libphobos/libdruntime/rt/lifetime.d +++ b/libphobos/libdruntime/rt/lifetime.d @@ -1649,195 +1649,207 @@ in do { import core.stdc.string; - - void* newdata; - auto tinext = unqualify(ti.next); - auto sizeelem = tinext.tsize; - auto initializer = tinext.initializer(); - auto initsize = initializer.length; - - assert(sizeelem); - assert(initsize); - assert(initsize <= sizeelem); - assert((sizeelem / initsize) * initsize == sizeelem); + import core.exception : onOutOfMemoryError; debug(PRINTF) { - printf("_d_arraysetlengthiT(p = %p, sizeelem = %d, newlength = %d, initsize = %d)\n", p, sizeelem, newlength, initsize); + //printf("_d_arraysetlengthiT(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength); if (p) - printf("\tp.data = %p, p.length = %d\n", (*p).ptr, (*p).length); + printf("\tp.ptr = %p, p.length = %d\n", (*p).ptr, (*p).length); } - if (newlength) + if (newlength <= (*p).length) { - version (D_InlineAsm_X86) - { - size_t newsize = void; + *p = (*p)[0 .. newlength]; + void* newdata = (*p).ptr; + return newdata[0 .. newlength]; + } + auto tinext = unqualify(ti.next); + size_t sizeelem = tinext.tsize; - asm - { - mov EAX,newlength ; - mul EAX,sizeelem ; - mov newsize,EAX ; - jc Loverflow ; - } + /* Calculate: newsize = newlength * sizeelem + */ + bool overflow = false; + version (D_InlineAsm_X86) + { + size_t newsize = void; + + asm pure nothrow @nogc + { + mov EAX, newlength; + mul EAX, sizeelem; + mov newsize, EAX; + setc overflow; } - else version (D_InlineAsm_X86_64) + } + else version (D_InlineAsm_X86_64) + { + size_t newsize = void; + + asm pure nothrow @nogc { - size_t newsize = void; + mov RAX, newlength; + mul RAX, sizeelem; + mov newsize, RAX; + setc overflow; + } + } + else + { + import core.checkedint : mulu; + const size_t newsize = mulu(sizeelem, newlength, overflow); + } + if (overflow) + { + onOutOfMemoryError(); + assert(0); + } - asm - { - mov RAX,newlength ; - mul RAX,sizeelem ; - mov newsize,RAX ; - jc Loverflow ; - } + debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength); + + const isshared = typeid(ti) is typeid(TypeInfo_Shared); + + static void doInitialize(void *start, void *end, const void[] initializer) + { + if (initializer.length == 1) + { + memset(start, *(cast(ubyte*)initializer.ptr), end - start); } else { - import core.checkedint : mulu; + auto q = initializer.ptr; + immutable initsize = initializer.length; + for (; start < end; start += initsize) + { + memcpy(start, q, initsize); + } + } + } - bool overflow = false; - size_t newsize = mulu(sizeelem, newlength, overflow); - if (overflow) - goto Loverflow; + if (!(*p).ptr) + { + // pointer was null, need to allocate + auto info = __arrayAlloc(newsize, ti, tinext); + if (info.base is null) + { + onOutOfMemoryError(); + assert(0); } - debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength); + __setArrayAllocLength(info, newsize, isshared, tinext); + if(!isshared) + __insertBlkInfoCache(info, null); + void* newdata = cast(byte *)__arrayStart(info); + doInitialize(newdata, newdata + newsize, tinext.initializer); + *p = newdata[0 .. newlength]; + return *p; + } + const size_t size = (*p).length * sizeelem; + auto bic = isshared ? null : __getBlkInfo((*p).ptr); + auto info = bic ? *bic : GC.query((*p).ptr); - size_t size = (*p).length * sizeelem; - auto isshared = typeid(ti) is typeid(TypeInfo_Shared); - if ((*p).ptr) + /* Attempt to extend past the end of the existing array. + * If not possible, allocate new space for entire array and copy. + */ + bool allocateAndCopy = false; + void* newdata = (*p).ptr; + + if(info.base && (info.attr & BlkAttr.APPENDABLE)) + { + // calculate the extent of the array given the base. + const size_t offset = (*p).ptr - __arrayStart(info); + if(info.size >= PAGESIZE) { - newdata = (*p).ptr; - if (newlength > (*p).length) + // size of array is at the front of the block + if(!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) { - auto bic = isshared ? null : __getBlkInfo((*p).ptr); - auto info = bic ? *bic : GC.query((*p).ptr); - - // calculate the extent of the array given the base. - size_t offset = (*p).ptr - __arrayStart(info); - if(info.base && (info.attr & BlkAttr.APPENDABLE)) - { - if(info.size >= PAGESIZE) - { - // size of array is at the front of the block - if(!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - // check to see if it failed because there is not - // enough space - if(*(cast(size_t*)info.base) == size + offset) - { - // not enough space, try extending - auto extendsize = newsize + offset + LARGEPAD - info.size; - auto u = GC.extend(info.base, extendsize, extendsize); - if(u) - { - // extend worked, now try setting the length - // again. - info.size = u; - if(__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - if(!isshared) - __insertBlkInfoCache(info, bic); - goto L1; - } - } - } - - // couldn't do it, reallocate - goto L2; - } - else if(!isshared && !bic) - { - // add this to the cache, it wasn't present previously. - __insertBlkInfoCache(info, null); - } - } - else if(!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - // could not resize in place - goto L2; - } - else if(!isshared && !bic) - { - // add this to the cache, it wasn't present previously. - __insertBlkInfoCache(info, null); - } - } - else + // check to see if it failed because there is not + // enough space + if(*(cast(size_t*)info.base) == size + offset) { - // not appendable or not part of the heap yet. - if(info.base) + // not enough space, try extending + auto extendsize = newsize + offset + LARGEPAD - info.size; + auto u = GC.extend(info.base, extendsize, extendsize); + if(u) { - L2: - if(bic) + // extend worked, now try setting the length + // again. + info.size = u; + if(__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) { - // a chance that flags have changed since this was cached, we should fetch the most recent flags - info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE; + if(!isshared) + __insertBlkInfoCache(info, bic); + doInitialize(newdata + size, newdata + newsize, tinext.initializer); + *p = newdata[0 .. newlength]; + return *p; } - info = __arrayAlloc(newsize, info, ti, tinext); - } - else - { - info = __arrayAlloc(newsize, ti, tinext); } - __setArrayAllocLength(info, newsize, isshared, tinext); - if(!isshared) - __insertBlkInfoCache(info, bic); - newdata = cast(byte *)__arrayStart(info); - newdata[0 .. size] = (*p).ptr[0 .. size]; - - // do postblit processing - __doPostblit(newdata, size, tinext); } - L1: ; + + // couldn't do it, reallocate + allocateAndCopy = true; + } + else if(!isshared && !bic) + { + // add this to the cache, it wasn't present previously. + __insertBlkInfoCache(info, null); } } - else + else if(!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) { - // length was zero, need to allocate - auto info = __arrayAlloc(newsize, ti, tinext); - __setArrayAllocLength(info, newsize, isshared, tinext); - if(!isshared) - __insertBlkInfoCache(info, null); - newdata = cast(byte *)__arrayStart(info); + // could not resize in place + allocateAndCopy = true; } - - auto q = initializer.ptr; // pointer to initializer - - if (newsize > size) + else if(!isshared && !bic) { - if (initsize == 1) - { - debug(PRINTF) printf("newdata = %p, size = %d, newsize = %d, *q = %d\n", newdata, size, newsize, *cast(byte*)q); - memset(newdata + size, *cast(byte*)q, newsize - size); - } - else - { - for (size_t u = size; u < newsize; u += initsize) - { - memcpy(newdata + u, q, initsize); - } - } + // add this to the cache, it wasn't present previously. + __insertBlkInfoCache(info, null); } } else + allocateAndCopy = true; + + if (allocateAndCopy) { - newdata = (*p).ptr; + if(info.base) + { + if(bic) + { + // a chance that flags have changed since this was cached, we should fetch the most recent flags + info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE; + } + info = __arrayAlloc(newsize, info, ti, tinext); + } + else + { + info = __arrayAlloc(newsize, ti, tinext); + } + + if (info.base is null) + { + onOutOfMemoryError(); + assert(0); + } + + __setArrayAllocLength(info, newsize, isshared, tinext); + if(!isshared) + __insertBlkInfoCache(info, bic); + newdata = cast(byte *)__arrayStart(info); + newdata[0 .. size] = (*p).ptr[0 .. size]; + + /* Do postblit processing, as we are making a copy and the + * original array may have references. + * Note that this may throw. + */ + __doPostblit(newdata, size, tinext); } + // Initialize the unused portion of the newly allocated space + doInitialize(newdata + size, newdata + newsize, tinext.initializer); *p = newdata[0 .. newlength]; return *p; - -Loverflow: - import core.exception : onOutOfMemoryError; - onOutOfMemoryError(); - assert(0); } - /** * Append y[] to array x[] */ diff --git a/libphobos/libdruntime/rt/sections_android.d b/libphobos/libdruntime/rt/sections_android.d index 83a91f033..c675ffc2c 100644 --- a/libphobos/libdruntime/rt/sections_android.d +++ b/libphobos/libdruntime/rt/sections_android.d @@ -69,6 +69,8 @@ void initSections() nothrow @nogc auto pbeg = cast(void*)&_tlsend; auto pend = cast(void*)&__bss_end__; + // _tlsend is a 32-bit int and may not be 64-bit void*-aligned, so align pbeg. + version(D_LP64) pbeg = cast(void*)(cast(size_t)(pbeg + 7) & ~cast(size_t)7); _sections._gcRanges[0] = pbeg[0 .. pend - pbeg]; } @@ -129,6 +131,17 @@ else version(ARM) return tls.ptr + offset; } } +else version(AArch64) +{ + extern(C) void* __tls_get_addr( void* p ) nothrow @nogc + { + debug(PRINTF) printf(" __tls_get_addr input - %p\n", p); + immutable offset = cast(size_t)(p - cast(void*)&_tlsstart); + auto tls = getTLSBlockAlloc(); + assert(offset < tls.length); + return tls.ptr + offset; + } +} else static assert( false, "Android architecture not supported." ); @@ -182,7 +195,7 @@ extern(C) size_t __bss_end__; - void* _tlsstart; - void* _tlsend; + int _tlsstart; + int _tlsend; } } diff --git a/libphobos/libdruntime/rt/sections_elf_shared.d b/libphobos/libdruntime/rt/sections_elf_shared.d index e75ededd2..46bd9de31 100644 --- a/libphobos/libdruntime/rt/sections_elf_shared.d +++ b/libphobos/libdruntime/rt/sections_elf_shared.d @@ -906,17 +906,17 @@ extern(C) void* __tls_get_addr(tls_index* ti) nothrow @nogc; * https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/mips/dl-tls.h;h=93a6dc050cb144b9f68b96fb3199c60f5b1fcd18;hb=HEAD#l32 */ version(X86) - enum TLS_DTV_OFFSET = 0x; + enum TLS_DTV_OFFSET = 0x0; else version(X86_64) - enum TLS_DTV_OFFSET = 0x; + enum TLS_DTV_OFFSET = 0x0; else version(ARM) - enum TLS_DTV_OFFSET = 0x; + enum TLS_DTV_OFFSET = 0x0; else version(AArch64) - enum TLS_DTV_OFFSET = 0x; + enum TLS_DTV_OFFSET = 0x0; else version(SPARC) - enum TLS_DTV_OFFSET = 0x; + enum TLS_DTV_OFFSET = 0x0; else version(SPARC64) - enum TLS_DTV_OFFSET = 0x; + enum TLS_DTV_OFFSET = 0x0; else version(PPC) enum TLS_DTV_OFFSET = 0x8000; else version(PPC64) diff --git a/libphobos/libdruntime/rt/sections_osx.d b/libphobos/libdruntime/rt/sections_osx_x86.d similarity index 86% rename from libphobos/libdruntime/rt/sections_osx.d rename to libphobos/libdruntime/rt/sections_osx_x86.d index b41f9982d..22be0b6ad 100644 --- a/libphobos/libdruntime/rt/sections_osx.d +++ b/libphobos/libdruntime/rt/sections_osx_x86.d @@ -1,25 +1,34 @@ /** * Written in the D programming language. - * This module provides OSX-specific support for sections. + * This module provides OS X x86 specific support for sections. * - * Copyright: Copyright Digital Mars 2008 - 2012. + * Copyright: Copyright Digital Mars 2008 - 2016. * License: Distributed under the * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * (See accompanying file LICENSE) - * Authors: Walter Bright, Sean Kelly, Martin Nowak - * Source: $(DRUNTIMESRC src/rt/_sections_osx.d) + * Authors: Walter Bright, Sean Kelly, Martin Nowak, Jacob Carlborg + * Source: $(DRUNTIMESRC rt/_sections_osx_x86.d) */ +module rt.sections_osx_x86; -module rt.sections_osx; +version (OSX) + version = Darwin; +else version (iOS) + version = Darwin; +else version (TVOS) + version = Darwin; +else version (WatchOS) + version = Darwin; -version(OSX): +version(Darwin): +version(X86): // debug = PRINTF; import core.stdc.stdio; import core.stdc.string, core.stdc.stdlib; import core.sys.posix.pthread; -import core.sys.osx.mach.dyld; -import core.sys.osx.mach.getsect; +import core.sys.darwin.mach.dyld; +import core.sys.darwin.mach.getsect; import rt.deh, rt.minfo; import rt.util.container.array; @@ -70,7 +79,7 @@ __gshared bool _isRuntimeInitialized; /**** * Gets called on program startup just before GC is initialized. */ -void initSections() +void initSections() nothrow @nogc { pthread_key_create(&_tlsKey, null); _dyld_register_func_for_add_image(§ions_osx_onAddImage); @@ -80,19 +89,19 @@ void initSections() /*** * Gets called on program shutdown just after GC is terminated. */ -void finiSections() +void finiSections() nothrow @nogc { _sections._gcRanges.reset(); pthread_key_delete(_tlsKey); _isRuntimeInitialized = false; } -void[]* initTLSRanges() +void[]* initTLSRanges() nothrow @nogc { return &getTLSBlock(); } -void finiTLSRanges(void[]* rng) +void finiTLSRanges(void[]* rng) nothrow @nogc { .free(rng.ptr); .free(rng); @@ -130,7 +139,7 @@ in assert(_sections._tlsImage[0].ptr !is null || _sections._tlsImage[1].ptr !is null); } -body +do { // NOTE: p is an address in the TLS static data emitted by the // compiler. If it isn't, something is disastrously wrong. @@ -148,7 +157,7 @@ body assert(0); } -ref void[] getTLSBlock() +ref void[] getTLSBlock() nothrow @nogc { auto pary = cast(void[]*)pthread_getspecific(_tlsKey); if (pary is null) @@ -180,7 +189,6 @@ ref void[] getTLSBlockAlloc() return *pary; } - __gshared SectionGroup _sections; extern (C) void sections_osx_onAddImage(in mach_header* h, intptr_t slide) @@ -199,12 +207,12 @@ extern (C) void sections_osx_onAddImage(in mach_header* h, intptr_t slide) // take the sections from the last static image which is the executable if (_isRuntimeInitialized) { - fprintf(stderr, "Loading shared libraries isn't yet supported on OSX.\n"); + fprintf(stderr, "Loading shared libraries isn't yet supported on Darwin.\n"); return; } else if (_sections.modules.ptr !is null) { - fprintf(stderr, "Shared libraries are not yet supported on OSX.\n"); + fprintf(stderr, "Shared libraries are not yet supported on Darwin.\n"); } debug(PRINTF) printf(" minfodata\n"); @@ -254,22 +262,8 @@ static immutable SegRef[] dataSegs = [{SEG_DATA, SECT_DATA}, ubyte[] getSection(in mach_header* header, intptr_t slide, in char* segmentName, in char* sectionName) { - version (X86) - { - assert(header.magic == MH_MAGIC); - auto sect = getsectbynamefromheader(header, - segmentName, - sectionName); - } - else version (X86_64) - { - assert(header.magic == MH_MAGIC_64); - auto sect = getsectbynamefromheader_64(cast(mach_header_64*)header, - segmentName, - sectionName); - } - else - static assert(0, "unimplemented"); + assert(header.magic == MH_MAGIC); + auto sect = getsectbynamefromheader(header, segmentName, sectionName); if (sect !is null && sect.size > 0) return (cast(ubyte*)sect.addr + slide)[0 .. cast(size_t)sect.size]; diff --git a/libphobos/libdruntime/rt/sections_osx_x86_64.d b/libphobos/libdruntime/rt/sections_osx_x86_64.d new file mode 100644 index 000000000..8cc77c92f --- /dev/null +++ b/libphobos/libdruntime/rt/sections_osx_x86_64.d @@ -0,0 +1,185 @@ +/** + * Written in the D programming language. + * This module provides OS X x86-64 specific support for sections. + * + * Copyright: Copyright Digital Mars 2016. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) + * Authors: Walter Bright, Sean Kelly, Martin Nowak, Jacob Carlborg + * Source: $(DRUNTIMESRC rt/_sections_osx_x86_64.d) + */ +module rt.sections_osx_x86_64; + +version (OSX) + version = Darwin; +else version (iOS) + version = Darwin; +else version (TVOS) + version = Darwin; +else version (WatchOS) + version = Darwin; + +version(Darwin): +version(X86_64): + +// debug = PRINTF; +import core.stdc.stdio; +import core.stdc.string, core.stdc.stdlib; +import core.sys.posix.pthread; +import core.sys.osx.mach.dyld; +import core.sys.osx.mach.getsect; +import rt.deh, rt.minfo; +import rt.util.container.array; + +struct SectionGroup +{ + static int opApply(scope int delegate(ref SectionGroup) dg) + { + return dg(_sections); + } + + static int opApplyReverse(scope int delegate(ref SectionGroup) dg) + { + return dg(_sections); + } + + @property immutable(ModuleInfo*)[] modules() const + { + return _moduleGroup.modules; + } + + @property ref inout(ModuleGroup) moduleGroup() inout + { + return _moduleGroup; + } + + @property inout(void[])[] gcRanges() inout + { + return _gcRanges[]; + } + + @property immutable(FuncTable)[] ehTables() const + { + return _ehTables[]; + } + +private: + immutable(FuncTable)[] _ehTables; + ModuleGroup _moduleGroup; + Array!(void[]) _gcRanges; +} + +/**** + * Boolean flag set to true while the runtime is initialized. + */ +__gshared bool _isRuntimeInitialized; + +/**** + * Gets called on program startup just before GC is initialized. + */ +void initSections() nothrow @nogc +{ + _dyld_register_func_for_add_image(§ions_osx_onAddImage); + _isRuntimeInitialized = true; +} + +/*** + * Gets called on program shutdown just after GC is terminated. + */ +void finiSections() nothrow @nogc +{ + _sections._gcRanges.reset(); + _isRuntimeInitialized = false; +} + +void[] initTLSRanges() nothrow @nogc +{ + void* start = null; + size_t size = 0; + _d_dyld_getTLSRange(&dummyTlsSymbol, &start, &size); + assert(start && size, "Could not determine TLS range."); + return start[0 .. size]; +} + +void finiTLSRanges(void[] rng) nothrow @nogc +{ + +} + +void scanTLSRanges(void[] rng, scope void delegate(void* pbeg, void* pend) nothrow dg) nothrow +{ + dg(rng.ptr, rng.ptr + rng.length); +} + +private: + +extern(C) void _d_dyld_getTLSRange(void*, void**, size_t*) nothrow @nogc; + +__gshared SectionGroup _sections; +ubyte dummyTlsSymbol; + +extern (C) void sections_osx_onAddImage(in mach_header* h, intptr_t slide) +{ + foreach (e; dataSegs) + { + auto sect = getSection(h, slide, e.seg.ptr, e.sect.ptr); + if (sect != null) + _sections._gcRanges.insertBack((cast(void*)sect.ptr)[0 .. sect.length]); + } + + auto minfosect = getSection(h, slide, "__DATA", "__minfodata"); + if (minfosect != null) + { + // no support for multiple images yet + // take the sections from the last static image which is the executable + if (_isRuntimeInitialized) + { + fprintf(stderr, "Loading shared libraries isn't yet supported on OSX.\n"); + return; + } + else if (_sections.modules.ptr !is null) + { + fprintf(stderr, "Shared libraries are not yet supported on OSX.\n"); + } + + debug(PRINTF) printf(" minfodata\n"); + auto p = cast(immutable(ModuleInfo*)*)minfosect.ptr; + immutable len = minfosect.length / (*p).sizeof; + + _sections._moduleGroup = ModuleGroup(p[0 .. len]); + } + + auto ehsect = getSection(h, slide, "__DATA", "__deh_eh"); + if (ehsect != null) + { + debug(PRINTF) printf(" deh_eh\n"); + auto p = cast(immutable(FuncTable)*)ehsect.ptr; + immutable len = ehsect.length / (*p).sizeof; + + _sections._ehTables = p[0 .. len]; + } +} + +struct SegRef +{ + string seg; + string sect; +} + +static immutable SegRef[] dataSegs = [{SEG_DATA, SECT_DATA}, + {SEG_DATA, SECT_BSS}, + {SEG_DATA, SECT_COMMON}]; + +ubyte[] getSection(in mach_header* header, intptr_t slide, + in char* segmentName, in char* sectionName) +{ + assert(header.magic == MH_MAGIC_64); + auto sect = getsectbynamefromheader_64(cast(mach_header_64*)header, + segmentName, + sectionName); + + if (sect !is null && sect.size > 0) + return (cast(ubyte*)sect.addr + slide)[0 .. cast(size_t)sect.size]; + return null; +} diff --git a/libphobos/libdruntime/rt/switch_.d b/libphobos/libdruntime/rt/switch_.d deleted file mode 100644 index ec124e3f7..000000000 --- a/libphobos/libdruntime/rt/switch_.d +++ /dev/null @@ -1,424 +0,0 @@ -/** - * Contains support code for switch blocks using string constants. - * - * Copyright: Copyright Digital Mars 2004 - 2010. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Walter Bright, Sean Kelly - */ - -/* Copyright Digital Mars 2004 - 2010. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ -module rt.switch_; - -private import core.stdc.string; - -/****************************************************** - * Support for switch statements switching on strings. - * Input: - * table[] sorted array of strings generated by compiler - * ca string to look up in table - * Output: - * result index of match in table[] - * -1 if not in table - */ - -extern (C): - -int _d_switch_string(char[][] table, char[] ca) -in -{ - //printf("in _d_switch_string()\n"); - assert(table.length >= 0); - assert(ca.length >= 0); - - // Make sure table[] is sorted correctly - for (size_t j = 1u; j < table.length; j++) - { - auto len1 = table[j - 1].length; - auto len2 = table[j].length; - - assert(len1 <= len2); - if (len1 == len2) - { - int ci; - - ci = memcmp(table[j - 1].ptr, table[j].ptr, len1); - assert(ci < 0); // ci==0 means a duplicate - } - } -} -out (result) -{ - int cj; - - //printf("out _d_switch_string()\n"); - if (result == -1) - { - // Not found - for (auto i = 0u; i < table.length; i++) - { - if (table[i].length == ca.length) - { cj = memcmp(table[i].ptr, ca.ptr, ca.length); - assert(cj != 0); - } - } - } - else - { - assert(0 <= result && cast(size_t)result < table.length); - for (auto i = 0u; 1; i++) - { - assert(i < table.length); - if (table[i].length == ca.length) - { - cj = memcmp(table[i].ptr, ca.ptr, ca.length); - if (cj == 0) - { - assert(i == result); - break; - } - } - } - } -} -body -{ - //printf("body _d_switch_string(%.*s)\n", ca.length, ca.ptr); - size_t low = 0; - size_t high = table.length; - - version (none) - { - // Print table - printf("ca[] = '%s'\n", ca.length, ca.ptr); - for (auto i = 0; i < high; i++) - { - auto pca = table[i]; - printf("table[%d] = %d, '%.*s'\n", i, pca.length, pca.length, pca.ptr); - } - } - if (high && - ca.length >= table[0].length && - ca.length <= table[high - 1].length) - { - // Looking for 0 length string, which would only be at the beginning - if (ca.length == 0) - return 0; - - char c1 = ca[0]; - - // Do binary search - while (low < high) - { - auto mid = (low + high) >> 1; - auto pca = table[mid]; - auto c = cast(sizediff_t)(ca.length - pca.length); - if (c == 0) - { - c = cast(ubyte)c1 - cast(ubyte)pca[0]; - if (c == 0) - { - c = memcmp(ca.ptr, pca.ptr, ca.length); - if (c == 0) - { //printf("found %d\n", mid); - return cast(int)mid; - } - } - } - if (c < 0) - { - high = mid; - } - else - { - low = mid + 1; - } - } - } - - //printf("not found\n"); - return -1; // not found -} - -unittest -{ - switch (cast(char []) "c") - { - case "coo": - default: - break; - } - - int bug5381(string s) - { - switch(s) - { - case "unittest": return 1; - case "D_Version2": return 2; - case "none": return 3; - case "all": return 4; - default: return 5; - } - } - int rc = bug5381("none"); - assert(rc == 3); -} - -/********************************** - * Same thing, but for wide chars. - */ - -int _d_switch_ustring(wchar[][] table, wchar[] ca) -in -{ - //printf("in _d_switch_ustring()\n"); - assert(table.length >= 0); - assert(ca.length >= 0); - - // Make sure table[] is sorted correctly - for (size_t j = 1u; j < table.length; j++) - { - auto len1 = table[j - 1].length; - auto len2 = table[j].length; - - assert(len1 <= len2); - if (len1 == len2) - { - int c; - - c = memcmp(table[j - 1].ptr, table[j].ptr, len1 * wchar.sizeof); - assert(c < 0); // c==0 means a duplicate - } - } -} -out (result) -{ - int c; - - //printf("out _d_switch_ustring()\n"); - if (result == -1) - { - // Not found - for (auto i = 0u; i < table.length; i++) - { - if (table[i].length == ca.length) - { c = memcmp(table[i].ptr, ca.ptr, ca.length * wchar.sizeof); - assert(c != 0); - } - } - } - else - { - assert(0 <= result && cast(size_t)result < table.length); - for (auto i = 0u; 1; i++) - { - assert(i < table.length); - if (table[i].length == ca.length) - { - c = memcmp(table[i].ptr, ca.ptr, ca.length * wchar.sizeof); - if (c == 0) - { - assert(i == result); - break; - } - } - } - } -} -body -{ - //printf("body _d_switch_ustring()\n"); - size_t low = 0; - auto high = table.length; - - version(none) - { - // Print table - wprintf("ca[] = '%.*s'\n", ca.length, ca.ptr); - for (auto i = 0; i < high; i++) - { - auto pca = table[i]; - wprintf("table[%d] = %d, '%.*s'\n", i, pca.length, pca.length, pca.ptr); - } - } - - // Do binary search - while (low < high) - { - auto mid = (low + high) >> 1; - auto pca = table[mid]; - auto c = cast(sizediff_t)(ca.length - pca.length); - if (c == 0) - { - c = memcmp(ca.ptr, pca.ptr, ca.length * wchar.sizeof); - if (c == 0) - { //printf("found %d\n", mid); - return cast(int)mid; - } - } - if (c < 0) - { - high = mid; - } - else - { - low = mid + 1; - } - } - //printf("not found\n"); - return -1; // not found -} - - -unittest -{ - switch (cast(wchar []) "c") - { - case "coo": - default: - break; - } - - int bug5381(wstring ws) - { - switch(ws) - { - case "unittest": return 1; - case "D_Version2": return 2; - case "none": return 3; - case "all": return 4; - default: return 5; - } - } - int rc = bug5381("none"w); - assert(rc == 3); -} - -/********************************** - * Same thing, but for wide chars. - */ - -int _d_switch_dstring(dchar[][] table, dchar[] ca) -in -{ - //printf("in _d_switch_dstring()\n"); - assert(table.length >= 0); - assert(ca.length >= 0); - - // Make sure table[] is sorted correctly - for (auto j = 1u; j < table.length; j++) - { - auto len1 = table[j - 1].length; - auto len2 = table[j].length; - - assert(len1 <= len2); - if (len1 == len2) - { - auto c = memcmp(table[j - 1].ptr, table[j].ptr, len1 * dchar.sizeof); - assert(c < 0); // c==0 means a duplicate - } - } -} -out (result) -{ - //printf("out _d_switch_dstring()\n"); - if (result == -1) - { - // Not found - for (auto i = 0u; i < table.length; i++) - { - if (table[i].length == ca.length) - { auto c = memcmp(table[i].ptr, ca.ptr, ca.length * dchar.sizeof); - assert(c != 0); - } - } - } - else - { - assert(0 <= result && cast(size_t)result < table.length); - for (auto i = 0u; 1; i++) - { - assert(i < table.length); - if (table[i].length == ca.length) - { - auto c = memcmp(table[i].ptr, ca.ptr, ca.length * dchar.sizeof); - if (c == 0) - { - assert(i == result); - break; - } - } - } - } -} -body -{ - //printf("body _d_switch_dstring()\n"); - size_t low = 0; - auto high = table.length; - - version(none) - { - // Print table - wprintf("ca[] = '%.*s'\n", ca.length, ca.ptr); - for (auto i = 0; i < high; i++) - { - auto pca = table[i]; - wprintf("table[%d] = %d, '%.*s'\n", i, pca.length, pca.length, pca.ptr); - } - } - - // Do binary search - while (low < high) - { - auto mid = (low + high) >> 1; - auto pca = table[mid]; - auto c = cast(sizediff_t)(ca.length - pca.length); - if (c == 0) - { - c = memcmp(ca.ptr, pca.ptr, ca.length * dchar.sizeof); - if (c == 0) - { //printf("found %d\n", mid); - return cast(int)mid; - } - } - if (c < 0) - { - high = mid; - } - else - { - low = mid + 1; - } - } - //printf("not found\n"); - return -1; // not found -} - - -unittest -{ - switch (cast(dchar []) "c") - { - case "coo": - default: - break; - } - - int bug5381(dstring ds) - { - switch(ds) - { - case "unittest": return 1; - case "D_Version2": return 2; - case "none": return 3; - case "all": return 4; - default: return 5; - } - } - int rc = bug5381("none"d); - assert(rc == 3); -} diff --git a/libphobos/libdruntime/rt/typeinfo/ti_Ag.d b/libphobos/libdruntime/rt/typeinfo/ti_Ag.d index e02dfa912..bdb055369 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_Ag.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_Ag.d @@ -14,7 +14,6 @@ module rt.typeinfo.ti_Ag; private import core.stdc.string; -private import rt.util.hash; private import core.internal.string; // byte[] @@ -28,7 +27,7 @@ class TypeInfo_Ag : TypeInfo_Array override size_t getHash(scope const void* p) @trusted const { const s = *cast(const void[]*)p; - return rt.util.hash.hashOf(s, 0); + return hashOf(s); } override bool equals(in void* p1, in void* p2) const @@ -121,51 +120,7 @@ class TypeInfo_Aa : TypeInfo_Ah override size_t getHash(scope const void* p) @trusted const { char[] s = *cast(char[]*)p; - size_t hash = 0; - -version (all) -{ - foreach (char c; s) - hash = hash * 11 + c; -} -else -{ - size_t len = s.length; - char *str = s; - - while (1) - { - switch (len) - { - case 0: - return hash; - - case 1: - hash *= 9; - hash += *cast(ubyte *)str; - return hash; - - case 2: - hash *= 9; - hash += *cast(ushort *)str; - return hash; - - case 3: - hash *= 9; - hash += (*cast(ushort *)str << 8) + - (cast(ubyte *)str)[2]; - return hash; - - default: - hash *= 9; - hash += *cast(uint *)str; - str += 4; - len -= 4; - break; - } - } -} - return hash; + return hashOf(s); } override @property inout(TypeInfo) next() inout diff --git a/libphobos/libdruntime/rt/typeinfo/ti_Aint.d b/libphobos/libdruntime/rt/typeinfo/ti_Aint.d index 43ee8d308..ebe8d80f9 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_Aint.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_Aint.d @@ -14,7 +14,6 @@ module rt.typeinfo.ti_Aint; private import core.stdc.string; -private import rt.util.hash; extern (C) void[] _adSort(void[] a, TypeInfo ti); @@ -28,8 +27,9 @@ class TypeInfo_Ai : TypeInfo_Array override size_t getHash(scope const void* p) @trusted const { - const s = *cast(const int[]*)p; - return rt.util.hash.hashOf(s, 0); + // Hash as if unsigned. + const s = *cast(const uint[]*)p; + return hashOf(s); } override bool equals(in void* p1, in void* p2) const diff --git a/libphobos/libdruntime/rt/typeinfo/ti_Along.d b/libphobos/libdruntime/rt/typeinfo/ti_Along.d index f76a67771..b67bb995b 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_Along.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_Along.d @@ -14,7 +14,6 @@ module rt.typeinfo.ti_Along; private import core.stdc.string; -private import rt.util.hash; // long[] @@ -26,8 +25,9 @@ class TypeInfo_Al : TypeInfo_Array override size_t getHash(scope const void* p) @trusted const { - const s = *cast(const long[]*)p; - return rt.util.hash.hashOf(s, 0); + // Hash as if unsigned. + const s = *cast(const ulong[]*)p; + return hashOf(s); } override bool equals(in void* p1, in void* p2) const diff --git a/libphobos/libdruntime/rt/typeinfo/ti_Ashort.d b/libphobos/libdruntime/rt/typeinfo/ti_Ashort.d index abf69a599..aea4be770 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_Ashort.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_Ashort.d @@ -14,7 +14,6 @@ module rt.typeinfo.ti_Ashort; private import core.stdc.string; -private import rt.util.hash; // short[] @@ -26,8 +25,9 @@ class TypeInfo_As : TypeInfo_Array override size_t getHash(scope const void* p) @trusted const { - const s = *cast(const short[]*)p; - return rt.util.hash.hashOf(s, 0); + // Hash as if unsigned. + const s = *cast(const ushort[]*)p; + return hashOf(s); } override bool equals(in void* p1, in void* p2) const diff --git a/libphobos/libdruntime/rt/typeinfo/ti_byte.d b/libphobos/libdruntime/rt/typeinfo/ti_byte.d index ba7fcc064..2cc3f9838 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_byte.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_byte.d @@ -26,7 +26,8 @@ class TypeInfo_g : TypeInfo override size_t getHash(scope const void* p) { - return *cast(byte *)p; + // Hash as if unsigned. + return *cast(const ubyte *)p; } override bool equals(in void* p1, in void* p2) diff --git a/libphobos/libdruntime/rt/typeinfo/ti_cent.d b/libphobos/libdruntime/rt/typeinfo/ti_cent.d index c38d908f4..8cd7b3cdd 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_cent.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_cent.d @@ -13,8 +13,6 @@ */ module rt.typeinfo.ti_cent; -private import rt.util.hash; - static if(is(cent)): // cent @@ -30,7 +28,8 @@ class TypeInfo_zi : TypeInfo override size_t getHash(scope const void* p) { - return rt.util.hash.hashOf(p[0 .. cent.sizeof], 0); + // Hash as if unsigned. + return hashOf(*cast(const ucent*) p); } override bool equals(in void* p1, in void* p2) diff --git a/libphobos/libdruntime/rt/typeinfo/ti_char.d b/libphobos/libdruntime/rt/typeinfo/ti_char.d index 2744a9f16..c804711af 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_char.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_char.d @@ -26,7 +26,7 @@ class TypeInfo_a : TypeInfo override size_t getHash(scope const void* p) { - return *cast(char *)p; + return *cast(const char *)p; } override bool equals(in void* p1, in void* p2) diff --git a/libphobos/libdruntime/rt/typeinfo/ti_dchar.d b/libphobos/libdruntime/rt/typeinfo/ti_dchar.d index 2f0b8557c..0051dfc4a 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_dchar.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_dchar.d @@ -26,7 +26,7 @@ class TypeInfo_w : TypeInfo override size_t getHash(scope const void* p) { - return *cast(dchar *)p; + return *cast(const dchar *)p; } override bool equals(in void* p1, in void* p2) diff --git a/libphobos/libdruntime/rt/typeinfo/ti_delegate.d b/libphobos/libdruntime/rt/typeinfo/ti_delegate.d index 28dee5811..fa3959344 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_delegate.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_delegate.d @@ -13,7 +13,6 @@ */ module rt.typeinfo.ti_delegate; -private import rt.util.hash; // delegate @@ -28,7 +27,7 @@ class TypeInfo_D : TypeInfo override size_t getHash(scope const void* p) { - return rt.util.hash.hashOf(p[0 .. dg.sizeof], 0); + return hashOf(*cast(dg*)p); } override bool equals(in void* p1, in void* p2) diff --git a/libphobos/libdruntime/rt/typeinfo/ti_int.d b/libphobos/libdruntime/rt/typeinfo/ti_int.d index 5eb9ce681..a4c47d3af 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_int.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_int.d @@ -26,7 +26,8 @@ class TypeInfo_i : TypeInfo override size_t getHash(scope const void* p) { - return *cast(uint *)p; + // Hash as if unsigned. + return *cast(const uint *)p; } override bool equals(in void* p1, in void* p2) diff --git a/libphobos/libdruntime/rt/typeinfo/ti_long.d b/libphobos/libdruntime/rt/typeinfo/ti_long.d index ac41ecad5..3f8ba8e65 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_long.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_long.d @@ -13,8 +13,6 @@ */ module rt.typeinfo.ti_long; -private import rt.util.hash; - // long class TypeInfo_l : TypeInfo @@ -28,7 +26,11 @@ class TypeInfo_l : TypeInfo override size_t getHash(scope const void* p) { - return rt.util.hash.hashOf(p[0 .. long.sizeof], 0); + // Hash as if unsigned. + static if (ulong.sizeof <= size_t.sizeof) + return *cast(const ulong*)p; + else + return hashOf(*cast(const ulong*)p); } override bool equals(in void* p1, in void* p2) diff --git a/libphobos/libdruntime/rt/typeinfo/ti_ptr.d b/libphobos/libdruntime/rt/typeinfo/ti_ptr.d index 1b88cdfc2..453c945d6 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_ptr.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_ptr.d @@ -25,7 +25,8 @@ class TypeInfo_P : TypeInfo override size_t getHash(scope const void* p) { - return cast(size_t)*cast(void**)p; + size_t addr = cast(size_t) *cast(const void**)p; + return addr ^ (addr >> 4); } override bool equals(in void* p1, in void* p2) diff --git a/libphobos/libdruntime/rt/typeinfo/ti_short.d b/libphobos/libdruntime/rt/typeinfo/ti_short.d index 688025fec..da445c274 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_short.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_short.d @@ -26,7 +26,8 @@ class TypeInfo_s : TypeInfo override size_t getHash(scope const void* p) { - return *cast(short *)p; + // Hash as if unsigned. + return *cast(const ushort *)p; } override bool equals(in void* p1, in void* p2) diff --git a/libphobos/libdruntime/rt/typeinfo/ti_ubyte.d b/libphobos/libdruntime/rt/typeinfo/ti_ubyte.d index cd38f27a1..09232fd1c 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_ubyte.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_ubyte.d @@ -26,7 +26,7 @@ class TypeInfo_h : TypeInfo override size_t getHash(scope const void* p) { - return *cast(ubyte *)p; + return *cast(const ubyte *)p; } override bool equals(in void* p1, in void* p2) diff --git a/libphobos/libdruntime/rt/typeinfo/ti_ucent.d b/libphobos/libdruntime/rt/typeinfo/ti_ucent.d index 89d3ff57a..8d31dbb9e 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_ucent.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_ucent.d @@ -13,8 +13,6 @@ */ module rt.typeinfo.ti_ucent; -private import rt.util.hash; - static if (is(ucent)): // ucent @@ -30,7 +28,7 @@ class TypeInfo_zk : TypeInfo override size_t getHash(scope const void* p) { - return rt.util.hash.hashOf(p[0 .. ucent.sizeof], 0); + return hashOf(*cast(const ucent*) p); } override bool equals(in void* p1, in void* p2) diff --git a/libphobos/libdruntime/rt/typeinfo/ti_uint.d b/libphobos/libdruntime/rt/typeinfo/ti_uint.d index 81ad1d5e3..8f8e42fa9 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_uint.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_uint.d @@ -26,7 +26,7 @@ class TypeInfo_k : TypeInfo override size_t getHash(scope const void* p) { - return *cast(uint *)p; + return *cast(const uint *)p; } override bool equals(in void* p1, in void* p2) diff --git a/libphobos/libdruntime/rt/typeinfo/ti_ulong.d b/libphobos/libdruntime/rt/typeinfo/ti_ulong.d index d791b05b3..7382e487a 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_ulong.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_ulong.d @@ -13,7 +13,6 @@ */ module rt.typeinfo.ti_ulong; -private import rt.util.hash; // ulong @@ -28,7 +27,10 @@ class TypeInfo_m : TypeInfo override size_t getHash(scope const void* p) { - return rt.util.hash.hashOf(p[0 .. ulong.sizeof], 0); + static if (ulong.sizeof <= size_t.sizeof) + return *cast(const ulong*)p; + else + return hashOf(*cast(const ulong*)p); } override bool equals(in void* p1, in void* p2) diff --git a/libphobos/libdruntime/rt/typeinfo/ti_ushort.d b/libphobos/libdruntime/rt/typeinfo/ti_ushort.d index 0034e9171..3a95eb464 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_ushort.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_ushort.d @@ -26,7 +26,7 @@ class TypeInfo_t : TypeInfo override size_t getHash(scope const void* p) { - return *cast(ushort *)p; + return *cast(const ushort *)p; } override bool equals(in void* p1, in void* p2) diff --git a/libphobos/libdruntime/rt/typeinfo/ti_wchar.d b/libphobos/libdruntime/rt/typeinfo/ti_wchar.d index aca21dbd8..a1ba04196 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_wchar.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_wchar.d @@ -26,7 +26,7 @@ class TypeInfo_u : TypeInfo override size_t getHash(scope const void* p) { - return *cast(wchar *)p; + return *cast(const wchar *)p; } override bool equals(in void* p1, in void* p2) diff --git a/libphobos/libdruntime/rt/util/container/hashtab.d b/libphobos/libdruntime/rt/util/container/hashtab.d index 697beeab7..66762cd8c 100644 --- a/libphobos/libdruntime/rt/util/container/hashtab.d +++ b/libphobos/libdruntime/rt/util/container/hashtab.d @@ -146,11 +146,10 @@ private: static hash_t hashOf(in ref Key key) @trusted { - import rt.util.hash : hashOf; static if (is(Key U : U[])) - return hashOf(key, 0); + return .hashOf(key, 0); else - return hashOf((&key)[0 .. 1], 0); + return .hashOf((&key)[0 .. 1], 0); } @property hash_t mask() const diff --git a/libphobos/libdruntime/rt/util/hash.d b/libphobos/libdruntime/rt/util/hash.d deleted file mode 100644 index 150985808..000000000 --- a/libphobos/libdruntime/rt/util/hash.d +++ /dev/null @@ -1,107 +0,0 @@ -/** - * The default hash implementation. - * - * Copyright: Copyright Sean Kelly 2009 - 2016. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Sean Kelly - * Source: $(DRUNTIMESRC rt/util/_hash.d) - */ -module rt.util.hash; - - -version( X86 ) - version = AnyX86; -version( X86_64 ) - version = AnyX86; -version( AnyX86 ) - version = HasUnalignedOps; - - -@trusted pure nothrow @nogc -size_t hashOf( const(void)[] buf, size_t seed ) -{ - /* - * This is Paul Hsieh's SuperFastHash algorithm, described here: - * http://www.azillionmonkeys.com/qed/hash.html - * It is protected by the following open source license: - * http://www.azillionmonkeys.com/qed/weblicense.html - */ - static uint get16bits( const (ubyte)* x ) pure nothrow @nogc - { - // CTFE doesn't support casting ubyte* -> ushort*, so revert to - // per-byte access when in CTFE. - version( HasUnalignedOps ) - { - if (!__ctfe) - return *cast(ushort*) x; - } - - return ((cast(uint) x[1]) << 8) + (cast(uint) x[0]); - } - - // NOTE: SuperFastHash normally starts with a zero hash value. The seed - // value was incorporated to allow chaining. - auto data = cast(const(ubyte)*) buf.ptr; - auto len = buf.length; - auto hash = seed; - - if( len == 0 || data is null ) - return 0; - - int rem = len & 3; - len >>= 2; - - for( ; len > 0; len-- ) - { - hash += get16bits( data ); - auto tmp = (get16bits( data + 2 ) << 11) ^ hash; - hash = (hash << 16) ^ tmp; - data += 2 * ushort.sizeof; - hash += hash >> 11; - } - - switch( rem ) - { - case 3: hash += get16bits( data ); - hash ^= hash << 16; - hash ^= data[ushort.sizeof] << 18; - hash += hash >> 11; - break; - case 2: hash += get16bits( data ); - hash ^= hash << 11; - hash += hash >> 17; - break; - case 1: hash += *data; - hash ^= hash << 10; - hash += hash >> 1; - break; - default: - break; - } - - /* Force "avalanching" of final 127 bits */ - hash ^= hash << 3; - hash += hash >> 5; - hash ^= hash << 4; - hash += hash >> 17; - hash ^= hash << 25; - hash += hash >> 6; - - return hash; -} - -unittest -{ - enum test_str = "Sample string"; - size_t hashval = hashOf(test_str, 5); - - //import core.stdc.stdio; - //printf("hashval = %lld\n", cast(long)hashval); - - if (hashval.sizeof == 4) - assert(hashval == 528740845); - else if (hashval.sizeof == 8) - assert(hashval == 8106800467257150594L); - else - assert(0); -} diff --git a/libphobos/libdruntime/rt/util/random.d b/libphobos/libdruntime/rt/util/random.d index 4ca553b1c..75646b7cf 100644 --- a/libphobos/libdruntime/rt/util/random.d +++ b/libphobos/libdruntime/rt/util/random.d @@ -12,10 +12,55 @@ struct Rand48 @safe @nogc nothrow: - void defaultSeed() + void defaultSeed() @trusted { - import ctime = core.stdc.time : time; - seed(cast(uint)ctime.time(null)); + version(D_InlineAsm_X86_64) + { + // RDTSC takes around 22 clock cycles. + ulong result = void; // Workaround for LDC issue #950, cannot access struct members in DMD asm. + asm @nogc nothrow + { + rdtsc; + // RAX: low 32 bits are low bits of timestamp, high 32 bits are 0. + // RDX: low 32 bits are high bits of timestamp, high 32 bits are 0. + // We combine these into a 48 bit value instead of a full 64 bits + // because `front` and `popFront` only make use of the bottom 48 + // bits of `rng_state`. + shl RDX, 16; + xor RDX, RAX; + mov result, RDX; + } + rng_state = result; + popFront(); + } + //else version(D_InlineAsm_X86) + //{ + // // We don't use `rdtsc` with version(D_InlineAsm_X86) because + // // some x86 processors don't support `rdtsc` and because on + // // x86 (but not x86-64) Linux `prctl` can disable a process's + // // ability to use `rdtsc`. + // static assert(0); + //} + else version(Windows) + { + // QueryPerformanceCounter takes about 1/4 the time of ctime.time. + import core.sys.windows.winbase : QueryPerformanceCounter; + QueryPerformanceCounter(cast(long*) &rng_state); + popFront(); + } + else version(OSX) + { + // mach_absolute_time is much faster than ctime.time. + import core.time : mach_absolute_time; + rng_state = mach_absolute_time(); + popFront(); + } + else + { + // Fallback to libc timestamp in seconds. + import ctime = core.stdc.time : time; + seed((cast(uint) ctime.time(null))); + } } pure: diff --git a/libphobos/libdruntime/rt/util/typeinfo.d b/libphobos/libdruntime/rt/util/typeinfo.d index 51c4632e9..9af6fa233 100644 --- a/libphobos/libdruntime/rt/util/typeinfo.d +++ b/libphobos/libdruntime/rt/util/typeinfo.d @@ -6,6 +6,7 @@ * Authors: Kenji Hara */ module rt.util.typeinfo; +static import core.internal.hash; template Floating(T) if (is(T == float) || is(T == double) || is(T == real)) @@ -32,19 +33,7 @@ if (is(T == float) || is(T == double) || is(T == real)) return (d1 == d2) ? 0 : ((d1 < d2) ? -1 : 1); } - size_t hashOf(T value) @trusted - { - if (value == 0) // +0.0 and -0.0 - value = 0; - - static if (is(T == float)) // special case? - return *cast(uint*)&value; - else - { - import rt.util.hash; - return rt.util.hash.hashOf((&value)[0 .. 1], 0); - } - } + public alias hashOf = core.internal.hash.hashOf; } template Floating(T) if (is(T == cfloat) || is(T == cdouble) || is(T == creal)) @@ -73,13 +62,7 @@ if (is(T == cfloat) || is(T == cdouble) || is(T == creal)) return result; } - size_t hashOf(T value) @trusted - { - if (value == 0 + 0i) - value = 0 + 0i; - import rt.util.hash; - return rt.util.hash.hashOf((&value)[0 .. 1], 0); - } + public alias hashOf = core.internal.hash.hashOf; } template Array(T) @@ -118,13 +101,7 @@ if (is(T == float) || is(T == double) || is(T == real) || return 0; } - size_t hashOf(T[] value) - { - size_t h = 0; - foreach (e; value) - h += Floating!T.hashOf(e); - return h; - } + public alias hashOf = core.internal.hash.hashOf; } version(unittest) diff --git a/libphobos/src/Makefile.am b/libphobos/src/Makefile.am index b582959fb..0343b1335 100644 --- a/libphobos/src/Makefile.am +++ b/libphobos/src/Makefile.am @@ -158,19 +158,19 @@ PHOBOS_DSOURCES = etc/c/curl.d etc/c/sqlite3.d etc/c/zlib.d \ std/experimental/logger/multilogger.d \ std/experimental/logger/nulllogger.d std/experimental/logger/package.d \ std/experimental/typecons.d std/file.d std/format.d std/functional.d \ - std/getopt.d std/internal/cstring.d std/internal/digest/sha_SSSE3.d \ - std/internal/math/biguintcore.d std/internal/math/biguintnoasm.d \ - std/internal/math/biguintx86.d std/internal/math/errorfunction.d \ - std/internal/math/gammafunction.d std/internal/scopebuffer.d \ - std/internal/test/dummyrange.d std/internal/test/range.d \ - std/internal/test/uda.d std/internal/unicode_comp.d \ - std/internal/unicode_decomp.d std/internal/unicode_grapheme.d \ - std/internal/unicode_norm.d std/internal/unicode_tables.d \ - std/internal/windows/advapi32.d std/json.d std/math.d \ - std/mathspecial.d std/meta.d std/mmfile.d std/net/curl.d \ - std/net/isemail.d std/numeric.d std/outbuffer.d std/parallelism.d \ - std/path.d std/process.d std/random.d std/range/interfaces.d \ - std/range/package.d std/range/primitives.d \ + std/getopt.d std/internal/attributes.d std/internal/cstring.d \ + std/internal/digest/sha_SSSE3.d std/internal/math/biguintcore.d \ + std/internal/math/biguintnoasm.d std/internal/math/biguintx86.d \ + std/internal/math/errorfunction.d std/internal/math/gammafunction.d \ + std/internal/scopebuffer.d std/internal/test/dummyrange.d \ + std/internal/test/range.d std/internal/test/uda.d \ + std/internal/unicode_comp.d std/internal/unicode_decomp.d \ + std/internal/unicode_grapheme.d std/internal/unicode_norm.d \ + std/internal/unicode_tables.d std/internal/windows/advapi32.d \ + std/json.d std/math.d std/mathspecial.d std/meta.d std/mmfile.d \ + std/net/curl.d std/net/isemail.d std/numeric.d std/outbuffer.d \ + std/parallelism.d std/path.d std/process.d std/random.d \ + std/range/interfaces.d std/range/package.d std/range/primitives.d \ std/regex/internal/backtracking.d std/regex/internal/generator.d \ std/regex/internal/ir.d std/regex/internal/kickstart.d \ std/regex/internal/parser.d std/regex/internal/tests.d \ diff --git a/libphobos/src/Makefile.in b/libphobos/src/Makefile.in index d0893f2cb..0bf216f00 100644 --- a/libphobos/src/Makefile.in +++ b/libphobos/src/Makefile.in @@ -178,8 +178,8 @@ am__objects_1 = etc/c/curl.lo etc/c/sqlite3.lo etc/c/zlib.lo \ std/experimental/logger/nulllogger.lo \ std/experimental/logger/package.lo \ std/experimental/typecons.lo std/file.lo std/format.lo \ - std/functional.lo std/getopt.lo std/internal/cstring.lo \ - std/internal/digest/sha_SSSE3.lo \ + std/functional.lo std/getopt.lo std/internal/attributes.lo \ + std/internal/cstring.lo std/internal/digest/sha_SSSE3.lo \ std/internal/math/biguintcore.lo \ std/internal/math/biguintnoasm.lo \ std/internal/math/biguintx86.lo \ @@ -269,7 +269,8 @@ am__DEPENDENCIES_1 = etc/c/curl.t.lo etc/c/sqlite3.t.lo \ std/experimental/logger/nulllogger.t.lo \ std/experimental/logger/package.t.lo \ std/experimental/typecons.t.lo std/file.t.lo std/format.t.lo \ - std/functional.t.lo std/getopt.t.lo std/internal/cstring.t.lo \ + std/functional.t.lo std/getopt.t.lo \ + std/internal/attributes.t.lo std/internal/cstring.t.lo \ std/internal/digest/sha_SSSE3.t.lo \ std/internal/math/biguintcore.t.lo \ std/internal/math/biguintnoasm.t.lo \ @@ -382,8 +383,8 @@ am__DEPENDENCIES_4 = etc/c/curl.t.o etc/c/sqlite3.t.o etc/c/zlib.t.o \ std/experimental/logger/nulllogger.t.o \ std/experimental/logger/package.t.o \ std/experimental/typecons.t.o std/file.t.o std/format.t.o \ - std/functional.t.o std/getopt.t.o std/internal/cstring.t.o \ - std/internal/digest/sha_SSSE3.t.o \ + std/functional.t.o std/getopt.t.o std/internal/attributes.t.o \ + std/internal/cstring.t.o std/internal/digest/sha_SSSE3.t.o \ std/internal/math/biguintcore.t.o \ std/internal/math/biguintnoasm.t.o \ std/internal/math/biguintx86.t.o \ @@ -736,19 +737,19 @@ PHOBOS_DSOURCES = etc/c/curl.d etc/c/sqlite3.d etc/c/zlib.d \ std/experimental/logger/multilogger.d \ std/experimental/logger/nulllogger.d std/experimental/logger/package.d \ std/experimental/typecons.d std/file.d std/format.d std/functional.d \ - std/getopt.d std/internal/cstring.d std/internal/digest/sha_SSSE3.d \ - std/internal/math/biguintcore.d std/internal/math/biguintnoasm.d \ - std/internal/math/biguintx86.d std/internal/math/errorfunction.d \ - std/internal/math/gammafunction.d std/internal/scopebuffer.d \ - std/internal/test/dummyrange.d std/internal/test/range.d \ - std/internal/test/uda.d std/internal/unicode_comp.d \ - std/internal/unicode_decomp.d std/internal/unicode_grapheme.d \ - std/internal/unicode_norm.d std/internal/unicode_tables.d \ - std/internal/windows/advapi32.d std/json.d std/math.d \ - std/mathspecial.d std/meta.d std/mmfile.d std/net/curl.d \ - std/net/isemail.d std/numeric.d std/outbuffer.d std/parallelism.d \ - std/path.d std/process.d std/random.d std/range/interfaces.d \ - std/range/package.d std/range/primitives.d \ + std/getopt.d std/internal/attributes.d std/internal/cstring.d \ + std/internal/digest/sha_SSSE3.d std/internal/math/biguintcore.d \ + std/internal/math/biguintnoasm.d std/internal/math/biguintx86.d \ + std/internal/math/errorfunction.d std/internal/math/gammafunction.d \ + std/internal/scopebuffer.d std/internal/test/dummyrange.d \ + std/internal/test/range.d std/internal/test/uda.d \ + std/internal/unicode_comp.d std/internal/unicode_decomp.d \ + std/internal/unicode_grapheme.d std/internal/unicode_norm.d \ + std/internal/unicode_tables.d std/internal/windows/advapi32.d \ + std/json.d std/math.d std/mathspecial.d std/meta.d std/mmfile.d \ + std/net/curl.d std/net/isemail.d std/numeric.d std/outbuffer.d \ + std/parallelism.d std/path.d std/process.d std/random.d \ + std/range/interfaces.d std/range/package.d std/range/primitives.d \ std/regex/internal/backtracking.d std/regex/internal/generator.d \ std/regex/internal/ir.d std/regex/internal/kickstart.d \ std/regex/internal/parser.d std/regex/internal/tests.d \ @@ -980,6 +981,7 @@ std/getopt.lo: std/$(am__dirstamp) std/internal/$(am__dirstamp): @$(MKDIR_P) std/internal @: > std/internal/$(am__dirstamp) +std/internal/attributes.lo: std/internal/$(am__dirstamp) std/internal/cstring.lo: std/internal/$(am__dirstamp) std/internal/digest/$(am__dirstamp): @$(MKDIR_P) std/internal/digest @@ -1260,6 +1262,8 @@ mostlyclean-compile: -rm -f std/functional.lo -rm -f std/getopt.$(OBJEXT) -rm -f std/getopt.lo + -rm -f std/internal/attributes.$(OBJEXT) + -rm -f std/internal/attributes.lo -rm -f std/internal/cstring.$(OBJEXT) -rm -f std/internal/cstring.lo -rm -f std/internal/digest/sha_SSSE3.$(OBJEXT) diff --git a/libphobos/src/index.d b/libphobos/src/index.d index 3f781238b..fd1fc237c 100644 --- a/libphobos/src/index.d +++ b/libphobos/src/index.d @@ -88,12 +88,12 @@ $(BOOKTABLE , $(TD Checked integral types.) ) $(TR - $(TDNW $(MREF std,digest,crc)) - $(TD Cyclic Redundancy Check (32-bit) implementation.) + $(TDNW $(MREF std,digest)) + $(TD Compute digests such as md5, sha1 and crc32.) ) $(TR - $(TDNW $(MREF std,digest,digest)) - $(TD Compute digests such as md5, sha1 and crc32.) + $(TDNW $(MREF std,digest,crc)) + $(TD Cyclic Redundancy Check (32-bit) implementation.) ) $(TR $(TDNW $(MREF std,digest,hmac)) diff --git a/libphobos/src/std/algorithm/comparison.d b/libphobos/src/std/algorithm/comparison.d index 4804c2d21..9c2ca62d2 100644 --- a/libphobos/src/std/algorithm/comparison.d +++ b/libphobos/src/std/algorithm/comparison.d @@ -66,6 +66,8 @@ import std.traits; import std.meta : allSatisfy; import std.typecons; // : tuple, Tuple, Flag, Yes; +import std.internal.attributes : betterC; + /** Find `value` _among `values`, returning the 1-based index of the first matching value in `values`, or `0` if `value` @@ -116,7 +118,7 @@ if (isExpressionTuple!values) } /// -@safe unittest +@safe @betterC unittest { assert(3.among(1, 42, 24, 3, 2)); @@ -133,7 +135,7 @@ if (isExpressionTuple!values) Alternatively, `values` can be passed at compile-time, allowing for a more efficient search, but one that only supports matching on equality: */ -@safe unittest +@safe @betterC unittest { assert(3.among!(2, 3, 4)); assert("bar".among!("foo", "bar", "baz") == 2); @@ -541,7 +543,7 @@ do } /// -@safe unittest +@safe @betterC unittest { assert(clamp(2, 1, 3) == 2); assert(clamp(0, 1, 3) == 1); @@ -1575,7 +1577,7 @@ if (T.length >= 2) } /// -@safe unittest +@safe @betterC unittest { int a = 5; short b = 6; @@ -1687,7 +1689,7 @@ if (T.length >= 2) } /// -@safe unittest +@safe @betterC unittest { int a = 5; short b = 6; @@ -1698,15 +1700,23 @@ if (T.length >= 2) auto e = min(a, b, c); static assert(is(typeof(e) == double)); assert(e == 2); +} - // With arguments of mixed signedness, the return type is the one that can - // store the lowest values. - a = -10; +/** +With arguments of mixed signedness, the return type is the one that can +store the lowest values. +*/ +@safe @betterC unittest +{ + int a = -10; uint f = 10; static assert(is(typeof(min(a, f)) == int)); assert(min(a, f) == -10); +} - // User-defined types that support comparison with < are supported. +/// User-defined types that support comparison with < are supported. +@safe unittest +{ import std.datetime; assert(min(Date(2012, 12, 21), Date(1982, 1, 4)) == Date(1982, 1, 4)); assert(min(Date(1982, 1, 4), Date(2012, 12, 21)) == Date(1982, 1, 4)); @@ -1985,7 +1995,7 @@ if (isInputRange!Range1 && } // Test CTFE -@safe pure unittest +@safe pure @betterC unittest { enum result1 = isSameLength([1, 2, 3], [4, 5, 6]); static assert(result1); @@ -2273,7 +2283,7 @@ if (alternatives.length >= 1 && } /// -@safe pure unittest +@safe pure @betterC unittest { const a = 1; const b = 2; @@ -2292,7 +2302,11 @@ if (alternatives.length >= 1 && auto ef = either(e, f); static assert(is(typeof(ef) == int)); assert(ef == f); +} +/// +@safe pure unittest +{ immutable p = 1; immutable q = 2; auto pq = either(p, q); @@ -2303,7 +2317,11 @@ if (alternatives.length >= 1 && assert(either(0, 4) == 4); assert(either(0, 0) == 0); assert(either("", "a") == ""); +} +/// +@safe pure unittest +{ string r = null; assert(either(r, "a") == "a"); assert(either("a", "") == "a"); diff --git a/libphobos/src/std/algorithm/iteration.d b/libphobos/src/std/algorithm/iteration.d index 006217c7c..a51a40895 100644 --- a/libphobos/src/std/algorithm/iteration.d +++ b/libphobos/src/std/algorithm/iteration.d @@ -74,6 +74,7 @@ module std.algorithm.iteration; import std.functional; // : unaryFun, binaryFun; import std.range.primitives; import std.traits; +import std.typecons : Flag; private template aggregate(fun...) if (fun.length >= 1) @@ -848,32 +849,40 @@ private struct MapResult(alias fun, Range) // each /** -Eagerly iterates over `r` and calls `pred` over _each element. +Eagerly iterates over `r` and calls `fun` over _each element. -If no predicate is specified, `each` will default to doing nothing -but consuming the entire range. `.front` will be evaluated, but this -can be avoided by explicitly specifying a predicate lambda with a -`lazy` parameter. +If no function to call is specified, `each` defaults to doing nothing but +consuming the entire range. `r.front` will be evaluated, but that can be avoided +by specifying a lambda with a `lazy` parameter. -`each` also supports `opApply`-based iterators, so it will work -with e.g. $(REF parallel, std,parallelism). +`each` also supports `opApply`-based types, so it works with e.g. $(REF +parallel, std,parallelism). + +Normally the entire range is iterated. If partial iteration (early stopping) is +desired, `fun` needs to return a value of type $(REF Flag, +std.typecons)`!"each"` (`Yes.each` to continue iteration, or `No.each` to stop +iteration). Params: - pred = predicate to apply to each element of the range - r = range or iterable over which each iterates + fun = function to apply to _each element of the range + r = range or iterable over which `each` iterates + +Returns: `Yes.each` if the entire range was iterated, `No.each` in case of early +stopping. See_Also: $(REF tee, std,range) */ -template each(alias pred = "a") +template each(alias fun = "a") { import std.meta : AliasSeq; import std.traits : Parameters; + import std.typecons : Flag, Yes, No; private: - alias BinaryArgs = AliasSeq!(pred, "i", "a"); + alias BinaryArgs = AliasSeq!(fun, "i", "a"); enum isRangeUnaryIterable(R) = - is(typeof(unaryFun!pred(R.init.front))); + is(typeof(unaryFun!fun(R.init.front))); enum isRangeBinaryIterable(R) = is(typeof(binaryFun!BinaryArgs(0, R.init.front))); @@ -885,25 +894,32 @@ private: enum isForeachUnaryIterable(R) = is(typeof((R r) { foreach (ref a; r) - cast(void) unaryFun!pred(a); + cast(void) unaryFun!fun(a); })); - enum isForeachBinaryIterable(R) = + enum isForeachUnaryWithIndexIterable(R) = is(typeof((R r) { - foreach (ref i, ref a; r) + foreach (i, ref a; r) cast(void) binaryFun!BinaryArgs(i, a); })); + enum isForeachBinaryIterable(R) = + is(typeof((R r) { + foreach (ref a, ref b; r) + cast(void) binaryFun!fun(a, b); + })); + enum isForeachIterable(R) = (!isForwardRange!R || isDynamicArray!R) && - (isForeachUnaryIterable!R || isForeachBinaryIterable!R); + (isForeachUnaryIterable!R || isForeachBinaryIterable!R || + isForeachUnaryWithIndexIterable!R); public: /** Params: r = range or iterable over which each iterates */ - void each(Range)(Range r) + Flag!"each" each(Range)(Range r) if (!isForeachIterable!Range && ( isRangeIterable!Range || __traits(compiles, typeof(r.front).length))) @@ -915,7 +931,15 @@ public: { while (!r.empty) { - cast(void) unaryFun!pred(r.front); + static if (!is(typeof(unaryFun!fun(r.front)) == Flag!"each")) + { + cast(void) unaryFun!fun(r.front); + } + else + { + if (unaryFun!fun(r.front) == No.each) return No.each; + } + r.popFront(); } } @@ -924,7 +948,14 @@ public: size_t i = 0; while (!r.empty) { - cast(void) binaryFun!BinaryArgs(i, r.front); + static if (!is(typeof(binaryFun!BinaryArgs(i, r.front)) == Flag!"each")) + { + cast(void) binaryFun!BinaryArgs(i, r.front); + } + else + { + if (binaryFun!BinaryArgs(i, r.front) == No.each) return No.each; + } r.popFront(); i++; } @@ -934,41 +965,100 @@ public: { // range interface with >2 parameters. for (auto range = r; !range.empty; range.popFront()) - pred(range.front.expand); + { + static if (!is(typeof(fun(r.front.expand)) == Flag!"each")) + { + cast(void) fun(range.front.expand); + } + else + { + if (fun(range.front.expand)) return No.each; + } + } } + return Yes.each; } - /** - Params: - r = range or iterable over which each iterates - */ - void each(Iterable)(auto ref Iterable r) + /// ditto + Flag!"each" each(Iterable)(auto ref Iterable r) if (isForeachIterable!Iterable || __traits(compiles, Parameters!(Parameters!(r.opApply)))) { static if (isForeachIterable!Iterable) { - debug(each) pragma(msg, "Using foreach for ", Iterable.stringof); static if (isForeachUnaryIterable!Iterable) { - foreach (ref e; r) - cast(void) unaryFun!pred(e); + debug(each) pragma(msg, "Using foreach UNARY for ", Iterable.stringof); + { + foreach (ref e; r) + { + static if (!is(typeof(unaryFun!fun(e)) == Flag!"each")) + { + cast(void) unaryFun!fun(e); + } + else + { + if (unaryFun!fun(e) == No.each) return No.each; + } + } + } + } + else static if (isForeachBinaryIterable!Iterable) + { + debug(each) pragma(msg, "Using foreach BINARY for ", Iterable.stringof); + foreach (ref a, ref b; r) + { + static if (!is(typeof(binaryFun!fun(a, b)) == Flag!"each")) + { + cast(void) binaryFun!fun(a, b); + } + else + { + if (binaryFun!fun(a, b) == No.each) return No.each; + } + } + } + else static if (isForeachUnaryWithIndexIterable!Iterable) + { + debug(each) pragma(msg, "Using foreach INDEX for ", Iterable.stringof); + foreach (i, ref e; r) + { + static if (!is(typeof(binaryFun!BinaryArgs(i, e)) == Flag!"each")) + { + cast(void) binaryFun!BinaryArgs(i, e); + } + else + { + if (binaryFun!BinaryArgs(i, e) == No.each) return No.each; + } + } } - else // if (isForeachBinaryIterable!Iterable) + else { - foreach (ref i, ref e; r) - cast(void) binaryFun!BinaryArgs(i, e); + static assert(0, "Invalid foreach iteratable type " ~ Iterable.stringof ~ " met."); } + return Yes.each; } else { // opApply with >2 parameters. count the delegate args. // only works if it is not templated (otherwise we cannot count the args) - auto dg(Parameters!(Parameters!(r.opApply)) params) { - pred(params); - return 0; // tells opApply to continue iteration + auto result = Yes.each; + auto dg(Parameters!(Parameters!(r.opApply)) params) + { + static if (!is(typeof(binaryFun!BinaryArgs(i, e)) == Flag!"each")) + { + fun(params); + return 0; // tells opApply to continue iteration + } + else + { + result = fun(params); + return result == Yes.each ? 0 : -1; + } } r.opApply(&dg); + return result; } } } @@ -977,17 +1067,20 @@ public: @system unittest { import std.range : iota; + import std.typecons : Flag, Yes, No; long[] arr; iota(5).each!(n => arr ~= n); assert(arr == [0, 1, 2, 3, 4]); + iota(5).each!((n) { arr ~= n; return No.each; }); + assert(arr == [0, 1, 2, 3, 4, 0]); // If the range supports it, the value can be mutated in place arr.each!((ref n) => n++); - assert(arr == [1, 2, 3, 4, 5]); + assert(arr == [1, 2, 3, 4, 5, 1]); arr.each!"a++"; - assert(arr == [2, 3, 4, 5, 6]); + assert(arr == [2, 3, 4, 5, 6, 2]); // by-ref lambdas are not allowed for non-ref ranges static assert(!is(typeof(arr.map!(n => n).each!((ref n) => n++)))); @@ -1000,7 +1093,7 @@ public: // Indexes are also available for in-place mutations arr[] = 0; arr.each!"a=i"(); - assert(arr == [0, 1, 2, 3, 4]); + assert(arr == [0, 1, 2, 3, 4, 5]); // opApply iterators work as well static class S @@ -1090,6 +1183,80 @@ public: assert(s.x == 2); } +// #15357: `each` should behave similar to foreach +/// `each` works with iterable objects which provide an index variable, along with each element +@safe unittest +{ + import std.range : iota, lockstep; + + auto arr = [1, 2, 3, 4]; + + // 1 ref parameter + arr.each!((ref e) => e = 0); + assert(arr.sum == 0); + + // 1 ref parameter and index + arr.each!((i, ref e) => e = cast(int) i); + assert(arr.sum == 4.iota.sum); +} + +// #15357: `each` should behave similar to foreach +@system unittest +{ + import std.range : iota, lockstep; + + // 2 ref parameters and index + auto arrA = [1, 2, 3, 4]; + auto arrB = [5, 6, 7, 8]; + lockstep(arrA, arrB).each!((ref a, ref b) { + a = 0; + b = 1; + }); + assert(arrA.sum == 0); + assert(arrB.sum == 4); + + // 3 ref parameters + auto arrC = [3, 3, 3, 3]; + lockstep(arrA, arrB, arrC).each!((ref a, ref b, ref c) { + a = 1; + b = 2; + c = 3; + }); + assert(arrA.sum == 4); + assert(arrB.sum == 8); + assert(arrC.sum == 12); +} + +// #15357: `each` should behave similar to foreach +@system unittest +{ + import std.range : lockstep; + import std.typecons : Tuple; + + auto a = "abc"; + auto b = "def"; + + // print each character with an index + { + alias Element = Tuple!(size_t, "index", dchar, "value"); + Element[] rForeach, rEach; + foreach (i, c ; a) rForeach ~= Element(i, c); + a.each!((i, c) => rEach ~= Element(i, c)); + assert(rForeach == rEach); + assert(rForeach == [Element(0, 'a'), Element(1, 'b'), Element(2, 'c')]); + } + + // print pairs of characters + { + alias Element = Tuple!(dchar, "a", dchar, "b"); + Element[] rForeach, rEach; + foreach (c1, c2 ; a.lockstep(b)) rForeach ~= Element(c1, c2); + a.lockstep(b).each!((c1, c2) => rEach ~= Element(c1, c2)); + assert(rForeach == rEach); + assert(rForeach == [Element('a', 'd'), Element('b', 'e'), Element('c', 'f')]); + } +} + // filter /** Implements the higher order filter function. The predicate is passed to @@ -2146,7 +2313,9 @@ Params: Returns: A range of elements in the joined range. This will be a forward range if both outer and inner ranges of `RoR` are forward ranges; otherwise it will -be only an input range. +be only an input range. The +$(REF_ALTTEXT range bidirectionality, isBidirectionalRange, std,range,primitives) +is propagated if no separator is specified. See_also: $(REF chain, std,range), which chains a sequence of ranges with compatible elements @@ -2479,50 +2648,33 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR)) private: RoR _items; ElementType!RoR _current; - enum prepare = - q{ - // Skip over empty subranges. - if (_items.empty) return; - while (_items.front.empty) - { - _items.popFront(); - if (_items.empty) return; - } - // We cannot export .save method unless we ensure subranges are not - // consumed when a .save'd copy of ourselves is iterated over. So - // we need to .save each subrange we traverse. - static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR)) - _current = _items.front.save; - else - _current = _items.front; - }; + enum isBidirectional = isForwardRange!RoR && isForwardRange!(ElementType!RoR) && + isBidirectionalRange!RoR && isBidirectionalRange!(ElementType!RoR); + static if (isBidirectional) + { + ElementType!RoR _currentBack; + bool reachedFinalElement; + } + this(RoR items, ElementType!RoR current) { _items = items; _current = current; + static if (isBidirectional && hasNested!Result) + _currentBack = typeof(_currentBack).init; } + public: this(RoR r) { _items = r; - //mixin(prepare); // _current should be initialized in place - - // Skip over empty subranges. - while (!_items.empty && _items.front.empty) - _items.popFront(); - if (_items.empty) - _current = _current.init; // set invalid state - else - { - // We cannot export .save method unless we ensure subranges are not - // consumed when a .save'd copy of ourselves is iterated over. So - // we need to .save each subrange we traverse. - static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR)) - _current = _items.front.save; - else - _current = _items.front; - } + static if (isBidirectional && hasNested!Result) + _currentBack = typeof(_currentBack).init; + // field _current must be initialized in constructor, because it is nested struct + mixin(popFrontEmptyElements); + static if (isBidirectional) + mixin(popBackEmptyElements); } static if (isInfinite!RoR) { @@ -2546,16 +2698,39 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR)) _current.popFront(); if (_current.empty) { - assert(!_items.empty); + assert(!_items.empty, "Attempting to popFront an empty joiner."); _items.popFront(); - mixin(prepare); + mixin(popFrontEmptyElements); } } + + private enum popFrontEmptyElements = q{ + // Skip over empty subranges. + if (_items.empty) goto end; + while (_items.front.empty) + { + _items.popFront(); + if (_items.empty) goto end; + } + // We cannot export .save method unless we ensure subranges are not + // consumed when a .save'd copy of ourselves is iterated over. So + // we need to .save each subrange we traverse. + static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR)) + _current = _items.front.save; + else + _current = _items.front; + end: + assert(1); // required to avoid 'EOF instead of statement' error + }; + static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR)) { @property auto save() { - return Result(_items.save, _current.save); + auto r = Result(_items.save, _current.save); + static if (isBidirectional) + r._currentBack = _currentBack.save; + return r; } } @@ -2573,28 +2748,130 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR)) _current.front = element; } } + + static if (isBidirectional) + { + bool checkFinalElement() + { + import std.range : dropOne; + + if (reachedFinalElement) + return true; + + static if (hasLength!(typeof(_items))) + { + if (_items.length == 1) + reachedFinalElement = true; + } + else + { + if (_items.save.dropOne.empty) + reachedFinalElement = true; + } + + return false; + } + + @property auto ref back() + { + assert(!empty, "Attempting to fetch the back of an empty joiner."); + if (reachedFinalElement) + return _current.back; + else + return _currentBack.back; + } + + void popBack() + { + assert(!_current.empty, "Attempting to popBack an empty joiner."); + if (checkFinalElement) + _current.popBack(); + else + _currentBack.popBack(); + + bool isEmpty = reachedFinalElement ? _current.empty : _currentBack.empty; + if (isEmpty) + { + assert(!_items.empty, "Attempting to popBack an empty joiner."); + _items.popBack(); + mixin(popBackEmptyElements); + } + } + + private enum popBackEmptyElements = q{ + // Skip over empty subranges. + if (_items.empty) goto end2; + while (_items.back.empty) + { + _items.popBack(); + if (_items.empty) goto end2; + } + checkFinalElement; + // We cannot export .save method unless we ensure subranges are not + // consumed when a .save'd copy of ourselves is iterated over. So + // we need to .save each subrange we traverse. + static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR)) + { + if (reachedFinalElement) + _current = _items.back.save; + else + _currentBack = _items.back.save; + } + else + { + if (reachedFinalElement) + _current = _items.back; + else + _currentBack = _items.back; + } + end2: + assert(1); + }; + + static if (hasAssignableElements!(ElementType!RoR)) + { + @property void back(ElementType!(ElementType!RoR) element) + { + assert(!empty, "Attempting to assign to back of an empty joiner."); + if (reachedFinalElement) + _current.back = element; + else + _currentBack.back = element; + } + + @property void back(ref ElementType!(ElementType!RoR) element) + { + assert(!empty, "Attempting to assign to back of an empty joiner."); + if (reachedFinalElement) + _current.back = element; + else + _currentBack.back = element; + } + } + } } return Result(r); } +/// @safe unittest { import std.algorithm.comparison : equal; - import std.range.interfaces : inputRangeObject; import std.range : repeat; - static assert(isInputRange!(typeof(joiner([""])))); - static assert(isForwardRange!(typeof(joiner([""])))); - assert(equal(joiner([""]), "")); - assert(equal(joiner(["", ""]), "")); - assert(equal(joiner(["", "abc"]), "abc")); - assert(equal(joiner(["abc", ""]), "abc")); - assert(equal(joiner(["abc", "def"]), "abcdef")); - assert(equal(joiner(["Mary", "has", "a", "little", "lamb"]), - "Maryhasalittlelamb")); - assert(equal(joiner(repeat("abc", 3)), "abcabcabc")); - - // joiner allows in-place mutation! + assert([""].joiner.equal("")); + assert(["", ""].joiner.equal("")); + assert(["", "abc"].joiner.equal("abc")); + assert(["abc", ""].joiner.equal("abc")); + assert(["abc", "def"].joiner.equal("abcdef")); + assert(["Mary", "has", "a", "little", "lamb"].joiner.equal("Maryhasalittlelamb")); + assert("abc".repeat(3).joiner.equal("abcabcabc")); +} + +/// joiner allows in-place mutation! +@safe unittest +{ + import std.algorithm.comparison : equal; auto a = [ [1, 2, 3], [42, 43] ]; auto j = joiner(a); j.front = 44; @@ -2602,14 +2879,59 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR)) assert(equal(j, [44, 2, 3, 42, 43])); } +/// insert characters fully lazily into a string +@safe pure unittest +{ + import std.algorithm.comparison : equal; + import std.range : chain, cycle, iota, only, retro, take, zip; + import std.format : format; + + static immutable number = "12345678"; + static immutable delimiter = ","; + auto formatted = number.retro + .zip(3.iota.cycle.take(number.length)) + .map!(z => chain(z[0].only, z[1] == 2 ? delimiter : null)) + .joiner + .retro; + static immutable expected = "12,345,678"; + assert(formatted.equal(expected)); +} + +@safe unittest +{ + import std.range.interfaces : inputRangeObject; + static assert(isInputRange!(typeof(joiner([""])))); + static assert(isForwardRange!(typeof(joiner([""])))); +} + +@safe unittest +{ + // Initial version of PR #6115 caused a compilation failure for + // https://github.com/BlackEdder/ggplotd/blob/d4428c08db5ffdc05dfd29690bf7da9073ea1dc5/source/ggplotd/stat.d#L562-L583 + import std.range : zip; + int[] xCoords = [1, 2, 3]; + int[] yCoords = [4, 5, 6]; + auto coords = zip(xCoords, xCoords[1..$]).map!( (xr) { + return zip(yCoords, yCoords[1..$]).map!( (yr) { + return [ + [[xr[0], xr[0], xr[1]], + [yr[0], yr[1], yr[1]]], + [[xr[0], xr[1], xr[1]], + [yr[0], yr[0], yr[1]]] + ]; + }).joiner; + }).joiner; +} @system unittest { import std.algorithm.comparison : equal; import std.range.interfaces : inputRangeObject; + import std.range : retro; // bugzilla 8240 assert(equal(joiner([inputRangeObject("")]), "")); + assert(equal(joiner([inputRangeObject("")]).retro, "")); // issue 8792 auto b = [[1], [2], [3]]; @@ -2626,6 +2948,109 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR)) assert(!equal(js2, js)); } +/// joiner can be bidirectional +@safe unittest +{ + import std.algorithm.comparison : equal; + import std.range : retro; + + auto a = [[1, 2, 3], [4, 5]]; + auto j = a.joiner; + j.back = 44; + assert(a == [[1, 2, 3], [4, 44]]); + assert(equal(j.retro, [44, 4, 3, 2, 1])); +} + +// bidirectional joiner: test for filtering empty elements +@safe unittest +{ + import std.algorithm.comparison : equal; + import std.range : retro; + + alias El = (e) => new int(e); + auto a = [null, [null, El(1), null, El(2), null, El(3), null], null, [null, El(4), null, El(5), null]]; + auto j = a.joiner; + + alias deref = a => a is null ? -1 : *a; + auto expected = [-1, 5, -1, 4, -1, -1, 3, -1, 2, -1, 1, -1]; + // works with .save. + assert(j.save.retro.map!deref.equal(expected)); + // and without .save + assert(j.retro.map!deref.equal(expected)); + assert(j.retro.map!deref.equal(expected)); +} + +// bidirectional joiner is @nogc +@safe @nogc unittest +{ + import std.algorithm.comparison : equal; + import std.range : iota, only, retro; + + auto a = only(iota(1, 4), iota(4, 6)); + auto j = a.joiner; + static immutable expected = [5 , 4, 3, 2, 1]; + assert(equal(j.retro, expected)); +} + +// bidirectional joiner supports assignment to the back +@safe unittest +{ + import std.algorithm.comparison : equal; + import std.range : popBackN; + + auto a = [[1, 2, 3], [4, 5]]; + auto j = a.joiner; + j.back = 55; + assert(a == [[1, 2, 3], [4, 55]]); + j.popBackN(2); + j.back = 33; + assert(a == [[1, 2, 33], [4, 55]]); +} + +// bidirectional joiner works with auto-decoding +@safe unittest +{ + import std.algorithm.comparison : equal; + import std.range : retro; + + auto a = ["😀😐", "😠"]; + auto j = a.joiner; + assert(j.retro.equal("😠😐😀")); +} + +// test two-side iteration +@safe unittest +{ + import std.algorithm.comparison : equal; + import std.range : popBackN; + + auto arrs = [ + [[1], [2], [3], [4], [5]], + [[1], [2, 3, 4], [5]], + [[1, 2, 3, 4, 5]], + ]; + foreach (arr; arrs) + { + auto a = arr.joiner; + assert(a.front == 1); + assert(a.back == 5); + a.popFront; + assert(a.front == 2); + assert(a.back == 5); + a.popBack; + assert(a.front == 2); + assert(a.back == 4); + a.popFront; + assert(a.front == 3); + assert(a.back == 4); + a.popBack; + assert(a.front == 3); + assert(a.back == 3); + a.popBack; + assert(a.empty); + } +} + @safe unittest { import std.algorithm.comparison : equal; @@ -2745,17 +3170,23 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR)) { return element; } + alias back = front; enum empty = false; - void popFront() + auto save() { + return this; } + void popFront() {} + alias popBack = popFront; + @property void front(int newValue) { element = newValue; } + alias back = front; } static assert(isInputRange!AssignableRange); @@ -2765,17 +3196,30 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR)) auto range = new AssignableRange(); assert(range.element == 0); + { + auto joined = joiner(repeat(range)); + joined.front = 5; + assert(range.element == 5); + assert(joined.front == 5); - auto joined = joiner(repeat(range)); - joined.front = 5; - assert(range.element == 5); - assert(joined.front == 5); + joined.popFront; + int byRef = 7; + joined.front = byRef; + assert(range.element == byRef); + assert(joined.front == byRef); + } + { + auto joined = joiner(repeat(range)); + joined.back = 5; + assert(range.element == 5); + assert(joined.back == 5); - joined.popFront; - int byRef = 7; - joined.front = byRef; - assert(range.element == byRef); - assert(joined.front == byRef); + joined.popBack; + int byRef = 7; + joined.back = byRef; + assert(range.element == byRef); + assert(joined.back == byRef); + } } /++ diff --git a/libphobos/src/std/algorithm/mutation.d b/libphobos/src/std/algorithm/mutation.d index 5d827c280..146ac7c81 100644 --- a/libphobos/src/std/algorithm/mutation.d +++ b/libphobos/src/std/algorithm/mutation.d @@ -415,7 +415,8 @@ if (!areCopyCompatibleArrays!(SourceRange, TargetRange) && } else { - put(target, source); + foreach (element; source) + put(target, element); return target; } } @@ -536,6 +537,24 @@ $(LINK2 http://en.cppreference.com/w/cpp/algorithm/copy_backward, STL's `copy_ba }} } +@safe unittest // issue 18804 +{ + static struct NullSink + { + void put(E)(E) {} + } + int line = 0; + struct R + { + int front; + @property bool empty() { return line == 1; } + void popFront() { line = 1; } + } + R r; + copy(r, NullSink()); + assert(line == 1); +} + /** Assigns `value` to each element of input range `range`. diff --git a/libphobos/src/std/algorithm/searching.d b/libphobos/src/std/algorithm/searching.d index 8aba54852..80c712227 100644 --- a/libphobos/src/std/algorithm/searching.d +++ b/libphobos/src/std/algorithm/searching.d @@ -4005,62 +4005,126 @@ if (isInputRange!Range && !isInfinite!Range && } /** -Skip over the initial portion of the first given range that matches the second -range, or if no second range is given skip over the elements that fullfil pred. +Skip over the initial portion of the first given range (`haystack`) that matches +any of the additionally given ranges (`needles`) fully, or +if no second range is given skip over the elements that fulfill pred. Do nothing if there is no match. Params: pred = The predicate that determines whether elements from each respective range match. Defaults to equality `"a == b"`. */ -template skipOver(alias pred = "a == b") +template skipOver(alias pred = (a, b) => a == b) { + import std.meta : allSatisfy; + + enum bool isPredComparable(T) = ifTestable!(T, binaryFun!pred); + /** - r1 = The $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to - move forward. - r2 = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - representing the initial segment of `r1` to skip over. - e = The element to match. + Params: + haystack = The $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to + move forward. + needles = The $(REF_ALTTEXT input ranges, isInputRange, std,range,primitives) + representing the prefix of `r1` to skip over. + es = The element to match. Returns: - true if the initial segment of `r1` matches `r2` or `pred` evaluates to true, - and `r1` has been advanced to the point past this segment; otherwise false, and - `r1` is left in its original position. + `true` if the prefix of `haystack` matches any range of `needles` fully + or `pred` evaluates to true, and `haystack` has been advanced to the point past this segment; + otherwise false, and `haystack` is left in its original position. + + Note: + By definition, empty ranges are matched fully and if `needles` contains an empty range, + `skipOver` will return `true`. */ - bool skipOver(R1, R2)(ref R1 r1, R2 r2) - if (is(typeof(binaryFun!pred(r1.front, r2.front))) && - isForwardRange!R1 && - isInputRange!R2) + bool skipOver(Haystack, Needles...)(ref Haystack haystack, Needles needles) + if (is(typeof(binaryFun!pred(haystack.front, needles[0].front))) && + isForwardRange!Haystack && + allSatisfy!(isInputRange, Needles) && + !is(CommonType!(staticMap!(ElementType, staticMap!(Unqual, Needles))) == void)) { - static if (is(typeof(pred) : string) && pred == "a == b" - && is(typeof(r1[0 .. $] == r2) : bool) - && is(typeof(r2.length > r1.length) : bool) - && is(typeof(r1 = r1[r2.length .. $]))) + static if (__traits(isSame, pred, (a, b) => a == b) + && is(typeof(haystack[0 .. $] == needles[0]) : bool) + && is(typeof(haystack = haystack[0 .. $])) + && hasLength!Haystack && allSatisfy!(hasLength, Needles)) { - if (r2.length > r1.length || r1[0 .. r2.length] != r2) + ptrdiff_t longestMatch = -1; + static foreach (r2; needles) { - return false; + if (r2.length <= haystack.length && longestMatch < ptrdiff_t(r2.length) + && (haystack[0 .. r2.length] == r2 || r2.length == 0)) + longestMatch = r2.length; } - r1 = r1[r2.length .. $]; - return true; + if (longestMatch >= 0) + { + if (longestMatch > 0) + haystack = haystack[longestMatch .. $]; + + return true; + } + return false; } else { - static if (hasLength!R1 && hasLength!R2) + import std.algorithm.comparison : min; + auto r = haystack.save; + + static if (hasLength!Haystack && allSatisfy!(hasLength, Needles)) { + import std.algorithm.iteration : map; + import std.algorithm.searching : minElement; + import std.range : only; // Shortcut opportunity! - if (r2.length > r1.length) + if (needles.only.map!(a => a.length).minElement > haystack.length) return false; } - auto r = r1.save; - while (!r2.empty && !r.empty && binaryFun!pred(r.front, r2.front)) + + // compatibility: return true if any range was empty + bool hasEmptyRanges; + static foreach (i, r2; needles) { - r.popFront(); - r2.popFront(); + if (r2.empty) + hasEmptyRanges = true; } - if (r2.empty) - r1 = r; - return r2.empty; + + bool hasNeedleMatch; + size_t inactiveNeedlesLen; + bool[Needles.length] inactiveNeedles; + for (; !r.empty; r.popFront) + { + static foreach (i, r2; needles) + { + if (!r2.empty && !inactiveNeedles[i]) + { + if (binaryFun!pred(r.front, r2.front)) + { + r2.popFront; + if (r2.empty) + { + // we skipped over a new match + hasNeedleMatch = true; + inactiveNeedlesLen++; + // skip over haystack + haystack = r; + } + } + else + { + inactiveNeedles[i] = true; + inactiveNeedlesLen++; + } + } + } + + // are we done? + if (inactiveNeedlesLen == needles.length) + break; + } + + if (hasNeedleMatch) + haystack.popFront; + + return hasNeedleMatch || hasEmptyRanges; } } @@ -4079,13 +4143,21 @@ template skipOver(alias pred = "a == b") } /// Ditto - bool skipOver(R, E)(ref R r, E e) - if (is(typeof(binaryFun!pred(r.front, e))) && isInputRange!R) + bool skipOver(R, Es...)(ref R r, Es es) + if (isInputRange!R && is(typeof(binaryFun!pred(r.front, es[0])))) { - if (r.empty || !binaryFun!pred(r.front, e)) + if (r.empty) return false; - r.popFront(); - return true; + + static foreach (e; es) + { + if (binaryFun!pred(r.front, e)) + { + r.popFront(); + return true; + } + } + return false; } } @@ -4097,15 +4169,19 @@ template skipOver(alias pred = "a == b") auto s1 = "Hello world"; assert(!skipOver(s1, "Ha")); assert(s1 == "Hello world"); - assert(skipOver(s1, "Hell") && s1 == "o world"); + assert(skipOver(s1, "Hell") && s1 == "o world", s1); string[] r1 = ["abc", "def", "hij"]; dstring[] r2 = ["abc"d]; - assert(!skipOver!((a, b) => a.equal(b))(r1, ["def"d])); + assert(!skipOver!((a, b) => a.equal(b))(r1, ["def"d]), r1[0]); assert(r1 == ["abc", "def", "hij"]); assert(skipOver!((a, b) => a.equal(b))(r1, r2)); assert(r1 == ["def", "hij"]); +} +/// +@safe unittest +{ import std.ascii : isWhite; import std.range.primitives : empty; @@ -4117,6 +4193,18 @@ template skipOver(alias pred = "a == b") assert(s4.skipOver!isWhite && s3.empty); } +/// Variadic skipOver +@safe unittest +{ + auto s = "Hello world"; + assert(!skipOver(s, "hello", "HellO")); + assert(s == "Hello world"); + + // the range is skipped over the longest matching needle is skipped + assert(skipOver(s, "foo", "hell", "Hello ")); + assert(s == "world"); +} + /// @safe unittest { @@ -4154,6 +4242,133 @@ template skipOver(alias pred = "a == b") assert(whitespaceSkiper(s4) && s3.empty); } +// variadic skipOver +@safe unittest +{ + auto s = "DLang.rocks"; + assert(!s.skipOver("dlang", "DLF", "DLang ")); + assert(s == "DLang.rocks"); + + assert(s.skipOver("dlang", "DLANG", "DLF", "D", "DL", "DLanpp")); + assert(s == "ang.rocks"); + s = "DLang.rocks"; + + assert(s.skipOver("DLang", "DLANG", "DLF", "D", "DL", "DLang ")); + assert(s == ".rocks"); + s = "DLang.rocks"; + + assert(s.skipOver("dlang", "DLANG", "DLF", "D", "DL", "DLang.")); + assert(s == "rocks"); +} + +// variadic with custom pred +@safe unittest +{ + import std.ascii : toLower; + + auto s = "DLang.rocks"; + assert(!s.skipOver("dlang", "DLF", "DLang ")); + assert(s == "DLang.rocks"); + + assert(s.skipOver!((a, b) => a.toLower == b.toLower)("dlang", "DLF", "DLang ")); + assert(s == ".rocks"); +} + +// variadic skipOver with mixed needles +@safe unittest +{ + auto s = "DLang.rocks"; + assert(!s.skipOver("dlang"d, "DLF", "DLang "w)); + assert(s == "DLang.rocks"); + + assert(s.skipOver("dlang", "DLANG"d, "DLF"w, "D"d, "DL", "DLanp")); + assert(s == "ang.rocks"); + s = "DLang.rocks"; + + assert(s.skipOver("DLang", "DLANG"w, "DLF"d, "D"d, "DL", "DLang ")); + assert(s == ".rocks"); + s = "DLang.rocks"; + + assert(s.skipOver("dlang", "DLANG"w, "DLF", "D"d, "DL"w, "DLang."d)); + assert(s == "rocks"); + + import std.algorithm.iteration : filter; + s = "DLang.rocks"; + assert(s.skipOver("dlang", "DLang".filter!(a => true))); + assert(s == ".rocks"); +} + +// variadic skipOver with auto-decoding +@safe unittest +{ + auto s = "☢☣☠.☺"; + assert(s.skipOver("a", "☢", "☢☣☠")); + assert(s == ".☺"); +} + +// skipOver with @nogc +@safe @nogc pure nothrow unittest +{ + static immutable s = [0, 1, 2]; + immutable(int)[] s2 = s[]; + + static immutable skip1 = [0, 2]; + static immutable skip2 = [0, 1]; + assert(s2.skipOver(skip1, skip2)); + assert(s2 == s[2 .. $]); +} + +// variadic skipOver with single elements +@safe unittest +{ + auto s = "DLang.rocks"; + assert(!s.skipOver('a', 'd', 'e')); + assert(s == "DLang.rocks"); + + assert(s.skipOver('a', 'D', 'd', 'D')); + assert(s == "Lang.rocks"); + s = "DLang.rocks"; + + assert(s.skipOver(wchar('a'), dchar('D'), 'd')); + assert(s == "Lang.rocks"); + + dstring dstr = "+Foo"; + assert(!dstr.skipOver('.', '-')); + assert(dstr == "+Foo"); + + assert(dstr.skipOver('+', '-')); + assert(dstr == "Foo"); +} + +// skipOver with empty ranges must return true (compatibility) +@safe unittest +{ + auto s = "DLang.rocks"; + assert(s.skipOver("")); + assert(s.skipOver("", "")); + assert(s.skipOver("", "foo")); + + auto s2 = "DLang.rocks"d; + assert(s2.skipOver("")); + assert(s2.skipOver("", "")); + assert(s2.skipOver("", "foo")); +} + +// dxml regression +@safe unittest +{ + import std.utf : byCodeUnit; + import std.algorithm.comparison : equal; + + bool stripStartsWith(Text)(ref Text text, string needle) + { + return text.skipOver(needle.byCodeUnit()); + } + auto text = ""d.byCodeUnit; + assert(stripStartsWith(text, "")); + assert(text.equal("")); +} + /** Checks whether the given $(REF_ALTTEXT input range, isInputRange, std,range,primitives) starts with (one diff --git a/libphobos/src/std/algorithm/sorting.d b/libphobos/src/std/algorithm/sorting.d index 089dd31a7..1a4ca5f6d 100644 --- a/libphobos/src/std/algorithm/sorting.d +++ b/libphobos/src/std/algorithm/sorting.d @@ -583,8 +583,8 @@ $(LI All elements `e` in subrange `r[0 .. k]` satisfy `!less(r[k], e)` (i.e. `r[k]` is greater than or equal to each element to its left according to predicate `less`)) -$(LI All elements `e` in subrange `r[0 .. k]` satisfy `!less(e$D( -r[k])) (i.e. `r[k]` is less than or equal to each element to its right +$(LI All elements `e` in subrange `r[k .. $]` satisfy `!less(e, r[k])` +(i.e. `r[k]` is less than or equal to each element to its right according to predicate `less`))) If `r` contains equivalent elements, multiple permutations of `r` satisfy these diff --git a/libphobos/src/std/array.d b/libphobos/src/std/array.d index ee8d12cad..b2ddd0a3c 100644 --- a/libphobos/src/std/array.d +++ b/libphobos/src/std/array.d @@ -219,6 +219,34 @@ if (isPointer!Range && isIterable!(PointerTarget!Range) && !isNarrowString!Range assert(a.length == 5); } +version(unittest) + private extern(C) void _d_delarray_t(void[] *p, TypeInfo_Struct ti); + +@system unittest +{ + // Issue 18995 + int nAlive = 0; + struct S + { + bool alive; + this(int) { alive = true; ++nAlive; } + this(this) { nAlive += alive; } + ~this() { nAlive -= alive; alive = false; } + } + + import std.algorithm.iteration : map; + import std.range : iota; + + auto arr = iota(3).map!(a => S(a)).array; + assert(nAlive == 3); + + // No good way to ensure the GC frees this, just call the lifetime function + // directly. If delete wasn't deprecated, this is what delete would do. + _d_delarray_t(cast(void[]*)&arr, typeid(S)); + + assert(nAlive == 0); +} + /** Convert a narrow string to an array type that fully supports random access. This is handled as a special case and always returns an array of `dchar` @@ -698,6 +726,9 @@ if (isDynamicArray!T && allSatisfy!(isIntegral, I)) } } +// from rt/lifetime.d +private extern(C) void[] _d_newarrayU(const TypeInfo ti, size_t length) pure nothrow; + private auto arrayAllocImpl(bool minimallyInitialized, T, I...)(I sizes) nothrow { static assert(I.length <= nDimensions!T, @@ -737,18 +768,24 @@ private auto arrayAllocImpl(bool minimallyInitialized, T, I...)(I sizes) nothrow } else { - import core.memory : GC; import core.stdc.string : memset; - import core.checkedint : mulu; - bool overflow; - const nbytes = mulu(size, E.sizeof, overflow); - if (overflow) assert(0); - - auto ptr = cast(E*) GC.malloc(nbytes, blockAttribute!E); + /+ + NOTES: + _d_newarrayU is part of druntime, and creates an uninitialized + block, just like GC.malloc. However, it also sets the appropriate + bits, and sets up the block as an appendable array of type E[], + which will inform the GC how to destroy the items in the block + when it gets collected. + + _d_newarrayU returns a void[], but with the length set according + to E.sizeof. + +/ + *(cast(void[]*)&ret) = _d_newarrayU(typeid(E[]), size); static if (minimallyInitialized && hasIndirections!E) - memset(ptr, 0, nbytes); - ret = ptr[0 .. size]; + // _d_newarrayU would have asserted if the multiplication below + // had overflowed, so we don't have to check it again. + memset(ret.ptr, 0, E.sizeof * ret.length); } } else static if (I.length > 1) @@ -796,7 +833,15 @@ private auto arrayAllocImpl(bool minimallyInitialized, T, I...)(I sizes) nothrow } ~this() { - assert(p != null); + // note, this assert is invalid -- a struct should always be able + // to run its dtor on the .init value, I'm leaving it here + // commented out because the original test case had it. I'm not + // sure what it's trying to prove. + // + // What happens now that minimallyInitializedArray adds the + // destructor run to the GC, is that this assert would fire in the + // GC, which triggers an invalid memory operation. + //assert(p != null); } } auto a = minimallyInitializedArray!(S[])(1); @@ -3988,3 +4033,270 @@ unittest assert(appS.data == "hellow"); assert(appA.data == "hellow"); } + +/++ +Constructs a static array from `a`. +The type of elements can be specified implicitly so that $(D [1, 2].staticArray) results in `int[2]`, +or explicitly, e.g. $(D [1, 2].staticArray!float) returns `float[2]`. +When `a` is a range whose length is not known at compile time, the number of elements must be +given as template argument (e.g. `myrange.staticArray!2`). +Size and type can be combined, if the source range elements are implicitly +convertible to the requested element type (eg: `2.iota.staticArray!(long[2])`). +When the range `a` is known at compile time, it can also be specified as a +template argument to avoid having to specify the number of elements +(e.g.: `staticArray!(2.iota)` or `staticArray!(double, 2.iota)`). + +Note: `staticArray` returns by value, so expressions involving large arrays may be inefficient. + +Params: + a = The input elements. If there are less elements than the specified length of the static array, + the rest of it is default-initialized. If there are more than specified, the first elements + up to the specified length are used. + rangeLength = outputs the number of elements used from `a` to it. Optional. + +Returns: A static array constructed from `a`. ++/ +pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a) +{ + return a; +} + +/// static array from array literal +nothrow pure @safe unittest +{ + auto a = [0, 1].staticArray; + static assert(is(typeof(a) == int[2])); + assert(a == [0, 1]); +} + +pragma(inline, true) U[n] staticArray(U, T, size_t n)(auto ref T[n] a) +if (!is(T == U) && is(T : U)) +{ + return a[].staticArray!(U[n]); +} + +/// static array from array with implicit casting of elements +nothrow pure @safe unittest +{ + auto b = [0, 1].staticArray!long; + static assert(is(typeof(b) == long[2])); + assert(b == [0, 1]); +} + +nothrow pure @safe unittest +{ + int val = 3; + static immutable gold = [1, 2, 3]; + [1, 2, val].staticArray.checkStaticArray!int([1, 2, 3]); + + @nogc void checkNogc() + { + [1, 2, val].staticArray.checkStaticArray!int(gold); + } + + checkNogc(); + + [1, 2, val].staticArray!double.checkStaticArray!double(gold); + [1, 2, 3].staticArray!int.checkStaticArray!int(gold); + + [1, 2, 3].staticArray!(const(int)).checkStaticArray!(const(int))(gold); + [1, 2, 3].staticArray!(const(double)).checkStaticArray!(const(double))(gold); + { + const(int)[3] a2 = [1, 2, 3].staticArray; + } + + [cast(byte) 1, cast(byte) 129].staticArray.checkStaticArray!byte([1, -127]); +} + +/// ditto +auto staticArray(size_t n, T)(scope T a) +if (isInputRange!T) +{ + alias U = ElementType!T; + return staticArray!(U[n], U, n)(a); +} + +/// ditto +auto staticArray(size_t n, T)(scope T a, out size_t rangeLength) +if (isInputRange!T) +{ + alias U = ElementType!T; + return staticArray!(U[n], U, n)(a, rangeLength); +} + +/// ditto +auto staticArray(Un : U[n], U, size_t n, T)(scope T a) +if (isInputRange!T && is(ElementType!T : U)) +{ + size_t extraStackSpace; + return staticArray!(Un, U, n)(a, extraStackSpace); +} + +/// ditto +auto staticArray(Un : U[n], U, size_t n, T)(scope T a, out size_t rangeLength) +if (isInputRange!T && is(ElementType!T : U)) +{ + import std.algorithm.mutation : uninitializedFill; + import std.range : take; + import std.conv : emplaceRef; + + if (__ctfe) + { + size_t i; + // Compile-time version to avoid unchecked memory access. + Unqual!U[n] ret; + for (auto iter = a.take(n); !iter.empty; iter.popFront()) + { + ret[i] = iter.front; + i++; + } + + rangeLength = i; + return (() @trusted => cast(U[n]) ret)(); + } + + auto ret = (() @trusted + { + Unqual!U[n] theArray = void; + return theArray; + }()); + + size_t i; + if (true) + { + // ret was void-initialized so let's initialize the unfilled part manually. + // also prevents destructors to be called on uninitialized memory if + // an exception is thrown + scope (exit) ret[i .. $].uninitializedFill(U.init); + + for (auto iter = a.take(n); !iter.empty; iter.popFront()) + { + emplaceRef!U(ret[i++], iter.front); + } + } + + rangeLength = i; + return (() @trusted => cast(U[n]) ret)(); +} + +/// static array from range + size +nothrow pure @safe unittest +{ + import std.range : iota; + + auto input = 3.iota; + auto a = input.staticArray!2; + static assert(is(typeof(a) == int[2])); + assert(a == [0, 1]); + auto b = input.staticArray!(long[4]); + static assert(is(typeof(b) == long[4])); + assert(b == [0, 1, 2, 0]); +} + +// Tests that code compiles when there is an elaborate destructor and exceptions +// are thrown. Unfortunately can't test that memory is initialized +// before having a destructor called on it. +// @system required because of issue 18872. +@system nothrow unittest +{ + // exists only to allow doing something in the destructor. Not tested + // at the end because value appears to depend on implementation of the. + // function. + static int preventersDestroyed = 0; + + static struct CopyPreventer + { + bool on = false; + this(this) + { + if (on) throw new Exception("Thou shalt not copy past me!"); + } + + ~this() + { + preventersDestroyed++; + } + } + auto normalArray = + [ + CopyPreventer(false), + CopyPreventer(false), + CopyPreventer(true), + CopyPreventer(false), + CopyPreventer(true), + ]; + + try + { + auto staticArray = normalArray.staticArray!5; + assert(false); + } + catch (Exception e){} +} + + +nothrow pure @safe unittest +{ + auto a = [1, 2].staticArray; + assert(is(typeof(a) == int[2]) && a == [1, 2]); + + import std.range : iota; + + 2.iota.staticArray!2.checkStaticArray!int([0, 1]); + 2.iota.staticArray!(double[2]).checkStaticArray!double([0, 1]); + 2.iota.staticArray!(long[2]).checkStaticArray!long([0, 1]); +} + +nothrow pure @system unittest +{ + import std.range : iota; + size_t copiedAmount; + 2.iota.staticArray!1(copiedAmount); + assert(copiedAmount == 1); + 2.iota.staticArray!3(copiedAmount); + assert(copiedAmount == 2); +} + +/// ditto +auto staticArray(alias a)() +if (isInputRange!(typeof(a))) +{ + return .staticArray!(size_t(a.length))(a); +} + +/// ditto +auto staticArray(U, alias a)() +if (isInputRange!(typeof(a))) +{ + return .staticArray!(U[size_t(a.length)])(a); +} + +/// static array from CT range +nothrow pure @safe unittest +{ + import std.range : iota; + + enum a = staticArray!(2.iota); + static assert(is(typeof(a) == int[2])); + assert(a == [0, 1]); + + enum b = staticArray!(long, 2.iota); + static assert(is(typeof(b) == long[2])); + assert(b == [0, 1]); +} + +nothrow pure @safe unittest +{ + import std.range : iota; + + enum a = staticArray!(2.iota); + staticArray!(2.iota).checkStaticArray!int([0, 1]); + staticArray!(double, 2.iota).checkStaticArray!double([0, 1]); + staticArray!(long, 2.iota).checkStaticArray!long([0, 1]); +} + +version(unittest) private void checkStaticArray(T, T1, T2)(T1 a, T2 b) nothrow @safe pure @nogc +{ + static assert(is(T1 == T[T1.length])); + assert(a == b); +} diff --git a/libphobos/src/std/base64.d b/libphobos/src/std/base64.d index 9279dd6ed..9b83bdb95 100644 --- a/libphobos/src/std/base64.d +++ b/libphobos/src/std/base64.d @@ -391,7 +391,7 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * The number of times the output range's `put` method was invoked. */ size_t encode(E, R)(scope const(E)[] source, auto ref R range) - if (is(E : ubyte) && isOutputRange!(R, char)) + if (is(E : ubyte) && isOutputRange!(R, char) && !is(R == char[])) out(result) { assert(result == encodeLength(source.length), "The number of put is different from the length of Base64"); diff --git a/libphobos/src/std/complex.d b/libphobos/src/std/complex.d index e87403ad7..679861d62 100644 --- a/libphobos/src/std/complex.d +++ b/libphobos/src/std/complex.d @@ -34,7 +34,7 @@ import std.traits; be `Complex!double`. Otherwise, the return type is deduced using $(D std.traits.CommonType!(R, I)). */ -auto complex(R)(R re) @safe pure nothrow @nogc +auto complex(R)(const R re) @safe pure nothrow @nogc if (is(R : double)) { static if (isFloatingPoint!R) @@ -44,7 +44,7 @@ if (is(R : double)) } /// ditto -auto complex(R, I)(R re, I im) @safe pure nothrow @nogc +auto complex(R, I)(const R re, const I im) @safe pure nothrow @nogc if (is(R : double) && is(I : double)) { static if (isFloatingPoint!R || isFloatingPoint!I) @@ -173,14 +173,14 @@ if (isFloatingPoint!T) } /// ditto - this(Rx : T, Ry : T)(Rx x, Ry y) + this(Rx : T, Ry : T)(const Rx x, const Ry y) { re = x; im = y; } /// ditto - this(R : T)(R r) + this(R : T)(const R r) { re = r; im = 0; @@ -197,7 +197,7 @@ if (isFloatingPoint!T) } // this = numeric - ref Complex opAssign(R : T)(R r) + ref Complex opAssign(R : T)(const R r) { re = r; im = 0; @@ -213,7 +213,7 @@ if (isFloatingPoint!T) } // this == numeric - bool opEquals(R : T)(R r) const + bool opEquals(R : T)(const R r) const { return re == r && im == 0; } @@ -245,7 +245,7 @@ if (isFloatingPoint!T) } // complex op numeric - Complex!(CommonType!(T,R)) opBinary(string op, R)(R r) const + Complex!(CommonType!(T,R)) opBinary(string op, R)(const R r) const if (isNumeric!R) { alias C = typeof(return); @@ -254,21 +254,21 @@ if (isFloatingPoint!T) } // numeric + complex, numeric * complex - Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R r) const + Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(const R r) const if ((op == "+" || op == "*") && (isNumeric!R)) { return opBinary!(op)(r); } // numeric - complex - Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R r) const + Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(const R r) const if (op == "-" && isNumeric!R) { return Complex(r - re, -im); } // numeric / complex - Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R r) const + Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(const R r) const if (op == "/" && isNumeric!R) { import std.math : fabs; @@ -294,7 +294,7 @@ if (isFloatingPoint!T) } // numeric ^^ complex - Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R lhs) const + Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(const R lhs) const if (op == "^^" && isNumeric!R) { import std.math : cos, exp, log, sin, PI; @@ -321,7 +321,7 @@ if (isFloatingPoint!T) // OP-ASSIGN OPERATORS // complex += complex, complex -= complex - ref Complex opOpAssign(string op, C)(C z) + ref Complex opOpAssign(string op, C)(const C z) if ((op == "+" || op == "-") && is(C R == Complex!R)) { mixin ("re "~op~"= z.re;"); @@ -330,7 +330,7 @@ if (isFloatingPoint!T) } // complex *= complex - ref Complex opOpAssign(string op, C)(C z) + ref Complex opOpAssign(string op, C)(const C z) if (op == "*" && is(C R == Complex!R)) { auto temp = re*z.re - im*z.im; @@ -340,7 +340,7 @@ if (isFloatingPoint!T) } // complex /= complex - ref Complex opOpAssign(string op, C)(C z) + ref Complex opOpAssign(string op, C)(const C z) if (op == "/" && is(C R == Complex!R)) { import std.math : fabs; @@ -366,7 +366,7 @@ if (isFloatingPoint!T) } // complex ^^= complex - ref Complex opOpAssign(string op, C)(C z) + ref Complex opOpAssign(string op, C)(const C z) if (op == "^^" && is(C R == Complex!R)) { import std.math : exp, log, cos, sin; @@ -381,7 +381,7 @@ if (isFloatingPoint!T) } // complex += numeric, complex -= numeric - ref Complex opOpAssign(string op, U : T)(U a) + ref Complex opOpAssign(string op, U : T)(const U a) if (op == "+" || op == "-") { mixin ("re "~op~"= a;"); @@ -389,7 +389,7 @@ if (isFloatingPoint!T) } // complex *= numeric, complex /= numeric - ref Complex opOpAssign(string op, U : T)(U a) + ref Complex opOpAssign(string op, U : T)(const U a) if (op == "*" || op == "/") { mixin ("re "~op~"= a;"); @@ -398,7 +398,7 @@ if (isFloatingPoint!T) } // complex ^^= real - ref Complex opOpAssign(string op, R)(R r) + ref Complex opOpAssign(string op, R)(const R r) if (op == "^^" && isFloatingPoint!R) { import std.math : cos, sin; @@ -410,7 +410,7 @@ if (isFloatingPoint!T) } // complex ^^= int - ref Complex opOpAssign(string op, U)(U i) + ref Complex opOpAssign(string op, U)(const U i) if (op == "^^" && isIntegral!U) { switch (i) @@ -735,7 +735,7 @@ T sqAbs(T)(Complex!T z) @safe pure nothrow @nogc } /// ditto -T sqAbs(T)(T x) @safe pure nothrow @nogc +T sqAbs(T)(const T x) @safe pure nothrow @nogc if (isFloatingPoint!T) { return x*x; @@ -795,7 +795,7 @@ Complex!T conj(T)(Complex!T z) @safe pure nothrow @nogc argument = The argument Returns: The complex number with the given modulus and argument. */ -Complex!(CommonType!(T, U)) fromPolar(T, U)(T modulus, U argument) +Complex!(CommonType!(T, U)) fromPolar(T, U)(const T modulus, const U argument) @safe pure nothrow @nogc { import std.math : sin, cos; diff --git a/libphobos/src/std/container/array.d b/libphobos/src/std/container/array.d index c3d19145d..980e452a4 100644 --- a/libphobos/src/std/container/array.d +++ b/libphobos/src/std/container/array.d @@ -1664,8 +1664,9 @@ if (is(Unqual!T == bool)) /// ditto Range opSlice(size_t low, size_t high) { + // Note: indexes start at 0, which is equivalent to _a assert( - _a <= low && low <= high && high <= _b, + low <= high && high <= (_b - _a), "Using out of bounds indexes on an Array" ); return Range(_outer, _a + low, _a + high); @@ -2178,6 +2179,7 @@ if (is(Unqual!T == bool)) slice.front = true; slice.back = true; slice[1] = true; + slice = slice[0 .. $]; // bug 19171 assert(slice.front == true); assert(slice.back == true); assert(slice[1] == true); diff --git a/libphobos/src/std/container/package.d b/libphobos/src/std/container/package.d index 2e110ac68..5a2065580 100644 --- a/libphobos/src/std/container/package.d +++ b/libphobos/src/std/container/package.d @@ -492,11 +492,6 @@ $(TR $(TD Removes an element from `c` by using its key `k`. The key's type is defined by the container.) ) -$(TR - $(TDNW ``) - $(TDNW ``) - $(TD ) -) ) Source: $(PHOBOSSRC std/container/package.d) diff --git a/libphobos/src/std/conv.d b/libphobos/src/std/conv.d index ad4b80f73..d952ae597 100644 --- a/libphobos/src/std/conv.d +++ b/libphobos/src/std/conv.d @@ -2730,7 +2730,7 @@ if (isSomeString!Source && !is(Source == enum) && * A floating point number of type `Target` * * Throws: - * A $(LREF ConvException) if `p` is empty, if no number could be + * A $(LREF ConvException) if `source` is empty, if no number could be * parsed, or if an overflow occurred. */ Target parse(Target, Source)(ref Source source) @@ -2761,7 +2761,7 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum { if (msg == null) msg = "Floating point conversion error"; - return new ConvException(text(msg, " for input \"", p, "\"."), fn, ln); + return new ConvException(text(msg, " for input \"", source, "\"."), fn, ln); } @@ -2776,29 +2776,24 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum enforce(!p.empty, bailOut()); if (toLower(p.front) == 'i') goto case 'i'; - enforce(!p.empty, bailOut()); break; case '+': p.popFront(); enforce(!p.empty, bailOut()); break; case 'i': case 'I': + // inf p.popFront(); - enforce(!p.empty, bailOut()); - if (toLower(p.front) == 'n') - { - p.popFront(); - enforce(!p.empty, bailOut()); - if (toLower(p.front) == 'f') - { - // 'inf' - p.popFront(); - static if (isNarrowString!Source) - source = cast(Source) p; - return sign ? -Target.infinity : Target.infinity; - } - } - goto default; + enforce(!p.empty && toUpper(p.front) == 'N', + bailOut("error converting input to floating point")); + p.popFront(); + enforce(!p.empty && toUpper(p.front) == 'F', + bailOut("error converting input to floating point")); + // skip past the last 'f' + p.popFront(); + static if (isNarrowString!Source) + source = cast(Source) p; + return sign ? -Target.infinity : Target.infinity; default: {} } @@ -2815,342 +2810,97 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum } isHex = p.front == 'x' || p.front == 'X'; + if (isHex) p.popFront(); } + else if (toLower(p.front) == 'n') + { + // nan + p.popFront(); + enforce(!p.empty && toUpper(p.front) == 'A', + bailOut("error converting input to floating point")); + p.popFront(); + enforce(!p.empty && toUpper(p.front) == 'N', + bailOut("error converting input to floating point")); + // skip past the last 'n' + p.popFront(); + static if (isNarrowString!Source) + source = cast(Source) p; + return typeof(return).nan; + } + + /* + * The following algorithm consists of 2 steps: + * 1) parseDigits processes the textual input into msdec and possibly + * lsdec/msscale variables, followed by the exponent parser which sets + * exp below. + * Hex: input is 0xaaaaa...p+000... where aaaa is the mantissa in hex + * and 000 is the exponent in decimal format with base 2. + * Decimal: input is 0.00333...p+000... where 0.0033 is the mantissa + * in decimal and 000 is the exponent in decimal format with base 10. + * 2) Convert msdec/lsdec and exp into native real format + */ real ldval = 0.0; char dot = 0; /* if decimal point has been seen */ int exp = 0; - long msdec = 0, lsdec = 0; + ulong msdec = 0, lsdec = 0; ulong msscale = 1; + bool sawDigits; - if (isHex) - { - /* - * The following algorithm consists of mainly 3 steps (and maybe should - * be refactored into functions accordingly) - * 1) Parse the textual input into msdec and exp variables: - * input is 0xaaaaa...p+000... where aaaa is the mantissa in hex and - * 000 is the exponent in decimal format. - * 2) Rounding, ... - * 3) Convert msdec and exp into native real format - */ - - int guard = 0; - // Used to enforce that any mantissa digits are present - bool anydigits = false; - // Number of mantissa digits (digit: base 16) we have processed, - // ignoring leading 0s - uint ndigits = 0; - - p.popFront(); - while (!p.empty) - { - int i = p.front; - while (isHexDigit(i)) - { - anydigits = true; - /* - * convert letter to binary representation: First clear bit - * to convert lower space chars to upperspace, then -('A'-10) - * converts letter A to 10, letter B to 11, ... - */ - i = isAlpha(i) ? ((i & ~0x20) - ('A' - 10)) : i - '0'; - // 16*4 = 64: The max we can store in a long value - if (ndigits < 16) - { - // base 16: Y = ... + y3*16^3 + y2*16^2 + y1*16^1 + y0*16^0 - msdec = msdec * 16 + i; - // ignore leading zeros - if (msdec) - ndigits++; - } - // All 64 bits of the long have been filled in now - else if (ndigits == 16) - { - while (msdec >= 0) - { - exp--; - msdec <<= 1; - i <<= 1; - if (i & 0x10) - msdec |= 1; - } - guard = i << 4; - ndigits++; - exp += 4; - } - else - { - guard |= i; - exp += 4; - } - exp -= dot; - p.popFront(); - if (p.empty) - break; - i = p.front; - if (i == '_') - { - p.popFront(); - if (p.empty) - break; - i = p.front; - } - } - if (i == '.' && !dot) - { - p.popFront(); - dot = 4; - } - else - break; - } - - // Round up if (guard && (sticky || odd)) - if (guard & 0x80 && (guard & 0x7F || msdec & 1)) - { - msdec++; - if (msdec == 0) // overflow - { - msdec = 0x8000000000000000L; - exp++; - } - } - - // Have we seen any mantissa digits so far? - enforce(anydigits, bailOut()); - enforce(!p.empty && (p.front == 'p' || p.front == 'P'), - bailOut("Floating point parsing: exponent is required")); - char sexp; - int e; - - sexp = 0; - p.popFront(); - if (!p.empty) - { - switch (p.front) - { - case '-': sexp++; - goto case; - case '+': p.popFront(); enforce(!p.empty, - new ConvException("Error converting input"~ - " to floating point")); - break; - default: {} - } - } - ndigits = 0; - e = 0; - while (!p.empty && isDigit(p.front)) - { - if (e < 0x7FFFFFFF / 10 - 10) // prevent integer overflow - { - e = e * 10 + p.front - '0'; - } - p.popFront(); - ndigits = 1; - } - exp += (sexp) ? -e : e; - enforce(ndigits, new ConvException("Error converting input"~ - " to floating point")); - - import std.math : floatTraits, RealFormat; - - static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple) - { - if (msdec) - { - /* - * For quad precision, we currently allow max mantissa precision - * of 64 bits, simply so we don't have to change the mantissa parser - * in the code above. Feel free to adapt the parser to support full - * 113 bit precision. - */ - - // Exponent bias + 112: - // After shifting 112 times left, exp must be 1 - int e2 = 0x3FFF + 112; - - /* - * left justify mantissa: The implicit bit (bit 112) must be 1 - * after this, (it is implicit and always defined as 1, so making - * sure we end up with 1 at 112 means we adjust mantissa and eponent - * to fit the ieee format) - * For quadruple, this is especially fun as we have to use 2 longs - * to store the mantissa and care about endianess... - * quad_mant[0] | quad_mant[1] - * S.EEEEEEEEEEEEEEE.MMMMM .. | MMMMM .. 00000 - * 48 0 | - */ - ulong[2] quad_mant; - quad_mant[1] = msdec; - while ((quad_mant[0] & 0x0001_0000_0000_0000) == 0) - { - // Shift high part one bit left - quad_mant[0] <<= 1; - // Transfer MSB from quad_mant[1] as new LSB - quad_mant[0] |= (quad_mant[1] & 0x8000_0000_0000_0000) ? 0b1 : 0b0; - // Now shift low part one bit left - quad_mant[1] <<= 1; - // Finally, decrease the exponent, as we increased the value - // by shifting of the mantissa - e2--; - } - - ()@trusted { - ulong* msw, lsw; - version (LittleEndian) - { - lsw = &(cast(ulong*)&ldval)[0]; - msw = &(cast(ulong*)&ldval)[1]; - } - else - { - msw = &(cast(ulong*)&ldval)[0]; - lsw = &(cast(ulong*)&ldval)[1]; - } - - // Stuff mantissa directly into double - // (first including implicit bit) - *msw = quad_mant[0]; - *lsw = quad_mant[1]; - - // Store exponent, now overwriting implicit bit - *msw &= 0x0000_FFFF_FFFF_FFFF; - *msw |= ((e2 & 0xFFFFUL) << 48); - }(); + enum { hex, decimal } - import std.math : ldexp; - - // Exponent is power of 2, not power of 10 - ldval = ldexp(ldval, exp); - } - } - else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended) + // sets msdec, lsdec/msscale, and sawDigits by parsing the mantissa digits + void parseDigits(alias FloatFormat)() + { + static if (FloatFormat == hex) { - if (msdec) - { - int e2 = 0x3FFF + 63; - - // left justify mantissa - while (msdec >= 0) - { - msdec <<= 1; - e2--; - } - - // Stuff mantissa directly into real - ()@trusted{ *cast(long*)&ldval = msdec; }(); - ()@trusted{ (cast(ushort*)&ldval)[4] = cast(ushort) e2; }(); - - import std.math : ldexp; - - // Exponent is power of 2, not power of 10 - ldval = ldexp(ldval,exp); - } + enum uint base = 16; + enum ulong msscaleMax = 0x1000_0000_0000_0000UL; // largest power of 16 a ulong holds + enum ubyte expIter = 4; // iterate the base-2 exponent by 4 for every hex digit + alias checkDigit = isHexDigit; + /* + * convert letter to binary representation: First clear bit + * to convert lower space chars to upperspace, then -('A'-10) + * converts letter A to 10, letter B to 11, ... + */ + alias convertDigit = (int x) => isAlpha(x) ? ((x & ~0x20) - ('A' - 10)) : x - '0'; + sawDigits = false; } - else static if (floatTraits!real.realFormat == RealFormat.ieeeDouble) + else static if (FloatFormat == decimal) { - if (msdec) - { - // Exponent bias + 52: - // After shifting 52 times left, exp must be 1 - int e2 = 0x3FF + 52; - - // right justify mantissa - // first 11 bits must be zero, rest is implied bit + mantissa - // shift one time less, do rounding, shift again - while ((msdec & 0xFFC0_0000_0000_0000) != 0) - { - msdec = ((cast(ulong) msdec) >> 1); - e2++; - } - - // Have to shift one more time - // and do rounding - if ((msdec & 0xFFE0_0000_0000_0000) != 0) - { - auto roundUp = (msdec & 0x1); - - msdec = ((cast(ulong) msdec) >> 1); - e2++; - if (roundUp) - { - msdec += 1; - // If mantissa was 0b1111... and we added +1 - // the mantissa should be 0b10000 (think of implicit bit) - // and the exponent increased - if ((msdec & 0x0020_0000_0000_0000) != 0) - { - msdec = 0x0010_0000_0000_0000; - e2++; - } - } - } - - - // left justify mantissa - // bit 11 must be 1 - while ((msdec & 0x0010_0000_0000_0000) == 0) - { - msdec <<= 1; - e2--; - } - - // Stuff mantissa directly into double - // (first including implicit bit) - ()@trusted{ *cast(long *)&ldval = msdec; }(); - // Store exponent, now overwriting implicit bit - ()@trusted{ *cast(long *)&ldval &= 0x000F_FFFF_FFFF_FFFF; }(); - ()@trusted{ *cast(long *)&ldval |= ((e2 & 0xFFFUL) << 52); }(); - - import std.math : ldexp; - - // Exponent is power of 2, not power of 10 - ldval = ldexp(ldval,exp); - } + enum uint base = 10; + enum ulong msscaleMax = 10_000_000_000_000_000_000UL; // largest power of 10 a ulong holds + enum ubyte expIter = 1; // iterate the base-10 exponent once for every decimal digit + alias checkDigit = isDigit; + alias convertDigit = (int x) => x - '0'; + // Used to enforce that any mantissa digits are present + sawDigits = startsWithZero; } else - static assert(false, "Floating point format of real type not supported"); - - goto L6; - } - else // not hex - { - if (toUpper(p.front) == 'N' && !startsWithZero) - { - // nan - p.popFront(); - enforce(!p.empty && toUpper(p.front) == 'A', - new ConvException("error converting input to floating point")); - p.popFront(); - enforce(!p.empty && toUpper(p.front) == 'N', - new ConvException("error converting input to floating point")); - // skip past the last 'n' - p.popFront(); - static if (isNarrowString!Source) - source = cast(Source) p; - return typeof(return).nan; - } - - bool sawDigits = startsWithZero; + static assert(false, "Unrecognized floating-point format used."); while (!p.empty) { int i = p.front; - while (isDigit(i)) + while (checkDigit(i)) { sawDigits = true; /* must have at least 1 digit */ - if (msdec < (0x7FFFFFFFFFFFL-10)/10) - msdec = msdec * 10 + (i - '0'); - else if (msscale < (0xFFFFFFFF-10)/10) + + i = convertDigit(i); + + if (msdec < (ulong.max - base)/base) + { + // For base 16: Y = ... + y3*16^3 + y2*16^2 + y1*16^1 + y0*16^0 + msdec = msdec * base + i; + } + else if (msscale < msscaleMax) { - lsdec = lsdec * 10 + (i - '0'); - msscale *= 10; + lsdec = lsdec * base + i; + msscale *= base; } else { - exp++; + exp += expIter; } exp -= dot; p.popFront(); @@ -3168,21 +2918,29 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum if (i == '.' && !dot) { p.popFront(); - dot++; + dot += expIter; } else - { break; - } } - enforce(sawDigits, new ConvException("no digits seen")); + + // Have we seen any mantissa digits so far? + enforce(sawDigits, bailOut("no digits seen")); + static if (FloatFormat == hex) + enforce(!p.empty && (p.front == 'p' || p.front == 'P'), + bailOut("Floating point parsing: exponent is required")); } - if (!p.empty && (p.front == 'e' || p.front == 'E')) + + if (isHex) + parseDigits!hex; + else + parseDigits!decimal; + + if (isHex || (!p.empty && (p.front == 'e' || p.front == 'E'))) { - char sexp; - int e; + char sexp = 0; + int e = 0; - sexp = 0; p.popFront(); enforce(!p.empty, new ConvException("Unexpected end of input")); switch (p.front) @@ -3193,8 +2951,7 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum break; default: {} } - bool sawDigits = 0; - e = 0; + sawDigits = false; while (!p.empty && isDigit(p.front)) { if (e < 0x7FFFFFFF / 10 - 10) // prevent integer overflow @@ -3202,7 +2959,7 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum e = e * 10 + p.front - '0'; } p.popFront(); - sawDigits = 1; + sawDigits = true; } exp += (sexp) ? -e : e; enforce(sawDigits, new ConvException("No digits seen.")); @@ -3211,7 +2968,14 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum ldval = msdec; if (msscale != 1) /* if stuff was accumulated in lsdec */ ldval = ldval * msscale + lsdec; - if (ldval) + if (isHex) + { + import std.math : ldexp; + + // Exponent is power of 2, not power of 10 + ldval = ldexp(ldval,exp); + } + else if (ldval) { uint u = 0; int pow = 4096; @@ -3238,10 +3002,10 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum u++; } } - L6: // if overflow occurred + + // if overflow occurred enforce(ldval != real.infinity, new ConvException("Range error")); - L1: static if (isNarrowString!Source) source = cast(Source) p; return sign ? -ldval : ldval; @@ -3339,6 +3103,14 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum } r = to!real(to!string(real.max)); assert(to!string(r) == to!string(real.max)); + + real pi = 3.1415926535897932384626433832795028841971693993751; + string fullPrecision = "3.1415926535897932384626433832795028841971693993751"; + assert(feq(parse!real(fullPrecision), pi, 2*real.epsilon)); + + real x = 0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252; + string full = "0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252"; + assert(parse!real(full) == x); } // Tests for the double implementation @@ -3447,8 +3219,7 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum int i; static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple) - // Our parser is currently limited to ieeeExtended precision - enum s = "0x1.FFFFFFFFFFFFFFFEp-16382"; + enum s = "0x1.FFFFFFFFFFFFFFFFFFFFFFFFFFFFp-16382"; else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended) enum s = "0x1.FFFFFFFFFFFFFFFEp-16382"; else static if (floatTraits!real.realFormat == RealFormat.ieeeDouble) @@ -3465,8 +3236,6 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum { version (CRuntime_Microsoft) ld1 = 0x1.FFFFFFFFFFFFFFFEp-16382L; // strtold currently mapped to strtod - else version (CRuntime_Bionic) - ld1 = 0x1.FFFFFFFFFFFFFFFEp-16382L; // strtold currently mapped to strtod else ld1 = strtold(s.ptr, null); } @@ -3514,9 +3283,11 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum assertThrown!ConvException(to!float("INF2")); //extra stress testing - auto ssOK = ["1.", "1.1.1", "1.e5", "2e1e", "2a", "2e1_1", - "inf", "-inf", "infa", "-infa", "inf2e2", "-inf2e2"]; - auto ssKO = ["", " ", "2e", "2e+", "2e-", "2ee", "2e++1", "2e--1", "2e_1", "+inf"]; + auto ssOK = ["1.", "1.1.1", "1.e5", "2e1e", "2a", "2e1_1", "3.4_", + "inf", "-inf", "infa", "-infa", "inf2e2", "-inf2e2", + "nan", "-NAN", "+NaN", "-nAna", "NAn2e2", "-naN2e2"]; + auto ssKO = ["", " ", "2e", "2e+", "2e-", "2ee", "2e++1", "2e--1", "2e_1", + "+inf", "-in", "I", "+N", "-NaD", "0x3.F"]; foreach (s; ssOK) parse!double(s); foreach (s; ssKO) @@ -4210,20 +3981,24 @@ if (T.length > 0) { return textImpl!dstring(args); } assert(dtext(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"d); } -// @@@DEPRECATED_2018-06@@@ -deprecated("Calling `text` with 0 arguments is deprecated") -string text(T...)(T args) -if (T.length == 0) { return textImpl!string(args); } +@safe unittest +{ + char c = 'h'; + wchar w = '你'; + dchar d = 'እ'; -// @@@DEPRECATED_2018-06@@@ -deprecated("Calling `wtext` with 0 arguments is deprecated") -wstring wtext(T...)(T args) -if (T.length == 0) { return textImpl!wstring(args); } + assert( text(c, "ello", ' ', w, "好 ", d, "ው ሰላም ነው") == "hello 你好 እው ሰላም ነው"c); + assert(wtext(c, "ello", ' ', w, "好 ", d, "ው ሰላም ነው") == "hello 你好 እው ሰላም ነው"w); + assert(dtext(c, "ello", ' ', w, "好 ", d, "ው ሰላም ነው") == "hello 你好 እው ሰላም ነው"d); -// @@@DEPRECATED_2018-06@@@ -deprecated("Calling `dtext` with 0 arguments is deprecated") -dstring dtext(T...)(T args) -if (T.length == 0) { return textImpl!dstring(args); } + string cs = "今日は"; + wstring ws = "여보세요"; + dstring ds = "Здравствуйте"; + + assert( text(cs, ' ', ws, " ", ds) == "今日は 여보세요 Здравствуйте"c); + assert(wtext(cs, ' ', ws, " ", ds) == "今日は 여보세요 Здравствуйте"w); + assert(dtext(cs, ' ', ws, " ", ds) == "今日は 여보세요 Здравствуйте"d); +} private S textImpl(S, U...)(U args) { diff --git a/libphobos/src/std/datetime/date.d b/libphobos/src/std/datetime/date.d index 8f230ef4e..da7996cac 100644 --- a/libphobos/src/std/datetime/date.d +++ b/libphobos/src/std/datetime/date.d @@ -164,7 +164,7 @@ public: date = The date portion of $(LREF DateTime). tod = The time portion of $(LREF DateTime). +/ - this(in Date date, in TimeOfDay tod = TimeOfDay.init) @safe pure nothrow @nogc + this(Date date, TimeOfDay tod = TimeOfDay.init) @safe pure nothrow @nogc { _date = date; _tod = tod; @@ -233,7 +233,7 @@ public: $(TR $(TD this > rhs) $(TD > 0)) ) +/ - int opCmp(in DateTime rhs) const @safe pure nothrow @nogc + int opCmp(DateTime rhs) const @safe pure nothrow @nogc { immutable dateResult = _date.opCmp(rhs._date); @@ -473,7 +473,7 @@ public: Params: date = The Date to set this $(LREF DateTime)'s date portion to. +/ - @property void date(in Date date) @safe pure nothrow @nogc + @property void date(Date date) @safe pure nothrow @nogc { _date = date; } @@ -526,7 +526,7 @@ public: tod = The $(REF TimeOfDay,std,datetime,date) to set this $(LREF DateTime)'s time portion to. +/ - @property void timeOfDay(in TimeOfDay tod) @safe pure nothrow @nogc + @property void timeOfDay(TimeOfDay tod) @safe pure nothrow @nogc { _tod = tod; } @@ -593,7 +593,7 @@ public: @safe unittest { - static void testDT(DateTime dt, int year, in DateTime expected, size_t line = __LINE__) + static void testDT(DateTime dt, int year, DateTime expected, size_t line = __LINE__) { dt.year = year; assert(dt == expected); @@ -637,7 +637,7 @@ public: @safe unittest { - assertThrown!DateTimeException((in DateTime dt){dt.yearBC;}(DateTime(Date(1, 1, 1)))); + assertThrown!DateTimeException((DateTime dt){dt.yearBC;}(DateTime(Date(1, 1, 1)))); auto dt = DateTime(1999, 7, 6, 12, 30, 33); const cdt = DateTime(1999, 7, 6, 12, 30, 33); @@ -735,7 +735,7 @@ public: @safe unittest { - static void testDT(DateTime dt, Month month, in DateTime expected = DateTime.init, size_t line = __LINE__) + static void testDT(DateTime dt, Month month, DateTime expected = DateTime.init, size_t line = __LINE__) { dt.month = month; assert(expected != DateTime.init); @@ -1253,7 +1253,7 @@ public: // Test roll!"hours"(). @safe unittest { - static void testDT(DateTime orig, int hours, in DateTime expected, size_t line = __LINE__) + static void testDT(DateTime orig, int hours, DateTime expected, size_t line = __LINE__) { orig.roll!"hours"(hours); assert(orig == expected); @@ -1556,7 +1556,7 @@ public: // Test roll!"minutes"(). @safe unittest { - static void testDT(DateTime orig, int minutes, in DateTime expected, size_t line = __LINE__) + static void testDT(DateTime orig, int minutes, DateTime expected, size_t line = __LINE__) { orig.roll!"minutes"(minutes); assert(orig == expected); @@ -1856,7 +1856,7 @@ public: // Test roll!"seconds"(). @safe unittest { - static void testDT(DateTime orig, int seconds, in DateTime expected, size_t line = __LINE__) + static void testDT(DateTime orig, int seconds, DateTime expected, size_t line = __LINE__) { orig.roll!"seconds"(seconds); assert(orig == expected); @@ -2330,7 +2330,7 @@ public: $(TR $(TD DateTime) $(TD -) $(TD DateTime) $(TD -->) $(TD duration)) ) +/ - Duration opBinary(string op)(in DateTime rhs) const @safe pure nothrow @nogc + Duration opBinary(string op)(DateTime rhs) const @safe pure nothrow @nogc if (op == "-") { immutable dateResult = _date - rhs.date; @@ -2417,7 +2417,7 @@ public: Params: rhs = The $(LREF DateTime) to subtract from this one. +/ - int diffMonths(in DateTime rhs) const @safe pure nothrow @nogc + int diffMonths(DateTime rhs) const @safe pure nothrow @nogc { return _date.diffMonths(rhs._date); } @@ -3130,7 +3130,7 @@ public: not in the ISO format or if the resulting $(LREF DateTime) would not be valid. +/ - static DateTime fromISOString(S)(in S isoString) @safe pure + static DateTime fromISOString(S)(scope const S isoString) @safe pure if (isSomeString!S) { import std.algorithm.searching : countUntil; @@ -3231,7 +3231,7 @@ public: not in the ISO Extended format or if the resulting $(LREF DateTime) would not be valid. +/ - static DateTime fromISOExtString(S)(in S isoExtString) @safe pure + static DateTime fromISOExtString(S)(scope const S isoExtString) @safe pure if (isSomeString!(S)) { import std.algorithm.searching : countUntil; @@ -3331,7 +3331,7 @@ public: not in the correct format or if the resulting $(LREF DateTime) would not be valid. +/ - static DateTime fromSimpleString(S)(in S simpleString) @safe pure + static DateTime fromSimpleString(S)(scope const S simpleString) @safe pure if (isSomeString!(S)) { import std.algorithm.searching : countUntil; @@ -3521,7 +3521,7 @@ private: @safe unittest { - static void testDT(DateTime orig, int seconds, in DateTime expected, size_t line = __LINE__) + static void testDT(DateTime orig, int seconds, DateTime expected, size_t line = __LINE__) { orig._addSeconds(seconds); assert(orig == expected); @@ -3766,7 +3766,7 @@ public: import std.exception : assertNotThrown; assert(Date(1, 1, 1) == Date.init); - static void testDate(in Date date, int year, int month, int day) + static void testDate(Date date, int year, int month, int day) { assert(date._year == year); assert(date._month == month); @@ -3961,7 +3961,7 @@ public: $(TR $(TD this > rhs) $(TD > 0)) ) +/ - int opCmp(in Date rhs) const @safe pure nothrow @nogc + int opCmp(Date rhs) const @safe pure nothrow @nogc { if (_year < rhs._year) return -1; @@ -4126,7 +4126,7 @@ public: date.year = year; } - static void testDate(Date date, int year, in Date expected) + static void testDate(Date date, int year, Date expected) { date.year = year; assert(date == expected); @@ -4170,7 +4170,7 @@ public: @safe unittest { - assertThrown!DateTimeException((in Date date){date.yearBC;}(Date(1, 1, 1))); + assertThrown!DateTimeException((Date date){date.yearBC;}(Date(1, 1, 1))); auto date = Date(0, 7, 6); const cdate = Date(0, 7, 6); @@ -4271,7 +4271,7 @@ public: @safe unittest { - static void testDate(Date date, Month month, in Date expected = Date.init) + static void testDate(Date date, Month month, Date expected = Date.init) { date.month = month; assert(expected != Date.init); @@ -6279,7 +6279,7 @@ public: $(TR $(TD Date) $(TD -) $(TD Date) $(TD -->) $(TD duration)) ) +/ - Duration opBinary(string op)(in Date rhs) const @safe pure nothrow @nogc + Duration opBinary(string op)(Date rhs) const @safe pure nothrow @nogc if (op == "-") { import core.time : dur; @@ -6335,7 +6335,7 @@ public: Params: rhs = The $(LREF Date) to subtract from this one. +/ - int diffMonths(in Date rhs) const @safe pure nothrow @nogc + int diffMonths(Date rhs) const @safe pure nothrow @nogc { immutable yearDiff = _year - rhs._year; immutable monthDiff = _month - rhs._month; @@ -7471,7 +7471,7 @@ public: not in the ISO format or if the resulting $(LREF Date) would not be valid. +/ - static Date fromISOString(S)(in S isoString) @safe pure + static Date fromISOString(S)(scope const S isoString) @safe pure if (isSomeString!S) { import std.algorithm.searching : startsWith; @@ -7614,7 +7614,7 @@ public: not in the ISO Extended format or if the resulting $(LREF Date) would not be valid. +/ - static Date fromISOExtString(S)(in S isoExtString) @safe pure + static Date fromISOExtString(S)(scope const S isoExtString) @safe pure if (isSomeString!(S)) { import std.algorithm.searching : startsWith; @@ -7752,7 +7752,7 @@ public: not in the correct format or if the resulting $(LREF Date) would not be valid. +/ - static Date fromSimpleString(S)(in S simpleString) @safe pure + static Date fromSimpleString(S)(scope const S simpleString) @safe pure if (isSomeString!(S)) { import std.algorithm.searching : startsWith; @@ -8227,7 +8227,7 @@ public: $(TR $(TD this > rhs) $(TD > 0)) ) +/ - int opCmp(in TimeOfDay rhs) const @safe pure nothrow @nogc + int opCmp(TimeOfDay rhs) const @safe pure nothrow @nogc { if (_hour < rhs._hour) return -1; @@ -8526,7 +8526,7 @@ public: // Test roll!"minutes"(). @safe unittest { - static void testTOD(TimeOfDay orig, int minutes, in TimeOfDay expected, size_t line = __LINE__) + static void testTOD(TimeOfDay orig, int minutes, TimeOfDay expected, size_t line = __LINE__) { orig.roll!"minutes"(minutes); assert(orig == expected); @@ -8610,7 +8610,7 @@ public: // Test roll!"seconds"(). @safe unittest { - static void testTOD(TimeOfDay orig, int seconds, in TimeOfDay expected, size_t line = __LINE__) + static void testTOD(TimeOfDay orig, int seconds, TimeOfDay expected, size_t line = __LINE__) { orig.roll!"seconds"(seconds); assert(orig == expected); @@ -8851,7 +8851,7 @@ public: Params: rhs = The $(LREF TimeOfDay) to subtract from this one. +/ - Duration opBinary(string op)(in TimeOfDay rhs) const @safe pure nothrow @nogc + Duration opBinary(string op)(TimeOfDay rhs) const @safe pure nothrow @nogc if (op == "-") { immutable lhsSec = _hour * 3600 + _minute * 60 + _second; @@ -9048,7 +9048,7 @@ public: not in the ISO format or if the resulting $(LREF TimeOfDay) would not be valid. +/ - static TimeOfDay fromISOString(S)(in S isoString) @safe pure + static TimeOfDay fromISOString(S)(scope const S isoString) @safe pure if (isSomeString!S) { import std.conv : to, text, ConvException; @@ -9173,7 +9173,7 @@ public: not in the ISO Extended format or if the resulting $(LREF TimeOfDay) would not be valid. +/ - static TimeOfDay fromISOExtString(S)(in S isoExtString) @safe pure + static TimeOfDay fromISOExtString(S)(scope const S isoExtString) @safe pure if (isSomeString!S) { import std.conv : ConvException, text, to; @@ -9363,7 +9363,7 @@ private: @safe unittest { - static void testTOD(TimeOfDay orig, int seconds, in TimeOfDay expected, size_t line = __LINE__) + static void testTOD(TimeOfDay orig, int seconds, TimeOfDay expected, size_t line = __LINE__) { orig._addSeconds(seconds); assert(orig == expected); diff --git a/libphobos/src/std/datetime/interval.d b/libphobos/src/std/datetime/interval.d index e680ee43b..41d7eb89d 100644 --- a/libphobos/src/std/datetime/interval.d +++ b/libphobos/src/std/datetime/interval.d @@ -135,7 +135,7 @@ public: Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); -------------------- +/ - this(U)(in TP begin, in U end) pure + this(U)(scope const TP begin, scope const U end) pure if (is(Unqual!TP == Unqual!U)) { if (!_valid(begin, end)) @@ -160,7 +160,7 @@ public: Interval!Date(Date(1996, 1, 2), Date(1996, 1, 5))); -------------------- +/ - this(D)(in TP begin, in D duration) pure + this(D)(scope const TP begin, scope const D duration) pure if (__traits(compiles, begin + duration)) { _begin = cast(TP) begin; @@ -311,7 +311,7 @@ public: Date(2012, 3, 1))); -------------------- +/ - bool contains(in TP timePoint) const pure + bool contains(scope const TP timePoint) const pure { _enforceNotEmpty(); return timePoint >= _begin && timePoint < _end; @@ -340,7 +340,7 @@ public: Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); -------------------- +/ - bool contains(in Interval interval) const pure + bool contains(scope const Interval interval) const pure { _enforceNotEmpty(); interval._enforceNotEmpty(); @@ -370,7 +370,7 @@ public: PosInfInterval!Date(Date(1999, 5, 4)))); -------------------- +/ - bool contains(in PosInfInterval!TP interval) const pure + bool contains(scope const PosInfInterval!TP interval) const pure { _enforceNotEmpty(); return false; @@ -397,7 +397,7 @@ public: NegInfInterval!Date(Date(1996, 5, 4)))); -------------------- +/ - bool contains(in NegInfInterval!TP interval) const pure + bool contains(scope const NegInfInterval!TP interval) const pure { _enforceNotEmpty(); return false; @@ -427,7 +427,7 @@ public: Date(2012, 3, 1))); -------------------- +/ - bool isBefore(in TP timePoint) const pure + bool isBefore(scope const TP timePoint) const pure { _enforceNotEmpty(); return _end <= timePoint; @@ -457,7 +457,7 @@ public: Interval!Date(Date(2012, 3, 1), Date(2013, 5, 1)))); -------------------- +/ - bool isBefore(in Interval interval) const pure + bool isBefore(scope const Interval interval) const pure { _enforceNotEmpty(); interval._enforceNotEmpty(); @@ -485,7 +485,7 @@ public: PosInfInterval!Date(Date(2013, 3, 7)))); -------------------- +/ - bool isBefore(in PosInfInterval!TP interval) const pure + bool isBefore(scope const PosInfInterval!TP interval) const pure { _enforceNotEmpty(); return _end <= interval._begin; @@ -512,7 +512,7 @@ public: NegInfInterval!Date(Date(1996, 5, 4)))); -------------------- +/ - bool isBefore(in NegInfInterval!TP interval) const pure + bool isBefore(scope const NegInfInterval!TP interval) const pure { _enforceNotEmpty(); return false; @@ -542,7 +542,7 @@ public: Date(2012, 3, 1))); -------------------- +/ - bool isAfter(in TP timePoint) const pure + bool isAfter(scope const TP timePoint) const pure { _enforceNotEmpty(); return timePoint < _begin; @@ -572,7 +572,7 @@ public: Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); -------------------- +/ - bool isAfter(in Interval interval) const pure + bool isAfter(scope const Interval interval) const pure { _enforceNotEmpty(); interval._enforceNotEmpty(); @@ -600,7 +600,7 @@ public: PosInfInterval!Date(Date(1999, 5, 4)))); -------------------- +/ - bool isAfter(in PosInfInterval!TP interval) const pure + bool isAfter(scope const PosInfInterval!TP interval) const pure { _enforceNotEmpty(); return false; @@ -624,7 +624,7 @@ public: NegInfInterval!Date(Date(1996, 1, 2)))); -------------------- +/ - bool isAfter(in NegInfInterval!TP interval) const pure + bool isAfter(scope const NegInfInterval!TP interval) const pure { _enforceNotEmpty(); return _begin >= interval._end; @@ -653,7 +653,7 @@ public: Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); -------------------- +/ - bool intersects(in Interval interval) const pure + bool intersects(scope const Interval interval) const pure { _enforceNotEmpty(); interval._enforceNotEmpty(); @@ -680,7 +680,7 @@ public: PosInfInterval!Date(Date(2012, 3, 1)))); -------------------- +/ - bool intersects(in PosInfInterval!TP interval) const pure + bool intersects(scope const PosInfInterval!TP interval) const pure { _enforceNotEmpty(); return _end > interval._begin; @@ -706,7 +706,7 @@ public: NegInfInterval!Date(Date(2000, 1, 2)))); -------------------- +/ - bool intersects(in NegInfInterval!TP interval) const pure + bool intersects(scope const NegInfInterval!TP interval) const pure { _enforceNotEmpty(); return _begin < interval._end; @@ -734,7 +734,7 @@ public: Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); -------------------- +/ - Interval intersection(in Interval interval) const + Interval intersection(scope const Interval interval) const { import std.format : format; @@ -769,7 +769,7 @@ public: Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); -------------------- +/ - Interval intersection(in PosInfInterval!TP interval) const + Interval intersection(scope const PosInfInterval!TP interval) const { import std.format : format; @@ -801,7 +801,7 @@ public: Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); -------------------- +/ - Interval intersection(in NegInfInterval!TP interval) const + Interval intersection(scope const NegInfInterval!TP interval) const { import std.format : format; @@ -835,7 +835,7 @@ public: Interval!Date(Date(1989, 3, 1), Date(2012, 3, 1)))); -------------------- +/ - bool isAdjacent(in Interval interval) const pure + bool isAdjacent(scope const Interval interval) const pure { _enforceNotEmpty(); interval._enforceNotEmpty(); @@ -863,7 +863,7 @@ public: PosInfInterval!Date(Date(2012, 3, 1)))); -------------------- +/ - bool isAdjacent(in PosInfInterval!TP interval) const pure + bool isAdjacent(scope const PosInfInterval!TP interval) const pure { _enforceNotEmpty(); return _end == interval._begin; @@ -890,7 +890,7 @@ public: NegInfInterval!Date(Date(2000, 1, 2)))); -------------------- +/ - bool isAdjacent(in NegInfInterval!TP interval) const pure + bool isAdjacent(scope const NegInfInterval!TP interval) const pure { _enforceNotEmpty(); return _begin == interval._end; @@ -918,7 +918,7 @@ public: Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); -------------------- +/ - Interval merge(in Interval interval) const + Interval merge(scope const Interval interval) const { import std.format : format; @@ -953,7 +953,7 @@ public: PosInfInterval!Date(Date(1996, 1 , 2))); -------------------- +/ - PosInfInterval!TP merge(in PosInfInterval!TP interval) const + PosInfInterval!TP merge(scope const PosInfInterval!TP interval) const { import std.format : format; @@ -985,7 +985,7 @@ public: NegInfInterval!Date(Date(2013, 1 , 12))); -------------------- +/ - NegInfInterval!TP merge(in NegInfInterval!TP interval) const + NegInfInterval!TP merge(scope const NegInfInterval!TP interval) const { import std.format : format; @@ -1019,7 +1019,7 @@ public: Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); -------------------- +/ - Interval span(in Interval interval) const pure + Interval span(scope const Interval interval) const pure { _enforceNotEmpty(); interval._enforceNotEmpty(); @@ -1054,7 +1054,7 @@ public: PosInfInterval!Date(Date(1996, 1 , 2))); -------------------- +/ - PosInfInterval!TP span(in PosInfInterval!TP interval) const pure + PosInfInterval!TP span(scope const PosInfInterval!TP interval) const pure { _enforceNotEmpty(); return PosInfInterval!TP(_begin < interval._begin ? _begin : interval._begin); @@ -1084,7 +1084,7 @@ public: NegInfInterval!Date(Date(2013, 1 , 12))); -------------------- +/ - NegInfInterval!TP span(in NegInfInterval!TP interval) const pure + NegInfInterval!TP span(scope const NegInfInterval!TP interval) const pure { _enforceNotEmpty(); return NegInfInterval!TP(_end > interval._end ? _end : interval._end); @@ -1403,7 +1403,7 @@ public: Example: -------------------- auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); - auto func = delegate (in Date date) // For iterating over even-numbered days. + auto func = delegate (scope const Date date) // For iterating over even-numbered days. { if ((date.day & 1) == 0) return date + dur!"days"(2); @@ -1431,7 +1431,7 @@ public: assert(range.empty); -------------------- +/ - IntervalRange!(TP, Direction.fwd) fwdRange(TP delegate(in TP) func, PopFirst popFirst = PopFirst.no) const + IntervalRange!(TP, Direction.fwd) fwdRange(TP delegate(scope const TP) func, PopFirst popFirst = PopFirst.no) const { _enforceNotEmpty(); @@ -1497,7 +1497,7 @@ public: Example: -------------------- auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); - auto func = delegate (in Date date) // For iterating over even-numbered days. + auto func = delegate (scope const Date date) // For iterating over even-numbered days. { if ((date.day & 1) == 0) return date - dur!"days"(2); @@ -1525,7 +1525,7 @@ public: assert(range.empty); -------------------- +/ - IntervalRange!(TP, Direction.bwd) bwdRange(TP delegate(in TP) func, PopFirst popFirst = PopFirst.no) const + IntervalRange!(TP, Direction.bwd) bwdRange(TP delegate(scope const TP) func, PopFirst popFirst = PopFirst.no) const { _enforceNotEmpty(); @@ -1588,7 +1588,7 @@ private: begin = The starting point of the interval. end = The end point of the interval. +/ - static bool _valid(in TP begin, in TP end) pure nothrow + static bool _valid(scope const TP begin, scope const TP end) pure nothrow @trusted { return begin <= end; } @@ -2368,7 +2368,7 @@ private: auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static void testInterval(in Interval!Date interval1, in Interval!Date interval2) + static void testInterval(scope const Interval!Date interval1, scope const Interval!Date interval2) { interval1.isAdjacent(interval2); } @@ -2477,7 +2477,7 @@ private: auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static void testInterval(I)(in Interval!Date interval1, in I interval2) + static void testInterval(I)(scope const Interval!Date interval1, scope const I interval2) { interval1.merge(interval2); } @@ -2622,7 +2622,7 @@ private: auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static void testInterval(in Interval!Date interval1, in Interval!Date interval2) + static void testInterval(scope const Interval!Date interval1, scope const Interval!Date interval2) { interval1.span(interval2); } @@ -2757,14 +2757,15 @@ private: auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static void testIntervalFail(Interval!Date interval, in Duration duration) + static void testIntervalFail(Interval!Date interval, scope const Duration duration) { interval.shift(duration); } assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), dur!"days"(1))); - static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + static void testInterval(I)(I interval, scope const Duration duration, + scope const I expected, size_t line = __LINE__) { interval.shift(duration); assert(interval == expected); @@ -2850,7 +2851,7 @@ private: auto interval = Interval!Date(Date(2000, 7, 4), Date(2012, 1, 7)); - static void testIntervalFail(I)(I interval, in Duration duration) + static void testIntervalFail(I)(I interval, scope const Duration duration) { interval.expand(duration); } @@ -2858,7 +2859,8 @@ private: assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), dur!"days"(1))); assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5)), dur!"days"(-5))); - static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + static void testInterval(I)(I interval, scope const Duration duration, + scope const I expected, size_t line = __LINE__) { interval.expand(duration); assert(interval == expected); @@ -3029,7 +3031,7 @@ private: // Verify Examples. { auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); - auto func = delegate (in Date date) + auto func = delegate (scope const Date date) { if ((date.day & 1) == 0) return date + dur!"days"(2); @@ -3097,7 +3099,7 @@ private: // Verify Examples. { auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); - auto func = delegate (in Date date) + auto func = delegate (scope const Date date) { if ((date.day & 1) == 0) return date - dur!"days"(2); @@ -3164,7 +3166,7 @@ public: auto interval = PosInfInterval!Date(Date(1996, 1, 2)); -------------------- +/ - this(in TP begin) pure nothrow + this(scope const TP begin) pure nothrow { _begin = cast(TP) begin; } @@ -3269,7 +3271,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).contains( Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); -------------------- +/ - bool contains(in Interval!TP interval) const pure + bool contains(scope const Interval!TP interval) const pure { interval._enforceNotEmpty(); return interval._begin >= _begin; @@ -3291,7 +3293,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains( PosInfInterval!Date(Date(1995, 7, 2)))); -------------------- +/ - bool contains(in PosInfInterval interval) const pure nothrow + bool contains(scope const PosInfInterval interval) const pure nothrow { return interval._begin >= _begin; } @@ -3312,7 +3314,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains( NegInfInterval!Date(Date(1996, 5, 4)))); -------------------- +/ - bool contains(in NegInfInterval!TP interval) const pure nothrow + bool contains(scope const NegInfInterval!TP interval) const pure nothrow { return false; } @@ -3334,7 +3336,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(1994, 12, 24))); assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(2000, 1, 5))); -------------------- +/ - bool isBefore(in TP timePoint) const pure nothrow + bool isBefore(scope const TP timePoint) const pure nothrow { return false; } @@ -3364,7 +3366,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); -------------------- +/ - bool isBefore(in Interval!TP interval) const pure + bool isBefore(scope const Interval!TP interval) const pure { interval._enforceNotEmpty(); return false; @@ -3390,7 +3392,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( PosInfInterval!Date(Date(2013, 3, 7)))); -------------------- +/ - bool isBefore(in PosInfInterval interval) const pure nothrow + bool isBefore(scope const PosInfInterval interval) const pure nothrow { return false; } @@ -3412,7 +3414,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( NegInfInterval!Date(Date(1996, 5, 4)))); -------------------- +/ - bool isBefore(in NegInfInterval!TP interval) const pure nothrow + bool isBefore(scope const NegInfInterval!TP interval) const pure nothrow { return false; } @@ -3431,7 +3433,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(1994, 12, 24))); assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(2000, 1, 5))); -------------------- +/ - bool isAfter(in TP timePoint) const pure nothrow + bool isAfter(scope const TP timePoint) const pure nothrow { return timePoint < _begin; } @@ -3460,7 +3462,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter( Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); -------------------- +/ - bool isAfter(in Interval!TP interval) const pure + bool isAfter(scope const Interval!TP interval) const pure { interval._enforceNotEmpty(); return _begin >= interval._end; @@ -3486,7 +3488,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( PosInfInterval!Date(Date(1999, 5, 4)))); -------------------- +/ - bool isAfter(in PosInfInterval interval) const pure nothrow + bool isAfter(scope const PosInfInterval interval) const pure nothrow { return false; } @@ -3508,7 +3510,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( NegInfInterval!Date(Date(2000, 7, 1)))); -------------------- +/ - bool isAfter(in NegInfInterval!TP interval) const pure nothrow + bool isAfter(scope const NegInfInterval!TP interval) const pure nothrow { return _begin >= interval._end; } @@ -3536,7 +3538,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects( Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); -------------------- +/ - bool intersects(in Interval!TP interval) const pure + bool intersects(scope const Interval!TP interval) const pure { interval._enforceNotEmpty(); return interval._end > _begin; @@ -3562,7 +3564,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( PosInfInterval!Date(Date(1999, 5, 4)))); -------------------- +/ - bool intersects(in PosInfInterval interval) const pure nothrow + bool intersects(scope const PosInfInterval interval) const pure nothrow { return true; } @@ -3584,7 +3586,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( NegInfInterval!Date(Date(2000, 7, 1)))); -------------------- +/ - bool intersects(in NegInfInterval!TP interval) const pure nothrow + bool intersects(scope const NegInfInterval!TP interval) const pure nothrow { return _begin < interval._end; } @@ -3611,7 +3613,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); -------------------- +/ - Interval!TP intersection(in Interval!TP interval) const + Interval!TP intersection(scope const Interval!TP interval) const { import std.format : format; @@ -3641,7 +3643,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( PosInfInterval!Date(Date(1999, 1 , 12))); -------------------- +/ - PosInfInterval intersection(in PosInfInterval interval) const pure nothrow + PosInfInterval intersection(scope const PosInfInterval interval) const pure nothrow { return PosInfInterval(_begin < interval._begin ? interval._begin : _begin); } @@ -3668,7 +3670,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( Interval!Date(Date(1996, 1 , 2), Date(2013, 1, 12))); -------------------- +/ - Interval!TP intersection(in NegInfInterval!TP interval) const + Interval!TP intersection(scope const NegInfInterval!TP interval) const { import std.format : format; @@ -3699,7 +3701,7 @@ assert(!PosInfInterval!Date(Date(1999, 1, 12)).isAdjacent( Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); -------------------- +/ - bool isAdjacent(in Interval!TP interval) const pure + bool isAdjacent(scope const Interval!TP interval) const pure { interval._enforceNotEmpty(); return _begin == interval._end; @@ -3725,7 +3727,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( PosInfInterval!Date(Date(1996, 1, 2)))); -------------------- +/ - bool isAdjacent(in PosInfInterval interval) const pure nothrow + bool isAdjacent(scope const PosInfInterval interval) const pure nothrow { return false; } @@ -3747,7 +3749,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( NegInfInterval!Date(Date(2000, 7, 1)))); -------------------- +/ - bool isAdjacent(in NegInfInterval!TP interval) const pure nothrow + bool isAdjacent(scope const NegInfInterval!TP interval) const pure nothrow { return _begin == interval._end; } @@ -3781,7 +3783,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).merge( PosInfInterval!Date(Date(1996, 1 , 2))); -------------------- +/ - PosInfInterval merge(in Interval!TP interval) const + PosInfInterval merge(scope const Interval!TP interval) const { import std.format : format; @@ -3815,7 +3817,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).merge( PosInfInterval!Date(Date(1996, 1 , 2))); -------------------- +/ - PosInfInterval merge(in PosInfInterval interval) const pure nothrow + PosInfInterval merge(scope const PosInfInterval interval) const pure nothrow { return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); } @@ -3855,7 +3857,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).span( PosInfInterval!Date(Date(1996, 1 , 2))); -------------------- +/ - PosInfInterval span(in Interval!TP interval) const pure + PosInfInterval span(scope const Interval!TP interval) const pure { interval._enforceNotEmpty(); return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); @@ -3888,7 +3890,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).span( PosInfInterval!Date(Date(1996, 1 , 2))); -------------------- +/ - PosInfInterval span(in PosInfInterval interval) const pure nothrow + PosInfInterval span(scope const PosInfInterval interval) const pure nothrow { return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); } @@ -4088,7 +4090,7 @@ assert(interval2 == PosInfInterval!Date(Date(1998, 1, 2))); Example: -------------------- auto interval = PosInfInterval!Date(Date(2010, 9, 1)); -auto func = delegate (in Date date) //For iterating over even-numbered days. +auto func = delegate (scope const Date date) //For iterating over even-numbered days. { if ((date.day & 1) == 0) return date + dur!"days"(2); @@ -4116,7 +4118,7 @@ range.popFront(); assert(!range.empty); -------------------- +/ - PosInfIntervalRange!(TP) fwdRange(TP delegate(in TP) func, PopFirst popFirst = PopFirst.no) const + PosInfIntervalRange!(TP) fwdRange(TP delegate(scope const TP) func, PopFirst popFirst = PopFirst.no) const { auto range = PosInfIntervalRange!(TP)(this, func); @@ -4258,7 +4260,7 @@ private: auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + static void testInterval(scope const PosInfInterval!Date posInfInterval, scope const Interval!Date interval) { posInfInterval.contains(interval); } @@ -4383,7 +4385,7 @@ private: auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + static void testInterval(scope const PosInfInterval!Date posInfInterval, scope const Interval!Date interval) { posInfInterval.isBefore(interval); } @@ -4507,7 +4509,7 @@ private: auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + static void testInterval(scope const PosInfInterval!Date posInfInterval, scope const Interval!Date interval) { posInfInterval.isAfter(interval); } @@ -4604,7 +4606,7 @@ private: auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + static void testInterval(scope const PosInfInterval!Date posInfInterval, scope const Interval!Date interval) { posInfInterval.intersects(interval); } @@ -4701,7 +4703,7 @@ private: auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static void testInterval(I, J)(in I interval1, in J interval2) + static void testInterval(I, J)(scope const I interval1, scope const J interval2) { interval1.intersection(interval2); } @@ -4819,7 +4821,7 @@ private: auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + static void testInterval(scope const PosInfInterval!Date posInfInterval, scope const Interval!Date interval) { posInfInterval.isAdjacent(interval); } @@ -4915,7 +4917,7 @@ private: auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + static void testInterval(scope const PosInfInterval!Date posInfInterval, scope const Interval!Date interval) { posInfInterval.merge(interval); } @@ -5024,7 +5026,7 @@ private: auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + static void testInterval(scope const PosInfInterval!Date posInfInterval, scope const Interval!Date interval) { posInfInterval.span(interval); } @@ -5135,7 +5137,8 @@ private: auto interval = PosInfInterval!Date(Date(2010, 7, 4)); - static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + static void testInterval(I)(I interval, scope const Duration duration, + scope const I expected, size_t line = __LINE__) { interval.shift(duration); assert(interval == expected); @@ -5214,7 +5217,8 @@ private: auto interval = PosInfInterval!Date(Date(2000, 7, 4)); - static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + static void testInterval(I)(I interval, scope const Duration duration, + scope const I expected, size_t line = __LINE__) { interval.expand(duration); assert(interval == expected); @@ -5308,7 +5312,7 @@ private: //Verify Examples. auto interval = PosInfInterval!Date(Date(2010, 9, 1)); - auto func = delegate (in Date date) + auto func = delegate (scope const Date date) { if ((date.day & 1) == 0) return date + dur!"days"(2); @@ -5374,7 +5378,7 @@ public: auto interval = PosInfInterval!Date(Date(1996, 1, 2)); -------------------- +/ - this(in TP end) pure nothrow + this(scope const TP end) pure nothrow { _end = cast(TP) end; } @@ -5480,7 +5484,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains( Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); -------------------- +/ - bool contains(in Interval!TP interval) const pure + bool contains(scope const Interval!TP interval) const pure { interval._enforceNotEmpty(); return interval._end <= _end; @@ -5502,7 +5506,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains( PosInfInterval!Date(Date(1999, 5, 4)))); -------------------- +/ - bool contains(in PosInfInterval!TP interval) const pure nothrow + bool contains(scope const PosInfInterval!TP interval) const pure nothrow { return false; } @@ -5523,7 +5527,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains( NegInfInterval!Date(Date(2013, 7, 9)))); -------------------- +/ - bool contains(in NegInfInterval interval) const pure nothrow + bool contains(scope const NegInfInterval interval) const pure nothrow { return interval._end <= _end; } @@ -5543,7 +5547,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2000, 1, 5))); assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); -------------------- +/ - bool isBefore(in TP timePoint) const pure nothrow + bool isBefore(scope const TP timePoint) const pure nothrow { return timePoint >= _end; } @@ -5572,7 +5576,7 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore( Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); -------------------- +/ - bool isBefore(in Interval!TP interval) const pure + bool isBefore(scope const Interval!TP interval) const pure { interval._enforceNotEmpty(); return _end <= interval._begin; @@ -5595,7 +5599,7 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore( PosInfInterval!Date(Date(2012, 3, 1)))); -------------------- +/ - bool isBefore(in PosInfInterval!TP interval) const pure nothrow + bool isBefore(scope const PosInfInterval!TP interval) const pure nothrow { return _end <= interval._begin; } @@ -5621,7 +5625,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( NegInfInterval!Date(Date(2013, 7, 9)))); -------------------- +/ - bool isBefore(in NegInfInterval interval) const pure nothrow + bool isBefore(scope const NegInfInterval interval) const pure nothrow { return false; } @@ -5644,7 +5648,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2000, 1, 5))); assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); -------------------- +/ - bool isAfter(in TP timePoint) const pure nothrow + bool isAfter(scope const TP timePoint) const pure nothrow { return false; } @@ -5677,7 +5681,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); -------------------- +/ - bool isAfter(in Interval!TP interval) const pure + bool isAfter(scope const Interval!TP interval) const pure { interval._enforceNotEmpty(); return false; @@ -5703,7 +5707,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( PosInfInterval!Date(Date(2012, 3, 1)))); -------------------- +/ - bool isAfter(in PosInfInterval!TP interval) const pure nothrow + bool isAfter(scope const PosInfInterval!TP interval) const pure nothrow { return false; } @@ -5728,7 +5732,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( NegInfInterval!Date(Date(2013, 7, 9)))); -------------------- +/ - bool isAfter(in NegInfInterval interval) const pure nothrow + bool isAfter(scope const NegInfInterval interval) const pure nothrow { return false; } @@ -5756,7 +5760,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects( Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); -------------------- +/ - bool intersects(in Interval!TP interval) const pure + bool intersects(scope const Interval!TP interval) const pure { interval._enforceNotEmpty(); return interval._begin < _end; @@ -5779,7 +5783,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects( PosInfInterval!Date(Date(2012, 3, 1)))); -------------------- +/ - bool intersects(in PosInfInterval!TP interval) const pure nothrow + bool intersects(scope const PosInfInterval!TP interval) const pure nothrow { return interval._begin < _end; } @@ -5803,7 +5807,7 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( NegInfInterval!Date(Date(2013, 7, 9)))); -------------------- +/ - bool intersects(in NegInfInterval!TP interval) const pure nothrow + bool intersects(scope const NegInfInterval!TP interval) const pure nothrow { return true; } @@ -5830,7 +5834,7 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); -------------------- +/ - Interval!TP intersection(in Interval!TP interval) const + Interval!TP intersection(scope const Interval!TP interval) const { import std.format : format; @@ -5864,7 +5868,7 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); -------------------- +/ - Interval!TP intersection(in PosInfInterval!TP interval) const + Interval!TP intersection(scope const PosInfInterval!TP interval) const { import std.format : format; @@ -5892,7 +5896,7 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( NegInfInterval!Date(Date(2012, 3 , 1))); -------------------- +/ - NegInfInterval intersection(in NegInfInterval interval) const nothrow + NegInfInterval intersection(scope const NegInfInterval interval) const nothrow { return NegInfInterval(_end < interval._end ? _end : interval._end); } @@ -5924,7 +5928,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); -------------------- +/ - bool isAdjacent(in Interval!TP interval) const pure + bool isAdjacent(scope const Interval!TP interval) const pure { interval._enforceNotEmpty(); return interval._begin == _end; @@ -5947,7 +5951,7 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( PosInfInterval!Date(Date(2012, 3, 1)))); -------------------- +/ - bool isAdjacent(in PosInfInterval!TP interval) const pure nothrow + bool isAdjacent(scope const PosInfInterval!TP interval) const pure nothrow { return interval._begin == _end; } @@ -5972,7 +5976,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( NegInfInterval!Date(Date(2012, 3, 1)))); -------------------- +/ - bool isAdjacent(in NegInfInterval interval) const pure nothrow + bool isAdjacent(scope const NegInfInterval interval) const pure nothrow { return false; } @@ -6005,7 +6009,7 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).merge( NegInfInterval!Date(Date(2015, 9 , 2))); -------------------- +/ - NegInfInterval merge(in Interval!TP interval) const + NegInfInterval merge(scope const Interval!TP interval) const { import std.format : format; @@ -6039,7 +6043,7 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).merge( NegInfInterval!Date(Date(2013, 1 , 12))); -------------------- +/ - NegInfInterval merge(in NegInfInterval interval) const pure nothrow + NegInfInterval merge(scope const NegInfInterval interval) const pure nothrow { return NegInfInterval(_end > interval._end ? _end : interval._end); } @@ -6079,7 +6083,7 @@ assert(NegInfInterval!Date(Date(1600, 1, 7)).span( NegInfInterval!Date(Date(2017, 7 , 1))); -------------------- +/ - NegInfInterval span(in Interval!TP interval) const pure + NegInfInterval span(scope const Interval!TP interval) const pure { interval._enforceNotEmpty(); return NegInfInterval(_end > interval._end ? _end : interval._end); @@ -6112,7 +6116,7 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).span( NegInfInterval!Date(Date(2013, 1 , 12))); -------------------- +/ - NegInfInterval span(in NegInfInterval interval) const pure nothrow + NegInfInterval span(scope const NegInfInterval interval) const pure nothrow { return NegInfInterval(_end > interval._end ? _end : interval._end); } @@ -6311,7 +6315,7 @@ assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); Example: -------------------- auto interval = NegInfInterval!Date(Date(2010, 9, 9)); -auto func = delegate (in Date date) //For iterating over even-numbered days. +auto func = delegate (scope const Date date) //For iterating over even-numbered days. { if ((date.day & 1) == 0) return date - dur!"days"(2); @@ -6338,7 +6342,7 @@ range.popFront(); assert(!range.empty); -------------------- +/ - NegInfIntervalRange!(TP) bwdRange(TP delegate(in TP) func, PopFirst popFirst = PopFirst.no) const + NegInfIntervalRange!(TP) bwdRange(TP delegate(scope const TP) func, PopFirst popFirst = PopFirst.no) const { auto range = NegInfIntervalRange!(TP)(this, func); @@ -6478,7 +6482,7 @@ private: auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + static void testInterval(scope const NegInfInterval!Date negInfInterval, scope const Interval!Date interval) { negInfInterval.contains(interval); } @@ -6604,7 +6608,7 @@ private: auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + static void testInterval(scope const NegInfInterval!Date negInfInterval, scope const Interval!Date interval) { negInfInterval.isBefore(interval); } @@ -6726,7 +6730,7 @@ private: auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + static void testInterval(scope const NegInfInterval!Date negInfInterval, scope const Interval!Date interval) { negInfInterval.isAfter(interval); } @@ -6827,7 +6831,7 @@ private: auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + static void testInterval(scope const NegInfInterval!Date negInfInterval, scope const Interval!Date interval) { negInfInterval.intersects(interval); } @@ -6924,7 +6928,7 @@ private: auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static void testInterval(I, J)(in I interval1, in J interval2) + static void testInterval(I, J)(scope const I interval1, scope const J interval2) { interval1.intersection(interval2); } @@ -7042,7 +7046,7 @@ private: auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + static void testInterval(scope const NegInfInterval!Date negInfInterval, scope const Interval!Date interval) { negInfInterval.isAdjacent(interval); } @@ -7140,7 +7144,7 @@ private: auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static void testInterval(I, J)(in I interval1, in J interval2) + static void testInterval(I, J)(scope const I interval1, scope const J interval2) { interval1.merge(interval2); } @@ -7249,7 +7253,7 @@ private: auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static void testInterval(I, J)(in I interval1, in J interval2) + static void testInterval(I, J)(scope const I interval1, scope const J interval2) { interval1.span(interval2); } @@ -7360,7 +7364,8 @@ private: auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + static void testInterval(I)(I interval, scope const Duration duration, + scope const I expected, size_t line = __LINE__) { interval.shift(duration); assert(interval == expected); @@ -7444,7 +7449,8 @@ private: auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + static void testInterval(I)(I interval, + scope const Duration duration, scope const I expected, size_t line = __LINE__) { interval.expand(duration); assert(interval == expected); @@ -7538,7 +7544,7 @@ private: //Verify Examples. auto interval = NegInfInterval!Date(Date(2010, 9, 9)); - auto func = delegate (in Date date) + auto func = delegate (scope const Date date) { if ((date.day & 1) == 0) return date - dur!"days"(2); @@ -7601,14 +7607,14 @@ private: `bwdRange`, use `Direction.bwd`. dayOfWeek = The week that each time point in the range will be. +/ -TP delegate(in TP) everyDayOfWeek(TP, Direction dir = Direction.fwd)(DayOfWeek dayOfWeek) nothrow +TP delegate(scope const TP) everyDayOfWeek(TP, Direction dir = Direction.fwd)(DayOfWeek dayOfWeek) nothrow if (isTimePoint!TP && (dir == Direction.fwd || dir == Direction.bwd) && __traits(hasMember, TP, "dayOfWeek") && !__traits(isStaticFunction, TP.dayOfWeek) && is(typeof(TP.dayOfWeek) == DayOfWeek)) { - TP func(in TP tp) + TP func(scope const TP tp) { TP retval = cast(TP) tp; immutable days = daysToDayOfWeek(retval.dayOfWeek, dayOfWeek); @@ -7711,7 +7717,7 @@ if (isTimePoint!TP && month = The month that each time point in the range will be in (January is 1). +/ -TP delegate(in TP) everyMonth(TP, Direction dir = Direction.fwd)(int month) +TP delegate(scope const TP) everyMonth(TP, Direction dir = Direction.fwd)(int month) if (isTimePoint!TP && (dir == Direction.fwd || dir == Direction.bwd) && __traits(hasMember, TP, "month") && @@ -7722,7 +7728,7 @@ if (isTimePoint!TP && enforceValid!"months"(month); - TP func(in TP tp) + TP func(scope const TP tp) { TP retval = cast(TP) tp; immutable months = monthsToMonth(retval.month, month); @@ -7841,12 +7847,12 @@ if (isTimePoint!TP && duration = The duration which separates each successive time point in the range. +/ -TP delegate(in TP) everyDuration(TP, Direction dir = Direction.fwd, D)(D duration) nothrow +TP delegate(scope const TP) everyDuration(TP, Direction dir = Direction.fwd, D)(D duration) nothrow if (isTimePoint!TP && __traits(compiles, TP.init + duration) && (dir == Direction.fwd || dir == Direction.bwd)) { - TP func(in TP tp) + TP func(scope const TP tp) { static if (dir == Direction.fwd) return tp + duration; @@ -7946,7 +7952,7 @@ if (isTimePoint!TP && duration = The duration to add to the time point passed to the delegate. +/ -TP delegate(in TP) everyDuration(TP, Direction dir = Direction.fwd, D) +TP delegate(scope const TP) everyDuration(TP, Direction dir = Direction.fwd, D) (int years, int months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes, @@ -7957,7 +7963,7 @@ if (isTimePoint!TP && __traits(compiles, TP.init.add!"months"(months)) && (dir == Direction.fwd || dir == Direction.bwd)) { - TP func(in TP tp) + TP func(scope const TP tp) { static if (dir == Direction.fwd) { @@ -8214,7 +8220,7 @@ public: /++ The function used to generate the next time point in the range. +/ - TP delegate(in TP) func() pure nothrow @property + TP delegate(scope const TP) func() pure nothrow @property { return _func; } @@ -8237,7 +8243,7 @@ private: func = The function used to generate the time points which are iterated over. +/ - this(in Interval!TP interval, TP delegate(in TP) func) pure nothrow + this(const Interval!TP interval, TP delegate(scope const TP) func) pure nothrow @safe { _func = func; _interval = interval; @@ -8261,7 +8267,7 @@ private: $(REF DateTimeException,std,datetime,date) if $(D_PARAM newTP) is in the wrong direction. +/ - void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const + void _enforceCorrectDirection(scope const TP newTP, size_t line = __LINE__) const { import std.format : format; @@ -8287,7 +8293,7 @@ private: Interval!TP _interval; - TP delegate(in TP) _func; + TP delegate(scope const TP) _func; } //Test that IntervalRange satisfies the range predicates that it's supposed to satisfy. @@ -8324,25 +8330,25 @@ private: import std.datetime.systime; { - Date dateFunc(in Date date) { return date; } + Date dateFunc(scope const Date date) { return date; } auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); auto ir = IntervalRange!(Date, Direction.fwd)(interval, &dateFunc); } { - TimeOfDay todFunc(in TimeOfDay tod) { return tod; } + TimeOfDay todFunc(scope const TimeOfDay tod) { return tod; } auto interval = Interval!TimeOfDay(TimeOfDay(12, 1, 7), TimeOfDay(14, 0, 0)); auto ir = IntervalRange!(TimeOfDay, Direction.fwd)(interval, &todFunc); } { - DateTime dtFunc(in DateTime dt) { return dt; } + DateTime dtFunc(scope const DateTime dt) { return dt; } auto interval = Interval!DateTime(DateTime(2010, 7, 4, 12, 1, 7), DateTime(2012, 1, 7, 14, 0, 0)); auto ir = IntervalRange!(DateTime, Direction.fwd)(interval, &dtFunc); } { - SysTime stFunc(in SysTime st) { return cast(SysTime) st; } + SysTime stFunc(scope const SysTime st) { return cast(SysTime) st; } auto interval = Interval!SysTime(SysTime(DateTime(2010, 7, 4, 12, 1, 7)), SysTime(DateTime(2012, 1, 7, 14, 0, 0))); auto ir = IntervalRange!(SysTime, Direction.fwd)(interval, &stFunc); @@ -8396,7 +8402,8 @@ private: { auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).fwdRange( everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); - assertThrown!DateTimeException((in IntervalRange!(Date, Direction.fwd) range){range.front;}(emptyRange)); + assertThrown!DateTimeException( + (scope const IntervalRange!(Date, Direction.fwd) range){range.front;}(emptyRange)); auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed)); assert(range.front == Date(2010, 7, 4)); @@ -8413,7 +8420,8 @@ private: { auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).bwdRange( everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); - assertThrown!DateTimeException((in IntervalRange!(Date, Direction.bwd) range){range.front;}(emptyRange)); + assertThrown!DateTimeException( + (scope const IntervalRange!(Date, Direction.bwd) range){range.front;}(emptyRange)); auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange( everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed)); @@ -8691,7 +8699,7 @@ public: /++ The function used to generate the next time point in the range. +/ - TP delegate(in TP) func() pure nothrow @property + TP delegate(scope const TP) func() pure nothrow @property { return _func; } @@ -8705,7 +8713,7 @@ private: func = The function used to generate the time points which are iterated over. +/ - this(in PosInfInterval!TP interval, TP delegate(in TP) func) pure nothrow + this(const PosInfInterval!TP interval, TP delegate(scope const TP) func) pure nothrow { _func = func; _interval = interval; @@ -8717,7 +8725,7 @@ private: $(REF DateTimeException,std,datetime,date) if $(D_PARAM newTP) is in the wrong direction. +/ - void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const + void _enforceCorrectDirection(scope const TP newTP, size_t line = __LINE__) const { import std.format : format; @@ -8731,7 +8739,7 @@ private: PosInfInterval!TP _interval; - TP delegate(in TP) _func; + TP delegate(scope const TP) _func; } //Test that PosInfIntervalRange satisfies the range predicates that it's supposed to satisfy. @@ -8767,25 +8775,25 @@ private: import std.datetime.systime; { - Date dateFunc(in Date date) { return date; } + Date dateFunc(scope const Date date) { return date; } auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); auto ir = PosInfIntervalRange!Date(posInfInterval, &dateFunc); } { - TimeOfDay todFunc(in TimeOfDay tod) { return tod; } + TimeOfDay todFunc(scope const TimeOfDay tod) { return tod; } auto posInfInterval = PosInfInterval!TimeOfDay(TimeOfDay(12, 1, 7)); auto ir = PosInfIntervalRange!(TimeOfDay)(posInfInterval, &todFunc); } { - DateTime dtFunc(in DateTime dt) { return dt; } + DateTime dtFunc(scope const DateTime dt) { return dt; } auto posInfInterval = PosInfInterval!DateTime(DateTime(2010, 7, 4, 12, 1, 7)); auto ir = PosInfIntervalRange!(DateTime)(posInfInterval, &dtFunc); } { - SysTime stFunc(in SysTime st) { return cast(SysTime) st; } + SysTime stFunc(scope const SysTime st) { return cast(SysTime) st; } auto posInfInterval = PosInfInterval!SysTime(SysTime(DateTime(2010, 7, 4, 12, 1, 7))); auto ir = PosInfIntervalRange!SysTime(posInfInterval, &stFunc); } @@ -8975,7 +8983,7 @@ public: /++ The function used to generate the next time point in the range. +/ - TP delegate(in TP) func() pure nothrow @property + TP delegate(scope const TP) func() pure nothrow @property { return _func; } @@ -8989,7 +8997,7 @@ private: func = The function used to generate the time points which are iterated over. +/ - this(in NegInfInterval!TP interval, TP delegate(in TP) func) pure nothrow + this(const NegInfInterval!TP interval, TP delegate(scope const TP) func) pure nothrow { _func = func; _interval = interval; @@ -9001,7 +9009,7 @@ private: $(REF DateTimeException,std,datetime,date) if $(D_PARAM newTP) is in the wrong direction. +/ - void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const + void _enforceCorrectDirection(scope const TP newTP, size_t line = __LINE__) const { import std.format : format; @@ -9015,7 +9023,7 @@ private: NegInfInterval!TP _interval; - TP delegate(in TP) _func; + TP delegate(scope const TP) _func; } //Test that NegInfIntervalRange satisfies the range predicates that it's supposed to satisfy. @@ -9049,25 +9057,25 @@ private: import std.datetime.systime; { - Date dateFunc(in Date date) { return date; } + Date dateFunc(scope const Date date) { return date; } auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); auto ir = NegInfIntervalRange!Date(negInfInterval, &dateFunc); } { - TimeOfDay todFunc(in TimeOfDay tod) { return tod; } + TimeOfDay todFunc(scope const TimeOfDay tod) { return tod; } auto negInfInterval = NegInfInterval!TimeOfDay(TimeOfDay(14, 0, 0)); auto ir = NegInfIntervalRange!(TimeOfDay)(negInfInterval, &todFunc); } { - DateTime dtFunc(in DateTime dt) { return dt; } + DateTime dtFunc(scope const DateTime dt) { return dt; } auto negInfInterval = NegInfInterval!DateTime(DateTime(2012, 1, 7, 14, 0, 0)); auto ir = NegInfIntervalRange!(DateTime)(negInfInterval, &dtFunc); } { - SysTime stFunc(in SysTime st) { return cast(SysTime)(st); } + SysTime stFunc(scope const SysTime st) { return cast(SysTime)(st); } auto negInfInterval = NegInfInterval!SysTime(SysTime(DateTime(2012, 1, 7, 14, 0, 0))); auto ir = NegInfIntervalRange!(SysTime)(negInfInterval, &stFunc); } diff --git a/libphobos/src/std/datetime/systime.d b/libphobos/src/std/datetime/systime.d index 81a57dc7a..687c02766 100644 --- a/libphobos/src/std/datetime/systime.d +++ b/libphobos/src/std/datetime/systime.d @@ -419,7 +419,7 @@ public: given $(REF DateTime,std,datetime,date) is assumed to be in the given time zone. +/ - this(in DateTime dateTime, immutable TimeZone tz = null) @safe nothrow + this(DateTime dateTime, immutable TimeZone tz = null) @safe nothrow { try this(dateTime, Duration.zero, tz); @@ -446,6 +446,11 @@ public: test(DateTime(1, 1, 1, 0, 0, 0), new immutable SimpleTimeZone(dur!"minutes"(-60)), 36_000_000_000L); test(DateTime(1, 1, 1, 0, 0, 0), new immutable SimpleTimeZone(Duration.zero), 0); test(DateTime(1, 1, 1, 0, 0, 0), new immutable SimpleTimeZone(dur!"minutes"(60)), -36_000_000_000L); + + static void testScope(scope ref DateTime dt) @safe + { + auto st = SysTime(dt); + } } /++ @@ -465,7 +470,7 @@ public: $(REF DateTimeException,std,datetime,date) if `fracSecs` is negative or if it's greater than or equal to one second. +/ - this(in DateTime dateTime, in Duration fracSecs, immutable TimeZone tz = null) @safe + this(DateTime dateTime, Duration fracSecs, immutable TimeZone tz = null) @safe { enforce(fracSecs >= Duration.zero, new DateTimeException("A SysTime cannot have negative fractional seconds.")); enforce(fracSecs < seconds(1), new DateTimeException("Fractional seconds must be less than one second.")); @@ -503,6 +508,11 @@ public: assertThrown!DateTimeException(SysTime(DateTime.init, hnsecs(-1), UTC())); assertThrown!DateTimeException(SysTime(DateTime.init, seconds(1), UTC())); + + static void testScope(scope ref DateTime dt, scope ref Duration d) @safe + { + auto st = SysTime(dt, d); + } } /++ @@ -517,7 +527,7 @@ public: given $(REF Date,std,datetime,date) is assumed to be in the given time zone. +/ - this(in Date date, immutable TimeZone tz = null) @safe nothrow + this(Date date, immutable TimeZone tz = null) @safe nothrow { _timezone = tz is null ? LocalTime() : tz; @@ -545,6 +555,11 @@ public: test(Date(1, 1, 1), UTC(), 0); test(Date(1, 1, 2), UTC(), 864000000000); test(Date(0, 12, 31), UTC(), -864000000000); + + static void testScope(scope ref Date d) @safe + { + auto st = SysTime(d); + } } /++ @@ -587,28 +602,37 @@ public: } } + /++ Params: rhs = The $(LREF SysTime) to assign to this one. + + Returns: The `this` of this `SysTime`. +/ - ref SysTime opAssign(const ref SysTime rhs) return @safe pure nothrow + ref SysTime opAssign()(auto ref const(SysTime) rhs) return @safe pure nothrow scope { _stdTime = rhs._stdTime; _timezone = rhs._timezone; return this; } - /++ - Params: - rhs = The $(LREF SysTime) to assign to this one. - +/ - ref SysTime opAssign(SysTime rhs) scope return @safe pure nothrow + @safe unittest { - _stdTime = rhs._stdTime; - _timezone = rhs._timezone; - return this; + SysTime st; + st = SysTime(DateTime(2012, 12, 21, 1, 2, 3), UTC()); + assert(st == SysTime(DateTime(2012, 12, 21, 1, 2, 3), UTC())); + + const other = SysTime(DateTime(19, 1, 7, 13, 14, 15), LocalTime()); + st = other; + assert(st == other); + + static void testScope(scope ref SysTime left, const scope SysTime right) @safe + { + left = right; + } } + /++ Checks for equality between this $(LREF SysTime) and the given $(LREF SysTime). @@ -616,13 +640,7 @@ public: Note that the time zone is ignored. Only the internal std times (which are in UTC) are compared. +/ - bool opEquals(const SysTime rhs) @safe const pure nothrow - { - return opEquals(rhs); - } - - /// ditto - bool opEquals(const ref SysTime rhs) @safe const pure nothrow + bool opEquals()(auto ref const(SysTime) rhs) @safe const pure nothrow scope { return _stdTime == rhs._stdTime; } @@ -658,18 +676,25 @@ public: auto st = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); const cst = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); assert(st == st); assert(st == cst); - //assert(st == ist); + assert(st == ist); assert(cst == st); assert(cst == cst); - //assert(cst == ist); - //assert(ist == st); - //assert(ist == cst); - //assert(ist == ist); + assert(cst == ist); + assert(ist == st); + assert(ist == cst); + assert(ist == ist); + + static void testScope(scope ref SysTime left, const scope SysTime right) @safe + { + assert(left == right); + assert(right == left); + } } + /++ Compares this $(LREF SysTime) with the given $(LREF SysTime). @@ -682,7 +707,7 @@ public: $(TR $(TD this > rhs) $(TD > 0)) ) +/ - int opCmp(in SysTime rhs) @safe const pure nothrow + int opCmp()(auto ref const(SysTime) rhs) @safe const pure nothrow scope { if (_stdTime < rhs._stdTime) return -1; @@ -746,22 +771,29 @@ public: auto st = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); const cst = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); assert(st.opCmp(st) == 0); assert(st.opCmp(cst) == 0); - //assert(st.opCmp(ist) == 0); + assert(st.opCmp(ist) == 0); assert(cst.opCmp(st) == 0); assert(cst.opCmp(cst) == 0); - //assert(cst.opCmp(ist) == 0); - //assert(ist.opCmp(st) == 0); - //assert(ist.opCmp(cst) == 0); - //assert(ist.opCmp(ist) == 0); + assert(cst.opCmp(ist) == 0); + assert(ist.opCmp(st) == 0); + assert(ist.opCmp(cst) == 0); + assert(ist.opCmp(ist) == 0); + + static void testScope(scope ref SysTime left, const scope SysTime right) @safe + { + assert(left < right); + assert(right > left); + } } - /** - * Returns: A hash of the $(LREF SysTime) - */ - size_t toHash() const @nogc pure nothrow @safe + + /++ + Returns: A hash of the $(LREF SysTime). + +/ + size_t toHash() const @nogc pure nothrow @safe scope { static if (is(size_t == ulong)) return _stdTime; @@ -798,13 +830,19 @@ public: immutable zone = new SimpleTimeZone(dur!"minutes"(60)); assert(SysTime(DateTime(2000, 1, 1, 1), zone).toHash == SysTime(DateTime(2000, 1, 1), UTC()).toHash); assert(SysTime(DateTime(2000, 1, 1), zone).toHash != SysTime(DateTime(2000, 1, 1), UTC()).toHash); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.toHash(); + } } + /++ Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive are B.C. +/ - @property short year() @safe const nothrow + @property short year() @safe const nothrow scope { return (cast(Date) this).year; } @@ -838,9 +876,14 @@ public: } const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); assert(cst.year == 1999); - //assert(ist.year == 1999); + assert(ist.year == 1999); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.year; + } } /++ @@ -854,7 +897,7 @@ public: $(REF DateTimeException,std,datetime,date) if the new year is not a leap year and the resulting date would be on February 29th. +/ - @property void year(int year) @safe + @property void year(int year) @safe scope { auto hnsecs = adjTime; auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; @@ -886,7 +929,7 @@ public: { import std.range : chain; - static void test(SysTime st, int year, in SysTime expected) + static void test(SysTime st, int year, SysTime expected) { st.year = year; assert(st == expected); @@ -926,9 +969,14 @@ public: } const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); static assert(!__traits(compiles, cst.year = 7)); - //static assert(!__traits(compiles, ist.year = 7)); + static assert(!__traits(compiles, ist.year = 7)); + + static void testScope(scope ref SysTime st) @safe + { + st.year = 42; + } } /++ @@ -937,7 +985,7 @@ public: Throws: $(REF DateTimeException,std,datetime,date) if `isAD` is true. +/ - @property ushort yearBC() @safe const + @property ushort yearBC() @safe const scope { return (cast(Date) this).yearBC; } @@ -967,11 +1015,16 @@ public: auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); st.year = 12; assert(st.year == 12); static assert(!__traits(compiles, cst.year = 12)); - //static assert(!__traits(compiles, ist.year = 12)); + static assert(!__traits(compiles, ist.year = 12)); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.yearBC; + } } @@ -985,7 +1038,7 @@ public: $(REF DateTimeException,std,datetime,date) if a non-positive value is given. +/ - @property void yearBC(int year) @safe + @property void yearBC(int year) @safe scope { auto hnsecs = adjTime; auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; @@ -1016,7 +1069,7 @@ public: @safe unittest { import std.range : chain; - static void test(SysTime st, int year, in SysTime expected) + static void test(SysTime st, int year, SysTime expected) { st.yearBC = year; assert(st == expected, format("SysTime: %s", st)); @@ -1063,17 +1116,23 @@ public: auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); st.yearBC = 12; assert(st.yearBC == 12); static assert(!__traits(compiles, cst.yearBC = 12)); - //static assert(!__traits(compiles, ist.yearBC = 12)); + static assert(!__traits(compiles, ist.yearBC = 12)); + + static void testScope(scope ref SysTime st) @safe + { + st.yearBC = 42; + } } + /++ Month of a Gregorian Year. +/ - @property Month month() @safe const nothrow + @property Month month() @safe const nothrow scope { return (cast(Date) this).month; } @@ -1118,9 +1177,14 @@ public: } const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); assert(cst.month == 7); - //assert(ist.month == 7); + assert(ist.month == 7); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.month; + } } @@ -1134,7 +1198,7 @@ public: $(REF DateTimeException,std,datetime,date) if the given month is not a valid month. +/ - @property void month(Month month) @safe + @property void month(Month month) @safe scope { auto hnsecs = adjTime; auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; @@ -1157,7 +1221,7 @@ public: import std.algorithm.iteration : filter; import std.range : chain; - static void test(SysTime st, Month month, in SysTime expected) + static void test(SysTime st, Month month, SysTime expected) { st.month = cast(Month) month; assert(st == expected); @@ -1226,15 +1290,20 @@ public: } const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.month = 12)); - //static assert(!__traits(compiles, ist.month = 12)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.month = Month.dec)); + static assert(!__traits(compiles, ist.month = Month.dec)); + + static void testScope(scope ref SysTime st) @safe + { + st.month = Month.dec; + } } /++ Day of a Gregorian Month. +/ - @property ubyte day() @safe const nothrow + @property ubyte day() @safe const nothrow scope { return (cast(Date) this).day; } @@ -1280,9 +1349,14 @@ public: } const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); assert(cst.day == 6); - //assert(ist.day == 6); + assert(ist.day == 6); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.day; + } } @@ -1296,7 +1370,7 @@ public: $(REF DateTimeException,std,datetime,date) if the given day is not a valid day of the current month. +/ - @property void day(int day) @safe + @property void day(int day) @safe scope { auto hnsecs = adjTime; auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; @@ -1380,16 +1454,21 @@ public: } const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); static assert(!__traits(compiles, cst.day = 27)); - //static assert(!__traits(compiles, ist.day = 27)); + static assert(!__traits(compiles, ist.day = 27)); + + static void testScope(scope ref SysTime st) @safe + { + st.day = 12; + } } /++ Hours past midnight. +/ - @property ubyte hour() @safe const nothrow + @property ubyte hour() @safe const nothrow scope { auto hnsecs = adjTime; auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; @@ -1439,9 +1518,14 @@ public: } const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); assert(cst.hour == 12); - //assert(ist.hour == 12); + assert(ist.hour == 12); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.hour; + } } @@ -1455,7 +1539,7 @@ public: $(REF DateTimeException,std,datetime,date) if the given hour are not a valid hour of the day. +/ - @property void hour(int hour) @safe + @property void hour(int hour) @safe scope { enforceValid!"hours"(hour); @@ -1498,16 +1582,21 @@ public: assertThrown!DateTimeException(st.hour = 60); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); static assert(!__traits(compiles, cst.hour = 27)); - //static assert(!__traits(compiles, ist.hour = 27)); + static assert(!__traits(compiles, ist.hour = 27)); + + static void testScope(scope ref SysTime st) @safe + { + st.hour = 12; + } } /++ Minutes past the current hour. +/ - @property ubyte minute() @safe const nothrow + @property ubyte minute() @safe const nothrow scope { auto hnsecs = adjTime; auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; @@ -1559,9 +1648,14 @@ public: } const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); assert(cst.minute == 30); - //assert(ist.minute == 30); + assert(ist.minute == 30); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.minute; + } } @@ -1575,7 +1669,7 @@ public: $(REF DateTimeException,std,datetime,date) if the given minute are not a valid minute of an hour. +/ - @property void minute(int minute) @safe + @property void minute(int minute) @safe scope { enforceValid!"minutes"(minute); @@ -1621,16 +1715,21 @@ public: assertThrown!DateTimeException(st.minute = 60); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); static assert(!__traits(compiles, cst.minute = 27)); - //static assert(!__traits(compiles, ist.minute = 27)); + static assert(!__traits(compiles, ist.minute = 27)); + + static void testScope(scope ref SysTime st) @safe + { + st.minute = 12; + } } /++ Seconds past the current minute. +/ - @property ubyte second() @safe const nothrow + @property ubyte second() @safe const nothrow scope { auto hnsecs = adjTime; auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; @@ -1683,9 +1782,14 @@ public: } const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); assert(cst.second == 33); - //assert(ist.second == 33); + assert(ist.second == 33); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.second; + } } @@ -1699,7 +1803,7 @@ public: $(REF DateTimeException,std,datetime,date) if the given second are not a valid second of a minute. +/ - @property void second(int second) @safe + @property void second(int second) @safe scope { enforceValid!"seconds"(second); @@ -1747,9 +1851,14 @@ public: assertThrown!DateTimeException(st.second = 60); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); static assert(!__traits(compiles, cst.seconds = 27)); - //static assert(!__traits(compiles, ist.seconds = 27)); + static assert(!__traits(compiles, ist.seconds = 27)); + + static void testScope(scope ref SysTime st) @safe + { + st.second = 12; + } } @@ -1757,7 +1866,7 @@ public: Fractional seconds past the second (i.e. the portion of a $(LREF SysTime) which is less than a second). +/ - @property Duration fracSecs() @safe const nothrow + @property Duration fracSecs() @safe const nothrow scope { auto hnsecs = removeUnitsFromHNSecs!"days"(adjTime); @@ -1815,9 +1924,14 @@ public: } const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); assert(cst.fracSecs == Duration.zero); - //assert(ist.fracSecs == Duration.zero); + assert(ist.fracSecs == Duration.zero); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.fracSecs; + } } @@ -1833,7 +1947,7 @@ public: $(REF DateTimeException,std,datetime,date) if the given duration is negative or if it's greater than or equal to one second. +/ - @property void fracSecs(Duration fracSecs) @safe + @property void fracSecs(Duration fracSecs) @safe scope { enforce(fracSecs >= Duration.zero, new DateTimeException("A SysTime cannot have negative fractional seconds.")); enforce(fracSecs < seconds(1), new DateTimeException("Fractional seconds must be less than one second.")); @@ -1898,9 +2012,14 @@ public: assertThrown!DateTimeException(st.fracSecs = seconds(1)); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); static assert(!__traits(compiles, cst.fracSecs = msecs(7))); - //static assert(!__traits(compiles, ist.fracSecs = msecs(7))); + static assert(!__traits(compiles, ist.fracSecs = msecs(7))); + + static void testScope(scope ref SysTime st) @safe + { + st.fracSecs = Duration.zero; + } } @@ -1908,7 +2027,7 @@ public: The total hnsecs from midnight, January 1st, 1 A.D. UTC. This is the internal representation of $(LREF SysTime). +/ - @property long stdTime() @safe const pure nothrow + @property long stdTime() @safe const pure nothrow scope { return _stdTime; } @@ -1923,9 +2042,14 @@ public: assert(SysTime(DateTime(1970, 1, 1, 0, 0, 0), UTC()).stdTime == 621_355_968_000_000_000L); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); assert(cst.stdTime > 0); - //assert(ist.stdTime > 0); + assert(ist.stdTime > 0); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.stdTime; + } } @@ -1936,7 +2060,7 @@ public: Params: stdTime = The number of hnsecs since January 1st, 1 A.D. UTC. +/ - @property void stdTime(long stdTime) @safe pure nothrow + @property void stdTime(long stdTime) @safe pure nothrow scope { _stdTime = stdTime; } @@ -1944,7 +2068,7 @@ public: @safe unittest { import core.time; - static void test(long stdTime, in SysTime expected, size_t line = __LINE__) + static void test(long stdTime, SysTime expected, size_t line = __LINE__) { auto st = SysTime(0, UTC()); st.stdTime = stdTime; @@ -1958,9 +2082,14 @@ public: test(621_355_968_000_000_000L, SysTime(DateTime(1970, 1, 1, 0, 0, 0), UTC())); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); static assert(!__traits(compiles, cst.stdTime = 27)); - //static assert(!__traits(compiles, ist.stdTime = 27)); + static assert(!__traits(compiles, ist.stdTime = 27)); + + static void testScope(scope ref SysTime st) @safe + { + st.stdTime = 42; + } } @@ -1971,11 +2100,22 @@ public: hours - adjust the time to this $(LREF SysTime)'s time zone before returning. +/ - @property immutable(TimeZone) timezone() @safe const pure nothrow + @property immutable(TimeZone) timezone() @safe const pure nothrow scope { return _timezone; } + @safe unittest + { + assert(SysTime.init.timezone is InitTimeZone()); + assert(SysTime(DateTime.init, UTC()).timezone is UTC()); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.timezone; + } + } + /++ The current time zone of this $(LREF SysTime). It's internal time is @@ -1988,7 +2128,7 @@ public: timezone = The $(REF _TimeZone,std,datetime,_timezone) to set this $(LREF SysTime)'s time zone to. +/ - @property void timezone(immutable TimeZone timezone) @safe pure nothrow + @property void timezone(immutable TimeZone timezone) @safe pure nothrow scope { if (timezone is null) _timezone = LocalTime(); @@ -1996,14 +2136,40 @@ public: _timezone = timezone; } + @safe unittest + { + SysTime st; + st.timezone = null; + assert(st.timezone is LocalTime()); + st.timezone = UTC(); + assert(st.timezone is UTC()); + + static void testScope(scope ref SysTime st) @safe + { + st.timezone = UTC(); + } + } + /++ Returns whether DST is in effect for this $(LREF SysTime). +/ - @property bool dstInEffect() @safe const nothrow + @property bool dstInEffect() @safe const nothrow scope { return _timezone.dstInEffect(_stdTime); - // This function's unit testing is done in the time zone classes. + } + + // This function's full unit testing is done in the time zone classes, but + // this verifies that SysTime.init works correctly, since historically, it + // has segfaulted due to a null _timezone. + @safe unittest + { + assert(!SysTime.init.dstInEffect); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.dstInEffect; + } } @@ -2011,17 +2177,30 @@ public: Returns what the offset from UTC is for this $(LREF SysTime). It includes the DST offset in effect at that time (if any). +/ - @property Duration utcOffset() @safe const nothrow + @property Duration utcOffset() @safe const nothrow scope { return _timezone.utcOffsetAt(_stdTime); } + // This function's full unit testing is done in the time zone classes, but + // this verifies that SysTime.init works correctly, since historically, it + // has segfaulted due to a null _timezone. + @safe unittest + { + assert(SysTime.init.utcOffset == Duration.zero); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.utcOffset; + } + } + /++ Returns a $(LREF SysTime) with the same std time as this one, but with $(REF LocalTime,std,datetime,timezone) as its time zone. +/ - SysTime toLocalTime() @safe const pure nothrow + SysTime toLocalTime() @safe const pure nothrow scope { return SysTime(_stdTime, LocalTime()); } @@ -2047,6 +2226,11 @@ public: assert(sysTime.toLocalTime().timezone !is UTC()); assert(sysTime.toLocalTime().timezone !is stz); } + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.toLocalTime(); + } } @@ -2054,7 +2238,7 @@ public: Returns a $(LREF SysTime) with the same std time as this one, but with `UTC` as its time zone. +/ - SysTime toUTC() @safe const pure nothrow + SysTime toUTC() @safe const pure nothrow scope { return SysTime(_stdTime, UTC()); } @@ -2068,6 +2252,11 @@ public: assert(sysTime.toUTC().timezone is UTC()); assert(sysTime.toUTC().timezone !is LocalTime()); assert(sysTime.toUTC().timezone !is sysTime.timezone); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.toUTC(); + } } @@ -2075,7 +2264,7 @@ public: Returns a $(LREF SysTime) with the same std time as this one, but with given time zone as its time zone. +/ - SysTime toOtherTZ(immutable TimeZone tz) @safe const pure nothrow + SysTime toOtherTZ(immutable TimeZone tz) @safe const pure nothrow scope { if (tz is null) return SysTime(_stdTime, LocalTime()); @@ -2093,6 +2282,12 @@ public: assert(sysTime.toOtherTZ(stz).timezone is stz); assert(sysTime.toOtherTZ(stz).timezone !is LocalTime()); assert(sysTime.toOtherTZ(stz).timezone !is UTC()); + assert(sysTime.toOtherTZ(null).timezone is LocalTime()); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.toOtherTZ(null); + } } @@ -2125,7 +2320,7 @@ public: A signed integer representing the unix time which is equivalent to this SysTime. +/ - T toUnixTime(T = time_t)() @safe const pure nothrow + T toUnixTime(T = time_t)() @safe const pure nothrow scope if (is(T == int) || is(T == long)) { return stdTimeToUnixTime!T(_stdTime); @@ -2148,6 +2343,11 @@ public: auto ca = SysTime(DateTime(2007, 12, 22, 8, 14, 45), pst); assert(ca.toUnixTime() == 1_198_340_085); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.toUnixTime(); + } } @safe unittest @@ -2237,7 +2437,7 @@ public: `tv_sec`. (so `int.max` if it goes over and `int.min` if it goes under). +/ - timeval toTimeVal() @safe const pure nothrow + timeval toTimeVal() @safe const pure nothrow scope { immutable tv_sec = toUnixTime!(typeof(timeval.tv_sec))(); immutable fracHNSecs = removeUnitsFromHNSecs!"seconds"(_stdTime - 621_355_968_000_000_000L); @@ -2266,6 +2466,11 @@ public: assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), msecs(999), UTC()).toTimeVal() == timeval(0, -1000)); assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()).toTimeVal() == timeval(-1, 0)); assert(SysTime(DateTime(1969, 12, 31, 23, 59, 58), usecs(17), UTC()).toTimeVal() == timeval(-1, -999_983)); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.toTimeVal(); + } } @@ -2277,11 +2482,11 @@ public: $(BLUE This function is Posix-Only.) +/ - timespec toTimeSpec() @safe const pure nothrow; + timespec toTimeSpec() @safe const pure nothrow scope; } else version(Posix) { - timespec toTimeSpec() @safe const pure nothrow + timespec toTimeSpec() @safe const pure nothrow scope { immutable tv_sec = toUnixTime!(typeof(timespec.tv_sec))(); immutable fracHNSecs = removeUnitsFromHNSecs!"seconds"(_stdTime - 621_355_968_000_000_000L); @@ -2317,13 +2522,18 @@ public: timespec(-1, 0)); assert(SysTime(DateTime(1969, 12, 31, 23, 59, 58), usecs(17), UTC()).toTimeSpec() == timespec(-1, -999_983_000)); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.toTimeSpec(); + } } } /++ Returns a `tm` which represents this $(LREF SysTime). +/ - tm toTM() @safe const nothrow + tm toTM() @safe const nothrow scope { auto dateTime = cast(DateTime) this; tm timeInfo; @@ -2342,7 +2552,7 @@ public: { import std.utf : toUTFz; timeInfo.tm_gmtoff = cast(int) convert!("hnsecs", "seconds")(adjTime - _stdTime); - auto zone = (timeInfo.tm_isdst ? _timezone.dstName : _timezone.stdName); + auto zone = timeInfo.tm_isdst ? _timezone.dstName : _timezone.stdName; timeInfo.tm_zone = zone.toUTFz!(char*)(); } @@ -2408,6 +2618,34 @@ public: assert(to!string(timeInfo.tm_zone) == "PDT"); } } + + // This is more to verify that SysTime.init.toTM() doesn't segfault and + // does something sane rather than that the value is anything + // particularly useful. + { + auto timeInfo = SysTime.init.toTM(); + + assert(timeInfo.tm_sec == 0); + assert(timeInfo.tm_min == 0); + assert(timeInfo.tm_hour == 0); + assert(timeInfo.tm_mday == 1); + assert(timeInfo.tm_mon == 0); + assert(timeInfo.tm_year == -1899); + assert(timeInfo.tm_wday == 1); + assert(timeInfo.tm_yday == 0); + assert(timeInfo.tm_isdst == 0); + + version(Posix) + { + assert(timeInfo.tm_gmtoff == 0); + assert(to!string(timeInfo.tm_zone) == "SysTime.init's timezone"); + } + } + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.toTM(); + } } @@ -2430,7 +2668,7 @@ public: allowOverflow = Whether the days should be allowed to overflow, causing the month to increment. +/ - ref SysTime add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow + ref SysTime add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow scope if (units == "years" || units == "months") { auto hnsecs = adjTime; @@ -2676,9 +2914,14 @@ public: } const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); static assert(!__traits(compiles, cst.add!"years"(4))); - //static assert(!__traits(compiles, ist.add!"years"(4))); + static assert(!__traits(compiles, ist.add!"years"(4))); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.add!"years"(42); + } } // Test add!"years"() with AllowDayOverflow.no @@ -3229,9 +3472,14 @@ public: } const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); static assert(!__traits(compiles, cst.add!"months"(4))); - //static assert(!__traits(compiles, ist.add!"months"(4))); + static assert(!__traits(compiles, ist.add!"months"(4))); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.add!"months"(42); + } } // Test add!"months"() with AllowDayOverflow.no @@ -3596,8 +3844,9 @@ public: allowOverflow = Whether the days should be allowed to overflow, causing the month to increment. +/ - ref SysTime roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow - if (units == "years") + ref SysTime roll(string units) + (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow scope + if (units == "years") { return add!"years"(value, allowOverflow); } @@ -3636,16 +3885,22 @@ public: { auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); st.roll!"years"(4); static assert(!__traits(compiles, cst.roll!"years"(4))); - //static assert(!__traits(compiles, ist.roll!"years"(4))); + static assert(!__traits(compiles, ist.roll!"years"(4))); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.roll!"years"(42); + } } // Shares documentation with "years" overload. - ref SysTime roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow - if (units == "months") + ref SysTime roll(string units) + (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow scope + if (units == "months") { auto hnsecs = adjTime; auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; @@ -4045,9 +4300,14 @@ public: } const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); static assert(!__traits(compiles, cst.roll!"months"(4))); - //static assert(!__traits(compiles, ist.roll!"months"(4))); + static assert(!__traits(compiles, ist.roll!"months"(4))); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.roll!"months"(42); + } } // Test roll!"months"() with AllowDayOverflow.no @@ -4446,7 +4706,7 @@ public: value = The number of $(D_PARAM units) to add to this $(LREF SysTime). +/ - ref SysTime roll(string units)(long value) @safe nothrow + ref SysTime roll(string units)(long value) @safe nothrow scope if (units == "days") { auto hnsecs = adjTime; @@ -4799,14 +5059,19 @@ public: } const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); static assert(!__traits(compiles, cst.roll!"days"(4))); - //static assert(!__traits(compiles, ist.roll!"days"(4))); + static assert(!__traits(compiles, ist.roll!"days"(4))); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.roll!"days"(42); + } } // Shares documentation with "days" version. - ref SysTime roll(string units)(long value) @safe nothrow + ref SysTime roll(string units)(long value) @safe nothrow scope if (units == "hours" || units == "minutes" || units == "seconds") { try @@ -4851,7 +5116,7 @@ public: @safe unittest { import core.time; - static void testST(SysTime orig, int hours, in SysTime expected, size_t line = __LINE__) + static void testST(SysTime orig, int hours, SysTime expected, size_t line = __LINE__) @safe { orig.roll!"hours"(hours); if (orig != expected) @@ -5060,16 +5325,21 @@ public: } const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); static assert(!__traits(compiles, cst.roll!"hours"(4))); - //static assert(!__traits(compiles, ist.roll!"hours"(4))); + static assert(!__traits(compiles, ist.roll!"hours"(4))); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.roll!"hours"(42); + } } // Test roll!"minutes"(). @safe unittest { import core.time; - static void testST(SysTime orig, int minutes, in SysTime expected, size_t line = __LINE__) + static void testST(SysTime orig, int minutes, SysTime expected, size_t line = __LINE__) @safe { orig.roll!"minutes"(minutes); if (orig != expected) @@ -5271,16 +5541,21 @@ public: } const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); static assert(!__traits(compiles, cst.roll!"minutes"(4))); - //static assert(!__traits(compiles, ist.roll!"minutes"(4))); + static assert(!__traits(compiles, ist.roll!"minutes"(4))); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.roll!"minutes"(42); + } } // Test roll!"seconds"(). @safe unittest { import core.time; - static void testST(SysTime orig, int seconds, in SysTime expected, size_t line = __LINE__) + static void testST(SysTime orig, int seconds, SysTime expected, size_t line = __LINE__) @safe { orig.roll!"seconds"(seconds); if (orig != expected) @@ -5460,14 +5735,19 @@ public: } const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); static assert(!__traits(compiles, cst.roll!"seconds"(4))); - //static assert(!__traits(compiles, ist.roll!"seconds"(4))); + static assert(!__traits(compiles, ist.roll!"seconds"(4))); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.roll!"seconds"(42); + } } // Shares documentation with "days" version. - ref SysTime roll(string units)(long value) @safe nothrow + ref SysTime roll(string units)(long value) @safe nothrow scope if (units == "msecs" || units == "usecs" || units == "hnsecs") { auto hnsecs = adjTime; @@ -5498,7 +5778,7 @@ public: @safe unittest { import core.time; - static void testST(SysTime orig, int milliseconds, in SysTime expected, size_t line = __LINE__) + static void testST(SysTime orig, int milliseconds, SysTime expected, size_t line = __LINE__) @safe { orig.roll!"msecs"(milliseconds); if (orig != expected) @@ -5595,16 +5875,21 @@ public: } const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.addMSecs(4))); - //static assert(!__traits(compiles, ist.addMSecs(4))); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.roll!"msecs"(4))); + static assert(!__traits(compiles, ist.roll!"msecs"(4))); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.roll!"msecs"(42); + } } // Test roll!"usecs"(). @safe unittest { import core.time; - static void testST(SysTime orig, long microseconds, in SysTime expected, size_t line = __LINE__) + static void testST(SysTime orig, long microseconds, SysTime expected, size_t line = __LINE__) @safe { orig.roll!"usecs"(microseconds); if (orig != expected) @@ -5725,16 +6010,21 @@ public: } const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); static assert(!__traits(compiles, cst.roll!"usecs"(4))); - //static assert(!__traits(compiles, ist.roll!"usecs"(4))); + static assert(!__traits(compiles, ist.roll!"usecs"(4))); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.roll!"usecs"(42); + } } // Test roll!"hnsecs"(). @safe unittest { import core.time; - static void testST(SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__) + static void testST(SysTime orig, long hnsecs, SysTime expected, size_t line = __LINE__) @safe { orig.roll!"hnsecs"(hnsecs); if (orig != expected) @@ -5867,9 +6157,14 @@ public: } const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); static assert(!__traits(compiles, cst.roll!"hnsecs"(4))); - //static assert(!__traits(compiles, ist.roll!"hnsecs"(4))); + static assert(!__traits(compiles, ist.roll!"hnsecs"(4))); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.roll!"hnsecs"(42); + } } @@ -5889,7 +6184,7 @@ public: duration = The $(REF Duration, core,time) to add to or subtract from this $(LREF SysTime). +/ - SysTime opBinary(string op)(Duration duration) @safe const pure nothrow + SysTime opBinary(string op)(Duration duration) @safe const pure nothrow scope if (op == "+" || op == "-") { SysTime retval = SysTime(this._stdTime, this._timezone); @@ -5956,7 +6251,7 @@ public: assert(st - dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_685))); assert(st - dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_671))); - static void testST(in SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__) + static void testST(SysTime orig, long hnsecs, SysTime expected, size_t line = __LINE__) @safe { auto result = orig + dur!"hnsecs"(hnsecs); if (result != expected) @@ -6080,11 +6375,16 @@ public: auto duration = dur!"seconds"(12); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); assert(cst + duration == SysTime(DateTime(1999, 7, 6, 12, 30, 45))); - //assert(ist + duration == SysTime(DateTime(1999, 7, 6, 12, 30, 45))); + assert(ist + duration == SysTime(DateTime(1999, 7, 6, 12, 30, 45))); assert(cst - duration == SysTime(DateTime(1999, 7, 6, 12, 30, 21))); - //assert(ist - duration == SysTime(DateTime(1999, 7, 6, 12, 30, 21))); + assert(ist - duration == SysTime(DateTime(1999, 7, 6, 12, 30, 21))); + + static void testScope(scope ref SysTime st, scope ref Duration d) @safe + { + auto result = st + d; + } } @@ -6104,7 +6404,7 @@ public: duration = The $(REF Duration, core,time) to add to or subtract from this $(LREF SysTime). +/ - ref SysTime opOpAssign(string op)(Duration duration) @safe pure nothrow + ref SysTime opOpAssign(string op)(Duration duration) @safe pure nothrow scope if (op == "+" || op == "-") { immutable hnsecs = duration.total!"hnsecs"; @@ -6152,7 +6452,7 @@ public: assert(before - dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(7))); assert(before - dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_993))); - static void testST(SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__) + static void testST(SysTime orig, long hnsecs, SysTime expected, size_t line = __LINE__) @safe { auto r = orig += dur!"hnsecs"(hnsecs); if (orig != expected) @@ -6284,11 +6584,17 @@ public: auto duration = dur!"seconds"(12); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); static assert(!__traits(compiles, cst += duration)); - //static assert(!__traits(compiles, ist += duration)); + static assert(!__traits(compiles, ist += duration)); static assert(!__traits(compiles, cst -= duration)); - //static assert(!__traits(compiles, ist -= duration)); + static assert(!__traits(compiles, ist -= duration)); + + static void testScope(scope ref SysTime st, scope ref Duration d) @safe + { + auto result1 = st += d; + auto result2 = st -= d; + } } @@ -6302,7 +6608,7 @@ public: $(TR $(TD SysTime) $(TD -) $(TD SysTime) $(TD -->) $(TD duration)) ) +/ - Duration opBinary(string op)(in SysTime rhs) @safe const pure nothrow + Duration opBinary(string op)(SysTime rhs) @safe const pure nothrow scope if (op == "-") { return dur!"hnsecs"(_stdTime - rhs._stdTime); @@ -6384,18 +6690,23 @@ public: auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); assert(st - st == Duration.zero); assert(cst - st == Duration.zero); - //assert(ist - st == Duration.zero); + assert(ist - st == Duration.zero); assert(st - cst == Duration.zero); assert(cst - cst == Duration.zero); - //assert(ist - cst == Duration.zero); + assert(ist - cst == Duration.zero); + + assert(st - ist == Duration.zero); + assert(cst - ist == Duration.zero); + assert(ist - ist == Duration.zero); - //assert(st - ist == Duration.zero); - //assert(cst - ist == Duration.zero); - //assert(ist - ist == Duration.zero); + static void testScope(scope ref SysTime left, scope ref SysTime right) @safe + { + auto result = left - right; + } } @@ -6420,7 +6731,7 @@ public: Params: rhs = The $(LREF SysTime) to subtract from this one. +/ - int diffMonths(in SysTime rhs) @safe const nothrow + int diffMonths(scope SysTime rhs) @safe const nothrow scope { return (cast(Date) this).diffMonths(cast(Date) rhs); } @@ -6449,25 +6760,30 @@ public: import core.time; auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); assert(st.diffMonths(st) == 0); assert(cst.diffMonths(st) == 0); - //assert(ist.diffMonths(st) == 0); + assert(ist.diffMonths(st) == 0); assert(st.diffMonths(cst) == 0); assert(cst.diffMonths(cst) == 0); - //assert(ist.diffMonths(cst) == 0); + assert(ist.diffMonths(cst) == 0); + + assert(st.diffMonths(ist) == 0); + assert(cst.diffMonths(ist) == 0); + assert(ist.diffMonths(ist) == 0); - //assert(st.diffMonths(ist) == 0); - //assert(cst.diffMonths(ist) == 0); - //assert(ist.diffMonths(ist) == 0); + static void testScope(scope ref SysTime left, scope ref SysTime right) @safe + { + auto result = left.diffMonths(right); + } } /++ Whether this $(LREF SysTime) is in a leap year. +/ - @property bool isLeapYear() @safe const nothrow + @property bool isLeapYear() @safe const nothrow scope { return (cast(Date) this).isLeapYear; } @@ -6477,17 +6793,22 @@ public: import core.time; auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); assert(!st.isLeapYear); assert(!cst.isLeapYear); - //assert(!ist.isLeapYear); + assert(!ist.isLeapYear); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.isLeapYear; + } } /++ Day of the week this $(LREF SysTime) is on. +/ - @property DayOfWeek dayOfWeek() @safe const nothrow + @property DayOfWeek dayOfWeek() @safe const nothrow scope { return getDayOfWeek(dayOfGregorianCal); } @@ -6497,17 +6818,22 @@ public: import core.time; auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); assert(st.dayOfWeek == DayOfWeek.tue); assert(cst.dayOfWeek == DayOfWeek.tue); - //assert(ist.dayOfWeek == DayOfWeek.tue); + assert(ist.dayOfWeek == DayOfWeek.tue); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.dayOfWeek; + } } /++ Day of the year this $(LREF SysTime) is on. +/ - @property ushort dayOfYear() @safe const nothrow + @property ushort dayOfYear() @safe const nothrow scope { return (cast(Date) this).dayOfYear; } @@ -6528,10 +6854,15 @@ public: import core.time; auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); assert(st.dayOfYear == 187); assert(cst.dayOfYear == 187); - //assert(ist.dayOfYear == 187); + assert(ist.dayOfYear == 187); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.dayOfYear; + } } @@ -6542,7 +6873,7 @@ public: day = The day of the year to set which day of the year this $(LREF SysTime) is on. +/ - @property void dayOfYear(int day) @safe + @property void dayOfYear(int day) @safe scope { immutable hnsecs = adjTime; immutable days = convert!("hnsecs", "days")(hnsecs); @@ -6561,18 +6892,23 @@ public: import core.time; auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); st.dayOfYear = 12; assert(st.dayOfYear == 12); static assert(!__traits(compiles, cst.dayOfYear = 12)); - //static assert(!__traits(compiles, ist.dayOfYear = 12)); + static assert(!__traits(compiles, ist.dayOfYear = 12)); + + static void testScope(scope ref SysTime st) @safe + { + st.dayOfYear = 42; + } } /++ The Xth day of the Gregorian Calendar that this $(LREF SysTime) is on. +/ - @property int dayOfGregorianCal() @safe const nothrow + @property int dayOfGregorianCal() @safe const nothrow scope { immutable adjustedTime = adjTime; @@ -6770,9 +7106,14 @@ public: assert(SysTime(DateTime(-3760, 9, 7, 0, 0, 0)).dayOfGregorianCal == -1_373_427); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); assert(cst.dayOfGregorianCal == 729_941); - //assert(ist.dayOfGregorianCal == 729_941); + assert(ist.dayOfGregorianCal == 729_941); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.dayOfGregorianCal; + } } @@ -6946,7 +7287,7 @@ public: days = The day of the Gregorian Calendar to set this $(LREF SysTime) to. +/ - @property void dayOfGregorianCal(int days) @safe nothrow + @property void dayOfGregorianCal(int days) @safe nothrow scope { auto hnsecs = adjTime; hnsecs = removeUnitsFromHNSecs!"days"(hnsecs); @@ -7000,7 +7341,7 @@ public: @safe unittest { import core.time; - void testST(SysTime orig, int day, in SysTime expected, size_t line = __LINE__) + void testST(SysTime orig, int day, SysTime expected, size_t line = __LINE__) @safe { orig.dayOfGregorianCal = day; if (orig != expected) @@ -7037,7 +7378,7 @@ public: auto st = SysTime(DateTime(1, 1, 1, 12, 2, 9), msecs(212)); - void testST2(int day, in SysTime expected, size_t line = __LINE__) + void testST2(int day, SysTime expected, size_t line = __LINE__) @safe { st.dayOfGregorianCal = day; if (st != expected) @@ -7192,9 +7533,14 @@ public: testST2(-735_173, SysTime(DateTime(-2012, 3, 1, 12, 2, 9), msecs(212))); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); static assert(!__traits(compiles, cst.dayOfGregorianCal = 7)); - //static assert(!__traits(compiles, ist.dayOfGregorianCal = 7)); + static assert(!__traits(compiles, ist.dayOfGregorianCal = 7)); + + static void testScope(scope ref SysTime st) @safe + { + st.dayOfGregorianCal = 42; + } } @@ -7204,7 +7550,7 @@ public: See_Also: $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date). +/ - @property ubyte isoWeek() @safe const nothrow + @property ubyte isoWeek() @safe const nothrow scope { return (cast(Date) this).isoWeek; } @@ -7224,12 +7570,20 @@ public: assert(ist.isoWeek == 41); } + @safe unittest + { + static void testScope(scope ref SysTime st) @safe + { + auto result = st.isoWeek; + } + } + /++ $(LREF SysTime) for the last day in the month that this Date is in. The time portion of endOfMonth is always 23:59:59.9999999. +/ - @property SysTime endOfMonth() @safe const nothrow + @property SysTime endOfMonth() @safe const nothrow scope { immutable hnsecs = adjTime; immutable days = getUnitsFromHNSecs!"days"(hnsecs); @@ -7310,16 +7664,21 @@ public: SysTime(DateTime(-1999, 12, 31, 23, 59, 59), hnsecs(9_999_999))); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); assert(cst.endOfMonth == SysTime(DateTime(1999, 7, 31, 23, 59, 59), hnsecs(9_999_999))); - //assert(ist.endOfMonth == SysTime(DateTime(1999, 7, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(ist.endOfMonth == SysTime(DateTime(1999, 7, 31, 23, 59, 59), hnsecs(9_999_999))); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.endOfMonth; + } } /++ The last day in the month that this $(LREF SysTime) is in. +/ - @property ubyte daysInMonth() @safe const nothrow + @property ubyte daysInMonth() @safe const nothrow scope { return Date(dayOfGregorianCal).daysInMonth; } @@ -7370,16 +7729,21 @@ public: assert(SysTime(DateTime(-1999, 12, 1, 12, 52, 13)).daysInMonth == 31); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); assert(cst.daysInMonth == 31); - //assert(ist.daysInMonth == 31); + assert(ist.daysInMonth == 31); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.daysInMonth; + } } /++ Whether the current year is a date in A.D. +/ - @property bool isAD() @safe const nothrow + @property bool isAD() @safe const nothrow scope { return adjTime >= 0; } @@ -7407,9 +7771,14 @@ public: assert(!SysTime(DateTime(-2010, 7, 4, 12, 2, 2)).isAD); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); assert(cst.isAD); - //assert(ist.isAD); + assert(ist.isAD); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.isAD; + } } @@ -7420,7 +7789,7 @@ public: this function returns 2_450_173, while from noon onward, the Julian day number would be 2_450_174, so this function returns 2_450_174. +/ - @property long julianDay() @safe const nothrow + @property long julianDay() @safe const nothrow scope { immutable jd = dayOfGregorianCal + 1_721_425; return hour < 12 ? jd - 1 : jd; @@ -7454,9 +7823,14 @@ public: assert(SysTime(DateTime(2010, 8, 24, 12, 0, 0)).julianDay == 2_455_433); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); assert(cst.julianDay == 2_451_366); - //assert(ist.julianDay == 2_451_366); + assert(ist.julianDay == 2_451_366); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.julianDay; + } } @@ -7465,7 +7839,7 @@ public: any time on this date (since, the modified Julian day changes at midnight). +/ - @property long modJulianDay() @safe const nothrow + @property long modJulianDay() @safe const nothrow scope { return dayOfGregorianCal + 1_721_425 - 2_400_001; } @@ -7480,16 +7854,21 @@ public: assert(SysTime(DateTime(2010, 8, 24, 12, 0, 0)).modJulianDay == 55_432); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); assert(cst.modJulianDay == 51_365); - //assert(ist.modJulianDay == 51_365); + assert(ist.modJulianDay == 51_365); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.modJulianDay; + } } /++ Returns a $(REF Date,std,datetime,date) equivalent to this $(LREF SysTime). +/ - Date opCast(T)() @safe const nothrow + Date opCast(T)() @safe const nothrow scope if (is(Unqual!T == Date)) { return Date(dayOfGregorianCal); @@ -7515,9 +7894,14 @@ public: assert(cast(Date) SysTime(DateTime(-2001, 1, 1, 14, 12, 11)) == Date(-2001, 1, 1)); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); assert(cast(Date) cst != Date.init); - //assert(cast(Date) ist != Date.init); + assert(cast(Date) ist != Date.init); + + static void testScope(scope ref SysTime st) @safe + { + auto result = cast(Date) st; + } } @@ -7525,7 +7909,7 @@ public: Returns a $(REF DateTime,std,datetime,date) equivalent to this $(LREF SysTime). +/ - DateTime opCast(T)() @safe const nothrow + DateTime opCast(T)() @safe const nothrow scope if (is(Unqual!T == DateTime)) { try @@ -7576,9 +7960,14 @@ public: DateTime(2011, 1, 13, 8, 17, 2)); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); assert(cast(DateTime) cst != DateTime.init); - //assert(cast(DateTime) ist != DateTime.init); + assert(cast(DateTime) ist != DateTime.init); + + static void testScope(scope ref SysTime st) @safe + { + auto result = cast(DateTime) st; + } } @@ -7586,7 +7975,7 @@ public: Returns a $(REF TimeOfDay,std,datetime,date) equivalent to this $(LREF SysTime). +/ - TimeOfDay opCast(T)() @safe const nothrow + TimeOfDay opCast(T)() @safe const nothrow scope if (is(Unqual!T == TimeOfDay)) { try @@ -7627,9 +8016,14 @@ public: assert(cast(TimeOfDay) SysTime(DateTime(-2001, 1, 1, 14, 12, 11)) == TimeOfDay(14, 12, 11)); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); assert(cast(TimeOfDay) cst != TimeOfDay.init); - //assert(cast(TimeOfDay) ist != TimeOfDay.init); + assert(cast(TimeOfDay) ist != TimeOfDay.init); + + static void testScope(scope ref SysTime st) @safe + { + auto result = cast(TimeOfDay) st; + } } @@ -7638,12 +8032,20 @@ public: // It may be a good idea to keep it though, since casting from a type to itself // should be allowed, and it doesn't work without this opCast() since opCast() // has already been defined for other types. - SysTime opCast(T)() @safe const pure nothrow + SysTime opCast(T)() @safe const pure nothrow scope if (is(Unqual!T == SysTime)) { return SysTime(_stdTime, _timezone); } + @safe unittest + { + static void testScope(scope ref SysTime st) @safe + { + auto result = cast(SysTime) st; + } + } + /++ Converts this $(LREF SysTime) to a string with the format @@ -7682,7 +8084,7 @@ public: Returns: A `string` when not using an output range; `void` otherwise. +/ - string toISOString() @safe const nothrow + string toISOString() @safe const nothrow scope { import std.array : appender; auto app = appender!string(); @@ -7695,7 +8097,7 @@ public: } /// ditto - void toISOString(W)(ref W writer) const + void toISOString(W)(ref W writer) const scope if (isOutputRange!(W, char)) { immutable adjustedTime = adjTime; @@ -7805,9 +8207,14 @@ public: assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), hnsecs(507890)).toISOString() == "-100001020T010101.050789"); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cast(TimeOfDay) cst != TimeOfDay.init); - //assert(cast(TimeOfDay) ist != TimeOfDay.init); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.toISOString() == "19990706T123033"); + assert(ist.toISOString() == "19990706T123033"); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.toISOString(); + } } @@ -7837,7 +8244,7 @@ public: Returns: A `string` when not using an output range; `void` otherwise. +/ - string toISOExtString() @safe const nothrow + string toISOExtString() @safe const nothrow scope { import std.array : appender; auto app = appender!string(); @@ -7850,7 +8257,7 @@ public: } /// ditto - void toISOExtString(W)(ref W writer) const + void toISOExtString(W)(ref W writer) const scope if (isOutputRange!(W, char)) { immutable adjustedTime = adjTime; @@ -7966,9 +8373,14 @@ public: "-10000-10-20T01:01:01.050789"); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cast(TimeOfDay) cst != TimeOfDay.init); - //assert(cast(TimeOfDay) ist != TimeOfDay.init); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.toISOExtString() == "1999-07-06T12:30:33"); + assert(ist.toISOExtString() == "1999-07-06T12:30:33"); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.toISOExtString(); + } } /++ @@ -7996,7 +8408,7 @@ public: Returns: A `string` when not using an output range; `void` otherwise. +/ - string toSimpleString() @safe const nothrow + string toSimpleString() @safe const nothrow scope { import std.array : appender; auto app = appender!string(); @@ -8009,7 +8421,7 @@ public: } /// ditto - void toSimpleString(W)(ref W writer) const + void toSimpleString(W)(ref W writer) const scope if (isOutputRange!(W, char)) { immutable adjustedTime = adjTime; @@ -8126,9 +8538,14 @@ public: "-10000-Oct-20 01:01:01.050789"); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cast(TimeOfDay) cst != TimeOfDay.init); - //assert(cast(TimeOfDay) ist != TimeOfDay.init); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.toSimpleString() == "1999-Jul-06 12:30:33"); + assert(ist.toSimpleString() == "1999-Jul-06 12:30:33"); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.toSimpleString(); + } } @@ -8161,13 +8578,13 @@ public: Returns: A `string` when not using an output range; `void` otherwise. +/ - string toString() @safe const nothrow + string toString() @safe const nothrow scope { return toSimpleString(); } /// ditto - void toString(W)(ref W writer) const + void toString(W)(ref W writer) const scope if (isOutputRange!(W, char)) { toSimpleString(writer); @@ -8178,10 +8595,15 @@ public: import core.time; auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(st.toString()); - assert(cst.toString()); - //assert(ist.toString()); + immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, st.toString())); + static assert(__traits(compiles, cst.toString())); + static assert(__traits(compiles, ist.toString())); + + static void testScope(scope ref SysTime st) @safe + { + auto result = st.toString(); + } } @@ -8233,7 +8655,7 @@ public: not in the ISO format or if the resulting $(LREF SysTime) would not be valid. +/ - static SysTime fromISOString(S)(in S isoString, immutable TimeZone tz = null) @safe + static SysTime fromISOString(S)(scope const S isoString, immutable TimeZone tz = null) @safe if (isSomeString!S) { import std.algorithm.searching : startsWith, find; @@ -8472,6 +8894,11 @@ public: test("20101222T172201.1234567+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_234_567), east60)); test("20101222T172201.0000000+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); test("20101222T172201.45+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), east480)); + + static void testScope(scope ref string str) @safe + { + auto result = SysTime.fromISOString(str); + } } // bug# 17801 @@ -8527,7 +8954,7 @@ public: not in the ISO format or if the resulting $(LREF SysTime) would not be valid. +/ - static SysTime fromISOExtString(S)(in S isoExtString, immutable TimeZone tz = null) @safe + static SysTime fromISOExtString(S)(scope const S isoExtString, immutable TimeZone tz = null) @safe if (isSomeString!(S)) { import std.algorithm.searching : countUntil, find; @@ -8714,6 +9141,11 @@ public: test("2010-12-22T17:22:01.0+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); test("2010-12-22T17:22:01.0000000+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); test("2010-12-22T17:22:01.45+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), east480)); + + static void testScope(scope ref string str) @safe + { + auto result = SysTime.fromISOExtString(str); + } } // bug# 17801 @@ -8770,7 +9202,7 @@ public: not in the ISO format or if the resulting $(LREF SysTime) would not be valid. +/ - static SysTime fromSimpleString(S)(in S simpleString, immutable TimeZone tz = null) @safe + static SysTime fromSimpleString(S)(scope const S simpleString, immutable TimeZone tz = null) @safe if (isSomeString!(S)) { import std.algorithm.searching : find; @@ -8960,6 +9392,11 @@ public: test("2010-Dec-22 17:22:01.0+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); test("2010-Dec-22 17:22:01.0000000+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); test("2010-Dec-22 17:22:01.45+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), east480)); + + static void testScope(scope ref string str) @safe + { + auto result = SysTime.fromSimpleString(str); + } } // bug# 17801 @@ -9035,18 +9472,65 @@ private: } - // Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=5058 - /+ - invariant() + final class InitTimeZone : TimeZone { - assert(_timezone !is null, "Invariant Failure: timezone is null. Were you foolish enough to use " ~ - "SysTime.init? (since timezone for SysTime.init can't be set at compile time)."); + public: + + static immutable(InitTimeZone) opCall() @safe pure nothrow @nogc { return _initTimeZone; } + + @property override bool hasDST() @safe const nothrow @nogc { return false; } + + override bool dstInEffect(long stdTime) @safe const nothrow @nogc { return false; } + + override long utcToTZ(long stdTime) @safe const nothrow @nogc { return 0; } + + override long tzToUTC(long adjTime) @safe const nothrow @nogc { return 0; } + + override Duration utcOffsetAt(long stdTime) @safe const nothrow @nogc { return Duration.zero; } + + private: + + this() @safe immutable pure + { + super("SysTime.init's timezone", "SysTime.init's timezone", "SysTime.init's timezone"); + } + + static immutable InitTimeZone _initTimeZone = new immutable(InitTimeZone); + } + + // https://issues.dlang.org/show_bug.cgi?id=17732 + @safe unittest + { + assert(SysTime.init.timezone is InitTimeZone()); + assert(SysTime.init.toISOString() == "00010101T000000+00:00"); + assert(SysTime.init.toISOExtString() == "0001-01-01T00:00:00+00:00"); + assert(SysTime.init.toSimpleString() == "0001-Jan-01 00:00:00+00:00"); + assert(SysTime.init.toString() == "0001-Jan-01 00:00:00+00:00"); + } + + // Assigning a value to _timezone in SysTime.init currently doesn't work due + // to https://issues.dlang.org/show_bug.cgi?id=17740. So, to hack around + // that problem, these accessors have been added so that we can insert a + // runtime check for null and then use InitTimeZone for SysTime.init (which + // which is the only case where _timezone would be null). This thus fixes + // the problem with segfaulting when using SysTime.init but at the cost of + // what should be an unnecessary null check. Once 17740 has finally been + // fixed, _timezoneStorage should be removed, these accessors should be + // removed, and the _timezone variable declaration should be restored. + pragma(inline, true) @property _timezone() @safe const pure nothrow @nogc + { + return _timezoneStorage is null ? InitTimeZone() : _timezoneStorage; + } + + pragma(inline, true) @property void _timezone(immutable TimeZone tz) @safe pure nothrow @nogc scope + { + _timezoneStorage = tz; } - +/ long _stdTime; - Rebindable!(immutable TimeZone) _timezone; + Rebindable!(immutable TimeZone) _timezoneStorage; + //Rebindable!(immutable TimeZone) _timezone = InitTimeZone(); } /// @@ -9279,7 +9763,7 @@ version(StdDdoc) unlikely to happen given that `SysTime.max` is in 29,228 A.D. and the maximum `SYSTEMTIME` is in 30,827 A.D. +/ - SysTime SYSTEMTIMEToSysTime(const SYSTEMTIME* st, immutable TimeZone tz = LocalTime()) @safe; + SysTime SYSTEMTIMEToSysTime(const scope SYSTEMTIME* st, immutable TimeZone tz = LocalTime()) @safe; /++ @@ -9299,7 +9783,7 @@ version(StdDdoc) $(LREF SysTime) will not fit in a `SYSTEMTIME`. This will only happen if the $(LREF SysTime)'s date is prior to 1601 A.D. +/ - SYSTEMTIME SysTimeToSYSTEMTIME(in SysTime sysTime) @safe; + SYSTEMTIME SysTimeToSYSTEMTIME(scope SysTime sysTime) @safe; /++ @@ -9366,11 +9850,11 @@ version(StdDdoc) $(REF DateTimeException,std,datetime,date) if the given $(LREF SysTime) will not fit in a `FILETIME`. +/ - FILETIME SysTimeToFILETIME(SysTime sysTime) @safe; + FILETIME SysTimeToFILETIME(scope SysTime sysTime) @safe; } else version(Windows) { - SysTime SYSTEMTIMEToSysTime(const SYSTEMTIME* st, immutable TimeZone tz = LocalTime()) @safe + SysTime SYSTEMTIMEToSysTime(const scope SYSTEMTIME* st, immutable TimeZone tz = LocalTime()) @safe { const max = SysTime.max; @@ -9426,10 +9910,15 @@ else version(Windows) auto converted = SYSTEMTIMEToSysTime(&st, UTC()); import core.time : abs; assert(abs((converted - sysTime)) <= dur!"seconds"(2)); + + static void testScope(scope SYSTEMTIME* st) @safe + { + auto result = SYSTEMTIMEToSysTime(st); + } } - SYSTEMTIME SysTimeToSYSTEMTIME(in SysTime sysTime) @safe + SYSTEMTIME SysTimeToSYSTEMTIME(scope SysTime sysTime) @safe { immutable dt = cast(DateTime) sysTime; @@ -9466,6 +9955,11 @@ else version(Windows) assert(st.wMinute == result.wMinute); assert(st.wSecond == result.wSecond); assert(st.wMilliseconds == result.wMilliseconds); + + static void testScope(scope ref SysTime st) @safe + { + auto result = SysTimeToSYSTEMTIME(st); + } } private enum hnsecsFrom1601 = 504_911_232_000_000_000L; @@ -9503,6 +9997,11 @@ else version(Windows) import core.time : abs; assert(abs((converted - sysTime)) <= dur!"seconds"(2)); + + static void testScope(scope FILETIME* ft) @safe + { + auto result = FILETIMEToSysTime(ft); + } } @@ -9521,7 +10020,7 @@ else version(Windows) return ft; } - FILETIME SysTimeToFILETIME(SysTime sysTime) @safe + FILETIME SysTimeToFILETIME(scope SysTime sysTime) @safe { return stdTimeToFILETIME(sysTime.stdTime); } @@ -9539,6 +10038,11 @@ else version(Windows) assert(ft.dwLowDateTime == result.dwLowDateTime); assert(ft.dwHighDateTime == result.dwHighDateTime); + + static void testScope(scope ref SysTime st) @safe + { + auto result = SysTimeToFILETIME(st); + } } } @@ -9589,6 +10093,14 @@ SysTime DosFileTimeToSysTime(DosFileTime dft, immutable TimeZone tz = LocalTime( assert(DosFileTimeToSysTime(0x3E3F8456) == SysTime(DateTime(2011, 1, 31, 16, 34, 44))); } +@safe unittest +{ + static void testScope(scope ref DosFileTime dft) @safe + { + auto result = DosFileTimeToSysTime(dft); + } +} + /++ Converts from $(LREF SysTime) to DOS file date/time. @@ -9600,7 +10112,7 @@ SysTime DosFileTimeToSysTime(DosFileTime dft, immutable TimeZone tz = LocalTime( $(REF DateTimeException,std,datetime,date) if the given $(LREF SysTime) cannot be converted to a `DosFileTime`. +/ -DosFileTime SysTimeToDosFileTime(SysTime sysTime) @safe +DosFileTime SysTimeToDosFileTime(scope SysTime sysTime) @safe { auto dateTime = cast(DateTime) sysTime; @@ -9631,6 +10143,14 @@ DosFileTime SysTimeToDosFileTime(SysTime sysTime) @safe assert(SysTimeToDosFileTime(SysTime(DateTime(2011, 1, 31, 16, 34, 44))) == 0x3E3F8456); } +@safe unittest +{ + static void testScope(scope ref SysTime st) @safe + { + auto result = SysTimeToDosFileTime(st); + } +} + /++ The given array of `char` or random-access range of `char` or @@ -9668,14 +10188,14 @@ DosFileTime SysTimeToDosFileTime(SysTime sysTime) @safe follow the grammar for a date-time field or if the resulting $(LREF SysTime) is invalid. +/ -SysTime parseRFC822DateTime()(in char[] value) @safe +SysTime parseRFC822DateTime()(scope const char[] value) @safe { import std.string : representation; return parseRFC822DateTime(value.representation); } /++ Ditto +/ -SysTime parseRFC822DateTime(R)(R value) @safe +SysTime parseRFC822DateTime(R)(scope R value) if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && (is(Unqual!(ElementType!R) == char) || is(Unqual!(ElementType!R) == ubyte))) { @@ -10185,6 +10705,11 @@ version(unittest) void testBadParse822(alias cr)(string str, size_t line = __LIN testBad((cast(string) currStr) ~ " "); } }} + + static void testScope(scope ref string str) @safe + { + auto result = parseRFC822DateTime(str); + } } // Obsolete Format per section 4.3 of RFC 5322. @@ -10471,7 +10996,7 @@ void fracSecsToISOString(W)(ref W writer, int hnsecs) Returns a Duration corresponding to to the given ISO string of fractional seconds. +/ -static Duration fracSecsFromISOString(S)(in S isoString) @trusted pure +static Duration fracSecsFromISOString(S)(scope const S isoString) @safe pure if (isSomeString!S) { import std.algorithm.searching : all; diff --git a/libphobos/src/std/datetime/timezone.d b/libphobos/src/std/datetime/timezone.d index 992aeecc5..fce854283 100644 --- a/libphobos/src/std/datetime/timezone.d +++ b/libphobos/src/std/datetime/timezone.d @@ -65,8 +65,13 @@ abstract class TimeZone public: /++ - The name of the time zone per the TZ Database. This is the name used to - get a $(LREF TimeZone) by name with `TimeZone.getTimeZone`. + The name of the time zone. Exactly how the time zone name is formatted + depends on the derived class. In the case of $(LREF PosixTimeZone), it's + the TZ Database name, whereas with $(LREF WindowsTimeZone), it's the + name that Windows chose to give the registry key for that time zone + (typically the name that they give $(LREF stdTime) if the OS is in + English). For other time zone types, what it is depends on how they're + implemented. See_Also: $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ @@ -162,40 +167,6 @@ public: return dur!"hnsecs"(utcToTZ(stdTime) - stdTime); } - // Explicitly undocumented. It will be removed in June 2018. @@@DEPRECATED_2018-07@@@ - deprecated("Use PosixTimeZone.getTimeZone or WindowsTimeZone.getTimeZone instead") - static immutable(TimeZone) getTimeZone(string name) @safe - { - version(Posix) - return PosixTimeZone.getTimeZone(name); - else version(Windows) - { - import std.datetime.date : DateTimeException; - import std.format : format; - auto windowsTZName = tzDatabaseNameToWindowsTZName(name); - if (windowsTZName != null) - { - try - return WindowsTimeZone.getTimeZone(windowsTZName); - catch (DateTimeException dte) - { - auto oldName = _getOldName(windowsTZName); - if (oldName != null) - return WindowsTimeZone.getTimeZone(oldName); - throw dte; - } - } - else - throw new DateTimeException(format("%s does not have an equivalent Windows time zone.", name)); - } - } - - /// - deprecated @safe unittest - { - auto tz = TimeZone.getTimeZone("America/Los_Angeles"); - } - // The purpose of this is to handle the case where a Windows time zone is // new and exists on an up-to-date Windows box but does not exist on Windows // boxes which have not been properly updated. The "date added" is included @@ -279,7 +250,7 @@ public: { setTZEnvVar(tzName); - static void testTM(in SysTime st) + static void testTM(scope const SysTime st) { import core.stdc.time : tm; import core.sys.posix.time : localtime_r; @@ -519,54 +490,6 @@ public: } - // Explicitly undocumented. It will be removed in June 2018. @@@DEPRECATED_2018-07@@@ - deprecated("Use PosixTimeZone.getInstalledTZNames or WindowsTimeZone.getInstalledTZNames instead") - static string[] getInstalledTZNames(string subName = "") @safe - { - version(Posix) - return PosixTimeZone.getInstalledTZNames(subName); - else version(Windows) - { - import std.algorithm.searching : startsWith; - import std.algorithm.sorting : sort; - import std.array : appender; - - auto windowsNames = WindowsTimeZone.getInstalledTZNames(); - auto retval = appender!(string[])(); - - foreach (winName; windowsNames) - { - auto tzName = windowsTZNameToTZDatabaseName(winName); - if (tzName !is null && tzName.startsWith(subName)) - retval.put(tzName); - } - - sort(retval.data); - return retval.data; - } - } - - deprecated @safe unittest - { - import std.exception : assertNotThrown; - import std.stdio : writeln; - static void testPZSuccess(string tzName) - { - scope(failure) writeln("TZName which threw: ", tzName); - TimeZone.getTimeZone(tzName); - } - - auto tzNames = getInstalledTZNames(); - // This was not previously tested, and it's currently failing, so I'm - // leaving it commented out until I can sort it out. - //assert(equal(tzNames, tzNames.uniq())); - - import std.datetime.date : DateTimeException; - foreach (tzName; tzNames) - assertNotThrown!DateTimeException(testPZSuccess(tzName)); - } - - protected: /++ @@ -619,14 +542,12 @@ public: version(StdDdoc) { /++ - The name of the time zone per the TZ Database. This is the name used - to get a $(LREF TimeZone) by name with `TimeZone.getTimeZone`. - - Note that this always returns the empty string. This is because time - zones cannot be uniquely identified by the attributes given by the - OS (such as the `stdName` and `dstName`), and neither Posix - systems nor Windows systems provide an easy way to get the TZ - Database name of the local time zone. + In principle, this is the name of the local time zone. However, + this always returns the empty string. This is because time zones + cannot be uniquely identified by the attributes given by the + OS (such as the `stdName` and `dstName`), and neither Posix systems + nor Windows systems provide an easy way to get the TZ Database name + of the local time zone. See_Also: $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ @@ -1729,7 +1650,7 @@ package: import core.exception : AssertError; import std.format : format; - static void test(in string isoString, int expectedOffset, size_t line = __LINE__) + static void test(scope const string isoString, int expectedOffset, size_t line = __LINE__) { auto stz = SimpleTimeZone.fromISOExtString(isoString); if (stz.utcOffset != dur!"minutes"(expectedOffset)) @@ -1891,7 +1812,7 @@ package: import core.exception : AssertError; import std.format : format; - static void test(in string isoExtString, int expectedOffset, size_t line = __LINE__) + static void test(scope const string isoExtString, int expectedOffset, size_t line = __LINE__) { auto stz = SimpleTimeZone.fromISOExtString(isoExtString); if (stz.utcOffset != dur!"minutes"(expectedOffset)) @@ -1939,10 +1860,8 @@ private: files on disk) on Windows by providing the TZ Database files and telling `PosixTimeZone.getTimeZone` where the directory holding them is. - To get a `PosixTimeZone`, either call `PosixTimeZone.getTimeZone` - (which allows specifying the location the time zone files) or call - `TimeZone.getTimeZone` (which will give a `PosixTimeZone` on Posix - systems and a $(LREF WindowsTimeZone) on Windows systems). + To get a `PosixTimeZone`, call `PosixTimeZone.getTimeZone` + (which allows specifying the location the time zone files). Note: Unless your system's local time zone deals with leap seconds (which is @@ -2629,7 +2548,7 @@ private: +/ struct TTInfo { - this(in TempTTInfo tempTTInfo, string abbrev) @safe immutable pure + this(scope const TempTTInfo tempTTInfo, string abbrev) @safe immutable pure { utcOffset = tempTTInfo.tt_gmtoff; isDST = tempTTInfo.tt_isdst; @@ -2913,10 +2832,7 @@ version(StdDdoc) `WindowsTimeZone` does not exist on Posix systems. - To get a `WindowsTimeZone`, either call - `WindowsTimeZone.getTimeZone` or call `TimeZone.getTimeZone` - (which will give a $(LREF PosixTimeZone) on Posix systems and a - `WindowsTimeZone` on Windows systems). + To get a `WindowsTimeZone`, call `WindowsTimeZone.getTimeZone`. See_Also: $(HTTP www.iana.org/time-zones, Home of the TZ Database files) @@ -3599,779 +3515,3 @@ For terms of use, see http://www.unicode.org/copyright.html assert(equal(value.uniq(), value), key); } } - - -// Explicitly undocumented. It will be removed in June 2018. @@@DEPRECATED_2018-07@@@ -deprecated("Use parseTZConversions instead") -string tzDatabaseNameToWindowsTZName(string tzName) @safe pure nothrow @nogc -{ - switch (tzName) - { - case "Africa/Abidjan": return "Greenwich Standard Time"; - case "Africa/Accra": return "Greenwich Standard Time"; - case "Africa/Addis_Ababa": return "E. Africa Standard Time"; - case "Africa/Algiers": return "W. Central Africa Standard Time"; - case "Africa/Asmera": return "E. Africa Standard Time"; - case "Africa/Bamako": return "Greenwich Standard Time"; - case "Africa/Bangui": return "W. Central Africa Standard Time"; - case "Africa/Banjul": return "Greenwich Standard Time"; - case "Africa/Bissau": return "Greenwich Standard Time"; - case "Africa/Blantyre": return "South Africa Standard Time"; - case "Africa/Brazzaville": return "W. Central Africa Standard Time"; - case "Africa/Bujumbura": return "South Africa Standard Time"; - case "Africa/Cairo": return "Egypt Standard Time"; - case "Africa/Casablanca": return "Morocco Standard Time"; - case "Africa/Ceuta": return "Romance Standard Time"; - case "Africa/Conakry": return "Greenwich Standard Time"; - case "Africa/Dakar": return "Greenwich Standard Time"; - case "Africa/Dar_es_Salaam": return "E. Africa Standard Time"; - case "Africa/Djibouti": return "E. Africa Standard Time"; - case "Africa/Douala": return "W. Central Africa Standard Time"; - case "Africa/El_Aaiun": return "Morocco Standard Time"; - case "Africa/Freetown": return "Greenwich Standard Time"; - case "Africa/Gaborone": return "South Africa Standard Time"; - case "Africa/Harare": return "South Africa Standard Time"; - case "Africa/Johannesburg": return "South Africa Standard Time"; - case "Africa/Juba": return "E. Africa Standard Time"; - case "Africa/Kampala": return "E. Africa Standard Time"; - case "Africa/Khartoum": return "E. Africa Standard Time"; - case "Africa/Kigali": return "South Africa Standard Time"; - case "Africa/Kinshasa": return "W. Central Africa Standard Time"; - case "Africa/Lagos": return "W. Central Africa Standard Time"; - case "Africa/Libreville": return "W. Central Africa Standard Time"; - case "Africa/Lome": return "Greenwich Standard Time"; - case "Africa/Luanda": return "W. Central Africa Standard Time"; - case "Africa/Lubumbashi": return "South Africa Standard Time"; - case "Africa/Lusaka": return "South Africa Standard Time"; - case "Africa/Malabo": return "W. Central Africa Standard Time"; - case "Africa/Maputo": return "South Africa Standard Time"; - case "Africa/Maseru": return "South Africa Standard Time"; - case "Africa/Mbabane": return "South Africa Standard Time"; - case "Africa/Mogadishu": return "E. Africa Standard Time"; - case "Africa/Monrovia": return "Greenwich Standard Time"; - case "Africa/Nairobi": return "E. Africa Standard Time"; - case "Africa/Ndjamena": return "W. Central Africa Standard Time"; - case "Africa/Niamey": return "W. Central Africa Standard Time"; - case "Africa/Nouakchott": return "Greenwich Standard Time"; - case "Africa/Ouagadougou": return "Greenwich Standard Time"; - case "Africa/Porto-Novo": return "W. Central Africa Standard Time"; - case "Africa/Sao_Tome": return "Greenwich Standard Time"; - case "Africa/Tripoli": return "Libya Standard Time"; - case "Africa/Tunis": return "W. Central Africa Standard Time"; - case "Africa/Windhoek": return "Namibia Standard Time"; - case "America/Adak": return "Aleutian Standard Time"; - case "America/Anchorage": return "Alaskan Standard Time"; - case "America/Anguilla": return "SA Western Standard Time"; - case "America/Antigua": return "SA Western Standard Time"; - case "America/Araguaina": return "SA Eastern Standard Time"; - case "America/Argentina/La_Rioja": return "Argentina Standard Time"; - case "America/Argentina/Rio_Gallegos": return "Argentina Standard Time"; - case "America/Argentina/Salta": return "Argentina Standard Time"; - case "America/Argentina/San_Juan": return "Argentina Standard Time"; - case "America/Argentina/San_Luis": return "Argentina Standard Time"; - case "America/Argentina/Tucuman": return "Argentina Standard Time"; - case "America/Argentina/Ushuaia": return "Argentina Standard Time"; - case "America/Arguaina": return "Tocantins Standard Time"; - case "America/Aruba": return "SA Western Standard Time"; - case "America/Asuncion": return "Paraguay Standard Time"; - case "America/Bahia": return "Bahia Standard Time"; - case "America/Bahia_Banderas": return "Central Standard Time (Mexico)"; - case "America/Barbados": return "SA Western Standard Time"; - case "America/Belem": return "SA Eastern Standard Time"; - case "America/Belize": return "Central America Standard Time"; - case "America/Blanc-Sablon": return "SA Western Standard Time"; - case "America/Boa_Vista": return "SA Western Standard Time"; - case "America/Bogota": return "SA Pacific Standard Time"; - case "America/Boise": return "Mountain Standard Time"; - case "America/Buenos_Aires": return "Argentina Standard Time"; - case "America/Cambridge_Bay": return "Mountain Standard Time"; - case "America/Campo_Grande": return "Central Brazilian Standard Time"; - case "America/Cancun": return "Eastern Standard Time (Mexico)"; - case "America/Caracas": return "Venezuela Standard Time"; - case "America/Catamarca": return "Argentina Standard Time"; - case "America/Cayenne": return "SA Eastern Standard Time"; - case "America/Cayman": return "SA Pacific Standard Time"; - case "America/Chicago": return "Central Standard Time"; - case "America/Chihuahua": return "Mountain Standard Time (Mexico)"; - case "America/Coral_Harbour": return "SA Pacific Standard Time"; - case "America/Cordoba": return "Argentina Standard Time"; - case "America/Costa_Rica": return "Central America Standard Time"; - case "America/Creston": return "US Mountain Standard Time"; - case "America/Cuiaba": return "Central Brazilian Standard Time"; - case "America/Curacao": return "SA Western Standard Time"; - case "America/Danmarkshavn": return "UTC"; - case "America/Dawson": return "Pacific Standard Time"; - case "America/Dawson_Creek": return "US Mountain Standard Time"; - case "America/Denver": return "Mountain Standard Time"; - case "America/Detroit": return "Eastern Standard Time"; - case "America/Dominica": return "SA Western Standard Time"; - case "America/Edmonton": return "Mountain Standard Time"; - case "America/Eirunepe": return "SA Pacific Standard Time"; - case "America/El_Salvador": return "Central America Standard Time"; - case "America/Fortaleza": return "SA Eastern Standard Time"; - case "America/Glace_Bay": return "Atlantic Standard Time"; - case "America/Godthab": return "Greenland Standard Time"; - case "America/Goose_Bay": return "Atlantic Standard Time"; - case "America/Grand_Turk": return "Turks And Caicos Standard Time"; - case "America/Grenada": return "SA Western Standard Time"; - case "America/Guadeloupe": return "SA Western Standard Time"; - case "America/Guatemala": return "Central America Standard Time"; - case "America/Guayaquil": return "SA Pacific Standard Time"; - case "America/Guyana": return "SA Western Standard Time"; - case "America/Halifax": return "Atlantic Standard Time"; - case "America/Havana": return "Cuba Standard Time"; - case "America/Hermosillo": return "US Mountain Standard Time"; - case "America/Indiana/Knox": return "Central Standard Time"; - case "America/Indiana/Marengo": return "US Eastern Standard Time"; - case "America/Indiana/Petersburg": return "Eastern Standard Time"; - case "America/Indiana/Tell_City": return "Central Standard Time"; - case "America/Indiana/Vevay": return "US Eastern Standard Time"; - case "America/Indiana/Vincennes": return "Eastern Standard Time"; - case "America/Indiana/Winamac": return "Eastern Standard Time"; - case "America/Indianapolis": return "US Eastern Standard Time"; - case "America/Inuvik": return "Mountain Standard Time"; - case "America/Iqaluit": return "Eastern Standard Time"; - case "America/Jamaica": return "SA Pacific Standard Time"; - case "America/Jujuy": return "Argentina Standard Time"; - case "America/Juneau": return "Alaskan Standard Time"; - case "America/Kentucky/Monticello": return "Eastern Standard Time"; - case "America/Kralendijk": return "SA Western Standard Time"; - case "America/La_Paz": return "SA Western Standard Time"; - case "America/Lima": return "SA Pacific Standard Time"; - case "America/Los_Angeles": return "Pacific Standard Time"; - case "America/Louisville": return "Eastern Standard Time"; - case "America/Lower_Princes": return "SA Western Standard Time"; - case "America/Maceio": return "SA Eastern Standard Time"; - case "America/Managua": return "Central America Standard Time"; - case "America/Manaus": return "SA Western Standard Time"; - case "America/Marigot": return "SA Western Standard Time"; - case "America/Martinique": return "SA Western Standard Time"; - case "America/Matamoros": return "Central Standard Time"; - case "America/Mazatlan": return "Mountain Standard Time (Mexico)"; - case "America/Mendoza": return "Argentina Standard Time"; - case "America/Menominee": return "Central Standard Time"; - case "America/Merida": return "Central Standard Time (Mexico)"; - case "America/Mexico_City": return "Central Standard Time (Mexico)"; - case "America/Miquelon": return "Saint Pierre Standard Time"; - case "America/Moncton": return "Atlantic Standard Time"; - case "America/Monterrey": return "Central Standard Time (Mexico)"; - case "America/Montevideo": return "Montevideo Standard Time"; - case "America/Montreal": return "Eastern Standard Time"; - case "America/Montserrat": return "SA Western Standard Time"; - case "America/Nassau": return "Eastern Standard Time"; - case "America/New_York": return "Eastern Standard Time"; - case "America/Nipigon": return "Eastern Standard Time"; - case "America/Nome": return "Alaskan Standard Time"; - case "America/Noronha": return "UTC-02"; - case "America/North_Dakota/Beulah": return "Central Standard Time"; - case "America/North_Dakota/Center": return "Central Standard Time"; - case "America/North_Dakota/New_Salem": return "Central Standard Time"; - case "America/Ojinaga": return "Mountain Standard Time"; - case "America/Panama": return "SA Pacific Standard Time"; - case "America/Pangnirtung": return "Eastern Standard Time"; - case "America/Paramaribo": return "SA Eastern Standard Time"; - case "America/Phoenix": return "US Mountain Standard Time"; - case "America/Port-au-Prince": return "Haiti Standard Time"; - case "America/Port_of_Spain": return "SA Western Standard Time"; - case "America/Porto_Velho": return "SA Western Standard Time"; - case "America/Puerto_Rico": return "SA Western Standard Time"; - case "America/Rainy_River": return "Central Standard Time"; - case "America/Rankin_Inlet": return "Central Standard Time"; - case "America/Recife": return "SA Eastern Standard Time"; - case "America/Regina": return "Canada Central Standard Time"; - case "America/Resolute": return "Central Standard Time"; - case "America/Rio_Branco": return "SA Pacific Standard Time"; - case "America/Santa_Isabel": return "Pacific Standard Time (Mexico)"; - case "America/Santarem": return "SA Eastern Standard Time"; - case "America/Santiago": return "Pacific SA Standard Time"; - case "America/Santo_Domingo": return "SA Western Standard Time"; - case "America/Sao_Paulo": return "E. South America Standard Time"; - case "America/Scoresbysund": return "Azores Standard Time"; - case "America/Sitka": return "Alaskan Standard Time"; - case "America/St_Barthelemy": return "SA Western Standard Time"; - case "America/St_Johns": return "Newfoundland Standard Time"; - case "America/St_Kitts": return "SA Western Standard Time"; - case "America/St_Lucia": return "SA Western Standard Time"; - case "America/St_Thomas": return "SA Western Standard Time"; - case "America/St_Vincent": return "SA Western Standard Time"; - case "America/Swift_Current": return "Canada Central Standard Time"; - case "America/Tegucigalpa": return "Central America Standard Time"; - case "America/Thule": return "Atlantic Standard Time"; - case "America/Thunder_Bay": return "Eastern Standard Time"; - case "America/Tijuana": return "Pacific Standard Time"; - case "America/Toronto": return "Eastern Standard Time"; - case "America/Tortola": return "SA Western Standard Time"; - case "America/Vancouver": return "Pacific Standard Time"; - case "America/Whitehorse": return "Pacific Standard Time"; - case "America/Winnipeg": return "Central Standard Time"; - case "America/Yakutat": return "Alaskan Standard Time"; - case "America/Yellowknife": return "Mountain Standard Time"; - case "Antarctica/Casey": return "W. Australia Standard Time"; - case "Antarctica/Davis": return "SE Asia Standard Time"; - case "Antarctica/DumontDUrville": return "West Pacific Standard Time"; - case "Antarctica/Macquarie": return "Central Pacific Standard Time"; - case "Antarctica/Mawson": return "West Asia Standard Time"; - case "Antarctica/McMurdo": return "New Zealand Standard Time"; - case "Antarctica/Palmer": return "Pacific SA Standard Time"; - case "Antarctica/Rothera": return "SA Eastern Standard Time"; - case "Antarctica/Syowa": return "E. Africa Standard Time"; - case "Antarctica/Vostok": return "Central Asia Standard Time"; - case "Arctic/Longyearbyen": return "W. Europe Standard Time"; - case "Asia/Aden": return "Arab Standard Time"; - case "Asia/Almaty": return "Central Asia Standard Time"; - case "Asia/Amman": return "Jordan Standard Time"; - case "Asia/Anadyr": return "Russia Time Zone 11"; - case "Asia/Aqtau": return "West Asia Standard Time"; - case "Asia/Aqtobe": return "West Asia Standard Time"; - case "Asia/Ashgabat": return "West Asia Standard Time"; - case "Asia/Baghdad": return "Arabic Standard Time"; - case "Asia/Bahrain": return "Arab Standard Time"; - case "Asia/Baku": return "Azerbaijan Standard Time"; - case "Asia/Bangkok": return "SE Asia Standard Time"; - case "Asia/Barnaul": return "Altai Standard Time"; - case "Asia/Beirut": return "Middle East Standard Time"; - case "Asia/Bishkek": return "Central Asia Standard Time"; - case "Asia/Brunei": return "Singapore Standard Time"; - case "Asia/Calcutta": return "India Standard Time"; - case "Asia/Chita": return "Transbaikal Standard Time"; - case "Asia/Choibalsan": return "Ulaanbaatar Standard Time"; - case "Asia/Colombo": return "Sri Lanka Standard Time"; - case "Asia/Damascus": return "Syria Standard Time"; - case "Asia/Dhaka": return "Bangladesh Standard Time"; - case "Asia/Dili": return "Tokyo Standard Time"; - case "Asia/Dubai": return "Arabian Standard Time"; - case "Asia/Dushanbe": return "West Asia Standard Time"; - case "Asia/Hebron": return "West Bank Standard Time"; - case "Asia/Hong_Kong": return "China Standard Time"; - case "Asia/Hovd": return "W. Mongolia Standard Time"; - case "Asia/Irkutsk": return "North Asia East Standard Time"; - case "Asia/Jakarta": return "SE Asia Standard Time"; - case "Asia/Jayapura": return "Tokyo Standard Time"; - case "Asia/Jerusalem": return "Israel Standard Time"; - case "Asia/Kabul": return "Afghanistan Standard Time"; - case "Asia/Kamchatka": return "Russia Time Zone 11"; - case "Asia/Karachi": return "Pakistan Standard Time"; - case "Asia/Katmandu": return "Nepal Standard Time"; - case "Asia/Khandyga": return "Yakutsk Standard Time"; - case "Asia/Krasnoyarsk": return "North Asia Standard Time"; - case "Asia/Kuala_Lumpur": return "Singapore Standard Time"; - case "Asia/Kuching": return "Singapore Standard Time"; - case "Asia/Kuwait": return "Arab Standard Time"; - case "Asia/Macau": return "China Standard Time"; - case "Asia/Magadan": return "Magadan Standard Time"; - case "Asia/Makassar": return "Singapore Standard Time"; - case "Asia/Manila": return "Singapore Standard Time"; - case "Asia/Muscat": return "Arabian Standard Time"; - case "Asia/Nicosia": return "GTB Standard Time"; - case "Asia/Novokuznetsk": return "North Asia Standard Time"; - case "Asia/Novosibirsk": return "N. Central Asia Standard Time"; - case "Asia/Omsk": return "N. Central Asia Standard Time"; - case "Asia/Oral": return "West Asia Standard Time"; - case "Asia/Phnom_Penh": return "SE Asia Standard Time"; - case "Asia/Pontianak": return "SE Asia Standard Time"; - case "Asia/Pyongyang": return "North Korea Standard Time"; - case "Asia/Qatar": return "Arab Standard Time"; - case "Asia/Qyzylorda": return "Central Asia Standard Time"; - case "Asia/Rangoon": return "Myanmar Standard Time"; - case "Asia/Riyadh": return "Arab Standard Time"; - case "Asia/Saigon": return "SE Asia Standard Time"; - case "Asia/Sakhalin": return "Sakhalin Standard Time"; - case "Asia/Samarkand": return "West Asia Standard Time"; - case "Asia/Seoul": return "Korea Standard Time"; - case "Asia/Shanghai": return "China Standard Time"; - case "Asia/Singapore": return "Singapore Standard Time"; - case "Asia/Srednekolymsk": return "Russia Time Zone 10"; - case "Asia/Taipei": return "Taipei Standard Time"; - case "Asia/Tashkent": return "West Asia Standard Time"; - case "Asia/Tbilisi": return "Georgian Standard Time"; - case "Asia/Tehran": return "Iran Standard Time"; - case "Asia/Thimphu": return "Bangladesh Standard Time"; - case "Asia/Tokyo": return "Tokyo Standard Time"; - case "Asia/Tomsk": return "Tomsk Standard Time"; - case "Asia/Ulaanbaatar": return "Ulaanbaatar Standard Time"; - case "Asia/Urumqi": return "Central Asia Standard Time"; - case "Asia/Ust-Nera": return "Vladivostok Standard Time"; - case "Asia/Vientiane": return "SE Asia Standard Time"; - case "Asia/Vladivostok": return "Vladivostok Standard Time"; - case "Asia/Yakutsk": return "Yakutsk Standard Time"; - case "Asia/Yekaterinburg": return "Ekaterinburg Standard Time"; - case "Asia/Yerevan": return "Caucasus Standard Time"; - case "Atlantic/Azores": return "Azores Standard Time"; - case "Atlantic/Bermuda": return "Atlantic Standard Time"; - case "Atlantic/Canary": return "GMT Standard Time"; - case "Atlantic/Cape_Verde": return "Cape Verde Standard Time"; - case "Atlantic/Faeroe": return "GMT Standard Time"; - case "Atlantic/Madeira": return "GMT Standard Time"; - case "Atlantic/Reykjavik": return "Greenwich Standard Time"; - case "Atlantic/South_Georgia": return "UTC-02"; - case "Atlantic/St_Helena": return "Greenwich Standard Time"; - case "Atlantic/Stanley": return "SA Eastern Standard Time"; - case "Australia/Adelaide": return "Cen. Australia Standard Time"; - case "Australia/Brisbane": return "E. Australia Standard Time"; - case "Australia/Broken_Hill": return "Cen. Australia Standard Time"; - case "Australia/Currie": return "Tasmania Standard Time"; - case "Australia/Darwin": return "AUS Central Standard Time"; - case "Australia/Eucla": return "Aus Central W. Standard Time"; - case "Australia/Hobart": return "Tasmania Standard Time"; - case "Australia/Lindeman": return "E. Australia Standard Time"; - case "Australia/Lord_Howe": return "Lord Howe Standard Time"; - case "Australia/Melbourne": return "AUS Eastern Standard Time"; - case "Australia/Perth": return "W. Australia Standard Time"; - case "Australia/Sydney": return "AUS Eastern Standard Time"; - case "CST6CDT": return "Central Standard Time"; - case "EST5EDT": return "Eastern Standard Time"; - case "Etc/GMT": return "UTC"; - case "Etc/GMT+1": return "Cape Verde Standard Time"; - case "Etc/GMT+10": return "Hawaiian Standard Time"; - case "Etc/GMT+11": return "UTC-11"; - case "Etc/GMT+12": return "Dateline Standard Time"; - case "Etc/GMT+2": return "UTC-02"; - case "Etc/GMT+3": return "SA Eastern Standard Time"; - case "Etc/GMT+4": return "SA Western Standard Time"; - case "Etc/GMT+5": return "SA Pacific Standard Time"; - case "Etc/GMT+6": return "Central America Standard Time"; - case "Etc/GMT+7": return "US Mountain Standard Time"; - case "Etc/GMT+8": return "UTC-08"; - case "Etc/GMT+9": return "UTC-09"; - case "Etc/GMT-1": return "W. Central Africa Standard Time"; - case "Etc/GMT-10": return "West Pacific Standard Time"; - case "Etc/GMT-11": return "Central Pacific Standard Time"; - case "Etc/GMT-12": return "UTC+12"; - case "Etc/GMT-13": return "Tonga Standard Time"; - case "Etc/GMT-14": return "Line Islands Standard Time"; - case "Etc/GMT-2": return "South Africa Standard Time"; - case "Etc/GMT-3": return "E. Africa Standard Time"; - case "Etc/GMT-4": return "Arabian Standard Time"; - case "Etc/GMT-5": return "West Asia Standard Time"; - case "Etc/GMT-6": return "Central Asia Standard Time"; - case "Etc/GMT-7": return "SE Asia Standard Time"; - case "Etc/GMT-8": return "Singapore Standard Time"; - case "Etc/GMT-9": return "Tokyo Standard Time"; - case "Europe/Amsterdam": return "W. Europe Standard Time"; - case "Europe/Andorra": return "W. Europe Standard Time"; - case "Europe/Astrakhan": return "Astrakhan Standard Time"; - case "Europe/Athens": return "GTB Standard Time"; - case "Europe/Belgrade": return "Central Europe Standard Time"; - case "Europe/Berlin": return "W. Europe Standard Time"; - case "Europe/Bratislava": return "Central Europe Standard Time"; - case "Europe/Brussels": return "Romance Standard Time"; - case "Europe/Bucharest": return "GTB Standard Time"; - case "Europe/Budapest": return "Central Europe Standard Time"; - case "Europe/Busingen": return "W. Europe Standard Time"; - case "Europe/Chisinau": return "GTB Standard Time"; - case "Europe/Copenhagen": return "Romance Standard Time"; - case "Europe/Dublin": return "GMT Standard Time"; - case "Europe/Gibraltar": return "W. Europe Standard Time"; - case "Europe/Guernsey": return "GMT Standard Time"; - case "Europe/Helsinki": return "FLE Standard Time"; - case "Europe/Isle_of_Man": return "GMT Standard Time"; - case "Europe/Istanbul": return "Turkey Standard Time"; - case "Europe/Jersey": return "GMT Standard Time"; - case "Europe/Kaliningrad": return "Kaliningrad Standard Time"; - case "Europe/Kiev": return "FLE Standard Time"; - case "Europe/Lisbon": return "GMT Standard Time"; - case "Europe/Ljubljana": return "Central Europe Standard Time"; - case "Europe/London": return "GMT Standard Time"; - case "Europe/Luxembourg": return "W. Europe Standard Time"; - case "Europe/Madrid": return "Romance Standard Time"; - case "Europe/Malta": return "W. Europe Standard Time"; - case "Europe/Mariehamn": return "FLE Standard Time"; - case "Europe/Minsk": return "Belarus Standard Time"; - case "Europe/Monaco": return "W. Europe Standard Time"; - case "Europe/Moscow": return "Russian Standard Time"; - case "Europe/Oslo": return "W. Europe Standard Time"; - case "Europe/Paris": return "Romance Standard Time"; - case "Europe/Podgorica": return "Central Europe Standard Time"; - case "Europe/Prague": return "Central Europe Standard Time"; - case "Europe/Riga": return "FLE Standard Time"; - case "Europe/Rome": return "W. Europe Standard Time"; - case "Europe/Samara": return "Russia Time Zone 3"; - case "Europe/San_Marino": return "W. Europe Standard Time"; - case "Europe/Sarajevo": return "Central European Standard Time"; - case "Europe/Simferopol": return "Russian Standard Time"; - case "Europe/Skopje": return "Central European Standard Time"; - case "Europe/Sofia": return "FLE Standard Time"; - case "Europe/Stockholm": return "W. Europe Standard Time"; - case "Europe/Tallinn": return "FLE Standard Time"; - case "Europe/Tirane": return "Central Europe Standard Time"; - case "Europe/Uzhgorod": return "FLE Standard Time"; - case "Europe/Vaduz": return "W. Europe Standard Time"; - case "Europe/Vatican": return "W. Europe Standard Time"; - case "Europe/Vienna": return "W. Europe Standard Time"; - case "Europe/Vilnius": return "FLE Standard Time"; - case "Europe/Volgograd": return "Russian Standard Time"; - case "Europe/Warsaw": return "Central European Standard Time"; - case "Europe/Zagreb": return "Central European Standard Time"; - case "Europe/Zaporozhye": return "FLE Standard Time"; - case "Europe/Zurich": return "W. Europe Standard Time"; - case "Indian/Antananarivo": return "E. Africa Standard Time"; - case "Indian/Chagos": return "Central Asia Standard Time"; - case "Indian/Christmas": return "SE Asia Standard Time"; - case "Indian/Cocos": return "Myanmar Standard Time"; - case "Indian/Comoro": return "E. Africa Standard Time"; - case "Indian/Kerguelen": return "West Asia Standard Time"; - case "Indian/Mahe": return "Mauritius Standard Time"; - case "Indian/Maldives": return "West Asia Standard Time"; - case "Indian/Mauritius": return "Mauritius Standard Time"; - case "Indian/Mayotte": return "E. Africa Standard Time"; - case "Indian/Reunion": return "Mauritius Standard Time"; - case "MST7MDT": return "Mountain Standard Time"; - case "PST8PDT": return "Pacific Standard Time"; - case "Pacific/Apia": return "Samoa Standard Time"; - case "Pacific/Auckland": return "New Zealand Standard Time"; - case "Pacific/Bougainville": return "Bougainville Standard Time"; - case "Pacific/Chatham": return "Chatham Islands Standard Time"; - case "Pacific/Easter": return "Easter Island Standard Time"; - case "Pacific/Efate": return "Central Pacific Standard Time"; - case "Pacific/Enderbury": return "Tonga Standard Time"; - case "Pacific/Fakaofo": return "Tonga Standard Time"; - case "Pacific/Fiji": return "Fiji Standard Time"; - case "Pacific/Funafuti": return "UTC+12"; - case "Pacific/Galapagos": return "Central America Standard Time"; - case "Pacific/Guadalcanal": return "Central Pacific Standard Time"; - case "Pacific/Guam": return "West Pacific Standard Time"; - case "Pacific/Honolulu": return "Hawaiian Standard Time"; - case "Pacific/Johnston": return "Hawaiian Standard Time"; - case "Pacific/Kiritimati": return "Line Islands Standard Time"; - case "Pacific/Kosrae": return "Central Pacific Standard Time"; - case "Pacific/Kwajalein": return "UTC+12"; - case "Pacific/Majuro": return "UTC+12"; - case "Pacific/Marquesas": return "Marquesas Standard Time"; - case "Pacific/Midway": return "UTC-11"; - case "Pacific/Nauru": return "UTC+12"; - case "Pacific/Niue": return "UTC-11"; - case "Pacific/Noumea": return "Central Pacific Standard Time"; - case "Pacific/Norfolk": return "Norfolk Standard Time"; - case "Pacific/Pago_Pago": return "UTC-11"; - case "Pacific/Palau": return "Tokyo Standard Time"; - case "Pacific/Ponape": return "Central Pacific Standard Time"; - case "Pacific/Port_Moresby": return "West Pacific Standard Time"; - case "Pacific/Rarotonga": return "Hawaiian Standard Time"; - case "Pacific/Saipan": return "West Pacific Standard Time"; - case "Pacific/Tahiti": return "Hawaiian Standard Time"; - case "Pacific/Tarawa": return "UTC+12"; - case "Pacific/Tongatapu": return "Tonga Standard Time"; - case "Pacific/Truk": return "West Pacific Standard Time"; - case "Pacific/Wake": return "UTC+12"; - case "Pacific/Wallis": return "UTC+12"; - default: return null; - } -} - -version(Windows) version(UpdateWindowsTZTranslations) deprecated @system unittest -{ - import std.stdio : stderr; - - foreach (tzName; TimeZone.getInstalledTZNames()) - { - if (tzDatabaseNameToWindowsTZName(tzName) is null) - stderr.writeln("Missing TZName to Windows translation: ", tzName); - } -} - - -// Explicitly undocumented. It will be removed in June 2018. @@@DEPRECATED_2018-07@@@ -deprecated("Use parseTZConversions instead") -string windowsTZNameToTZDatabaseName(string tzName) @safe pure nothrow @nogc -{ - switch (tzName) - { - case "AUS Central Standard Time": return "Australia/Darwin"; - case "AUS Eastern Standard Time": return "Australia/Sydney"; - case "Aus Central W. Standard Time": return "Australia/Eucla"; - case "Afghanistan Standard Time": return "Asia/Kabul"; - case "Haiti Standard Time": return "America/Port-au-Prince"; - case "Alaskan Standard Time": return "America/Anchorage"; - case "Aleutian Standard Time": return "America/Adak"; - case "Altai Standard Time": return "Asia/Barnaul"; - case "Arab Standard Time": return "Asia/Riyadh"; - case "Arabian Standard Time": return "Asia/Dubai"; - case "Arabic Standard Time": return "Asia/Baghdad"; - case "Argentina Standard Time": return "America/Buenos_Aires"; - case "Astrakhan Standard Time": return "Europe/Astrakhan"; - case "Atlantic Standard Time": return "America/Halifax"; - case "Azerbaijan Standard Time": return "Asia/Baku"; - case "Azores Standard Time": return "Atlantic/Azores"; - case "Bahia Standard Time": return "America/Bahia"; - case "Bangladesh Standard Time": return "Asia/Dhaka"; - case "Belarus Standard Time": return "Europe/Minsk"; - case "Bougainville Standard Time": return "Pacific/Bougainville"; - case "Canada Central Standard Time": return "America/Regina"; - case "Cape Verde Standard Time": return "Atlantic/Cape_Verde"; - case "Caucasus Standard Time": return "Asia/Yerevan"; - case "Cen. Australia Standard Time": return "Australia/Adelaide"; - case "Central America Standard Time": return "America/Guatemala"; - case "Central Asia Standard Time": return "Asia/Almaty"; - case "Central Brazilian Standard Time": return "America/Cuiaba"; - case "Central Europe Standard Time": return "Europe/Budapest"; - case "Central European Standard Time": return "Europe/Warsaw"; - case "Central Pacific Standard Time": return "Pacific/Guadalcanal"; - case "Central Standard Time": return "America/Chicago"; - case "Central Standard Time (Mexico)": return "America/Mexico_City"; - case "Chatham Islands Standard Time": return "Pacific/Chatham"; - case "China Standard Time": return "Asia/Shanghai"; - case "Cuba Standard Time": return "America/Havana"; - case "Dateline Standard Time": return "Etc/GMT+12"; - case "E. Africa Standard Time": return "Africa/Nairobi"; - case "E. Australia Standard Time": return "Australia/Brisbane"; - // This doesn't appear to be in the current stuff from MS, but the autotester - // is failing without it (probably because its time zone data hasn't been - // updated recently enough). - case "E. Europe Standard Time": return "Europe/Minsk"; - case "E. South America Standard Time": return "America/Sao_Paulo"; - case "Easter Island Standard Time": return "Pacific/Easter"; - case "Eastern Standard Time": return "America/New_York"; - case "Eastern Standard Time (Mexico)": return "America/Cancun"; - case "Egypt Standard Time": return "Africa/Cairo"; - case "Ekaterinburg Standard Time": return "Asia/Yekaterinburg"; - case "FLE Standard Time": return "Europe/Kiev"; - case "Fiji Standard Time": return "Pacific/Fiji"; - case "GMT Standard Time": return "Europe/London"; - case "GTB Standard Time": return "Europe/Athens"; - case "Georgian Standard Time": return "Asia/Tbilisi"; - case "Greenland Standard Time": return "America/Godthab"; - case "Greenwich Standard Time": return "Atlantic/Reykjavik"; - case "Hawaiian Standard Time": return "Pacific/Honolulu"; - case "India Standard Time": return "Asia/Calcutta"; - case "Iran Standard Time": return "Asia/Tehran"; - case "Israel Standard Time": return "Asia/Jerusalem"; - case "Jordan Standard Time": return "Asia/Amman"; - case "Kaliningrad Standard Time": return "Europe/Kaliningrad"; - // Same as with E. Europe Standard Time. - case "Kamchatka Standard Time": return "Asia/Kamchatka"; - case "Korea Standard Time": return "Asia/Seoul"; - case "Libya Standard Time": return "Africa/Tripoli"; - case "Line Islands Standard Time": return "Pacific/Kiritimati"; - case "Lord Howe Standard Time": return "Australia/Lord_Howe"; - case "Magadan Standard Time": return "Asia/Magadan"; - case "Marquesas Standard Time": return "Pacific/Marquesas"; - case "Mauritius Standard Time": return "Indian/Mauritius"; - // Same as with E. Europe Standard Time. - case "Mexico Standard Time": return "America/Mexico_City"; - // Same as with E. Europe Standard Time. - case "Mexico Standard Time 2": return "America/Chihuahua"; - // Same as with E. Europe Standard Time. - case "Mid-Atlantic Standard Time": return "Etc/GMT+2"; - case "Middle East Standard Time": return "Asia/Beirut"; - case "Montevideo Standard Time": return "America/Montevideo"; - case "Morocco Standard Time": return "Africa/Casablanca"; - case "Mountain Standard Time": return "America/Denver"; - case "Mountain Standard Time (Mexico)": return "America/Chihuahua"; - case "Myanmar Standard Time": return "Asia/Rangoon"; - case "N. Central Asia Standard Time": return "Asia/Novosibirsk"; - case "Namibia Standard Time": return "Africa/Windhoek"; - case "Nepal Standard Time": return "Asia/Katmandu"; - case "New Zealand Standard Time": return "Pacific/Auckland"; - case "Newfoundland Standard Time": return "America/St_Johns"; - case "Norfolk Standard Time": return "Pacific/Norfolk"; - case "North Asia East Standard Time": return "Asia/Irkutsk"; - case "North Asia Standard Time": return "Asia/Krasnoyarsk"; - case "North Korea Standard Time": return "Asia/Pyongyang"; - case "Pacific SA Standard Time": return "America/Santiago"; - case "Pacific Standard Time": return "America/Los_Angeles"; - case "Pacific Standard Time (Mexico)": return "America/Santa_Isabel"; - case "Pakistan Standard Time": return "Asia/Karachi"; - case "Paraguay Standard Time": return "America/Asuncion"; - case "Romance Standard Time": return "Europe/Paris"; - case "Russia Time Zone 10": return "Asia/Srednekolymsk"; - case "Russia Time Zone 11": return "Asia/Anadyr"; - case "Russia Time Zone 3": return "Europe/Samara"; - case "Russian Standard Time": return "Europe/Moscow"; - case "SA Eastern Standard Time": return "America/Cayenne"; - case "SA Pacific Standard Time": return "America/Bogota"; - case "SA Western Standard Time": return "America/La_Paz"; - case "SE Asia Standard Time": return "Asia/Bangkok"; - case "Sakhalin Standard Time": return "Asia/Sakhalin"; - case "Saint Pierre Standard Time": return "America/Miquelon"; - case "Samoa Standard Time": return "Pacific/Apia"; - case "Singapore Standard Time": return "Asia/Singapore"; - case "South Africa Standard Time": return "Africa/Johannesburg"; - case "Sri Lanka Standard Time": return "Asia/Colombo"; - case "Syria Standard Time": return "Asia/Damascus"; - case "Taipei Standard Time": return "Asia/Taipei"; - case "Tasmania Standard Time": return "Australia/Hobart"; - case "Tocantins Standard Time": return "America/Arguaina"; - case "Tokyo Standard Time": return "Asia/Tokyo"; - case "Tomsk Standard Time": return "Asia/Tomsk"; - case "Tonga Standard Time": return "Pacific/Tongatapu"; - case "Transbaikal Standard Time": return "Asia/Chita"; - case "Turkey Standard Time": return "Europe/Istanbul"; - case "Turks And Caicos Standard Time": return "America/Grand_Turk"; - case "US Eastern Standard Time": return "America/Indianapolis"; - case "US Mountain Standard Time": return "America/Phoenix"; - case "UTC": return "Etc/GMT"; - case "UTC+12": return "Etc/GMT-12"; - case "UTC-02": return "Etc/GMT+2"; - case "UTC-08": return "Etc/GMT+8"; - case "UTC-09": return "Etc/GMT+9"; - case "UTC-11": return "Etc/GMT+11"; - case "Ulaanbaatar Standard Time": return "Asia/Ulaanbaatar"; - case "Venezuela Standard Time": return "America/Caracas"; - case "Vladivostok Standard Time": return "Asia/Vladivostok"; - case "W. Australia Standard Time": return "Australia/Perth"; - case "W. Central Africa Standard Time": return "Africa/Lagos"; - case "W. Europe Standard Time": return "Europe/Berlin"; - case "W. Mongolia Standard Time": return "Asia/Hovd"; - case "West Asia Standard Time": return "Asia/Tashkent"; - case "West Bank Standard Time": return "Asia/Hebron"; - case "West Pacific Standard Time": return "Pacific/Port_Moresby"; - case "Yakutsk Standard Time": return "Asia/Yakutsk"; - default: return null; - } -} - -version(Windows) version(UpdateWindowsTZTranslations) deprecated @system unittest -{ - import std.stdio : stderr; - - foreach (winName; WindowsTimeZone.getInstalledTZNames()) - { - if (windowsTZNameToTZDatabaseName(winName) is null) - stderr.writeln("Missing Windows to TZName translation: ", winName); - } -} - - -// This script is for regenerating tzDatabaseNameToWindowsTZName and -// windowsTZNameToTZDatabaseName from -// http://unicode.org/cldr/data/common/supplemental/windowsZones.xml - -/+ -#!/bin/rdmd - -import std.algorithm; -import std.array; -import std.conv; -import std.datetime; -import std.exception; -import std.path; -import std.stdio; -import std.string; - -int main(string[] args) -{ - if (args.length != 4 || args[1].baseName != "windowsZones.xml") - { - stderr.writeln("genTZs.d windowsZones.xml "); - return -1; - } - - string[][string] win2Nix; - string[][string] nix2Win; - immutable f1 = ` %s", nix, wins)); - - // We'll try to eliminate multiples by favoring a conversion if it's already - // in Phobos, but if it's new, then the correct one will have to be chosen - // manually from the results. - string[] haveMultiple; - foreach (win, nixes; win2Nix) - { - if (nixes.length > 1) - haveMultiple ~= win; - } - bool[string] haveConflicts; - foreach (win; haveMultiple) - { - if (auto curr = windowsTZNameToTZDatabaseName(win)) - { - if (auto other = curr in nix2Win) - { - if ((*other)[0] == win) - { - win2Nix[win] = [curr]; - continue; - } - } - } - haveConflicts[win] = true; - writefln("Warning: %s -> %s", win, win2Nix[win]); - } - - - string[] nix2WinLines = [ - `string tzDatabaseNameToWindowsTZName(string tzName) @safe pure nothrow @nogc`, - `{`, - ` switch (tzName)`, - ` {`]; - - foreach (nix; nix2Win.keys.sort()) - nix2WinLines ~= format(` case "%s": return "%s";`, nix, nix2Win[nix][0]); - - nix2WinLines ~= [ - ` default: return null;`, - ` }`, - `}`]; - - - string[] win2NixLines = [ - `string windowsTZNameToTZDatabaseName(string tzName) @safe pure nothrow @nogc`, - `{`, - ` switch (tzName)`, - ` {`]; - foreach (win; win2Nix.keys.sort()) - { - immutable hasMultiple = cast(bool)(win in haveConflicts); - foreach (nix; win2Nix[win]) - win2NixLines ~= format(` case "%s": return "%s";%s`, win, nix, hasMultiple ? " FIXME" : ""); - } - - win2NixLines ~= [ - ` default: return null;`, - ` }`, - `}`]; - - - auto nix2WinFile = args[2]; - std.file.write(nix2WinFile, nix2WinLines.join("\n")); - - auto win2NixFile = args[3]; - std.file.write(win2NixFile, win2NixLines.join("\n")); - - return 0; -} -+/ diff --git a/libphobos/src/std/exception.d b/libphobos/src/std/exception.d index 3e95b22b1..324a99348 100644 --- a/libphobos/src/std/exception.d +++ b/libphobos/src/std/exception.d @@ -622,16 +622,20 @@ private void bailOut(E : Throwable = Exception)(string file, size_t line, scope $(D new ErrnoException(msg)) is thrown. It is assumed that the last operation set `errno` to an error code corresponding with the failed condition. - - Example: - -------------------- - auto f = errnoEnforce(fopen("data.txt")); - auto line = readln(f); - enforce(line.length); // expect a non-empty line - -------------------- +/ alias errnoEnforce = enforce!ErrnoException; +/// +@system unittest +{ + import core.stdc.stdio : fclose, fgets, fopen; + auto f = fopen(__FILE_FULL_PATH__, "r").errnoEnforce; + scope(exit) fclose(f); + char[100] buf; + auto line = fgets(buf.ptr, buf.length, f); + enforce(line !is null); // expect a non-empty line +} + // @@@DEPRECATED_2.089@@@ /++ $(RED Deprecated. Please use $(LREF enforce) instead. This function will be removed 2.089.) @@ -1847,7 +1851,7 @@ expression. } version(unittest) package -@property void assertCTFEable(alias dg)() +void assertCTFEable(alias dg)() { static assert({ cast(void) dg(); return true; }()); cast(void) dg(); diff --git a/libphobos/src/std/experimental/allocator/mmap_allocator.d b/libphobos/src/std/experimental/allocator/mmap_allocator.d index 47c2e40fe..bf40b07a8 100644 --- a/libphobos/src/std/experimental/allocator/mmap_allocator.d +++ b/libphobos/src/std/experimental/allocator/mmap_allocator.d @@ -17,7 +17,7 @@ managed by fine-granular allocators. struct MmapAllocator { /// The one shared instance. - static shared MmapAllocator instance; + static shared const MmapAllocator instance; /** Alignment is page-size and hardcoded to 4096 (even though on certain systems @@ -28,24 +28,29 @@ struct MmapAllocator version(Posix) { /// Allocator API. - nothrow @nogc @safe - void[] allocate(size_t bytes) shared + pure nothrow @nogc @safe + void[] allocate(size_t bytes) shared const { - import core.sys.posix.sys.mman : mmap, MAP_ANON, PROT_READ, + import core.sys.posix.sys.mman : MAP_ANON, PROT_READ, PROT_WRITE, MAP_PRIVATE, MAP_FAILED; if (!bytes) return null; - auto p = (() @trusted => mmap(null, bytes, PROT_READ | PROT_WRITE, + const errnosave = (() @trusted => fakePureErrno())(); // For purity revert changes to errno. + auto p = (() @trusted => fakePureMmap(null, bytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0))(); - if (p is MAP_FAILED) return null; + if (p is MAP_FAILED) + { + (() @trusted => fakePureErrno() = errnosave)(); // errno only changed on MAP_FAILED. + return null; + } return (() @trusted => p[0 .. bytes])(); } /// Ditto - nothrow @nogc - bool deallocate(void[] b) shared + pure nothrow @nogc + bool deallocate(void[] b) shared const { - import core.sys.posix.sys.mman : munmap; - if (b.ptr) munmap(b.ptr, b.length) == 0 || assert(0); + // Because we assert(0) on error we don't need to reset errno for purity. + if (b.ptr) fakePureMunmap(b.ptr, b.length) == 0 || assert(0); return true; } @@ -66,24 +71,31 @@ struct MmapAllocator } else version(Windows) { - import core.sys.windows.windows : VirtualAlloc, VirtualFree, MEM_COMMIT, - PAGE_READWRITE, MEM_RELEASE; + import core.sys.windows.windows : MEM_COMMIT, PAGE_READWRITE, MEM_RELEASE; /// Allocator API. - nothrow @nogc @safe - void[] allocate(size_t bytes) shared + pure nothrow @nogc @safe + void[] allocate(size_t bytes) shared const { if (!bytes) return null; + // For purity ensure last-error does not visibly change. + const lastErrorSave = (() @trusted => GetLastError())(); auto p = (() @trusted => VirtualAlloc(null, bytes, MEM_COMMIT, PAGE_READWRITE))(); if (p == null) + { + // Last-error only changed if allocation failed. + (() @trusted => SetLastError(lastErrorSave))(); return null; + } return (() @trusted => p[0 .. bytes])(); } /// Ditto - nothrow @nogc - bool deallocate(void[] b) shared + pure nothrow @nogc + bool deallocate(void[] b) shared const { + const lastErrorSave = GetLastError(); // For purity ensure last-error does not visibly change. + scope(exit) SetLastError(lastErrorSave); return b.ptr is null || VirtualFree(b.ptr, 0, MEM_RELEASE) != 0; } @@ -91,7 +103,33 @@ struct MmapAllocator } } -nothrow @safe @nogc unittest +// pure wrappers around `mmap` and `munmap` because they are used here locally +// solely to perform allocation and deallocation which in this case is `pure` +version(Posix) +extern (C) private pure @system @nogc nothrow +{ + import core.sys.posix.sys.types : off_t; + pragma(mangle, "fakePureErrnoImpl") ref int fakePureErrno(); + pragma(mangle, "mmap") void* fakePureMmap(void*, size_t, int, int, int, off_t); + pragma(mangle, "munmap") int fakePureMunmap(void*, size_t); +} + +// Pure wrappers around VirtualAlloc/VirtualFree for use here only. Their use is sound +// because when we call them we ensure that last-error is not visibly changed. +version(Windows) +extern (Windows) private pure @system @nogc nothrow +{ + import core.sys.windows.basetsd : SIZE_T; + import core.sys.windows.windef : BOOL, DWORD; + import core.sys.windows.winnt : LPVOID, PVOID; + + DWORD GetLastError(); + void SetLastError(DWORD); + PVOID VirtualAlloc(PVOID, SIZE_T, DWORD, DWORD); + BOOL VirtualFree(PVOID, SIZE_T, DWORD); +} + +pure nothrow @safe @nogc unittest { alias alloc = MmapAllocator.instance; auto p = alloc.allocate(100); diff --git a/libphobos/src/std/file.d b/libphobos/src/std/file.d index a92c88071..0684b466e 100644 --- a/libphobos/src/std/file.d +++ b/libphobos/src/std/file.d @@ -54,6 +54,8 @@ $(TR $(TD Timestamp) $(TD $(LREF getTimesWin) $(LREF setTimes) $(LREF timeLastModified) + $(LREF timeLastAccessed) + $(LREF timeStatusChanged) )) $(TR $(TD Other) $(TD $(LREF DirEntry) @@ -1182,7 +1184,7 @@ if (isConvertibleToString!R) // Reads a time field from a stat_t with full precision. version(Posix) -private SysTime statTimeToStdTime(char which)(ref stat_t statbuf) +private SysTime statTimeToStdTime(char which)(ref const stat_t statbuf) { auto unixTime = mixin(`statbuf.st_` ~ which ~ `time`); long stdTime = unixTimeToStdTime(unixTime); @@ -1786,6 +1788,57 @@ if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R)) assert(target.timeLastModified(SysTime.min) >= source.timeLastModified); } +version(StdDdoc) +{ + /++ + $(BLUE This function is Posix-Only.) + + Returns the time that the given file was last modified. + Params: + statbuf = stat_t retrieved from file. + +/ + SysTime timeLastModified()(auto ref stat_t statbuf) pure nothrow {assert(false);} + /++ + $(BLUE This function is Posix-Only.) + + Returns the time that the given file was last accessed. + Params: + statbuf = stat_t retrieved from file. + +/ + SysTime timeLastAccessed()(auto ref stat_t statbuf) pure nothrow {assert(false);} + /++ + $(BLUE This function is Posix-Only.) + + Returns the time that the given file was last changed. + Params: + statbuf = stat_t retrieved from file. + +/ + SysTime timeStatusChanged()(auto ref stat_t statbuf) pure nothrow {assert(false);} +} +else version(Posix) +{ + SysTime timeLastModified()(auto ref stat_t statbuf) pure nothrow + { + return statTimeToStdTime!'m'(statbuf); + } + SysTime timeLastAccessed()(auto ref stat_t statbuf) pure nothrow + { + return statTimeToStdTime!'a'(statbuf); + } + SysTime timeStatusChanged()(auto ref stat_t statbuf) pure nothrow + { + return statTimeToStdTime!'c'(statbuf); + } + + @safe unittest + { + stat_t statbuf; + // check that both lvalues and rvalues work + timeLastAccessed(statbuf); + timeLastAccessed(stat_t.init); + } +} + @safe unittest { //std.process.system("echo a > deleteme") == 0 || assert(false); diff --git a/libphobos/src/std/format.d b/libphobos/src/std/format.d index 921781ac6..dd415f108 100644 --- a/libphobos/src/std/format.d +++ b/libphobos/src/std/format.d @@ -620,6 +620,14 @@ uint formattedWrite(Writer, Char, A...)(auto ref Writer w, in Char[] fmt, A args assert(w.data == "@safe/pure 42"); } +@safe pure unittest +{ + char[20] buf; + auto w = buf[]; + formattedWrite(w, "%s %d", "@safe/pure", 42); + assert(buf[0 .. $ - w.length] == "@safe/pure 42"); +} + /** Reads characters from $(REF_ALTTEXT input range, isInputRange, std,range,primitives) `r`, converts them according to `fmt`, and writes them to `args`. @@ -2348,13 +2356,14 @@ private void formatUnsigned(Writer, T, Char) size_t leftpad = 0; size_t rightpad = 0; + immutable size_t dlen = digits.length == 0 ? 0 : digits.length - 1; immutable ptrdiff_t spacesToPrint = fs.width - ( (prefix1 != 0) + (prefix2 != 0) + zerofill + digits.length - + ((fs.flSeparator != 0) * ((digits.length - 1) / fs.separators)) + + ((fs.flSeparator != 0) * (dlen / fs.separators)) ); if (spacesToPrint > 0) // need to do some padding { @@ -2421,6 +2430,11 @@ private void formatUnsigned(Writer, T, Char) put(w, ' '); } +@safe pure unittest // bugzilla 18838 +{ + assert("%12,d".format(0) == " 0"); +} + @safe pure unittest { assert(collectExceptionMsg!FormatException(format("%c", 5)).back == 'c'); @@ -3761,38 +3775,46 @@ private template hasToString(T, Char) static assert(hasToString!(L, char) == 0); } +// Like NullSink, but toString() isn't even called at all. Used to test the format string. +private struct NoOpSink +{ + void put(E)(scope const E) pure @safe @nogc nothrow {} +} + // object formatting with toString private void formatObject(Writer, T, Char)(ref Writer w, ref T val, const ref FormatSpec!Char f) if (hasToString!(T, Char)) { enum overload = hasToString!(T, Char); + enum noop = is(Writer == NoOpSink); + static if (overload == 6) { - val.toString(w, f); + static if (!noop) val.toString(w, f); } else static if (overload == 5) { - val.toString(w); + static if (!noop) val.toString(w); } // not using the overload enum to not break badly defined toString overloads // e.g. defining the FormatSpec as ref and not const ref led this function // to ignore that toString overload else static if (is(typeof(val.toString((scope const(char)[] s){}, f)))) { - val.toString((scope const(char)[] s) { put(w, s); }, f); + static if (!noop) val.toString((scope const(char)[] s) { put(w, s); }, f); } else static if (is(typeof(val.toString((scope const(char)[] s){}, "%s")))) { - val.toString((const(char)[] s) { put(w, s); }, f.getCurFmtStr()); + static if (!noop) val.toString((const(char)[] s) { put(w, s); }, f.getCurFmtStr()); } else static if (is(typeof(val.toString((scope const(char)[] s){})))) { - val.toString((scope const(char)[] s) { put(w, s); }); + static if (!noop) val.toString((scope const(char)[] s) { put(w, s); }); } else static if (is(typeof(val.toString()) S) && isSomeString!S) { - put(w, val.toString()); + static if (!noop) put(w, val.toString()); } else { @@ -4004,6 +4026,27 @@ version(unittest) assert(s == "shared(std.format.C)"); } +// https://issues.dlang.org/show_bug.cgi?id=19003 +@safe unittest +{ + struct S + { + int i; + + @disable this(); + + invariant { assert(this.i); } + + this(int i) @safe in { assert(i); } do { this.i = i; } + + string toString() { return "S"; } + } + + S s = S(1); + + format!"%s"(s); +} + // https://issues.dlang.org/show_bug.cgi?id=7879 @safe unittest { @@ -6164,8 +6207,14 @@ deprecated // Used to check format strings are compatible with argument types package static const checkFormatException(alias fmt, Args...) = { + import std.conv : text; + try - .format(fmt, Args.init); + { + auto n = .formattedWrite(NoOpSink(), fmt, Args.init); + + enforceFmt(n == Args.length, text("Orphan format arguments: args[", n, "..", Args.length, "]")); + } catch (Exception e) return (e.msg == ctfpMessage) ? null : e; return null; diff --git a/libphobos/src/std/functional.d b/libphobos/src/std/functional.d index 98c09071a..26412f15b 100644 --- a/libphobos/src/std/functional.d +++ b/libphobos/src/std/functional.d @@ -716,15 +716,11 @@ Returns: */ template partial(alias fun, alias arg) { - static if (is(typeof(fun) == delegate) || is(typeof(fun) == function)) - { - import std.traits : ReturnType; - ReturnType!fun partial(Parameters!fun[1..$] args2) - { - return fun(arg, args2); - } - } - else + import std.traits : isCallable; + // Check whether fun is a user defined type which implements opCall or a template. + // As opCall itself can be templated, std.traits.isCallable does not work here. + enum isSomeFunctor = (is(typeof(fun) == struct) || is(typeof(fun) == class)) && __traits(hasMember, fun, "opCall"); + static if (isSomeFunctor || __traits(isTemplate, fun)) { auto partial(Ts...)(Ts args2) { @@ -747,6 +743,36 @@ template partial(alias fun, alias arg) } } } + else static if (!isCallable!fun) + { + static assert(false, "Cannot apply partial to a non-callable '" ~ fun.stringof ~ "'."); + } + else // Assume fun is callable and uniquely defined. + { + static if (Parameters!fun.length == 0) + { + static assert(0, "Cannot partially apply '" ~ fun.stringof ~ "'." ~ + "'" ~ fun.stringof ~ "' has 0 arguments."); + } + else static if (!is(typeof(arg) : Parameters!fun[0])) + { + string errorMsg() + { + string msg = "Argument mismatch for '" ~ fun.stringof ~ "': expected " ~ + Parameters!fun[0].stringof ~ ", but got " ~ typeof(arg).stringof ~ "."; + return msg; + } + static assert(0, errorMsg()); + } + else + { + import std.traits : ReturnType; + ReturnType!fun partial(Parameters!fun[1..$] args2) + { + return fun(arg, args2); + } + } + } } /// @@ -833,6 +859,11 @@ template partial(alias fun, alias arg) assert(partial!(tcallable, 5)(6) == 11); static assert(!is(typeof(partial!(tcallable, "5")(6)))); + static struct NonCallable{} + static assert(!__traits(compiles, partial!(NonCallable, 5)), "Partial should not work on non-callable structs."); + static assert(!__traits(compiles, partial!(NonCallable.init, 5)), + "Partial should not work on instances of non-callable structs."); + static A funOneArg(A)(A a) { return a; } alias funOneArg1 = partial!(funOneArg, 1); assert(funOneArg1() == 1); @@ -846,6 +877,28 @@ template partial(alias fun, alias arg) assert(dg2() == 1); } +// Fix issue 15732 +@safe unittest +{ + // Test whether it works with functions. + auto partialFunction(){ + auto fullFunction = (float a, float b, float c) => a + b / c; + alias apply1 = partial!(fullFunction, 1); + return &apply1; + } + auto result = partialFunction()(2, 4); + assert(result == 1.5f); + + // And with delegates. + auto partialDelegate(float c){ + auto fullDelegate = (float a, float b) => a + b / c; + alias apply1 = partial!(fullDelegate, 1); + return &apply1; + } + auto result2 = partialDelegate(4)(2); + assert(result2 == 1.5f); +} + /** Takes multiple functions and adjoins them together. diff --git a/libphobos/src/std/internal/attributes.d b/libphobos/src/std/internal/attributes.d new file mode 100644 index 000000000..2405326b9 --- /dev/null +++ b/libphobos/src/std/internal/attributes.d @@ -0,0 +1,11 @@ +module std.internal.attributes; + +/** +Used to annotate `unittest`s which need to be tested in a `-betterC` environment. + +Such `unittest`s will be compiled and executed without linking druntime in, with +a `__traits(getUnitTests, mixin(__MODULE__))` style test runner. +Note that just like any other `unittest` in phobos, they will also be compiled +and executed without `-betterC`. +*/ +package(std) enum betterC = 1; diff --git a/libphobos/src/std/json.d b/libphobos/src/std/json.d index 235fdbf17..a6c7ba711 100644 --- a/libphobos/src/std/json.d +++ b/libphobos/src/std/json.d @@ -6,7 +6,7 @@ JavaScript Object Notation Copyright: Copyright Jeremie Pelletier 2008 - 2009. License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: Jeremie Pelletier, David Herberth -References: $(LINK http://json.org/) +References: $(LINK http://json.org/), $(LINK http://seriot.ch/parsing_json.html) Source: $(PHOBOSSRC std/json.d) */ /* @@ -39,7 +39,7 @@ import std.traits; long x; if (const(JSONValue)* code = "code" in j) { - if (code.type() == JSON_TYPE.INTEGER) + if (code.type() == JSONType.integer) x = code.integer; else x = to!int(code.str); @@ -77,25 +77,39 @@ enum JSONOptions specialFloatLiterals = 0x1, /// encode NaN and Inf float values as strings escapeNonAsciiChars = 0x2, /// encode non ascii characters with an unicode escape sequence doNotEscapeSlashes = 0x4, /// do not escape slashes ('/') + strictParsing = 0x8, /// Strictly follow RFC-8259 grammar when parsing } /** JSON type enumeration */ -enum JSON_TYPE : byte +enum JSONType : byte { /// Indicates the type of a `JSONValue`. - NULL, - STRING, /// ditto - INTEGER, /// ditto - UINTEGER,/// ditto - FLOAT, /// ditto - OBJECT, /// ditto - ARRAY, /// ditto - TRUE, /// ditto - FALSE /// ditto + null_, + string, /// ditto + integer, /// ditto + uinteger, /// ditto + float_, /// ditto + array, /// ditto + object, /// ditto + true_, /// ditto + false_, /// ditto + /// These symbols will be deprecated after 2.082. + NULL = null_, + STRING = string, + INTEGER = integer, + UINTEGER = uinteger, + FLOAT = float_, + ARRAY = array, + OBJECT = object, + TRUE = true_, + FALSE = false_, } +/// This alias will be deprecated after 2.082. +alias JSON_TYPE = JSONType; + /** JSON value node */ @@ -113,12 +127,12 @@ struct JSONValue JSONValue[] array; } private Store store; - private JSON_TYPE type_tag; + private JSONType type_tag; /** - Returns the JSON_TYPE of the value stored in this structure. + Returns the JSONType of the value stored in this structure. */ - @property JSON_TYPE type() const pure nothrow @safe @nogc + @property JSONType type() const pure nothrow @safe @nogc { return type_tag; } @@ -127,18 +141,18 @@ struct JSONValue { string s = "{ \"language\": \"D\" }"; JSONValue j = parseJSON(s); - assert(j.type == JSON_TYPE.OBJECT); - assert(j["language"].type == JSON_TYPE.STRING); + assert(j.type == JSONType.object); + assert(j["language"].type == JSONType.string); } /*** - * Value getter/setter for `JSON_TYPE.STRING`. + * Value getter/setter for `JSONType.string`. * Throws: `JSONException` for read access if `type` is not - * `JSON_TYPE.STRING`. + * `JSONType.string`. */ @property string str() const pure @trusted { - enforce!JSONException(type == JSON_TYPE.STRING, + enforce!JSONException(type == JSONType.string, "JSONValue is not a string"); return store.str; } @@ -162,13 +176,13 @@ struct JSONValue } /*** - * Value getter/setter for `JSON_TYPE.INTEGER`. + * Value getter/setter for `JSONType.integer`. * Throws: `JSONException` for read access if `type` is not - * `JSON_TYPE.INTEGER`. + * `JSONType.integer`. */ @property inout(long) integer() inout pure @safe { - enforce!JSONException(type == JSON_TYPE.INTEGER, + enforce!JSONException(type == JSONType.integer, "JSONValue is not an integer"); return store.integer; } @@ -180,13 +194,13 @@ struct JSONValue } /*** - * Value getter/setter for `JSON_TYPE.UINTEGER`. + * Value getter/setter for `JSONType.uinteger`. * Throws: `JSONException` for read access if `type` is not - * `JSON_TYPE.UINTEGER`. + * `JSONType.uinteger`. */ @property inout(ulong) uinteger() inout pure @safe { - enforce!JSONException(type == JSON_TYPE.UINTEGER, + enforce!JSONException(type == JSONType.uinteger, "JSONValue is not an unsigned integer"); return store.uinteger; } @@ -198,14 +212,14 @@ struct JSONValue } /*** - * Value getter/setter for `JSON_TYPE.FLOAT`. Note that despite + * Value getter/setter for `JSONType.float_`. Note that despite * the name, this is a $(B 64)-bit `double`, not a 32-bit `float`. * Throws: `JSONException` for read access if `type` is not - * `JSON_TYPE.FLOAT`. + * `JSONType.float_`. */ @property inout(double) floating() inout pure @safe { - enforce!JSONException(type == JSON_TYPE.FLOAT, + enforce!JSONException(type == JSONType.float_, "JSONValue is not a floating type"); return store.floating; } @@ -217,9 +231,9 @@ struct JSONValue } /*** - * Value getter/setter for `JSON_TYPE.OBJECT`. + * Value getter/setter for `JSONType.object`. * Throws: `JSONException` for read access if `type` is not - * `JSON_TYPE.OBJECT`. + * `JSONType.object`. * Note: this is @system because of the following pattern: --- auto a = &(json.object()); @@ -229,7 +243,7 @@ struct JSONValue */ @property ref inout(JSONValue[string]) object() inout pure @system { - enforce!JSONException(type == JSON_TYPE.OBJECT, + enforce!JSONException(type == JSONType.object, "JSONValue is not an object"); return store.object; } @@ -241,7 +255,7 @@ struct JSONValue } /*** - * Value getter for `JSON_TYPE.OBJECT`. + * Value getter for `JSONType.object`. * Unlike `object`, this retrieves the object by value and can be used in @safe code. * * A caveat is that, if the returned value is null, modifications will not be visible: @@ -253,19 +267,19 @@ struct JSONValue * --- * * Throws: `JSONException` for read access if `type` is not - * `JSON_TYPE.OBJECT`. + * `JSONType.object`. */ @property inout(JSONValue[string]) objectNoRef() inout pure @trusted { - enforce!JSONException(type == JSON_TYPE.OBJECT, + enforce!JSONException(type == JSONType.object, "JSONValue is not an object"); return store.object; } /*** - * Value getter/setter for `JSON_TYPE.ARRAY`. + * Value getter/setter for `JSONType.array`. * Throws: `JSONException` for read access if `type` is not - * `JSON_TYPE.ARRAY`. + * `JSONType.array`. * Note: this is @system because of the following pattern: --- auto a = &(json.array()); @@ -275,7 +289,7 @@ struct JSONValue */ @property ref inout(JSONValue[]) array() inout pure @system { - enforce!JSONException(type == JSON_TYPE.ARRAY, + enforce!JSONException(type == JSONType.array, "JSONValue is not an array"); return store.array; } @@ -287,7 +301,7 @@ struct JSONValue } /*** - * Value getter for `JSON_TYPE.ARRAY`. + * Value getter for `JSONType.array`. * Unlike `array`, this retrieves the array by value and can be used in @safe code. * * A caveat is that, if you append to the returned array, the new values aren't visible in the @@ -300,37 +314,37 @@ struct JSONValue * --- * * Throws: `JSONException` for read access if `type` is not - * `JSON_TYPE.ARRAY`. + * `JSONType.array`. */ @property inout(JSONValue[]) arrayNoRef() inout pure @trusted { - enforce!JSONException(type == JSON_TYPE.ARRAY, + enforce!JSONException(type == JSONType.array, "JSONValue is not an array"); return store.array; } - /// Test whether the type is `JSON_TYPE.NULL` + /// Test whether the type is `JSONType.null_` @property bool isNull() const pure nothrow @safe @nogc { - return type == JSON_TYPE.NULL; + return type == JSONType.null_; } private void assign(T)(T arg) @safe { static if (is(T : typeof(null))) { - type_tag = JSON_TYPE.NULL; + type_tag = JSONType.null_; } else static if (is(T : string)) { - type_tag = JSON_TYPE.STRING; + type_tag = JSONType.string; string t = arg; () @trusted { store.str = t; }(); } else static if (isSomeString!T) // issue 15884 { - type_tag = JSON_TYPE.STRING; - // FIXME: std.array.array(Range) is not deduced as 'pure' + type_tag = JSONType.string; + // FIXME: std.Array.Array(Range) is not deduced as 'pure' () @trusted { import std.utf : byUTF; store.str = cast(immutable)(arg.byUTF!char.array); @@ -338,27 +352,27 @@ struct JSONValue } else static if (is(T : bool)) { - type_tag = arg ? JSON_TYPE.TRUE : JSON_TYPE.FALSE; + type_tag = arg ? JSONType.true_ : JSONType.false_; } else static if (is(T : ulong) && isUnsigned!T) { - type_tag = JSON_TYPE.UINTEGER; + type_tag = JSONType.uinteger; store.uinteger = arg; } else static if (is(T : long)) { - type_tag = JSON_TYPE.INTEGER; + type_tag = JSONType.integer; store.integer = arg; } else static if (isFloatingPoint!T) { - type_tag = JSON_TYPE.FLOAT; + type_tag = JSONType.float_; store.floating = arg; } else static if (is(T : Value[Key], Key, Value)) { static assert(is(Key : string), "AA key must be string"); - type_tag = JSON_TYPE.OBJECT; + type_tag = JSONType.object; static if (is(Value : JSONValue)) { JSONValue[string] t = arg; @@ -374,7 +388,7 @@ struct JSONValue } else static if (isArray!T) { - type_tag = JSON_TYPE.ARRAY; + type_tag = JSONType.array; static if (is(ElementEncodingType!T : JSONValue)) { JSONValue[] t = arg; @@ -395,13 +409,13 @@ struct JSONValue } else { - static assert(false, text(`unable to convert type "`, T.stringof, `" to json`)); + static assert(false, text(`unable to convert type "`, T.Stringof, `" to json`)); } } private void assignRef(T)(ref T arg) if (isStaticArray!T) { - type_tag = JSON_TYPE.ARRAY; + type_tag = JSONType.array; static if (is(ElementEncodingType!T : JSONValue)) { store.array = arg; @@ -418,8 +432,8 @@ struct JSONValue /** * Constructor for `JSONValue`. If `arg` is a `JSONValue` * its value and type will be copied to the new `JSONValue`. - * Note that this is a shallow copy: if type is `JSON_TYPE.OBJECT` - * or `JSON_TYPE.ARRAY` then only the reference to the data will + * Note that this is a shallow copy: if type is `JSONType.object` + * or `JSONType.array` then only the reference to the data will * be copied. * Otherwise, `arg` must be implicitly convertible to one of the * following types: `typeof(null)`, `string`, `ulong`, @@ -449,10 +463,10 @@ struct JSONValue j = JSONValue(42); j = JSONValue( [1, 2, 3] ); - assert(j.type == JSON_TYPE.ARRAY); + assert(j.type == JSONType.array); j = JSONValue( ["language": "D"] ); - assert(j.type == JSON_TYPE.OBJECT); + assert(j.type == JSONType.object); } void opAssign(T)(T arg) if (!isStaticArray!T && !is(T : JSONValue)) @@ -467,7 +481,7 @@ struct JSONValue /*** * Array syntax for json arrays. - * Throws: `JSONException` if `type` is not `JSON_TYPE.ARRAY`. + * Throws: `JSONException` if `type` is not `JSONType.array`. */ ref inout(JSONValue) opIndex(size_t i) inout pure @safe { @@ -486,7 +500,7 @@ struct JSONValue /*** * Hash syntax for json objects. - * Throws: `JSONException` if `type` is not `JSON_TYPE.OBJECT`. + * Throws: `JSONException` if `type` is not `JSONType.object`. */ ref inout(JSONValue) opIndex(string k) inout pure @safe { @@ -507,15 +521,15 @@ struct JSONValue * If JSON value is null, then operator initializes it with object and then * sets `value` for it. * - * Throws: `JSONException` if `type` is not `JSON_TYPE.OBJECT` - * or `JSON_TYPE.NULL`. + * Throws: `JSONException` if `type` is not `JSONType.object` + * or `JSONType.null_`. */ void opIndexAssign(T)(auto ref T value, string key) pure { - enforce!JSONException(type == JSON_TYPE.OBJECT || type == JSON_TYPE.NULL, + enforce!JSONException(type == JSONType.object || type == JSONType.null_, "JSONValue must be object or null"); JSONValue[string] aa = null; - if (type == JSON_TYPE.OBJECT) + if (type == JSONType.object) { aa = this.objectNoRef; } @@ -591,8 +605,8 @@ struct JSONValue * when found, the `const(JSONValue)*` that matches to the key, * otherwise `null`. * - * Throws: `JSONException` if the right hand side argument `JSON_TYPE` - * is not `OBJECT`. + * Throws: `JSONException` if the right hand side argument `JSONType` + * is not `object`. */ auto opBinaryRight(string op : "in")(string k) const @safe { @@ -619,21 +633,21 @@ struct JSONValue final switch (type_tag) { - case JSON_TYPE.STRING: + case JSONType.string: return store.str == rhs.store.str; - case JSON_TYPE.INTEGER: + case JSONType.integer: return store.integer == rhs.store.integer; - case JSON_TYPE.UINTEGER: + case JSONType.uinteger: return store.uinteger == rhs.store.uinteger; - case JSON_TYPE.FLOAT: + case JSONType.float_: return store.floating == rhs.store.floating; - case JSON_TYPE.OBJECT: + case JSONType.object: return store.object == rhs.store.object; - case JSON_TYPE.ARRAY: + case JSONType.array: return store.array == rhs.store.array; - case JSON_TYPE.TRUE: - case JSON_TYPE.FALSE: - case JSON_TYPE.NULL: + case JSONType.true_: + case JSONType.false_: + case JSONType.null_: return true; } } @@ -656,7 +670,7 @@ struct JSONValue /// Implements the foreach `opApply` interface for json objects. int opApply(scope int delegate(string key, ref JSONValue) dg) @system { - enforce!JSONException(type == JSON_TYPE.OBJECT, + enforce!JSONException(type == JSONType.object, "JSONValue is not an object"); int result; @@ -694,7 +708,8 @@ struct JSONValue /** Parses a serialized string and returns a tree of JSON values. -Throws: $(LREF JSONException) if the depth exceeds the max depth. +Throws: $(LREF JSONException) if string does not follow the JSON grammar or the depth exceeds the max depth, + $(LREF ConvException) if a number in the input cannot be represented by a native D type. Params: json = json-formatted string to parse maxDepth = maximum depth of nesting allowed, -1 disables depth checking @@ -703,10 +718,10 @@ Params: JSONValue parseJSON(T)(T json, int maxDepth = -1, JSONOptions options = JSONOptions.none) if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T)) { - import std.ascii : isWhite, isDigit, isHexDigit, toUpper, toLower; - import std.typecons : Yes; + import std.ascii : isDigit, isHexDigit, toUpper, toLower; + import std.typecons : Nullable, Yes; JSONValue root; - root.type_tag = JSON_TYPE.NULL; + root.type_tag = JSONType.null_; // Avoid UTF decoding when possible, as it is unnecessary when // processing JSON. @@ -715,17 +730,37 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T)) else alias Char = Unqual!(ElementType!T); - if (json.empty) return root; - int depth = -1; - Char next = 0; + Nullable!Char next; int line = 1, pos = 0; + immutable bool strict = (options & JSONOptions.strictParsing) != 0; void error(string msg) { throw new JSONException(msg, line, pos); } + if (json.empty) + { + if (strict) + { + error("Empty JSON body"); + } + return root; + } + + bool isWhite(dchar c) + { + if (strict) + { + // RFC 7159 has a stricter definition of whitespace than general ASCII. + return c == ' ' || c == '\t' || c == '\n' || c == '\r'; + } + import std.ascii : isWhite; + // Accept ASCII NUL as whitespace in non-strict mode. + return c == 0 || isWhite(c); + } + Char popChar() { if (json.empty) error("Unexpected end of data."); @@ -755,17 +790,35 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T)) Char peekChar() { - if (!next) + if (next.isNull) { if (json.empty) return '\0'; next = popChar(); } + return next.get; + } + + Nullable!Char peekCharNullable() + { + if (next.isNull && !json.empty) + { + next = popChar(); + } return next; } void skipWhitespace() { - while (isWhite(peekChar())) next = 0; + while (true) + { + auto c = peekCharNullable(); + if (c.isNull || + !isWhite(c.get)) + { + return; + } + next.nullify(); + } } Char getChar(bool SkipWhitespace = false)() @@ -773,10 +826,10 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T)) static if (SkipWhitespace) skipWhitespace(); Char c; - if (next) + if (!next.isNull) { - c = next; - next = 0; + c = next.get; + next.nullify(); } else c = popChar(); @@ -784,11 +837,11 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T)) return c; } - void checkChar(bool SkipWhitespace = true, bool CaseSensitive = true)(char c) + void checkChar(bool SkipWhitespace = true)(char c, bool caseSensitive = true) { static if (SkipWhitespace) skipWhitespace(); auto c2 = getChar(); - static if (!CaseSensitive) c2 = toLower(c2); + if (!caseSensitive) c2 = toLower(c2); if (c2 != c) error(text("Found '", c2, "' when expecting '", c, "'.")); } @@ -819,7 +872,6 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T)) string parseString() { - import std.ascii : isControl; import std.uni : isSurrogateHi, isSurrogateLo; import std.utf : encode, decode; @@ -880,8 +932,12 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T)) default: // RFC 7159 states that control characters U+0000 through // U+001F must not appear unescaped in a JSON string. + // Note: std.ascii.isControl can't be used for this test + // because it considers ASCII DEL (0x7f) to be a control + // character but RFC 7159 does not. + // Accept unescaped ASCII NULs in non-strict mode. auto c = getChar(); - if (isControl(c)) + if (c < 0x20 && (strict || c != 0)) error("Illegal control character."); str.put(c); goto Next; @@ -943,7 +999,7 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T)) case '[': if (testChar(']')) { - value.type_tag = JSON_TYPE.ARRAY; + value.type_tag = JSONType.array; break; } @@ -968,11 +1024,11 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T)) tryGetSpecialFloat(str, value.store.floating)) { // found a special float, its value was placed in value.store.floating - value.type_tag = JSON_TYPE.FLOAT; + value.type_tag = JSONType.float_; break; } - value.type_tag = JSON_TYPE.STRING; + value.type_tag = JSONType.string; value.store.str = str; break; @@ -1001,7 +1057,18 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T)) isNegative = true; } - readInteger(); + if (strict && c == '0') + { + number.put('0'); + if (isDigit(peekChar())) + { + error("Additional digits not allowed after initial zero digit"); + } + } + else + { + readInteger(); + } if (testChar('.')) { @@ -1023,7 +1090,7 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T)) string data = number.data; if (isFloat) { - value.type_tag = JSON_TYPE.FLOAT; + value.type_tag = JSONType.float_; value.store.floating = parse!double(data); } else @@ -1034,33 +1101,39 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T)) value.store.uinteger = parse!ulong(data); value.type_tag = !isNegative && value.store.uinteger & (1UL << 63) ? - JSON_TYPE.UINTEGER : JSON_TYPE.INTEGER; + JSONType.uinteger : JSONType.integer; } break; - case 't': case 'T': - value.type_tag = JSON_TYPE.TRUE; - checkChar!(false, false)('r'); - checkChar!(false, false)('u'); - checkChar!(false, false)('e'); + if (strict) goto default; + goto case; + case 't': + value.type_tag = JSONType.true_; + checkChar!false('r', strict); + checkChar!false('u', strict); + checkChar!false('e', strict); break; - case 'f': case 'F': - value.type_tag = JSON_TYPE.FALSE; - checkChar!(false, false)('a'); - checkChar!(false, false)('l'); - checkChar!(false, false)('s'); - checkChar!(false, false)('e'); + if (strict) goto default; + goto case; + case 'f': + value.type_tag = JSONType.false_; + checkChar!false('a', strict); + checkChar!false('l', strict); + checkChar!false('s', strict); + checkChar!false('e', strict); break; - case 'n': case 'N': - value.type_tag = JSON_TYPE.NULL; - checkChar!(false, false)('u'); - checkChar!(false, false)('l'); - checkChar!(false, false)('l'); + if (strict) goto default; + goto case; + case 'n': + value.type_tag = JSONType.null_; + checkChar!false('u', strict); + checkChar!false('l', strict); + checkChar!false('l', strict); break; default: @@ -1071,16 +1144,21 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T)) } parseValue(root); + if (strict) + { + skipWhitespace(); + if (!peekCharNullable().isNull) error("Trailing non-whitespace characters"); + } return root; } @safe unittest { enum issue15742objectOfObject = `{ "key1": { "key2": 1 }}`; - static assert(parseJSON(issue15742objectOfObject).type == JSON_TYPE.OBJECT); + static assert(parseJSON(issue15742objectOfObject).type == JSONType.object); enum issue15742arrayOfArray = `[[1]]`; - static assert(parseJSON(issue15742arrayOfArray).type == JSON_TYPE.ARRAY); + static assert(parseJSON(issue15742arrayOfArray).type == JSONType.array); } @safe unittest @@ -1228,7 +1306,7 @@ string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions o final switch (value.type) { - case JSON_TYPE.OBJECT: + case JSONType.object: auto obj = value.objectNoRef; if (!obj.length) { @@ -1275,7 +1353,7 @@ string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions o } break; - case JSON_TYPE.ARRAY: + case JSONType.array: auto arr = value.arrayNoRef; if (arr.empty) { @@ -1297,19 +1375,19 @@ string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions o } break; - case JSON_TYPE.STRING: + case JSONType.string: toString(value.str); break; - case JSON_TYPE.INTEGER: + case JSONType.integer: json.put(to!string(value.store.integer)); break; - case JSON_TYPE.UINTEGER: + case JSONType.uinteger: json.put(to!string(value.store.uinteger)); break; - case JSON_TYPE.FLOAT: + case JSONType.float_: import std.math : isNaN, isInfinity; auto val = value.store.floating; @@ -1349,15 +1427,15 @@ string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions o } break; - case JSON_TYPE.TRUE: + case JSONType.true_: json.put("true"); break; - case JSON_TYPE.FALSE: + case JSONType.false_: json.put("false"); break; - case JSON_TYPE.NULL: + case JSONType.null_: json.put("null"); break; } @@ -1405,7 +1483,7 @@ class JSONException : Exception { import std.exception; JSONValue jv = "123"; - assert(jv.type == JSON_TYPE.STRING); + assert(jv.type == JSONType.string); assertNotThrown(jv.str); assertThrown!JSONException(jv.integer); assertThrown!JSONException(jv.uinteger); @@ -1416,19 +1494,19 @@ class JSONException : Exception assertThrown!JSONException(jv[2]); jv = -3; - assert(jv.type == JSON_TYPE.INTEGER); + assert(jv.type == JSONType.integer); assertNotThrown(jv.integer); jv = cast(uint) 3; - assert(jv.type == JSON_TYPE.UINTEGER); + assert(jv.type == JSONType.uinteger); assertNotThrown(jv.uinteger); jv = 3.0; - assert(jv.type == JSON_TYPE.FLOAT); + assert(jv.type == JSONType.float_); assertNotThrown(jv.floating); jv = ["key" : "value"]; - assert(jv.type == JSON_TYPE.OBJECT); + assert(jv.type == JSONType.object); assertNotThrown(jv.object); assertNotThrown(jv["key"]); assert("key" in jv); @@ -1442,39 +1520,39 @@ class JSONException : Exception { static assert(is(typeof(value) == JSONValue)); assert(key == "key"); - assert(value.type == JSON_TYPE.STRING); + assert(value.type == JSONType.string); assertNotThrown(value.str); assert(value.str == "value"); } jv = [3, 4, 5]; - assert(jv.type == JSON_TYPE.ARRAY); + assert(jv.type == JSONType.array); assertNotThrown(jv.array); assertNotThrown(jv[2]); foreach (size_t index, value; jv) { static assert(is(typeof(value) == JSONValue)); - assert(value.type == JSON_TYPE.INTEGER); + assert(value.type == JSONType.integer); assertNotThrown(value.integer); assert(index == (value.integer-3)); } jv = null; - assert(jv.type == JSON_TYPE.NULL); + assert(jv.type == JSONType.null_); assert(jv.isNull); jv = "foo"; assert(!jv.isNull); jv = JSONValue("value"); - assert(jv.type == JSON_TYPE.STRING); + assert(jv.type == JSONType.string); assert(jv.str == "value"); JSONValue jv2 = JSONValue("value"); - assert(jv2.type == JSON_TYPE.STRING); + assert(jv2.type == JSONType.string); assert(jv2.str == "value"); JSONValue jv3 = JSONValue("\u001c"); - assert(jv3.type == JSON_TYPE.STRING); + assert(jv3.type == JSONType.string); assert(jv3.str == "\u001C"); } @@ -1483,41 +1561,41 @@ class JSONException : Exception // Bugzilla 11504 JSONValue jv = 1; - assert(jv.type == JSON_TYPE.INTEGER); + assert(jv.type == JSONType.integer); jv.str = "123"; - assert(jv.type == JSON_TYPE.STRING); + assert(jv.type == JSONType.string); assert(jv.str == "123"); jv.integer = 1; - assert(jv.type == JSON_TYPE.INTEGER); + assert(jv.type == JSONType.integer); assert(jv.integer == 1); jv.uinteger = 2u; - assert(jv.type == JSON_TYPE.UINTEGER); + assert(jv.type == JSONType.uinteger); assert(jv.uinteger == 2u); jv.floating = 1.5; - assert(jv.type == JSON_TYPE.FLOAT); + assert(jv.type == JSONType.float_); assert(jv.floating == 1.5); jv.object = ["key" : JSONValue("value")]; - assert(jv.type == JSON_TYPE.OBJECT); + assert(jv.type == JSONType.object); assert(jv.object == ["key" : JSONValue("value")]); jv.array = [JSONValue(1), JSONValue(2), JSONValue(3)]; - assert(jv.type == JSON_TYPE.ARRAY); + assert(jv.type == JSONType.array); assert(jv.array == [JSONValue(1), JSONValue(2), JSONValue(3)]); jv = true; - assert(jv.type == JSON_TYPE.TRUE); + assert(jv.type == JSONType.true_); jv = false; - assert(jv.type == JSON_TYPE.FALSE); + assert(jv.type == JSONType.false_); enum E{True = true} jv = E.True; - assert(jv.type == JSON_TYPE.TRUE); + assert(jv.type == JSONType.true_); } @system pure unittest @@ -1658,27 +1736,27 @@ class JSONException : Exception JSONValue jv; jv["int"] = 123; - assert(jv.type == JSON_TYPE.OBJECT); + assert(jv.type == JSONType.object); assert("int" in jv); assert(jv["int"].integer == 123); jv["array"] = [1, 2, 3, 4, 5]; - assert(jv["array"].type == JSON_TYPE.ARRAY); + assert(jv["array"].type == JSONType.array); assert(jv["array"][2].integer == 3); jv["str"] = "D language"; - assert(jv["str"].type == JSON_TYPE.STRING); + assert(jv["str"].type == JSONType.string); assert(jv["str"].str == "D language"); jv["bool"] = false; - assert(jv["bool"].type == JSON_TYPE.FALSE); + assert(jv["bool"].type == JSONType.false_); assert(jv.object.length == 4); jv = [5, 4, 3, 2, 1]; - assert( jv.type == JSON_TYPE.ARRAY ); - assert( jv[3].integer == 2 ); + assert(jv.type == JSONType.array); + assert(jv[3].integer == 2); } @safe unittest @@ -1759,9 +1837,9 @@ pure nothrow @safe unittest // issue 15884 void Test(C)() { C[] a = ['x']; JSONValue testVal = a; - assert(testVal.type == JSON_TYPE.STRING); + assert(testVal.type == JSONType.string); testVal = a.idup; - assert(testVal.type == JSON_TYPE.STRING); + assert(testVal.type == JSONType.string); } Test!char(); Test!wchar(); @@ -1849,3 +1927,72 @@ pure nothrow @safe unittest // issue 15884 assert(parseJSON(`"/"`).toString(JSONOptions.doNotEscapeSlashes) == `"/"`); assert(parseJSON(`"\/"`).toString(JSONOptions.doNotEscapeSlashes) == `"/"`); } + +@safe unittest // JSONOptions.strictParsing (issue 16639) +{ + import std.exception : assertThrown; + + // Unescaped ASCII NULs + assert(parseJSON("[\0]").type == JSONType.array); + assertThrown!JSONException(parseJSON("[\0]", JSONOptions.strictParsing)); + assert(parseJSON("\"\0\"").str == "\0"); + assertThrown!JSONException(parseJSON("\"\0\"", JSONOptions.strictParsing)); + + // Unescaped ASCII DEL (0x7f) in strings + assert(parseJSON("\"\x7f\"").str == "\x7f"); + assert(parseJSON("\"\x7f\"", JSONOptions.strictParsing).str == "\x7f"); + + // "true", "false", "null" case sensitivity + assert(parseJSON("true").type == JSONType.true_); + assert(parseJSON("true", JSONOptions.strictParsing).type == JSONType.true_); + assert(parseJSON("True").type == JSONType.true_); + assertThrown!JSONException(parseJSON("True", JSONOptions.strictParsing)); + assert(parseJSON("tRUE").type == JSONType.true_); + assertThrown!JSONException(parseJSON("tRUE", JSONOptions.strictParsing)); + + assert(parseJSON("false").type == JSONType.false_); + assert(parseJSON("false", JSONOptions.strictParsing).type == JSONType.false_); + assert(parseJSON("False").type == JSONType.false_); + assertThrown!JSONException(parseJSON("False", JSONOptions.strictParsing)); + assert(parseJSON("fALSE").type == JSONType.false_); + assertThrown!JSONException(parseJSON("fALSE", JSONOptions.strictParsing)); + + assert(parseJSON("null").type == JSONType.null_); + assert(parseJSON("null", JSONOptions.strictParsing).type == JSONType.null_); + assert(parseJSON("Null").type == JSONType.null_); + assertThrown!JSONException(parseJSON("Null", JSONOptions.strictParsing)); + assert(parseJSON("nULL").type == JSONType.null_); + assertThrown!JSONException(parseJSON("nULL", JSONOptions.strictParsing)); + + // Whitespace characters + assert(parseJSON("[\f\v]").type == JSONType.array); + assertThrown!JSONException(parseJSON("[\f\v]", JSONOptions.strictParsing)); + assert(parseJSON("[ \t\r\n]").type == JSONType.array); + assert(parseJSON("[ \t\r\n]", JSONOptions.strictParsing).type == JSONType.array); + + // Empty input + assert(parseJSON("").type == JSONType.null_); + assertThrown!JSONException(parseJSON("", JSONOptions.strictParsing)); + + // Numbers with leading '0's + assert(parseJSON("01").integer == 1); + assertThrown!JSONException(parseJSON("01", JSONOptions.strictParsing)); + assert(parseJSON("-01").integer == -1); + assertThrown!JSONException(parseJSON("-01", JSONOptions.strictParsing)); + assert(parseJSON("0.01").floating == 0.01); + assert(parseJSON("0.01", JSONOptions.strictParsing).floating == 0.01); + assert(parseJSON("0e1").floating == 0); + assert(parseJSON("0e1", JSONOptions.strictParsing).floating == 0); + + // Trailing characters after JSON value + assert(parseJSON(`""asdf`).str == ""); + assertThrown!JSONException(parseJSON(`""asdf`, JSONOptions.strictParsing)); + assert(parseJSON("987\0").integer == 987); + assertThrown!JSONException(parseJSON("987\0", JSONOptions.strictParsing)); + assert(parseJSON("987\0\0").integer == 987); + assertThrown!JSONException(parseJSON("987\0\0", JSONOptions.strictParsing)); + assert(parseJSON("[]]").type == JSONType.array); + assertThrown!JSONException(parseJSON("[]]", JSONOptions.strictParsing)); + assert(parseJSON("123 \t\r\n").integer == 123); // Trailing whitespace is OK + assert(parseJSON("123 \t\r\n", JSONOptions.strictParsing).integer == 123); +} diff --git a/libphobos/src/std/math.d b/libphobos/src/std/math.d index dd903f972..b32fe03c4 100644 --- a/libphobos/src/std/math.d +++ b/libphobos/src/std/math.d @@ -396,11 +396,6 @@ template floatTraits(T) enum EXPPOS_SHORT = 0; enum SIGNPOS_BYTE = 0; } - // For IBM doubledouble the larger magnitude double comes first. - // It's really a double[2] and arrays don't index differently - // between little and big-endian targets. - enum DOUBLEPAIR_MSB = 0; - enum DOUBLEPAIR_LSB = 1; } else static assert(false, "No traits support for " ~ T.stringof); @@ -636,10 +631,8 @@ template isDeprecatedComplex(T) * the return type will be the same as the input; */ auto abs(Num)(Num x) -// workaround for https://issues.dlang.org/show_bug.cgi?id=18251 -//if (!isDeprecatedComplex!Num && - //(is(typeof(Num.init >= 0)) && is(typeof(-Num.init)) || - //(is(Unqual!Num == short) || is(Unqual!Num == byte)))) +if ((is(Unqual!Num == short) || is(Unqual!Num == byte)) || + (is(typeof(Num.init >= 0)) && is(typeof(-Num.init)))) { static if (isFloatingPoint!(Num)) return fabs(x); @@ -662,6 +655,7 @@ auto abs(Num)(Num x) assert(abs(2321312L) == 2321312L); } +version(TestComplex) deprecated @safe pure nothrow @nogc unittest { @@ -691,6 +685,7 @@ deprecated }} } +version(TestComplex) deprecated @safe pure nothrow @nogc unittest { @@ -721,6 +716,22 @@ static foreach (Num; AliasSeq!(cfloat, cdouble, creal, ifloat, idouble, ireal)) } } +// https://issues.dlang.org/show_bug.cgi?id=19162 +@safe unittest +{ + struct Vector(T, int size) + { + T x, y, z; + } + + static auto abs(T, int size)(auto ref const Vector!(T, size) v) + { + return v; + } + Vector!(int, 3) v; + assert(abs(v) == v); +} + /* * Complex conjugate * @@ -927,8 +938,32 @@ deprecated * $(TR $(TD $(PLUSMNINF)) $(TD $(NAN)) $(TD yes)) * ) */ +real tan(real x) @trusted pure nothrow @nogc // TODO: @safe +{ + version(InlineAsm_X86_Any) + { + if (!__ctfe) + return tanAsm(x); + } + return tanImpl(x); +} + +/// ditto +double tan(double x) @safe pure nothrow @nogc { return __ctfe ? cast(double) tan(cast(real) x) : tanImpl(x); } + +/// ditto +float tan(float x) @safe pure nothrow @nogc { return __ctfe ? cast(float) tan(cast(real) x) : tanImpl(x); } + +/// +@safe unittest +{ + assert(isIdentical(tan(0.0), 0.0)); + assert(tan(PI).approxEqual(0)); + assert(tan(PI / 3).approxEqual(sqrt(3.0))); +} -real tan(real x) @trusted pure nothrow @nogc +version(InlineAsm_X86_Any) +private real tanAsm(real x) @trusted pure nothrow @nogc { version(D_InlineAsm_X86) { @@ -1019,170 +1054,209 @@ Clear1: asm pure nothrow @nogc{ Lret: {} } else + static assert(0); +} + +private T tanImpl(T)(T x) @safe pure nothrow @nogc +{ + // Coefficients for tan(x) and PI/4 split into three parts. + enum realFormat = floatTraits!T.realFormat; + static if (realFormat == RealFormat.ieeeQuadruple) { - enum realFormat = floatTraits!real.realFormat; + static immutable T[6] P = [ + 2.883414728874239697964612246732416606301E10L, + -2.307030822693734879744223131873392503321E9L, + 5.160188250214037865511600561074819366815E7L, + -4.249691853501233575668486667664718192660E5L, + 1.272297782199996882828849455156962260810E3L, + -9.889929415807650724957118893791829849557E-1L + ]; + static immutable T[7] Q = [ + 8.650244186622719093893836740197250197602E10L + -4.152206921457208101480801635640958361612E10L, + 2.758476078803232151774723646710890525496E9L, + -5.733709132766856723608447733926138506824E7L, + 4.529422062441341616231663543669583527923E5L, + -1.317243702830553658702531997959756728291E3L, + 1.0 + ]; - // Coefficients for tan(x) and PI/4 split into three parts. - static if (realFormat == RealFormat.ieeeQuadruple) - { - static immutable real[6] P = [ - 2.883414728874239697964612246732416606301E10L, - -2.307030822693734879744223131873392503321E9L, - 5.160188250214037865511600561074819366815E7L, - -4.249691853501233575668486667664718192660E5L, - 1.272297782199996882828849455156962260810E3L, - -9.889929415807650724957118893791829849557E-1L - ]; - static immutable real[7] Q = [ - 8.650244186622719093893836740197250197602E10L - -4.152206921457208101480801635640958361612E10L, - 2.758476078803232151774723646710890525496E9L, - -5.733709132766856723608447733926138506824E7L, - 4.529422062441341616231663543669583527923E5L, - -1.317243702830553658702531997959756728291E3L, - 1.0 - ]; + enum T P1 = + 7.853981633974483067550664827649598009884357452392578125E-1L; + enum T P2 = + 2.8605943630549158983813312792950660807511260829685741796657E-18L; + enum T P3 = + 2.1679525325309452561992610065108379921905808E-35L; + } + else static if (realFormat == RealFormat.ieeeExtended || + realFormat == RealFormat.ieeeDouble) + { + static immutable T[3] P = [ + -1.7956525197648487798769E7L, + 1.1535166483858741613983E6L, + -1.3093693918138377764608E4L, + ]; + static immutable T[5] Q = [ + -5.3869575592945462988123E7L, + 2.5008380182335791583922E7L, + -1.3208923444021096744731E6L, + 1.3681296347069295467845E4L, + 1.0000000000000000000000E0L, + ]; - enum real P1 = - 7.853981633974483067550664827649598009884357452392578125E-1L; - enum real P2 = - 2.8605943630549158983813312792950660807511260829685741796657E-18L; - enum real P3 = - 2.1679525325309452561992610065108379921905808E-35L; - } - else - { - static immutable real[3] P = [ - -1.7956525197648487798769E7L, - 1.1535166483858741613983E6L, - -1.3093693918138377764608E4L, - ]; - static immutable real[5] Q = [ - -5.3869575592945462988123E7L, - 2.5008380182335791583922E7L, - -1.3208923444021096744731E6L, - 1.3681296347069295467845E4L, - 1.0000000000000000000000E0L, - ]; + enum T P1 = 7.853981554508209228515625E-1L; + enum T P2 = 7.946627356147928367136046290398E-9L; + enum T P3 = 3.061616997868382943065164830688E-17L; + } + else static if (realFormat == RealFormat.ieeeSingle) + { + static immutable T[6] P = [ + 3.33331568548E-1, + 1.33387994085E-1, + 5.34112807005E-2, + 2.44301354525E-2, + 3.11992232697E-3, + 9.38540185543E-3, + ]; - enum real P1 = 7.853981554508209228515625E-1L; - enum real P2 = 7.946627356147928367136046290398E-9L; - enum real P3 = 3.061616997868382943065164830688E-17L; - } + enum T P1 = 0.78515625; + enum T P2 = 2.4187564849853515625E-4; + enum T P3 = 3.77489497744594108E-8; + } + else + static assert(0, "no coefficients for tan()"); - // Special cases. - if (x == 0.0 || isNaN(x)) - return x; - if (isInfinity(x)) - return real.nan; + // Special cases. + if (x == cast(T) 0.0 || isNaN(x)) + return x; + if (isInfinity(x)) + return T.nan; - // Make argument positive but save the sign. - bool sign = false; - if (signbit(x)) - { - sign = true; - x = -x; - } + // Make argument positive but save the sign. + bool sign = false; + if (signbit(x)) + { + sign = true; + x = -x; + } - // Compute x mod PI/4. - real y = floor(x / PI_4); + // Compute x mod PI/4. + static if (realFormat == RealFormat.ieeeSingle) + { + enum T FOPI = 4 / PI; + int j = cast(int) (FOPI * x); + T y = j; + T z; + } + else + { + T y = floor(x / cast(T) PI_4); // Strip high bits of integer part. - enum numHighBits = (realFormat == RealFormat.ieeeDouble ? 3 : 4); - real z = ldexp(y, -numHighBits); + enum T highBitsFactor = (realFormat == RealFormat.ieeeDouble ? 0x1p3 : 0x1p4); + enum T highBitsInv = 1.0 / highBitsFactor; + T z = y * highBitsInv; // Compute y - 2^numHighBits * (y / 2^numHighBits). - z = y - ldexp(floor(z), numHighBits); + z = y - highBitsFactor * floor(z); // Integer and fraction part modulo one octant. int j = cast(int)(z); + } - // Map zeros and singularities to origin. - if (j & 1) - { - j += 1; - y += 1.0; - } + // Map zeros and singularities to origin. + if (j & 1) + { + j += 1; + y += cast(T) 1.0; + } - z = ((x - y * P1) - y * P2) - y * P3; - const real zz = z * z; + z = ((x - y * P1) - y * P2) - y * P3; + const T zz = z * z; - enum zzThreshold = (realFormat == RealFormat.ieeeDouble ? 1.0e-14L : 1.0e-20L); - if (zz > zzThreshold) - y = z + z * (zz * poly(zz, P) / poly(zz, Q)); + enum T zzThreshold = (realFormat == RealFormat.ieeeSingle ? 1.0e-4L : + realFormat == RealFormat.ieeeDouble ? 1.0e-14L : 1.0e-20L); + if (zz > zzThreshold) + { + static if (realFormat == RealFormat.ieeeSingle) + y = z + z * (zz * poly(zz, P)); else - y = z; + y = z + z * (zz * poly(zz, P) / poly(zz, Q)); + } + else + y = z; - if (j & 2) - y = -1.0 / y; + if (j & 2) + y = (cast(T) -1.0) / y; - return (sign) ? -y : y; - } + return (sign) ? -y : y; } -@safe nothrow @nogc unittest +@safe @nogc nothrow unittest { - static real[2][] vals = // angle,tan + static void testTan(T)() + { + // ±0 + const T zero = 0.0; + assert(isIdentical(tan(zero), zero)); + assert(isIdentical(tan(-zero), -zero)); + // ±∞ + const T inf = T.infinity; + assert(isNaN(tan(inf))); + assert(isNaN(tan(-inf))); + // NaN + const T specialNaN = NaN(0x0123L); + assert(isIdentical(tan(specialNaN), specialNaN)); + + static immutable T[2][] vals = [ - [ 0, 0], - [ .5, .5463024898], - [ 1, 1.557407725], - [ 1.5, 14.10141995], - [ 2, -2.185039863], - [ 2.5,-.7470222972], - [ 3, -.1425465431], - [ 3.5, .3745856402], - [ 4, 1.157821282], - [ 4.5, 4.637332055], - [ 5, -3.380515006], - [ 5.5,-.9955840522], - [ 6, -.2910061914], - [ 6.5, .2202772003], - [ 10, .6483608275], - - // special angles - [ PI_4, 1], - //[ PI_2, real.infinity], // PI_2 is not _exactly_ pi/2. - [ 3*PI_4, -1], - [ PI, 0], - [ 5*PI_4, 1], - //[ 3*PI_2, -real.infinity], - [ 7*PI_4, -1], - [ 2*PI, 0], + // angle, tan + [ .5, .5463024898], + [ 1, 1.557407725], + [ 1.5, 14.10141995], + [ 2, -2.185039863], + [ 2.5,-.7470222972], + [ 3, -.1425465431], + [ 3.5, .3745856402], + [ 4, 1.157821282], + [ 4.5, 4.637332055], + [ 5, -3.380515006], + [ 5.5,-.9955840522], + [ 6, -.2910061914], + [ 6.5, .2202772003], + [ 10, .6483608275], + + // special angles + [ PI_4, 1], + //[ PI_2, T.infinity], // PI_2 is not _exactly_ pi/2. + [ 3*PI_4, -1], + [ PI, 0], + [ 5*PI_4, 1], + //[ 3*PI_2, -T.infinity], + [ 7*PI_4, -1], + [ 2*PI, 0], ]; - int i; - for (i = 0; i < vals.length; i++) - { - real x = vals[i][0]; - real r = vals[i][1]; - real t = tan(x); + foreach (ref val; vals) + { + T x = val[0]; + T r = val[1]; + T t = tan(x); - //printf("tan(%Lg) = %Lg, should be %Lg\n", x, t, r); - if (!isIdentical(r, t)) assert(fabs(r-t) <= .0000001); + //printf("tan(%Lg) = %Lg, should be %Lg\n", cast(real) x, cast(real) t, cast(real) r); + assert(approxEqual(r, t)); - x = -x; - r = -r; - t = tan(x); - //printf("tan(%Lg) = %Lg, should be %Lg\n", x, t, r); - if (!isIdentical(r, t) && !(r != r && t != t)) assert(fabs(r-t) <= .0000001); + x = -x; + r = -r; + t = tan(x); + //printf("tan(%Lg) = %Lg, should be %Lg\n", cast(real) x, cast(real) t, cast(real) r); + assert(approxEqual(r, t)); + } } - // overflow - assert(isNaN(tan(real.infinity))); - assert(isNaN(tan(-real.infinity))); - // NaN propagation - assert(isIdentical( tan(NaN(0x0123L)), NaN(0x0123L) )); -} -/// -@safe unittest -{ - assert(isIdentical(tan(0.0), 0.0)); - assert(tan(PI).approxEqual(0)); - assert(tan(PI / 3).approxEqual(sqrt(3.0))); -} + import std.meta : AliasSeq; + foreach (T; AliasSeq!(real, double, float)) + testTan!T(); -@safe @nogc nothrow unittest -{ - assert(equalsDigit(tan(PI / 3), std.math.sqrt(3.0), useDigits)); + assert(equalsDigit(tan(PI / 3), std.math.sqrt(3.0L), useDigits)); } /*************** @@ -1269,113 +1343,229 @@ real atan(real x) @safe pure nothrow @nogc { version(InlineAsm_X86_Any) { - return atan2(x, 1.0L); + if (!__ctfe) + return atan2Asm(x, 1.0L); + } + return atanImpl(x); +} + +/// ditto +double atan(double x) @safe pure nothrow @nogc { return __ctfe ? cast(double) atan(cast(real) x) : atanImpl(x); } + +/// ditto +float atan(float x) @safe pure nothrow @nogc { return __ctfe ? cast(float) atan(cast(real) x) : atanImpl(x); } + +/// +@safe unittest +{ + assert(isIdentical(atan(0.0), 0.0)); + assert(atan(sqrt(3.0)).approxEqual(PI / 3)); +} + +private T atanImpl(T)(T x) @safe pure nothrow @nogc +{ + // Coefficients for atan(x) + enum realFormat = floatTraits!T.realFormat; + static if (realFormat == RealFormat.ieeeQuadruple) + { + static immutable T[9] P = [ + -6.880597774405940432145577545328795037141E2L, + -2.514829758941713674909996882101723647996E3L, + -3.696264445691821235400930243493001671932E3L, + -2.792272753241044941703278827346430350236E3L, + -1.148164399808514330375280133523543970854E3L, + -2.497759878476618348858065206895055957104E2L, + -2.548067867495502632615671450650071218995E1L, + -8.768423468036849091777415076702113400070E-1L, + -6.635810778635296712545011270011752799963E-4L + ]; + static immutable T[9] Q = [ + 2.064179332321782129643673263598686441900E3L, + 8.782996876218210302516194604424986107121E3L, + 1.547394317752562611786521896296215170819E4L, + 1.458510242529987155225086911411015961174E4L, + 7.928572347062145288093560392463784743935E3L, + 2.494680540950601626662048893678584497900E3L, + 4.308348370818927353321556740027020068897E2L, + 3.566239794444800849656497338030115886153E1L, + 1.0 + ]; + } + else static if (realFormat == RealFormat.ieeeExtended) + { + static immutable T[5] P = [ + -5.0894116899623603312185E1L, + -9.9988763777265819915721E1L, + -6.3976888655834347413154E1L, + -1.4683508633175792446076E1L, + -8.6863818178092187535440E-1L, + ]; + static immutable T[6] Q = [ + 1.5268235069887081006606E2L, + 3.9157570175111990631099E2L, + 3.6144079386152023162701E2L, + 1.4399096122250781605352E2L, + 2.2981886733594175366172E1L, + 1.0000000000000000000000E0L, + ]; + } + else static if (realFormat == RealFormat.ieeeDouble) + { + static immutable T[5] P = [ + -6.485021904942025371773E1L, + -1.228866684490136173410E2L, + -7.500855792314704667340E1L, + -1.615753718733365076637E1L, + -8.750608600031904122785E-1L, + ]; + static immutable T[6] Q = [ + 1.945506571482613964425E2L, + 4.853903996359136964868E2L, + 4.328810604912902668951E2L, + 1.650270098316988542046E2L, + 2.485846490142306297962E1L, + 1.000000000000000000000E0L, + ]; + + enum T MOREBITS = 6.123233995736765886130E-17L; + } + else static if (realFormat == RealFormat.ieeeSingle) + { + static immutable T[4] P = [ + -3.33329491539E-1, + 1.99777106478E-1, + -1.38776856032E-1, + 8.05374449538E-2, + ]; } else + static assert(0, "no coefficients for atan()"); + + // tan(PI/8) + enum T TAN_PI_8 = 0.414213562373095048801688724209698078569672L; + // tan(3 * PI/8) + enum T TAN3_PI_8 = 2.414213562373095048801688724209698078569672L; + + // Special cases. + if (x == cast(T) 0.0) + return x; + if (isInfinity(x)) + return copysign(cast(T) PI_2, x); + + // Make argument positive but save the sign. + bool sign = false; + if (signbit(x)) { - // Coefficients for atan(x) - static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple) + sign = true; + x = -x; + } + + static if (realFormat == RealFormat.ieeeDouble) // special case for double precision + { + short flag = 0; + T y; + if (x > TAN3_PI_8) { - static immutable real[9] P = [ - -6.880597774405940432145577545328795037141E2L, - -2.514829758941713674909996882101723647996E3L, - -3.696264445691821235400930243493001671932E3L, - -2.792272753241044941703278827346430350236E3L, - -1.148164399808514330375280133523543970854E3L, - -2.497759878476618348858065206895055957104E2L, - -2.548067867495502632615671450650071218995E1L, - -8.768423468036849091777415076702113400070E-1L, - -6.635810778635296712545011270011752799963E-4L - ]; - static immutable real[9] Q = [ - 2.064179332321782129643673263598686441900E3L, - 8.782996876218210302516194604424986107121E3L, - 1.547394317752562611786521896296215170819E4L, - 1.458510242529987155225086911411015961174E4L, - 7.928572347062145288093560392463784743935E3L, - 2.494680540950601626662048893678584497900E3L, - 4.308348370818927353321556740027020068897E2L, - 3.566239794444800849656497338030115886153E1L, - 1.0 - ]; + y = PI_2; + flag = 1; + x = -(1.0 / x); } - else + else if (x <= 0.66) { - static immutable real[5] P = [ - -5.0894116899623603312185E1L, - -9.9988763777265819915721E1L, - -6.3976888655834347413154E1L, - -1.4683508633175792446076E1L, - -8.6863818178092187535440E-1L, - ]; - static immutable real[6] Q = [ - 1.5268235069887081006606E2L, - 3.9157570175111990631099E2L, - 3.6144079386152023162701E2L, - 1.4399096122250781605352E2L, - 2.2981886733594175366172E1L, - 1.0000000000000000000000E0L, - ]; + y = 0.0; } - - // tan(PI/8) - enum real TAN_PI_8 = 0.414213562373095048801688724209698078569672L; - // tan(3 * PI/8) - enum real TAN3_PI_8 = 2.414213562373095048801688724209698078569672L; - - // Special cases. - if (x == 0.0) - return x; - if (isInfinity(x)) - return copysign(PI_2, x); - - // Make argument positive but save the sign. - bool sign = false; - if (signbit(x)) + else { - sign = true; - x = -x; + y = PI_4; + flag = 2; + x = (x - 1.0)/(x + 1.0); } + T z = x * x; + z = z * poly(z, P) / poly(z, Q); + z = x * z + x; + if (flag == 2) + z += 0.5 * MOREBITS; + else if (flag == 1) + z += MOREBITS; + y = y + z; + } + else + { // Range reduction. - real y; + T y; if (x > TAN3_PI_8) { y = PI_2; - x = -(1.0 / x); + x = -((cast(T) 1.0) / x); } else if (x > TAN_PI_8) { y = PI_4; - x = (x - 1.0)/(x + 1.0); + x = (x - cast(T) 1.0)/(x + cast(T) 1.0); } else y = 0.0; // Rational form in x^^2. - const real z = x * x; - y = y + (poly(z, P) / poly(z, Q)) * z * x + x; - - return (sign) ? -y : y; + const T z = x * x; + static if (realFormat == RealFormat.ieeeSingle) + y += poly(z, P) * z * x + x; + else + y = y + (poly(z, P) / poly(z, Q)) * z * x + x; } -} - -/// ditto -double atan(double x) @safe pure nothrow @nogc { return atan(cast(real) x); } -/// ditto -float atan(float x) @safe pure nothrow @nogc { return atan(cast(real) x); } - -/// -@safe unittest -{ - assert(isIdentical(atan(0.0), 0.0)); - assert(atan(sqrt(3.0)).approxEqual(PI / 3)); + return (sign) ? -y : y; } @safe @nogc nothrow unittest { - assert(equalsDigit(atan(std.math.sqrt(3.0)), PI / 3, useDigits)); + static void testAtan(T)() + { + // ±0 + const T zero = 0.0; + assert(isIdentical(atan(zero), zero)); + assert(isIdentical(atan(-zero), -zero)); + // ±∞ + const T inf = T.infinity; + assert(approxEqual(atan(inf), cast(T) PI_2)); + assert(approxEqual(atan(-inf), cast(T) -PI_2)); + // NaN + const T specialNaN = NaN(0x0123L); + assert(isIdentical(atan(specialNaN), specialNaN)); + + static immutable T[2][] vals = + [ + // x, atan(x) + [ 0.25, 0.2449786631 ], + [ 0.5, 0.4636476090 ], + [ 1, PI_4 ], + [ 1.5, 0.9827937232 ], + [ 10, 1.4711276743 ], + ]; + + foreach (ref val; vals) + { + T x = val[0]; + T r = val[1]; + T a = atan(x); + + //printf("atan(%Lg) = %Lg, should be %Lg\n", cast(real) x, cast(real) a, cast(real) r); + assert(approxEqual(r, a)); + + x = -x; + r = -r; + a = atan(x); + //printf("atan(%Lg) = %Lg, should be %Lg\n", cast(real) x, cast(real) a, cast(real) r); + assert(approxEqual(r, a)); + } + } + + import std.meta : AliasSeq; + foreach (T; AliasSeq!(real, double, float)) + testAtan!T(); + + assert(equalsDigit(atan(std.math.sqrt(3.0L)), PI / 3, useDigits)); } /*************** @@ -1399,91 +1589,26 @@ float atan(float x) @safe pure nothrow @nogc { return atan(cast(real) x); } * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD -$(INFIN)) $(TD $(PLUSMN)3$(PI)/4)) * ) */ -real atan2(real y, real x) @trusted pure nothrow @nogc +real atan2(real y, real x) @trusted pure nothrow @nogc // TODO: @safe { version(InlineAsm_X86_Any) { - version (Win64) - { - asm pure nothrow @nogc { - naked; - fld real ptr [RDX]; // y - fld real ptr [RCX]; // x - fpatan; - ret; - } - } - else - { - asm pure nothrow @nogc { - fld y; - fld x; - fpatan; - } - } - } - else - { - // Special cases. - if (isNaN(x) || isNaN(y)) - return real.nan; - if (y == 0.0) - { - if (x >= 0 && !signbit(x)) - return copysign(0, y); - else - return copysign(PI, y); - } - if (x == 0.0) - return copysign(PI_2, y); - if (isInfinity(x)) - { - if (signbit(x)) - { - if (isInfinity(y)) - return copysign(3*PI_4, y); - else - return copysign(PI, y); - } - else - { - if (isInfinity(y)) - return copysign(PI_4, y); - else - return copysign(0.0, y); - } - } - if (isInfinity(y)) - return copysign(PI_2, y); - - // Call atan and determine the quadrant. - real z = atan(y / x); - - if (signbit(x)) - { - if (signbit(y)) - z = z - PI; - else - z = z + PI; - } - - if (z == 0.0) - return copysign(z, y); - - return z; + if (!__ctfe) + return atan2Asm(y, x); } + return atan2Impl(y, x); } /// ditto double atan2(double y, double x) @safe pure nothrow @nogc { - return atan2(cast(real) y, cast(real) x); + return __ctfe ? cast(double) atan2(cast(real) y, cast(real) x) : atan2Impl(y, x); } /// ditto float atan2(float y, float x) @safe pure nothrow @nogc { - return atan2(cast(real) y, cast(real) x); + return __ctfe ? cast(float) atan2(cast(real) y, cast(real) x) : atan2Impl(y, x); } /// @@ -1492,8 +1617,152 @@ float atan2(float y, float x) @safe pure nothrow @nogc assert(atan2(1.0, sqrt(3.0)).approxEqual(PI / 6)); } +version(InlineAsm_X86_Any) +private real atan2Asm(real y, real x) @trusted pure nothrow @nogc +{ + version (Win64) + { + asm pure nothrow @nogc { + naked; + fld real ptr [RDX]; // y + fld real ptr [RCX]; // x + fpatan; + ret; + } + } + else + { + asm pure nothrow @nogc { + fld y; + fld x; + fpatan; + } + } +} + +private T atan2Impl(T)(T y, T x) @safe pure nothrow @nogc +{ + // Special cases. + if (isNaN(x) || isNaN(y)) + return T.nan; + if (y == cast(T) 0.0) + { + if (x >= 0 && !signbit(x)) + return copysign(0, y); + else + return copysign(cast(T) PI, y); + } + if (x == cast(T) 0.0) + return copysign(cast(T) PI_2, y); + if (isInfinity(x)) + { + if (signbit(x)) + { + if (isInfinity(y)) + return copysign(3 * cast(T) PI_4, y); + else + return copysign(cast(T) PI, y); + } + else + { + if (isInfinity(y)) + return copysign(cast(T) PI_4, y); + else + return copysign(cast(T) 0.0, y); + } + } + if (isInfinity(y)) + return copysign(cast(T) PI_2, y); + + // Call atan and determine the quadrant. + T z = atan(y / x); + + if (signbit(x)) + { + if (signbit(y)) + z = z - cast(T) PI; + else + z = z + cast(T) PI; + } + + if (z == cast(T) 0.0) + return copysign(z, y); + + return z; +} + @safe @nogc nothrow unittest { + static void testAtan2(T)() + { + // NaN + const T nan = T.nan; + assert(isNaN(atan2(nan, cast(T) 1))); + assert(isNaN(atan2(cast(T) 1, nan))); + + const T inf = T.infinity; + static immutable T[3][] vals = + [ + // y, x, atan2(y, x) + + // ±0 + [ 0.0, 1.0, 0.0 ], + [ -0.0, 1.0, -0.0 ], + [ 0.0, 0.0, 0.0 ], + [ -0.0, 0.0, -0.0 ], + [ 0.0, -1.0, PI ], + [ -0.0, -1.0, -PI ], + [ 0.0, -0.0, PI ], + [ -0.0, -0.0, -PI ], + [ 1.0, 0.0, PI_2 ], + [ 1.0, -0.0, PI_2 ], + [ -1.0, 0.0, -PI_2 ], + [ -1.0, -0.0, -PI_2 ], + + // ±∞ + [ 1.0, inf, 0.0 ], + [ -1.0, inf, -0.0 ], + [ 1.0, -inf, PI ], + [ -1.0, -inf, -PI ], + [ inf, 1.0, PI_2 ], + [ inf, -1.0, PI_2 ], + [ -inf, 1.0, -PI_2 ], + [ -inf, -1.0, -PI_2 ], + [ inf, inf, PI_4 ], + [ -inf, inf, -PI_4 ], + [ inf, -inf, 3 * PI_4 ], + [ -inf, -inf, -3 * PI_4 ], + + [ 1.0, 1.0, PI_4 ], + [ -2.0, 2.0, -PI_4 ], + [ 3.0, -3.0, 3 * PI_4 ], + [ -4.0, -4.0, -3 * PI_4 ], + + [ 0.75, 0.25, 1.249045772398 ], + [ -0.5, 0.375, -0.927295218002 ], + [ 0.5, -0.125, 1.815774989922 ], + [ -0.75, -0.5, -2.158798930342 ], + ]; + + foreach (ref val; vals) + { + const T y = val[0]; + const T x = val[1]; + const T r = val[2]; + const T a = atan2(y, x); + + //printf("atan2(%Lg, %Lg) = %Lg, should be %Lg\n", cast(real) y, cast(real) x, cast(real) a, cast(real) r); + if (r == 0) + assert(isIdentical(r, a)); // check sign + else + assert(approxEqual(r, a)); + } + } + + import std.meta : AliasSeq; + foreach (T; AliasSeq!(real, double, float)) + testAtan2!T(); + assert(equalsDigit(atan2(1.0L, std.math.sqrt(3.0L)), PI / 6, useDigits)); } @@ -1935,145 +2204,301 @@ auto sqrt(creal z) @nogc @safe pure nothrow * $(TR $(TD $(NAN)) $(TD $(NAN)) ) * ) */ -real exp(real x) @trusted pure nothrow @nogc +real exp(real x) @trusted pure nothrow @nogc // TODO: @safe { version(D_InlineAsm_X86) { // e^^x = 2^^(LOG2E*x) // (This is valid because the overflow & underflow limits for exp // and exp2 are so similar). - return exp2(LOG2E*x); + if (!__ctfe) + return exp2Asm(LOG2E*x); } else version(D_InlineAsm_X86_64) { - // e^^x = 2^^(LOG2E*x) - // (This is valid because the overflow & underflow limits for exp - // and exp2 are so similar). - return exp2(LOG2E*x); + // e^^x = 2^^(LOG2E*x) + // (This is valid because the overflow & underflow limits for exp + // and exp2 are so similar). + if (!__ctfe) + return exp2Asm(LOG2E*x); + } + return expImpl(x); +} + +/// ditto +double exp(double x) @safe pure nothrow @nogc { return __ctfe ? cast(double) exp(cast(real) x) : expImpl(x); } + +/// ditto +float exp(float x) @safe pure nothrow @nogc { return __ctfe ? cast(float) exp(cast(real) x) : expImpl(x); } + +/// +@safe unittest +{ + assert(exp(0.0) == 1.0); + assert(exp(3.0).feqrel(E * E * E) > 16); +} + +private T expImpl(T)(T x) @safe pure nothrow @nogc +{ + alias F = floatTraits!T; + static if (F.realFormat == RealFormat.ieeeSingle) + { + static immutable T[6] P = [ + 5.0000001201E-1, + 1.6666665459E-1, + 4.1665795894E-2, + 8.3334519073E-3, + 1.3981999507E-3, + 1.9875691500E-4, + ]; + + enum T C1 = 0.693359375; + enum T C2 = -2.12194440e-4; + + // Overflow and Underflow limits. + enum T OF = 88.72283905206835; + enum T UF = -103.278929903431851103; // ln(2^-149) + } + else static if (F.realFormat == RealFormat.ieeeDouble) + { + // Coefficients for exp(x) + static immutable T[3] P = [ + 9.99999999999999999910E-1L, + 3.02994407707441961300E-2L, + 1.26177193074810590878E-4L, + ]; + static immutable T[4] Q = [ + 2.00000000000000000009E0L, + 2.27265548208155028766E-1L, + 2.52448340349684104192E-3L, + 3.00198505138664455042E-6L, + ]; + + // C1 + C2 = LN2. + enum T C1 = 6.93145751953125E-1; + enum T C2 = 1.42860682030941723212E-6; + + // Overflow and Underflow limits. + enum T OF = 7.09782712893383996732E2; // ln((1-2^-53) * 2^1024) + enum T UF = -7.451332191019412076235E2; // ln(2^-1075) + } + else static if (F.realFormat == RealFormat.ieeeExtended) + { + // Coefficients for exp(x) + static immutable T[3] P = [ + 9.9999999999999999991025E-1L, + 3.0299440770744196129956E-2L, + 1.2617719307481059087798E-4L, + ]; + static immutable T[4] Q = [ + 2.0000000000000000000897E0L, + 2.2726554820815502876593E-1L, + 2.5244834034968410419224E-3L, + 3.0019850513866445504159E-6L, + ]; + + // C1 + C2 = LN2. + enum T C1 = 6.9314575195312500000000E-1L; + enum T C2 = 1.4286068203094172321215E-6L; + + // Overflow and Underflow limits. + enum T OF = 1.1356523406294143949492E4L; // ln((1-2^-64) * 2^16384) + enum T UF = -1.13994985314888605586758E4L; // ln(2^-16446) + } + else static if (F.realFormat == RealFormat.ieeeQuadruple) + { + // Coefficients for exp(x) - 1 + static immutable T[5] P = [ + 9.999999999999999999999999999999999998502E-1L, + 3.508710990737834361215404761139478627390E-2L, + 2.708775201978218837374512615596512792224E-4L, + 6.141506007208645008909088812338454698548E-7L, + 3.279723985560247033712687707263393506266E-10L + ]; + static immutable T[6] Q = [ + 2.000000000000000000000000000000000000150E0, + 2.368408864814233538909747618894558968880E-1L, + 3.611828913847589925056132680618007270344E-3L, + 1.504792651814944826817779302637284053660E-5L, + 1.771372078166251484503904874657985291164E-8L, + 2.980756652081995192255342779918052538681E-12L + ]; + + // C1 + C2 = LN2. + enum T C1 = 6.93145751953125E-1L; + enum T C2 = 1.428606820309417232121458176568075500134E-6L; + + // Overflow and Underflow limits. + enum T OF = 1.135583025911358400418251384584930671458833e4L; + enum T UF = -1.143276959615573793352782661133116431383730e4L; + } + else + static assert(0, "Not implemented for this architecture"); + + // Special cases. + if (isNaN(x)) + return x; + if (x > OF) + return real.infinity; + if (x < UF) + return 0.0; + + // Express: e^^x = e^^g * 2^^n + // = e^^g * e^^(n * LOG2E) + // = e^^(g + n * LOG2E) + T xx = floor((cast(T) LOG2E) * x + cast(T) 0.5); + const int n = cast(int) xx; + x -= xx * C1; + x -= xx * C2; + + static if (F.realFormat == RealFormat.ieeeSingle) + { + xx = x * x; + x = poly(x, P) * xx + x + 1.0f; } else { - alias F = floatTraits!real; - static if (F.realFormat == RealFormat.ieeeDouble) - { - // Coefficients for exp(x) - static immutable real[3] P = [ - 9.99999999999999999910E-1L, - 3.02994407707441961300E-2L, - 1.26177193074810590878E-4L, - ]; - static immutable real[4] Q = [ - 2.00000000000000000009E0L, - 2.27265548208155028766E-1L, - 2.52448340349684104192E-3L, - 3.00198505138664455042E-6L, - ]; + // Rational approximation for exponential of the fractional part: + // e^^x = 1 + 2x P(x^^2) / (Q(x^^2) - P(x^^2)) + xx = x * x; + const T px = x * poly(xx, P); + x = px / (poly(xx, Q) - px); + x = (cast(T) 1.0) + (cast(T) 2.0) * x; + } + + // Scale by power of 2. + x = ldexp(x, n); - // C1 + C2 = LN2. - enum real C1 = 6.93145751953125E-1; - enum real C2 = 1.42860682030941723212E-6; + return x; +} - // Overflow and Underflow limits. - enum real OF = 7.09782712893383996732E2; // ln((1-2^-53) * 2^1024) - enum real UF = -7.451332191019412076235E2; // ln(2^-1075) - } - else static if (F.realFormat == RealFormat.ieeeExtended) +@safe @nogc nothrow unittest +{ + FloatingPointControl ctrl; + if (FloatingPointControl.hasExceptionTraps) + ctrl.disableExceptions(FloatingPointControl.allExceptions); + ctrl.rounding = FloatingPointControl.roundToNearest; + + static void testExp(T)() + { + enum realFormat = floatTraits!T.realFormat; + static if (realFormat == RealFormat.ieeeQuadruple) { - // Coefficients for exp(x) - static immutable real[3] P = [ - 9.9999999999999999991025E-1L, - 3.0299440770744196129956E-2L, - 1.2617719307481059087798E-4L, + static immutable T[2][] exptestpoints = + [ // x exp(x) + [ 1.0L, E ], + [ 0.5L, 0x1.a61298e1e069bc972dfefab6df34p+0L ], + [ 3.0L, E*E*E ], + [ 0x1.6p+13L, 0x1.6e509d45728655cdb4840542acb5p+16250L ], // near overflow + [ 0x1.7p+13L, T.infinity ], // close overflow + [ 0x1p+80L, T.infinity ], // far overflow + [ T.infinity, T.infinity ], + [-0x1.18p+13L, 0x1.5e4bf54b4807034ea97fef0059a6p-12927L ], // near underflow + [-0x1.625p+13L, 0x1.a6bd68a39d11fec3a250cd97f524p-16358L ], // ditto + [-0x1.62dafp+13L, 0x0.cb629e9813b80ed4d639e875be6cp-16382L ], // near underflow - subnormal + [-0x1.6549p+13L, 0x0.0000000000000000000000000001p-16382L ], // ditto + [-0x1.655p+13L, 0 ], // close underflow + [-0x1p+30L, 0 ], // far underflow ]; - static immutable real[4] Q = [ - 2.0000000000000000000897E0L, - 2.2726554820815502876593E-1L, - 2.5244834034968410419224E-3L, - 3.0019850513866445504159E-6L, + } + else static if (realFormat == RealFormat.ieeeExtended) + { + static immutable T[2][] exptestpoints = + [ // x exp(x) + [ 1.0L, E ], + [ 0.5L, 0x1.a61298e1e069bc97p+0L ], + [ 3.0L, E*E*E ], + [ 0x1.1p+13L, 0x1.29aeffefc8ec645p+12557L ], // near overflow + [ 0x1.7p+13L, T.infinity ], // close overflow + [ 0x1p+80L, T.infinity ], // far overflow + [ T.infinity, T.infinity ], + [-0x1.18p+13L, 0x1.5e4bf54b4806db9p-12927L ], // near underflow + [-0x1.625p+13L, 0x1.a6bd68a39d11f35cp-16358L ], // ditto + [-0x1.62dafp+13L, 0x1.96c53d30277021dp-16383L ], // near underflow - subnormal + [-0x1.643p+13L, 0x1p-16444L ], // ditto + [-0x1.645p+13L, 0 ], // close underflow + [-0x1p+30L, 0 ], // far underflow ]; - - // C1 + C2 = LN2. - enum real C1 = 6.9314575195312500000000E-1L; - enum real C2 = 1.4286068203094172321215E-6L; - - // Overflow and Underflow limits. - enum real OF = 1.1356523406294143949492E4L; // ln((1-2^-64) * 2^16384) - enum real UF = -1.13994985314888605586758E4L; // ln(2^-16446) } - else static if (F.realFormat == RealFormat.ieeeQuadruple) - { - // Coefficients for exp(x) - 1 - static immutable real[5] P = [ - 9.999999999999999999999999999999999998502E-1L, - 3.508710990737834361215404761139478627390E-2L, - 2.708775201978218837374512615596512792224E-4L, - 6.141506007208645008909088812338454698548E-7L, - 3.279723985560247033712687707263393506266E-10L + else static if (realFormat == RealFormat.ieeeDouble) + { + static immutable T[2][] exptestpoints = + [ // x, exp(x) + [ 1.0L, E ], + [ 0.5L, 0x1.a61298e1e069cp+0L ], + [ 3.0L, E*E*E ], + [ 0x1.6p+9L, 0x1.93bf4ec282efbp+1015L ], // near overflow + [ 0x1.7p+9L, T.infinity ], // close overflow + [ 0x1p+80L, T.infinity ], // far overflow + [ T.infinity, T.infinity ], + [-0x1.6p+9L, 0x1.44a3824e5285fp-1016L ], // near underflow + [-0x1.64p+9L, 0x0.06f84920bb2d4p-1022L ], // near underflow - subnormal + [-0x1.743p+9L, 0x0.0000000000001p-1022L ], // ditto + [-0x1.8p+9L, 0 ], // close underflow + [-0x1p+30L, 0 ], // far underflow ]; - static immutable real[6] Q = [ - 2.000000000000000000000000000000000000150E0, - 2.368408864814233538909747618894558968880E-1L, - 3.611828913847589925056132680618007270344E-3L, - 1.504792651814944826817779302637284053660E-5L, - 1.771372078166251484503904874657985291164E-8L, - 2.980756652081995192255342779918052538681E-12L + } + else static if (realFormat == RealFormat.ieeeSingle) + { + static immutable T[2][] exptestpoints = + [ // x, exp(x) + [ 1.0L, E ], + [ 0.5L, 0x1.a61299p+0L ], + [ 3.0L, E*E*E ], + [ 0x1.62p+6L, 0x1.99b988p+127L ], // near overflow + [ 0x1.7p+6L, T.infinity ], // close overflow + [ 0x1p+80L, T.infinity ], // far overflow + [ T.infinity, T.infinity ], + [-0x1.5cp+6L, 0x1.666d0ep-126L ], // near underflow + [-0x1.7p+6L, 0x0.026a42p-126L ], // near underflow - subnormal + [-0x1.9cp+6L, 0x0.000002p-126L ], // ditto + [-0x1.ap+6L, 0 ], // close underflow + [-0x1p+30L, 0 ], // far underflow ]; - - // C1 + C2 = LN2. - enum real C1 = 6.93145751953125E-1L; - enum real C2 = 1.428606820309417232121458176568075500134E-6L; - - // Overflow and Underflow limits. - enum real OF = 1.135583025911358400418251384584930671458833e4L; - enum real UF = -1.143276959615573793352782661133116431383730e4L; } else - static assert(0, "Not implemented for this architecture"); + static assert(0, "No exp() tests for real type!"); - // Special cases. - if (isNaN(x)) - return x; - if (x > OF) - return real.infinity; - if (x < UF) - return 0.0; + const minEqualMantissaBits = T.mant_dig - 13; + T x; + IeeeFlags f; + foreach (ref pair; exptestpoints) + { + resetIeeeFlags(); + x = exp(pair[0]); + //printf("exp(%La) = %La, should be %La\n", cast(real) pair[0], cast(real) x, cast(real) pair[1]); + assert(feqrel(x, pair[1]) >= minEqualMantissaBits); + } - // Express: e^^x = e^^g * 2^^n - // = e^^g * e^^(n * LOG2E) - // = e^^(g + n * LOG2E) - int n = cast(int) floor(LOG2E * x + 0.5); - x -= n * C1; - x -= n * C2; + // Ideally, exp(0) would not set the inexact flag. + // Unfortunately, fldl2e sets it! + // So it's not realistic to avoid setting it. + assert(exp(cast(T) 0.0) == 1.0); - // Rational approximation for exponential of the fractional part: - // e^^x = 1 + 2x P(x^^2) / (Q(x^^2) - P(x^^2)) - const real xx = x * x; - const real px = x * poly(xx, P); - x = px / (poly(xx, Q) - px); - x = 1.0 + ldexp(x, 1); + // NaN propagation. Doesn't set flags, bcos was already NaN. + resetIeeeFlags(); + x = exp(T.nan); + f = ieeeFlags; + assert(isIdentical(abs(x), T.nan)); + assert(f.flags == 0); - // Scale by power of 2. - x = ldexp(x, n); + resetIeeeFlags(); + x = exp(-T.nan); + f = ieeeFlags; + assert(isIdentical(abs(x), T.nan)); + assert(f.flags == 0); - return x; + x = exp(NaN(0x123)); + assert(isIdentical(x, NaN(0x123))); } -} - -/// ditto -double exp(double x) @safe pure nothrow @nogc { return exp(cast(real) x); } -/// ditto -float exp(float x) @safe pure nothrow @nogc { return exp(cast(real) x); } + import std.meta : AliasSeq; + foreach (T; AliasSeq!(real, double, float)) + testExp!T(); -/// -@safe unittest -{ - assert(exp(0.0) == 1.0); - assert(exp(3.0).feqrel(E * E * E) > 16); -} + // High resolution test (verified against GNU MPFR/Mathematica). + assert(exp(0.5L) == 0x1.A612_98E1_E069_BC97_2DFE_FAB6_DF34p+0L); -@safe @nogc nothrow unittest -{ assert(equalsDigit(exp(3.0L), E * E * E, useDigits)); } @@ -2092,7 +2517,39 @@ float exp(float x) @safe pure nothrow @nogc { return exp(cast(real) x); } * $(TR $(TD $(NAN)) $(TD $(NAN)) ) * ) */ -real expm1(real x) @trusted pure nothrow @nogc +real expm1(real x) @trusted pure nothrow @nogc // TODO: @safe +{ + version(InlineAsm_X86_Any) + { + if (!__ctfe) + return expm1Asm(x); + } + return expm1Impl(x); +} + +/// ditto +double expm1(double x) @safe pure nothrow @nogc +{ + return __ctfe ? cast(double) expm1(cast(real) x) : expm1Impl(x); +} + +/// ditto +float expm1(float x) @safe pure nothrow @nogc +{ + // no single-precision version in Cephes => use double precision + return __ctfe ? cast(float) expm1(cast(real) x) : cast(float) expm1Impl(cast(double) x); +} + +/// +@safe unittest +{ + assert(isIdentical(expm1(0.0), 0.0)); + assert(expm1(1.0).feqrel(1.71828) > 16); + assert(expm1(2.0).feqrel(6.3890) > 16); +} + +version(InlineAsm_X86_Any) +private real expm1Asm(real x) @trusted pure nothrow @nogc { version(D_InlineAsm_X86) { @@ -2256,98 +2713,147 @@ L_largenegative: } } else + static assert(0); +} + +private T expm1Impl(T)(T x) @safe pure nothrow @nogc +{ + // Coefficients for exp(x) - 1 and overflow/underflow limits. + enum realFormat = floatTraits!T.realFormat; + static if (realFormat == RealFormat.ieeeQuadruple) { - // Coefficients for exp(x) - 1 and overflow/underflow limits. - static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple) - { - static immutable real[8] P = [ - 2.943520915569954073888921213330863757240E8L, - -5.722847283900608941516165725053359168840E7L, - 8.944630806357575461578107295909719817253E6L, - -7.212432713558031519943281748462837065308E5L, - 4.578962475841642634225390068461943438441E4L, - -1.716772506388927649032068540558788106762E3L, - 4.401308817383362136048032038528753151144E1L, - -4.888737542888633647784737721812546636240E-1L - ]; + static immutable T[8] P = [ + 2.943520915569954073888921213330863757240E8L, + -5.722847283900608941516165725053359168840E7L, + 8.944630806357575461578107295909719817253E6L, + -7.212432713558031519943281748462837065308E5L, + 4.578962475841642634225390068461943438441E4L, + -1.716772506388927649032068540558788106762E3L, + 4.401308817383362136048032038528753151144E1L, + -4.888737542888633647784737721812546636240E-1L + ]; - static immutable real[9] Q = [ - 1.766112549341972444333352727998584753865E9L, - -7.848989743695296475743081255027098295771E8L, - 1.615869009634292424463780387327037251069E8L, - -2.019684072836541751428967854947019415698E7L, - 1.682912729190313538934190635536631941751E6L, - -9.615511549171441430850103489315371768998E4L, - 3.697714952261803935521187272204485251835E3L, - -8.802340681794263968892934703309274564037E1L, - 1.0 - ]; + static immutable T[9] Q = [ + 1.766112549341972444333352727998584753865E9L, + -7.848989743695296475743081255027098295771E8L, + 1.615869009634292424463780387327037251069E8L, + -2.019684072836541751428967854947019415698E7L, + 1.682912729190313538934190635536631941751E6L, + -9.615511549171441430850103489315371768998E4L, + 3.697714952261803935521187272204485251835E3L, + -8.802340681794263968892934703309274564037E1L, + 1.0 + ]; - enum real OF = 1.1356523406294143949491931077970764891253E4L; - enum real UF = -1.143276959615573793352782661133116431383730e4L; - } - else - { - static immutable real[5] P = [ - -1.586135578666346600772998894928250240826E4L, - 2.642771505685952966904660652518429479531E3L, - -3.423199068835684263987132888286791620673E2L, - 1.800826371455042224581246202420972737840E1L, - -5.238523121205561042771939008061958820811E-1L, - ]; - static immutable real[6] Q = [ - -9.516813471998079611319047060563358064497E4L, - 3.964866271411091674556850458227710004570E4L, - -7.207678383830091850230366618190187434796E3L, - 7.206038318724600171970199625081491823079E2L, - -4.002027679107076077238836622982900945173E1L, - 1.0 - ]; + enum T OF = 1.1356523406294143949491931077970764891253E4L; + enum T UF = -1.143276959615573793352782661133116431383730e4L; + } + else static if (realFormat == RealFormat.ieeeExtended) + { + static immutable T[5] P = [ + -1.586135578666346600772998894928250240826E4L, + 2.642771505685952966904660652518429479531E3L, + -3.423199068835684263987132888286791620673E2L, + 1.800826371455042224581246202420972737840E1L, + -5.238523121205561042771939008061958820811E-1L, + ]; + static immutable T[6] Q = [ + -9.516813471998079611319047060563358064497E4L, + 3.964866271411091674556850458227710004570E4L, + -7.207678383830091850230366618190187434796E3L, + 7.206038318724600171970199625081491823079E2L, + -4.002027679107076077238836622982900945173E1L, + 1.0 + ]; - enum real OF = 1.1356523406294143949492E4L; - enum real UF = -4.5054566736396445112120088E1L; - } + enum T OF = 1.1356523406294143949492E4L; + enum T UF = -4.5054566736396445112120088E1L; + } + else static if (realFormat == RealFormat.ieeeDouble) + { + static immutable T[3] P = [ + 9.9999999999999999991025E-1, + 3.0299440770744196129956E-2, + 1.2617719307481059087798E-4, + ]; + static immutable T[4] Q = [ + 2.0000000000000000000897E0, + 2.2726554820815502876593E-1, + 2.5244834034968410419224E-3, + 3.0019850513866445504159E-6, + ]; + } + else + static assert(0, "no coefficients for expm1()"); + static if (realFormat == RealFormat.ieeeDouble) // special case for double precision + { + if (x < -0.5 || x > 0.5) + return exp(x) - 1.0; + if (x == 0.0) + return x; + const T xx = x * x; + x = x * poly(xx, P); + x = x / (poly(xx, Q) - x); + return x + x; + } + else + { // C1 + C2 = LN2. - enum real C1 = 6.9314575195312500000000E-1L; - enum real C2 = 1.428606820309417232121458176568075500134E-6L; + enum T C1 = 6.9314575195312500000000E-1L; + enum T C2 = 1.428606820309417232121458176568075500134E-6L; // Special cases. if (x > OF) return real.infinity; - if (x == 0.0) + if (x == cast(T) 0.0) return x; if (x < UF) return -1.0; // Express x = LN2 (n + remainder), remainder not exceeding 1/2. - int n = cast(int) floor(0.5 + x / LN2); + int n = cast(int) floor((cast(T) 0.5) + x / cast(T) LN2); x -= n * C1; x -= n * C2; // Rational approximation: // exp(x) - 1 = x + 0.5 x^^2 + x^^3 P(x) / Q(x) - real px = x * poly(x, P); - real qx = poly(x, Q); - const real xx = x * x; - qx = x + (0.5 * xx + xx * px / qx); + T px = x * poly(x, P); + T qx = poly(x, Q); + const T xx = x * x; + qx = x + ((cast(T) 0.5) * xx + xx * px / qx); // We have qx = exp(remainder LN2) - 1, so: // exp(x) - 1 = 2^^n (qx + 1) - 1 = 2^^n qx + 2^^n - 1. - px = ldexp(1.0, n); - x = px * qx + (px - 1.0); + px = ldexp(cast(T) 1.0, n); + x = px * qx + (px - cast(T) 1.0); return x; } } -/// -@safe unittest +@safe @nogc nothrow unittest { - assert(isIdentical(expm1(0.0), 0.0)); - assert(expm1(1.0).feqrel(1.71828) > 16); - assert(expm1(2.0).feqrel(6.3890) > 16); + static void testExpm1(T)() + { + // NaN + assert(isNaN(expm1(cast(T) T.nan))); + + static immutable T[] xs = [ -2, -0.75, -0.3, 0.0, 0.1, 0.2, 0.5, 1.0 ]; + foreach (x; xs) + { + const T e = expm1(x); + const T r = exp(x) - 1; + + //printf("expm1(%Lg) = %Lg, should approximately be %Lg\n", cast(real) x, cast(real) e, cast(real) r); + assert(approxEqual(r, e)); + } + } + + import std.meta : AliasSeq; + foreach (T; AliasSeq!(real, double)) + testExpm1!T(); } /** @@ -2360,22 +2866,22 @@ L_largenegative: * $(TR $(TD $(NAN)) $(TD $(NAN)) ) * ) */ -pragma(inline, true) -real exp2(real x) @nogc @trusted pure nothrow +real exp2(real x) @nogc @trusted pure nothrow // TODO: @safe { version(InlineAsm_X86_Any) { if (!__ctfe) return exp2Asm(x); - else - return exp2Impl(x); - } - else - { - return exp2Impl(x); } + return exp2Impl(x); } +/// ditto +double exp2(double x) @nogc @safe pure nothrow { return __ctfe ? cast(double) exp2(cast(real) x) : exp2Impl(x); } + +/// ditto +float exp2(float x) @nogc @safe pure nothrow { return __ctfe ? cast(float) exp2(cast(real) x) : exp2Impl(x); } + /// @safe unittest { @@ -2384,6 +2890,16 @@ real exp2(real x) @nogc @trusted pure nothrow assert(exp2(8.0).feqrel(256.0) > 16); } +@safe unittest +{ + version(CRuntime_Microsoft) {} else // aexp2/exp2f/exp2l not implemented + { + assert( core.stdc.math.exp2f(0.0f) == 1 ); + assert( core.stdc.math.exp2 (0.0) == 1 ); + assert( core.stdc.math.exp2l(0.0L) == 1 ); + } +} + version(InlineAsm_X86_Any) private real exp2Asm(real x) @nogc @trusted pure nothrow { @@ -2578,12 +3094,13 @@ L_was_nan: static assert(0); } -private real exp2Impl(real x) @nogc @trusted pure nothrow +private T exp2Impl(T)(T x) @nogc @safe pure nothrow { // Coefficients for exp2(x) - static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple) + enum realFormat = floatTraits!T.realFormat; + static if (realFormat == RealFormat.ieeeQuadruple) { - static immutable real[5] P = [ + static immutable T[5] P = [ 9.079594442980146270952372234833529694788E12L, 1.530625323728429161131811299626419117557E11L, 5.677513871931844661829755443994214173883E8L, @@ -2591,7 +3108,7 @@ private real exp2Impl(real x) @nogc @trusted pure nothrow 1.587171580015525194694938306936721666031E2L ]; - static immutable real[6] Q = [ + static immutable T[6] Q = [ 2.619817175234089411411070339065679229869E13L, 1.490560994263653042761789432690793026977E12L, 1.092141473886177435056423606755843616331E10L, @@ -2600,24 +3117,50 @@ private real exp2Impl(real x) @nogc @trusted pure nothrow 1.0 ]; } - else + else static if (realFormat == RealFormat.ieeeExtended) { - static immutable real[3] P = [ + static immutable T[3] P = [ 2.0803843631901852422887E6L, 3.0286971917562792508623E4L, 6.0614853552242266094567E1L, ]; - static immutable real[4] Q = [ + static immutable T[4] Q = [ 6.0027204078348487957118E6L, 3.2772515434906797273099E5L, 1.7492876999891839021063E3L, 1.0000000000000000000000E0L, ]; } + else static if (realFormat == RealFormat.ieeeDouble) + { + static immutable T[3] P = [ + 1.51390680115615096133E3L, + 2.02020656693165307700E1L, + 2.30933477057345225087E-2L, + ]; + static immutable T[3] Q = [ + 4.36821166879210612817E3L, + 2.33184211722314911771E2L, + 1.00000000000000000000E0L, + ]; + } + else static if (realFormat == RealFormat.ieeeSingle) + { + static immutable T[6] P = [ + 6.931472028550421E-001L, + 2.402264791363012E-001L, + 5.550332471162809E-002L, + 9.618437357674640E-003L, + 1.339887440266574E-003L, + 1.535336188319500E-004L, + ]; + } + else + static assert(0, "no coefficients for exp2()"); // Overflow and Underflow limits. - enum real OF = 16_384.0L; - enum real UF = -16_382.0L; + enum T OF = T.max_exp; + enum T UF = T.min_exp - 1; // Special cases. if (isNaN(x)) @@ -2627,16 +3170,40 @@ private real exp2Impl(real x) @nogc @trusted pure nothrow if (x < UF) return 0.0; - // Separate into integer and fractional parts. - int n = cast(int) floor(x + 0.5); - x -= n; + static if (realFormat == RealFormat.ieeeSingle) // special case for single precision + { + // The following is necessary because range reduction blows up. + if (x == 0.0f) + return 1.0f; + + // Separate into integer and fractional parts. + const T i = floor(x); + int n = cast(int) i; + x -= i; + if (x > 0.5f) + { + n += 1; + x -= 1.0f; + } + + // Rational approximation: + // exp2(x) = 1.0 + x P(x) + x = 1.0f + x * poly(x, P); + } + else + { + // Separate into integer and fractional parts. + const T i = floor(x + cast(T) 0.5); + int n = cast(int) i; + x -= i; - // Rational approximation: - // exp2(x) = 1.0 + 2x P(x^^2) / (Q(x^^2) - P(x^^2)) - const real xx = x * x; - const real px = x * poly(xx, P); - x = px / (poly(xx, Q) - px); - x = 1.0 + ldexp(x, 1); + // Rational approximation: + // exp2(x) = 1.0 + 2x P(x^^2) / (Q(x^^2) - P(x^^2)) + const T xx = x * x; + const T px = x * poly(xx, P); + x = px / (poly(xx, Q) - px); + x = (cast(T) 1.0) + (cast(T) 2.0) * x; + } // Scale by power of 2. x = ldexp(x, n); @@ -2644,128 +3211,50 @@ private real exp2Impl(real x) @nogc @trusted pure nothrow return x; } -/// -@safe unittest +@safe @nogc nothrow unittest { assert(feqrel(exp2(0.5L), SQRT2) >= real.mant_dig -1); assert(exp2(8.0L) == 256.0); assert(exp2(-9.0L)== 1.0L/512.0); -} -@safe unittest -{ - version(CRuntime_Microsoft) {} else // aexp2/exp2f/exp2l not implemented + static void testExp2(T)() { - assert( core.stdc.math.exp2f(0.0f) == 1 ); - assert( core.stdc.math.exp2 (0.0) == 1 ); - assert( core.stdc.math.exp2l(0.0L) == 1 ); - } -} + // NaN + const T specialNaN = NaN(0x0123L); + assert(isIdentical(exp2(specialNaN), specialNaN)); -@system unittest -{ - FloatingPointControl ctrl; - if (FloatingPointControl.hasExceptionTraps) - ctrl.disableExceptions(FloatingPointControl.allExceptions); - ctrl.rounding = FloatingPointControl.roundToNearest; + // over-/underflow + enum T OF = T.max_exp; + enum T UF = T.min_exp - T.mant_dig; + assert(isIdentical(exp2(OF + 1), cast(T) T.infinity)); + assert(isIdentical(exp2(UF - 1), cast(T) 0.0)); - alias F = floatTraits!real; - - static if (F.realFormat == RealFormat.ieeeQuadruple) - { - static immutable real[2][] exptestpoints = - [ // x exp(x) - [ 1.0L, E ], - [ 0.5L, 0x1.a61298e1e069bc972dfefab6df34p+0L ], - [ 3.0L, E*E*E ], - [ 0x1.6p+13L, 0x1.6e509d45728655cdb4840542acb5p+16250L ], // near overflow - [ 0x1.7p+13L, real.infinity ], // close overflow - [ 0x1p+80L, real.infinity ], // far overflow - [ real.infinity, real.infinity ], - [-0x1.18p+13L, 0x1.5e4bf54b4807034ea97fef0059a6p-12927L ], // near underflow - [-0x1.625p+13L, 0x1.a6bd68a39d11fec3a250cd97f524p-16358L ], // ditto - [-0x1.62dafp+13L, 0x0.cb629e9813b80ed4d639e875be6cp-16382L ], // near underflow - subnormal - [-0x1.6549p+13L, 0x0.0000000000000000000000000001p-16382L ], // ditto - [-0x1.655p+13L, 0 ], // close underflow - [-0x1p+30L, 0 ], // far underflow - ]; - } - else static if (F.realFormat == RealFormat.ieeeExtended) - { - static immutable real[2][] exptestpoints = - [ // x exp(x) - [ 1.0L, E ], - [ 0.5L, 0x1.a61298e1e069bc97p+0L ], - [ 3.0L, E*E*E ], - [ 0x1.1p+13L, 0x1.29aeffefc8ec645p+12557L ], // near overflow - [ 0x1.7p+13L, real.infinity ], // close overflow - [ 0x1p+80L, real.infinity ], // far overflow - [ real.infinity, real.infinity ], - [-0x1.18p+13L, 0x1.5e4bf54b4806db9p-12927L ], // near underflow - [-0x1.625p+13L, 0x1.a6bd68a39d11f35cp-16358L ], // ditto - [-0x1.62dafp+13L, 0x1.96c53d30277021dp-16383L ], // near underflow - subnormal - [-0x1.643p+13L, 0x1p-16444L ], // ditto - [-0x1.645p+13L, 0 ], // close underflow - [-0x1p+30L, 0 ], // far underflow - ]; - } - else static if (F.realFormat == RealFormat.ieeeDouble) - { - static immutable real[2][] exptestpoints = - [ // x, exp(x) - [ 1.0L, E ], - [ 0.5L, 0x1.a61298e1e069cp+0L ], - [ 3.0L, E*E*E ], - [ 0x1.6p+9L, 0x1.93bf4ec282efbp+1015L ], // near overflow - [ 0x1.7p+9L, real.infinity ], // close overflow - [ 0x1p+80L, real.infinity ], // far overflow - [ real.infinity, real.infinity ], - [-0x1.6p+9L, 0x1.44a3824e5285fp-1016L ], // near underflow - [-0x1.64p+9L, 0x0.06f84920bb2d4p-1022L ], // near underflow - subnormal - [-0x1.743p+9L, 0x0.0000000000001p-1022L ], // ditto - [-0x1.8p+9L, 0 ], // close underflow - [-0x1p30L, 0 ], // far underflow + static immutable T[2][] vals = + [ + // x, exp2(x) + [ 0.0, 1.0 ], + [ -0.0, 1.0 ], + [ 0.5, SQRT2 ], + [ 8.0, 256.0 ], + [ -9.0, 1.0 / 512 ], ]; - } - else - static assert(0, "No exp() tests for real type!"); - - const minEqualDecimalDigits = real.dig - 3; - real x; - IeeeFlags f; - foreach (ref pair; exptestpoints) - { - resetIeeeFlags(); - x = exp(pair[0]); - assert(equalsDigit(x, pair[1], minEqualDecimalDigits)); - } - - // Ideally, exp(0) would not set the inexact flag. - // Unfortunately, fldl2e sets it! - // So it's not realistic to avoid setting it. - assert(exp(0.0L) == 1.0); - // NaN propagation. Doesn't set flags, bcos was already NaN. - resetIeeeFlags(); - x = exp(real.nan); - f = ieeeFlags; - assert(isIdentical(abs(x), real.nan)); - assert(f.flags == 0); - - resetIeeeFlags(); - x = exp(-real.nan); - f = ieeeFlags; - assert(isIdentical(abs(x), real.nan)); - assert(f.flags == 0); + foreach (ref val; vals) + { + const T x = val[0]; + const T r = val[1]; + const T e = exp2(x); - x = exp(NaN(0x123)); - assert(isIdentical(x, NaN(0x123))); + //printf("exp2(%Lg) = %Lg, should be %Lg\n", cast(real) x, cast(real) e, cast(real) r); + assert(approxEqual(r, e)); + } + } - // High resolution test (verified against GNU MPFR/Mathematica). - assert(exp(0.5L) == 0x1.A612_98E1_E069_BC97_2DFE_FAB6_DF34p+0L); + import std.meta : AliasSeq; + foreach (T; AliasSeq!(real, double, float)) + testExp2!T(); } - /* * Calculate cos(y) + i sin(y). * @@ -3410,7 +3899,8 @@ float ldexp(float n, int exp) @safe pure nothrow @nogc { return ldexp(cast(real) @safe pure nothrow @nogc unittest { - static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended) + static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended || + floatTraits!(real).realFormat == RealFormat.ieeeQuadruple) { assert(ldexp(1.0L, -16384) == 0x1p-16384L); assert(ldexp(1.0L, -16382) == 0x1p-16382L); @@ -4836,6 +5326,7 @@ long lrint(real x) @trusted pure nothrow @nogc const j = sign ? -OF : OF; x = (j + x) - j; + const exp = (vu[F.EXPPOS_SHORT] & F.EXPMASK) - (F.EXPBIAS + 1); const implicitOne = 1UL << 48; auto vl = cast(ulong*)(&x); vl[MANTISSA_MSB] &= implicitOne - 1; @@ -4843,7 +5334,6 @@ long lrint(real x) @trusted pure nothrow @nogc long result; - const exp = (vu[F.EXPPOS_SHORT] & F.EXPMASK) - (F.EXPBIAS + 1); if (exp < 0) result = 0; else if (exp <= 48) @@ -5027,7 +5517,7 @@ real trunc(real x) @trusted nothrow @nogc pure * $(TR $(TH x) $(TH y) $(TH remainder(x, y)) $(TH n) $(TH invalid?)) * $(TR $(TD $(PLUSMN)0.0) $(TD not 0.0) $(TD $(PLUSMN)0.0) $(TD 0.0) $(TD no)) * $(TR $(TD $(PLUSMNINF)) $(TD anything) $(TD -$(NAN)) $(TD ?) $(TD yes)) - * $(TR $(TD anything) $(TD $(PLUSMN)0.0) $(TD -$(NAN)) $(TD ?) $(TD yes)) + * $(TR $(TD anything) $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)$(NAN)) $(TD ?) $(TD yes)) * $(TR $(TD != $(PLUSMNINF)) $(TD $(PLUSMNINF)) $(TD x) $(TD ?) $(TD no)) * ) * @@ -5062,8 +5552,8 @@ real remquo(real x, real y, out int n) @trusted nothrow @nogc /// ditto assert(remainder(-5.1, 3.0).feqrel(0.9) > 16); assert(remainder(0.0, 3.0) == 0.0); - assert(remainder(1.0, 0.0) is -real.nan); - assert(remainder(-1.0, 0.0) is -real.nan); + assert(isNaN(remainder(1.0, 0.0))); + assert(isNaN(remainder(-1.0, 0.0))); } } @@ -5946,24 +6436,22 @@ private: /// @safe unittest { - // GCC floating point emulation doesn't allow changing - // rounding modes, getting error bits etc - version(GNU) version(D_SoftFloat) - return; - - FloatingPointControl fpctrl; + version(D_HardFloat) + { + FloatingPointControl fpctrl; - fpctrl.rounding = FloatingPointControl.roundDown; - assert(lrint(1.5) == 1.0); + fpctrl.rounding = FloatingPointControl.roundDown; + assert(lrint(1.5) == 1.0); - fpctrl.rounding = FloatingPointControl.roundUp; - assert(lrint(1.4) == 2.0); + fpctrl.rounding = FloatingPointControl.roundUp; + assert(lrint(1.4) == 2.0); - fpctrl.rounding = FloatingPointControl.roundToNearest; - assert(lrint(1.5) == 2.0); + fpctrl.rounding = FloatingPointControl.roundToNearest; + assert(lrint(1.5) == 2.0); + } } -@safe unittest +version(D_HardFloat) @safe unittest { void ensureDefaults() { @@ -5978,15 +6466,12 @@ private: } ensureDefaults(); - version(D_HardFloat) { - { - FloatingPointControl ctrl; - ctrl.rounding = FloatingPointControl.roundDown; - assert(FloatingPointControl.rounding == FloatingPointControl.roundDown); - } - ensureDefaults(); + FloatingPointControl ctrl; + ctrl.rounding = FloatingPointControl.roundDown; + assert(FloatingPointControl.rounding == FloatingPointControl.roundDown); } + ensureDefaults(); if (FloatingPointControl.hasExceptionTraps) { @@ -8174,6 +8659,20 @@ do } } +/// ditto +Unqual!(CommonType!(T1, T2)) poly(T1, T2, int N)(T1 x, ref const T2[N] A) @safe pure nothrow @nogc +if (isFloatingPoint!T1 && isFloatingPoint!T2 && N > 0 && N <= 10) +{ + // statically unrolled version for up to 10 coefficients + typeof(return) r = A[N - 1]; + static foreach (i; 1 .. N) + { + r *= x; + r += A[N - 1 - i]; + } + return r; +} + /// @safe nothrow @nogc unittest { diff --git a/libphobos/src/std/net/curl.d b/libphobos/src/std/net/curl.d index 471adbc0f..ee480fc00 100644 --- a/libphobos/src/std/net/curl.d +++ b/libphobos/src/std/net/curl.d @@ -681,7 +681,7 @@ if (is(T == char) || is(T == ubyte)) s.send(httpOK(req.bdy)); }); auto res = post(host ~ "/path", ["name1" : "value1", "name2" : "value2"]); - assert(res == "name1=value1&name2=value2"); + assert(res == "name1=value1&name2=value2" || res == "name2=value2&name1=value1"); } } diff --git a/libphobos/src/std/parallelism.d b/libphobos/src/std/parallelism.d index b8fc10b72..41ad2c569 100644 --- a/libphobos/src/std/parallelism.d +++ b/libphobos/src/std/parallelism.d @@ -3560,6 +3560,22 @@ ParallelForeach!R parallel(R)(R range, size_t workUnitSize) return taskPool.parallel(range, workUnitSize); } +// #17019: `each` should be usable with parallel +@system unittest +{ + import std.algorithm.iteration : each, sum; + import std.range : iota; + + // check behavior with parallel + auto arr = new int[10]; + parallel(arr).each!((ref e) => e += 1); + assert(arr.sum == 10); + + auto arrIndex = new int[10]; + parallel(arrIndex).each!((i, ref e) => e += i); + assert(arrIndex.sum == 10.iota.sum); +} + // Thrown when a parallel foreach loop is broken from. class ParallelForeachError : Error { diff --git a/libphobos/src/std/path.d b/libphobos/src/std/path.d index 11b0053e8..8082e6fd6 100644 --- a/libphobos/src/std/path.d +++ b/libphobos/src/std/path.d @@ -13,7 +13,7 @@ To differentiate between these cases, use $(REF isDir, std,file) and $(REF exists, std,file). - Note that on Windows, both the backslash (``\``) and the slash (``/``) + Note that on Windows, both the backslash ($(D `\`)) and the slash ($(D `/`)) are in principle valid directory separators. This module treats them both on equal footing, but in cases where a $(I new) separator is added, a backslash will be used. Furthermore, the $(LREF buildNormalizedPath) @@ -141,8 +141,8 @@ else static assert(0, "unsupported platform"); /** Determines whether the given character is a directory separator. - On Windows, this includes both ``\`` and ``/``. - On POSIX, it's just ``/``. + On Windows, this includes both $(D `\`) and $(D `/`). + On POSIX, it's just $(D `/`). */ bool isDirSeparator(dchar c) @safe pure nothrow @nogc { @@ -1425,7 +1425,7 @@ private auto _withDefaultExtension(R, C)(R path, C[] ext) preceding segments will be dropped. On Windows, if one of the path segments are rooted, but not absolute - (e.g. ``\foo``), all preceding path segments down to the previous + (e.g. $(D `\foo`)), all preceding path segments down to the previous root will be dropped. (See below for an example.) This function always allocates memory to hold the resulting path. @@ -2639,7 +2639,7 @@ if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || --- On Windows, an absolute path starts at the root directory of - a specific drive. Hence, it must start with ``d:\`` or ``d:/``, + a specific drive. Hence, it must start with $(D `d:\`) or $(D `d:/`), where `d` is the drive letter. Alternatively, it may be a network path, i.e. a path starting with a double (back)slash. --- @@ -2849,14 +2849,14 @@ if (isConvertibleToString!R) taken to be the current working directory. If specified, `base` must be an absolute path, and it is always assumed to refer to a directory. If `path` and `base` refer to - the same directory, the function returns ``.``. + the same directory, the function returns $(D `.`). The following algorithm is used: $(OL $(LI If `path` is a relative directory, return it unaltered.) $(LI Find a common root between `path` and `base`. If there is no common root, return `path` unaltered.) - $(LI Prepare a string with as many ``../`` or ``..\`` as + $(LI Prepare a string with as many $(D `../`) or $(D `..\`) as necessary to reach the common root from base path.) $(LI Append the remaining segments of `path` to the string and return.) @@ -3100,7 +3100,7 @@ if (isConvertibleToString!R1 || isConvertibleToString!R2) comparison. This is controlled through the `cs` template parameter which, if not specified, is given by $(LREF CaseSensitive)`.osDefault`. - On Windows, the backslash and slash characters (``\`` and ``/``) + On Windows, the backslash and slash characters ($(D `\`) and $(D `/`)) are considered equal. Params: @@ -3732,10 +3732,10 @@ unittest $(LI If `path` is on the form $(D `\\$(I server)\$(I share)\...`) (UNC path), $(LREF isValidFilename) is applied to $(I server) and $(I share) as well.) - $(LI If `path` starts with ``\\?\`` (long UNC path), the + $(LI If `path` starts with $(D `\\?\`) (long UNC path), the only requirement for the rest of the string is that it does not contain the null character.) - $(LI If `path` starts with ``\\.\`` (Win32 device namespace) + $(LI If `path` starts with $(D `\\.\`) (Win32 device namespace) this function returns `false`; such paths are beyond the scope of this module.) ) diff --git a/libphobos/src/std/random.d b/libphobos/src/std/random.d index e56579065..cbc9ad72d 100644 --- a/libphobos/src/std/random.d +++ b/libphobos/src/std/random.d @@ -1798,22 +1798,22 @@ If we start at `UpperType.max` and walk backwards `upperDist - 1` spaces, then the space we land on is the last acceptable position where a full bucket can fit: -``` +--- bucketFront UpperType.max v v [..., 0, 1, 2, ..., upperDist - 1] ^~~ upperDist - 1 ~~^ -``` +--- If the bucket starts any later, then it must have lost at least one number and at least that number won't be represented fairly. -``` +--- bucketFront UpperType.max v v [..., upperDist - 1, 0, 1, 2, ..., upperDist - 2] ^~~~~~~~ upperDist - 1 ~~~~~~~^ -``` +--- Hence, our condition to reroll is `bucketFront > (UpperType.max - (upperDist - 1))` @@ -2015,6 +2015,49 @@ if ((isIntegral!(CommonType!(T1, T2)) || isSomeChar!(CommonType!(T1, T2))) && } } +/+ +Generates an unsigned integer in the half-open range `[0, k)`. +Non-public because we locally guarantee `k > 0`. + +Params: + k = unsigned exclusive upper bound; caller guarantees this is non-zero + rng = random number generator to use + +Returns: + Pseudo-random unsigned integer strictly less than `k`. ++/ +private UInt _uniformIndex(UniformRNG, UInt = size_t)(const UInt k, ref UniformRNG rng) +if (isUnsigned!UInt && isUniformRNG!UniformRNG) +{ + alias ResultType = UInt; + alias UpperType = Unsigned!(typeof(k - 0)); + alias upperDist = k; + + assert(upperDist != 0); + + // For backwards compatibility use same algorithm as uniform(0, k, rng). + UpperType offset, rnum, bucketFront; + do + { + rnum = uniform!UpperType(rng); + offset = rnum % upperDist; + bucketFront = rnum - offset; + } // while we're in an unfair bucket... + while (bucketFront > (UpperType.max - (upperDist - 1))); + + return cast(ResultType) offset; +} + +pure @safe unittest +{ + // For backwards compatibility check that _uniformIndex(k, rng) + // has the same result as uniform(0, k, rng). + auto rng1 = Xorshift(123_456_789); + auto rng2 = rng1.save(); + const size_t k = (1U << 31) - 1; + assert(_uniformIndex(k, rng1) == uniform(0, k, rng2)); +} + /** Generates a uniformly-distributed number in the range $(D [T.min, T.max]) for any integral or character type `T`. If no random @@ -2383,7 +2426,13 @@ Returns: Range randomShuffle(Range, RandomGen)(Range r, ref RandomGen gen) if (isRandomAccessRange!Range && isUniformRNG!RandomGen) { - return partialShuffle!(Range, RandomGen)(r, r.length, gen); + import std.algorithm.mutation : swapAt; + const n = r.length; + foreach (i; 0 .. n) + { + r.swapAt(i, i + _uniformIndex(n - i, gen)); + } + return r; } /// ditto @@ -2405,12 +2454,14 @@ if (isRandomAccessRange!Range) @safe unittest { + int[10] sa = void; + int[10] sb = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; import std.algorithm.sorting : sort; foreach (RandomGen; PseudoRngTypes) { - // Also tests partialShuffle indirectly. - auto a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - auto b = a.dup; + sa[] = sb[]; + auto a = sa[]; + auto b = sb[]; auto gen = RandomGen(123_456_789); randomShuffle(a, gen); sort(a); @@ -2419,6 +2470,15 @@ if (isRandomAccessRange!Range) sort(a); assert(a == b); } + // For backwards compatibility verify randomShuffle(r, gen) + // is equivalent to partialShuffle(r, 0, r.length, gen). + auto gen1 = Xorshift(123_456_789); + auto gen2 = gen1.save(); + sa[] = sb[]; + // Issue 19156 - @nogc std.random.randomShuffle. + () @nogc nothrow pure { randomShuffle(sa[], gen1); }(); + partialShuffle(sb[], sb.length, gen2); + assert(sa[] == sb[]); } @safe unittest // bugzilla 18501 @@ -2633,6 +2693,105 @@ do assert(i == 0); } +/+ @nogc bool array designed for RandomCover. +- constructed with an invariable length +- small length means 0 alloc and bit field (if up to 32(x86) or 64(x64) choices to cover) +- bigger length means non-GC heap allocation(s) and dealloc. +/ +private struct RandomCoverChoices +{ + private void* buffer; + private immutable size_t _length; + private immutable bool hasPackedBits; + + void opAssign(T)(T) @disable; + + this(this) pure nothrow @nogc @trusted + { + import core.memory : pureMalloc; + import core.stdc.string : memcpy; + import core.exception : onOutOfMemoryError; + + if (!hasPackedBits && buffer !is null) + { + void* nbuffer = pureMalloc(_length); + if (nbuffer is null) + onOutOfMemoryError(); + buffer = memcpy(nbuffer, buffer, _length); + } + } + + this(size_t numChoices) pure nothrow @nogc @trusted + { + import core.memory : pureCalloc; + import core.exception : onOutOfMemoryError; + + _length = numChoices; + hasPackedBits = _length <= size_t.sizeof * 8; + if (!hasPackedBits) + { + buffer = pureCalloc(numChoices, 1); + if (buffer is null) + onOutOfMemoryError(); + } + } + + size_t length() const pure nothrow @nogc @safe @property {return _length;} + + ~this() pure nothrow @nogc @trusted + { + import core.memory : pureFree; + + if (!hasPackedBits && buffer !is null) + pureFree(buffer); + } + + bool opIndex(size_t index) const pure nothrow @nogc @trusted + { + assert(index < _length); + if (!hasPackedBits) + return *((cast(bool*) buffer) + index); + else + return ((cast(size_t) buffer) >> index) & size_t(1); + } + + void opIndexAssign(bool value, size_t index) pure nothrow @nogc @trusted + { + assert(index < _length); + if (!hasPackedBits) + { + *((cast(bool*) buffer) + index) = value; + } + else + { + if (value) + (*cast(size_t*) &buffer) |= size_t(1) << index; + else + (*cast(size_t*) &buffer) &= ~(size_t(1) << index); + } + } +} + +@safe @nogc nothrow unittest +{ + static immutable lengths = [3, 32, 65, 256]; + foreach (length; lengths) + { + RandomCoverChoices c = RandomCoverChoices(length); + assert(c.hasPackedBits == (length <= size_t.sizeof * 8)); + c[0] = true; + c[2] = true; + assert(c[0]); + assert(!c[1]); + assert(c[2]); + c[0] = false; + c[1] = true; + c[2] = false; + assert(!c[0]); + assert(c[1]); + assert(!c[2]); + } +} + /** Covers a given range `r` in a random manner, i.e. goes through each element of `r` once and only once, just in a random order. `r` @@ -2656,7 +2815,7 @@ struct RandomCover(Range, UniformRNG = void) if (isRandomAccessRange!Range && (isUniformRNG!UniformRNG || is(UniformRNG == void))) { private Range _input; - private bool[] _chosen; + private RandomCoverChoices _chosen; private size_t _current; private size_t _alreadyChosen = 0; private bool _isEmpty = false; @@ -2666,14 +2825,14 @@ if (isRandomAccessRange!Range && (isUniformRNG!UniformRNG || is(UniformRNG == vo this(Range input) { _input = input; - _chosen.length = _input.length; + _chosen = RandomCoverChoices(_input.length); if (_input.empty) { _isEmpty = true; } else { - _current = uniform(0, _chosen.length); + _current = _uniformIndex(_chosen.length, rndGen); } } } @@ -2685,14 +2844,14 @@ if (isRandomAccessRange!Range && (isUniformRNG!UniformRNG || is(UniformRNG == vo { _input = input; _rng = rng; - _chosen.length = _input.length; + _chosen = RandomCoverChoices(_input.length); if (_input.empty) { _isEmpty = true; } else { - _current = uniform(0, _chosen.length, rng); + _current = _uniformIndex(_chosen.length, rng); } } @@ -2735,11 +2894,11 @@ if (isRandomAccessRange!Range && (isUniformRNG!UniformRNG || is(UniformRNG == vo // Roll a dice with k faces static if (is(UniformRNG == void)) { - auto chooseMe = uniform(0, k) == 0; + auto chooseMe = _uniformIndex(k, rndGen) == 0; } else { - auto chooseMe = uniform(0, k, _rng) == 0; + auto chooseMe = _uniformIndex(k, _rng) == 0; } assert(k > 1 || chooseMe); if (chooseMe) @@ -2793,6 +2952,28 @@ if (isRandomAccessRange!Range) assert(10.iota.randomCover(rnd).equal([7, 4, 2, 0, 1, 6, 8, 3, 9, 5])); } +@safe unittest // cover RandomCoverChoices postblit for heap storage +{ + import std.array : array; + import std.range : iota; + auto a = 1337.iota.randomCover().array; + assert(a.length == 1337); +} + +@nogc nothrow pure @safe unittest +{ + // Issue 14001 - Optionally @nogc std.random.randomCover + auto rng = Xorshift(123_456_789); + int[5] sa = [1, 2, 3, 4, 5]; + auto r = randomCover(sa[], rng); + assert(!r.empty); + const x = r.front; + r.popFront(); + assert(!r.empty); + const y = r.front; + assert(x != y); +} + @safe unittest { import std.algorithm; diff --git a/libphobos/src/std/range/package.d b/libphobos/src/std/range/package.d index a3981ae90..3d9ba8440 100644 --- a/libphobos/src/std/range/package.d +++ b/libphobos/src/std/range/package.d @@ -10182,8 +10182,8 @@ template isTwoWayCompatible(alias fn, T1, T2) T1 foo(); T2 bar(); - fn(foo(), bar()); - fn(bar(), foo()); + cast(void) fn(foo(), bar()); + cast(void) fn(bar(), foo()); } )); } @@ -10904,7 +10904,20 @@ cost $(BIGOH n), use $(REF isSorted, std,algorithm,sorting). auto assumeSorted(alias pred = "a < b", R)(R r) if (isInputRange!(Unqual!R)) { - return SortedRange!(Unqual!R, pred)(r); + // Avoid senseless `SortedRange!(SortedRange!(...), pred)` nesting. + static if (is(R == SortedRange!(RRange, RPred), RRange, alias RPred)) + { + static if (isInputRange!R && __traits(isSame, pred, RPred)) + // If the predicate is the same and we don't need to cast away + // constness for the result to be an input range. + return r; + else + return SortedRange!(Unqual!(typeof(r._input)), pred)(r._input); + } + else + { + return SortedRange!(Unqual!R, pred)(r); + } } /// @@ -10930,6 +10943,10 @@ if (isInputRange!(Unqual!R)) assert(equal(p, [4, 4, 5, 6 ])); p = assumeSorted(a).upperBound(4.2); assert(equal(p, [ 5, 6 ])); + + // Issue 18933 - don't create senselessly nested SortedRange types. + assert(is(typeof(assumeSorted(a)) == typeof(assumeSorted(assumeSorted(a))))); + assert(is(typeof(assumeSorted(a)) == typeof(assumeSorted(assumeSorted!"a > b"(a))))); } @safe unittest diff --git a/libphobos/src/std/range/primitives.d b/libphobos/src/std/range/primitives.d index 515be0e31..e1e0c89e1 100644 --- a/libphobos/src/std/range/primitives.d +++ b/libphobos/src/std/range/primitives.d @@ -253,7 +253,7 @@ enum bool isInputRange(R) = puts the whole raw element `e` into `r`. doPut will not attempt to iterate, slice or transcode `e` in any way shape or form. It will $(B only) call the correct primitive (`r.put(e)`, $(D r.front = e) or -`r(0)` once. +`r(e)` once. This can be important when `e` needs to be placed in `r` unchanged. Furthermore, it can be useful when working with `InputRange`s, as doPut @@ -272,6 +272,20 @@ private void doPut(R, E)(ref R r, auto ref E e) "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); r.put(e); } + else static if (isNarrowString!R && is(const(E) == const(typeof(r[0])))) + { + // one character, we can put it + r[0] = e; + r = r[1 .. $]; + } + else static if (isNarrowString!R && isNarrowString!E && is(typeof(r[] = e))) + { + // slice assign. Note that this is a duplicate from put, but because + // putChar uses doPut exclusively, we have to copy it here. + immutable len = e.length; + r[0 .. len] = e; + r = r[len .. $]; + } else static if (isInputRange!R) { static assert(is(typeof(r.front = e)), @@ -308,7 +322,7 @@ private void doPut(R, E)(ref R r, auto ref E e) static assert( isNativeOutputRange!(int[4][], int)); //Scary! static assert( isNativeOutputRange!(int[4][], int[4])); - static assert(!isNativeOutputRange!( char[], char)); + static assert( isNativeOutputRange!( char[], char)); static assert(!isNativeOutputRange!( char[], dchar)); static assert( isNativeOutputRange!(dchar[], char)); static assert( isNativeOutputRange!(dchar[], dchar)); @@ -453,22 +467,32 @@ void put(R, E)(ref R r, E e) } /** - * Because of auto-decoding, the `front` of a `string` is a `dchar`, - * so using `put` with `char` arrays is disallowed. In order to fill - * any `char` type array, use $(REF byCodeUnit, std, utf). + * It's also possible to `put` any width strings or characters into narrow + * strings -- put does the conversion for you. + * + * Note that putting the same width character as the target buffer type is + * `nothrow`, but transcoding can throw a $(REF UTFException, std, utf). */ -@safe pure nothrow unittest +@safe pure unittest { - import std.utf : byCodeUnit; - // the elements must be mutable, so using string or const(char)[] // won't compile char[] s1 = new char[13]; - auto r1 = s1.byCodeUnit; + auto r1 = s1; + put(r1, "Hello, World!"w); + assert(s1 == "Hello, World!"); +} + +@safe pure nothrow unittest +{ + // same thing, just using same character width. + char[] s1 = new char[13]; + auto r1 = s1; put(r1, "Hello, World!"); assert(s1 == "Hello, World!"); } + @safe pure nothrow @nogc unittest { static struct R() { void put(in char[]) {} } @@ -486,9 +510,9 @@ if (isSomeChar!E) ref const(wchar)[] wstringInit(); ref const(dchar)[] dstringInit(); - enum csCond = !isDynamicArray!R && is(typeof(doPut(r, cstringInit()))); - enum wsCond = !isDynamicArray!R && is(typeof(doPut(r, wstringInit()))); - enum dsCond = !isDynamicArray!R && is(typeof(doPut(r, dstringInit()))); + enum csCond = is(typeof(doPut(r, cstringInit()))); + enum wsCond = is(typeof(doPut(r, wstringInit()))); + enum dsCond = is(typeof(doPut(r, dstringInit()))); //Use "max" to avoid static type demotion enum ccCond = is(typeof(doPut(r, char.max))); @@ -585,9 +609,64 @@ pure @safe unittest char[] a = new char[10]; static assert(!__traits(compiles, put(a, 1.0L))); static assert(!__traits(compiles, put(a, 1))); - // char[] is NOT output range. - static assert(!__traits(compiles, put(a, 'a'))); - static assert(!__traits(compiles, put(a, "ABC"))); + //char[] is now an output range for char, wchar, dchar, and ranges of such. + static assert(__traits(compiles, putChar(a, 'a'))); + static assert(__traits(compiles, put(a, wchar('a')))); + static assert(__traits(compiles, put(a, dchar('a')))); + static assert(__traits(compiles, put(a, "ABC"))); + static assert(__traits(compiles, put(a, "ABC"w))); + static assert(__traits(compiles, put(a, "ABC"d))); +} + +@safe unittest +{ + // attempt putting into narrow strings by transcoding + char[] a = new char[10]; + auto b = a; + put(a, "ABC"w); + assert(b[0 .. 3] == "ABC"); + assert(a.length == 7); + + a = b; // reset + put(a, 'λ'); + assert(b[0 .. 2] == "λ"); + assert(a.length == 8); + + a = b; // reset + put(a, "ABC"d); + assert(b[0 .. 3] == "ABC"); + assert(a.length == 7); + + a = b; // reset + put(a, '𐐷'); + assert(b[0 .. 4] == "𐐷"); + assert(a.length == 6); + + wchar[] aw = new wchar[10]; + auto bw = aw; + put(aw, "ABC"); + assert(bw[0 .. 3] == "ABC"w); + assert(aw.length == 7); + + aw = bw; // reset + put(aw, 'λ'); + assert(bw[0 .. 1] == "λ"w); + assert(aw.length == 9); + + aw = bw; // reset + put(aw, "ABC"d); + assert(bw[0 .. 3] == "ABC"w); + assert(aw.length == 7); + + aw = bw; // reset + put(aw, '𐐷'); + assert(bw[0 .. 2] == "𐐷"w); + assert(aw.length == 8); + + aw = bw; // reset + put(aw, "𐐷"); // try transcoding from char[] + assert(bw[0 .. 2] == "𐐷"w); + assert(aw.length == 8); } @safe unittest @@ -833,7 +912,7 @@ enum bool isOutputRange(R, E) = void myprint(in char[] s) { } static assert(isOutputRange!(typeof(&myprint), char)); - static assert(!isOutputRange!(char[], char)); + static assert( isOutputRange!(char[], char)); static assert( isOutputRange!(dchar[], wchar)); static assert( isOutputRange!(dchar[], dchar)); } @@ -848,7 +927,7 @@ enum bool isOutputRange(R, E) = static assert( isOutputRange!(Appender!string, string)); static assert( isOutputRange!(Appender!string*, string)); static assert(!isOutputRange!(Appender!string, int)); - static assert(!isOutputRange!(wchar[], wchar)); + static assert( isOutputRange!(wchar[], wchar)); static assert( isOutputRange!(dchar[], char)); static assert( isOutputRange!(dchar[], string)); static assert( isOutputRange!(dchar[], wstring)); diff --git a/libphobos/src/std/stdio.d b/libphobos/src/std/stdio.d index c4ee8594a..25ceef542 100644 --- a/libphobos/src/std/stdio.d +++ b/libphobos/src/std/stdio.d @@ -427,7 +427,7 @@ Throws: `ErrnoException` if the file could not be opened. import std.conv : text; import std.exception : errnoEnforce; - this(errnoEnforce(.fopen(name, stdioOpenmode), + this(errnoEnforce(_fopen(name, stdioOpenmode), text("Cannot open file `", name, "' in mode `", stdioOpenmode, "'")), name); @@ -521,12 +521,12 @@ Throws: `ErrnoException` in case of error. { if (isPopened) { - errnoEnforce(handle = .popen(name, stdioOpenmode), + errnoEnforce(handle = _popen(name, stdioOpenmode), "Cannot run command `"~name~"'"); } else { - errnoEnforce(handle = .fopen(name, stdioOpenmode), + errnoEnforce(handle = _fopen(name, stdioOpenmode), text("Cannot open file `", name, "' in mode `", stdioOpenmode, "'")); } @@ -534,7 +534,7 @@ Throws: `ErrnoException` in case of error. else { assert(isPopened == false); - errnoEnforce(handle = .fopen(name, stdioOpenmode), + errnoEnforce(handle = _fopen(name, stdioOpenmode), text("Cannot open file `", name, "' in mode `", stdioOpenmode, "'")); } @@ -706,7 +706,6 @@ Throws: `ErrnoException` in case of error. // mucking with the file descriptor. POSIX standard requires the // new fdopen'd file to retain the given file descriptor's // position. - import core.stdc.stdio : fopen; auto fp = fopen("NUL", modez); errnoEnforce(fp, "Cannot open placeholder NUL stream"); FLOCK(fp); @@ -4213,7 +4212,7 @@ if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum) && * (to `_wfopen` on Windows) * with appropriately-constructed C-style strings. */ -private FILE* fopen(R1, R2)(R1 name, R2 mode = "r") +private FILE* _fopen(R1, R2)(R1 name, R2 mode = "r") if ((isInputRange!R1 && isSomeChar!(ElementEncodingType!R1) || isSomeString!R1) && (isInputRange!R2 && isSomeChar!(ElementEncodingType!R2) || isSomeString!R2)) { @@ -4222,7 +4221,7 @@ if ((isInputRange!R1 && isSomeChar!(ElementEncodingType!R1) || isSomeString!R1) auto namez = name.tempCString!FSChar(); auto modez = mode.tempCString!FSChar(); - static fopenImpl(const(FSChar)* namez, const(FSChar)* modez) @trusted nothrow @nogc + static _fopenImpl(const(FSChar)* namez, const(FSChar)* modez) @trusted nothrow @nogc { version(Windows) { @@ -4243,10 +4242,10 @@ if ((isInputRange!R1 && isSomeChar!(ElementEncodingType!R1) || isSomeString!R1) } else { - return .fopen(namez, modez); + return fopen(namez, modez); } } - return fopenImpl(namez, modez); + return _fopenImpl(namez, modez); } version (Posix) @@ -4255,7 +4254,7 @@ version (Posix) * Convenience function that forwards to `core.sys.posix.stdio.popen` * with appropriately-constructed C-style strings. */ - FILE* popen(R1, R2)(R1 name, R2 mode = "r") @trusted nothrow @nogc + FILE* _popen(R1, R2)(R1 name, R2 mode = "r") @trusted nothrow @nogc if ((isInputRange!R1 && isSomeChar!(ElementEncodingType!R1) || isSomeString!R1) && (isInputRange!R2 && isSomeChar!(ElementEncodingType!R2) || isSomeString!R2)) { diff --git a/libphobos/src/std/string.d b/libphobos/src/std/string.d index 3cf112bac..7fe403eef 100644 --- a/libphobos/src/std/string.d +++ b/libphobos/src/std/string.d @@ -5531,30 +5531,8 @@ do assert(buffer.data == "h5 rd"); } -//@@@DEPRECATED_2018-05@@@ -/*********************************************** - * $(RED This function is deprecated and will be removed May 2018.) - * Please use the functions in $(MREF std, regex) and $(MREF std, algorithm) - * instead. If you still need this function, it will be available in - * $(LINK2 https://github.com/dlang/undeaD, undeaD). - * - * See if character c is in the pattern. - * Patterns: - * - * A $(I pattern) is an array of characters much like a $(I character - * class) in regular expressions. A sequence of characters - * can be given, such as "abcde". The '-' can represent a range - * of characters, as "a-e" represents the same pattern as "abcde". - * "a-fA-F0-9" represents all the hex characters. - * If the first character of a pattern is '^', then the pattern - * is negated, i.e. "^0-9" means any character except a digit. - * The functions inPattern, $(B countchars), $(B removeschars), - * and $(B squeeze) use patterns. - * - * Note: In the future, the pattern syntax may be improved - * to be more like regular expression character classes. - */ -deprecated("This function is obsolete and will be removed May 2018. See the docs for more details") +//@@@DEPRECATED_2.086@@@ +deprecated("This function is obsolete. It is available in https://github.com/dlang/undeaD if necessary.") bool inPattern(S)(dchar c, in S pattern) @safe pure @nogc if (isSomeString!S) { @@ -5619,16 +5597,8 @@ deprecated }); } -//@@@DEPRECATED_2018-05@@@ -/*********************************************** - * $(RED This function is deprecated and will be removed May 2018.) - * Please use the functions in $(MREF std, regex) and $(MREF std, algorithm) - * instead. If you still need this function, it will be available in - * $(LINK2 https://github.com/dlang/undeaD, undeaD). - * - * See if character c is in the intersection of the patterns. - */ -deprecated("This function is obsolete and will be removed May 2018. See the docs for more details") +//@@@DEPRECATED_2.086@@@ +deprecated("This function is obsolete. It is available in https://github.com/dlang/undeaD if necessary.") bool inPattern(S)(dchar c, S[] patterns) @safe pure @nogc if (isSomeString!S) { @@ -5642,16 +5612,8 @@ if (isSomeString!S) return true; } -//@@@DEPRECATED_2018-05@@@ -/******************************************** - * $(RED This function is deprecated and will be removed May 2018.) - * Please use the functions in $(MREF std, regex) and $(MREF std, algorithm) - * instead. If you still need this function, it will be available in - * $(LINK2 https://github.com/dlang/undeaD, undeaD). - * - * Count characters in s that match pattern. - */ -deprecated("This function is obsolete and will be removed May 2018. See the docs for more details") +//@@@DEPRECATED_2.086@@@ +deprecated("This function is obsolete. It is available in https://github.com/dlang/undeaD if necessary.") size_t countchars(S, S1)(S s, in S1 pattern) @safe pure @nogc if (isSomeString!S && isSomeString!S1) { @@ -5676,16 +5638,8 @@ deprecated }); } -//@@@DEPRECATED_2018-05@@@ -/******************************************** - * $(RED This function is deprecated and will be removed May 2018.) - * Please use the functions in $(MREF std, regex) and $(MREF std, algorithm) - * instead. If you still need this function, it will be available in - * $(LINK2 https://github.com/dlang/undeaD, undeaD). - * - * Return string that is s with all characters removed that match pattern. - */ -deprecated("This function is obsolete and will be removed May 2018. See the docs for more details") +//@@@DEPRECATED_2.086@@@ +deprecated("This function is obsolete. It is available in https://github.com/dlang/undeaD if necessary.") S removechars(S)(S s, in S pattern) @safe pure if (isSomeString!S) { @@ -5737,18 +5691,8 @@ deprecated assert(removechars("abc", "x") == "abc"); } -//@@@DEPRECATED_2018-05@@@ -/*************************************************** - * $(RED This function is deprecated and will be removed May 2018.) - * Please use the functions in $(MREF std, regex) and $(MREF std, algorithm) - * instead. If you still need this function, it will be available in - * $(LINK2 https://github.com/dlang/undeaD, undeaD). - * - * Return string where sequences of a character in s[] from pattern[] - * are replaced with a single instance of that character. - * If pattern is null, it defaults to all characters. - */ -deprecated("This function is obsolete and will be removed May 2018. See the docs for more details") +//@@@DEPRECATED_2.086@@@ +deprecated("This function is obsolete. It is available in https://github.com/dlang/undeaD if necessary.") S squeeze(S)(S s, in S pattern = null) { import std.utf : encode, stride; @@ -5813,24 +5757,8 @@ deprecated }); } -//@@@DEPRECATED_2018-05@@@ -/*************************************************************** - $(RED This function is deprecated and will be removed May 2018.) - Please use the functions in $(MREF std, regex) and $(MREF std, algorithm) - instead. If you still need this function, it will be available in - $(LINK2 https://github.com/dlang/undeaD, undeaD). - - Finds the position $(D_PARAM pos) of the first character in $(D_PARAM - s) that does not match $(D_PARAM pattern) (in the terminology used by - $(REF inPattern, std,string)). Updates $(D_PARAM s = - s[pos..$]). Returns the slice from the beginning of the original - (before update) string up to, and excluding, $(D_PARAM pos). - -The $(D_PARAM munch) function is mostly convenient for skipping -certain category of characters (e.g. whitespace) when parsing -strings. (In such cases, the return value is not used.) - */ -deprecated("This function is obsolete and will be removed May 2018. See the docs for more details") +//@@@DEPRECATED_2.086@@@ +deprecated("This function is obsolete. It is available in https://github.com/dlang/undeaD if necessary.") S1 munch(S1, S2)(ref S1 s, S2 pattern) @safe pure @nogc { size_t j = s.length; diff --git a/libphobos/src/std/typecons.d b/libphobos/src/std/typecons.d index 203ab05f4..291a0d417 100644 --- a/libphobos/src/std/typecons.d +++ b/libphobos/src/std/typecons.d @@ -68,7 +68,6 @@ Authors: $(HTTP erdani.org, Andrei Alexandrescu), */ module std.typecons; -import core.stdc.stdint : uintptr_t; import std.format : singleSpec, FormatSpec, formatValue; import std.meta; // : AliasSeq, allSatisfy; import std.range.primitives : isOutputRange; @@ -2570,25 +2569,13 @@ Practically `Nullable!T` stores a `T` and a `bool`. */ struct Nullable(T) { - // simple case: type is freely constructable - static if (__traits(compiles, { T _value; })) + private union DontCallDestructorT { - private T _value; - } - // type is not constructable, but also has no way to notice - // that we're assigning to an uninitialized variable. - else static if (!hasElaborateAssign!T) - { - private T _value = T.init; - } - else - { - static assert(false, - "Cannot construct " ~ typeof(this).stringof ~ - ": type has no default constructor and overloaded assignment." - ); + T payload; } + private DontCallDestructorT _value = DontCallDestructorT.init; + private bool _isNull = true; /** @@ -2599,10 +2586,21 @@ Params: */ this(inout T value) inout { - _value = value; + _value.payload = value; _isNull = false; } + static if (is(T == struct) && hasElaborateDestructor!T) + { + ~this() + { + if (!_isNull) + { + destroy(_value.payload); + } + } + } + /** If they are both null, then they are equal. If one is null and the other is not, then they are not equal. If they are both non-null, then they are @@ -2614,14 +2612,14 @@ Params: return rhs._isNull; if (rhs._isNull) return false; - return _value == rhs._value; + return _value.payload == rhs._value.payload; } /// Ditto bool opEquals(U)(auto ref const(U) rhs) const if (is(typeof(this.get == rhs))) { - return _isNull ? false : rhs == _value; + return _isNull ? false : rhs == _value.payload; } /// @@ -2707,7 +2705,7 @@ Params: if (isNull) put(writer, "Nullable.null"); else - formatValue(writer, _value, fmt); + formatValue(writer, _value.payload, fmt); } //@@@DEPRECATED_2.086@@@ @@ -2720,7 +2718,7 @@ Params: } else { - sink.formatValue(_value, fmt); + sink.formatValue(_value.payload, fmt); } } @@ -2735,7 +2733,7 @@ Params: } else { - sink.formatValue(_value, fmt); + sink.formatValue(_value.payload, fmt); } } @@ -2778,9 +2776,9 @@ Forces `this` to the null state. void nullify()() { static if (is(T == class) || is(T == interface)) - _value = null; + _value.payload = null; else - .destroy(_value); + .destroy(_value.payload); _isNull = true; } @@ -2803,7 +2801,21 @@ Params: */ void opAssign()(T value) { - _value = value; + import std.algorithm.mutation : moveEmplace, move; + + // the lifetime of the value in copy shall be managed by + // this Nullable, so we must avoid calling its destructor. + auto copy = DontCallDestructorT(value); + + if (_isNull) + { + // trusted since payload is known to be T.init here. + () @trusted { moveEmplace(copy.payload, _value.payload); }(); + } + else + { + move(copy.payload, _value.payload); + } _isNull = false; } @@ -2843,13 +2855,13 @@ Returns: { enum message = "Called `get' on null Nullable!" ~ T.stringof ~ "."; assert(!isNull, message); - return _value; + return _value.payload; } /// ditto @property get(U)(inout(U) fallback) inout @safe pure nothrow { - return isNull ? fallback : _value; + return isNull ? fallback : _value.payload; } /// @@ -3275,6 +3287,102 @@ auto nullable(T)(T t) assert(foo.approxEqual(bar)); } +// bugzilla issue 19037 +@safe unittest +{ + import std.datetime : SysTime; + + struct Test + { + bool b; + + nothrow invariant { assert(b == true); } + + SysTime _st; + + static bool destroyed; + + @disable this(); + this(bool b) { this.b = b; } + ~this() @safe { destroyed = true; } + + // mustn't call opAssign on Test.init in Nullable!Test, because the invariant + // will be called before opAssign on the Test.init that is in Nullable + // and Test.init violates its invariant. + void opAssign(Test rhs) @safe { assert(false); } + } + + { + Nullable!Test nt; + + nt = Test(true); + + // destroy value + Test.destroyed = false; + + nt.nullify; + + assert(Test.destroyed); + + Test.destroyed = false; + } + // don't run destructor on T.init in Nullable on scope exit! + assert(!Test.destroyed); +} +// check that the contained type's destructor is called on assignment +@system unittest +{ + struct S + { + // can't be static, since we need a specific value's pointer + bool* destroyedRef; + + ~this() + { + if (this.destroyedRef) + { + *this.destroyedRef = true; + } + } + } + + Nullable!S ns; + + bool destroyed; + + ns = S(&destroyed); + + // reset from rvalue destruction in Nullable's opAssign + destroyed = false; + + // overwrite Nullable + ns = S(null); + + // the original S should be destroyed. + assert(destroyed == true); +} +// check that the contained type's destructor is still called when required +@system unittest +{ + bool destructorCalled = false; + + struct S + { + bool* destroyed; + ~this() { *this.destroyed = true; } + } + + { + Nullable!S ns; + } + assert(!destructorCalled); + { + Nullable!S ns = Nullable!S(S(&destructorCalled)); + + destructorCalled = false; // reset after S was destroyed in the NS constructor + } + assert(destructorCalled); +} /** Just like `Nullable!T`, except that the null state is defined as a @@ -3407,7 +3515,9 @@ Params: */ void opAssign()(T value) { - _value = value; + import std.algorithm.mutation : swap; + + swap(value, _value); } /** @@ -7177,7 +7287,7 @@ struct Typedef(T, T init = T.init, string cookie=null) /** * Convert wrapped value to a human readable string */ - string toString() + string toString(this T)() { import std.array : appender; auto app = appender!string(); @@ -7187,7 +7297,7 @@ struct Typedef(T, T init = T.init, string cookie=null) } /// ditto - void toString(W)(ref W writer, const ref FormatSpec!char fmt) + void toString(this T, W)(ref W writer, const ref FormatSpec!char fmt) if (isOutputRange!(W, char)) { formatValue(writer, Typedef_payload, fmt); @@ -7486,8 +7596,18 @@ template TypedefType(T) int*, int[], int[2], int[int])) {{ T t; + Typedef!T td; + Typedef!(const T) ctd; + Typedef!(immutable T) itd; + assert(t.to!string() == td.to!string()); + + static if (!(is(T == TestS) || is(T == TestC))) + { + assert(t.to!string() == ctd.to!string()); + assert(t.to!string() == itd.to!string()); + } }} } @@ -7524,7 +7644,7 @@ if (is(T == class)) @property inout(T) Scoped_payload() inout { - void* alignedStore = cast(void*) aligned(cast(uintptr_t) Scoped_store.ptr); + void* alignedStore = cast(void*) aligned(cast(size_t) Scoped_store.ptr); // As `Scoped` can be unaligned moved in memory class instance should be moved accordingly. immutable size_t d = alignedStore - Scoped_store.ptr; size_t* currD = cast(size_t*) &Scoped_store[$ - size_t.sizeof]; @@ -7557,7 +7677,7 @@ if (is(T == class)) import std.conv : emplace; Scoped result = void; - void* alignedStore = cast(void*) aligned(cast(uintptr_t) result.Scoped_store.ptr); + void* alignedStore = cast(void*) aligned(cast(size_t) result.Scoped_store.ptr); immutable size_t d = alignedStore - result.Scoped_store.ptr; *cast(size_t*) &result.Scoped_store[$ - size_t.sizeof] = d; emplace!(Unqual!T)(result.Scoped_store[d .. $ - size_t.sizeof], args); @@ -7647,7 +7767,7 @@ if (is(T == class)) destroy(*b2); // calls A's destructor for b2.a } -private uintptr_t _alignUp(uintptr_t alignment)(uintptr_t n) +private size_t _alignUp(size_t alignment)(size_t n) if (alignment > 0 && !((alignment - 1) & alignment)) { enum badEnd = alignment - 1; // 0b11, 0b111, ... diff --git a/libphobos/src/std/uni.d b/libphobos/src/std/uni.d index 9aafefe0d..82742cbc0 100644 --- a/libphobos/src/std/uni.d +++ b/libphobos/src/std/uni.d @@ -241,8 +241,7 @@ $(TR $(TD Building blocks) $(TD assert(normalize!NFKD("2¹⁰") == "210"); } --- - $(SECTION Terminology - ) + $(SECTION Terminology) $(P The following is a list of important Unicode notions and definitions. Any conventions used specifically in this module alone are marked as such. The descriptions are based on the formal @@ -383,8 +382,7 @@ $(TR $(TD Building blocks) $(TD ) $(P $(DEF Spacing mark) A combining character that is not a nonspacing mark. ) - $(SECTION Normalization - ) + $(SECTION Normalization) $(P The concepts of $(S_LINK Canonical equivalent, canonical equivalent) or $(S_LINK Compatibility equivalent, compatibility equivalent) characters in the Unicode Standard make it necessary to have a full, formal @@ -424,8 +422,7 @@ $(TR $(TD Building blocks) $(TD identifiers, especially where there are security concerns. NFD and NFKD are the most useful for internal processing. ) - $(SECTION Construction of lookup tables - ) + $(SECTION Construction of lookup tables) $(P The Unicode standard describes a set of algorithms that depend on having the ability to quickly look up various properties of a code point. Given the the codespace of about 1 million $(CODEPOINTS), @@ -487,8 +484,7 @@ $(TR $(TD Building blocks) $(TD can be turned into a trie. The trie object in this module is read-only (immutable); it's effectively frozen after construction. ) - $(SECTION Unicode properties - ) + $(SECTION Unicode properties) $(P This is a full list of Unicode properties accessible through $(LREF unicode) with specific helpers per category nested within. Consult the $(HTTP www.unicode.org/cldr/utility/properties.jsp, CLDR utility) @@ -2174,20 +2170,23 @@ public struct InversionList(SP=GcPolicy) /** Get range that spans all of the $(CODEPOINT) intervals in this $(LREF InversionList). + */ + @property auto byInterval() scope + { + // TODO: change this to data[] once the -dip1000 errors have been fixed + // see e.g. https://github.com/dlang/phobos/pull/6638 + import std.array : array; + return Intervals!(typeof(data.array))(data.array); + } - Example: - ----------- + @safe unittest + { import std.algorithm.comparison : equal; import std.typecons : tuple; auto set = CodepointSet('A', 'D'+1, 'a', 'd'+1); assert(set.byInterval.equal([tuple('A','E'), tuple('a','e')])); - ----------- - */ - @property auto byInterval() scope - { - return Intervals!(typeof(data))(data); } package @property const(CodepointInterval)[] intervals() const @@ -9015,8 +9014,8 @@ if (isSomeString!S || (isRandomAccessRange!S && hasLength!S && hasSlicing!S && i if (idx == ushort.max) continue; auto result = appender!(C[])(); - result.put(s[0 .. i]); result.reserve(s.length); + result.put(s[0 .. i]); foreach (dchar c; s[i .. $].byDchar) { if (c.isASCII) diff --git a/libphobos/src/std/uri.d b/libphobos/src/std/uri.d index 86b2bdf7b..72dc2404f 100644 --- a/libphobos/src/std/uri.d +++ b/libphobos/src/std/uri.d @@ -407,7 +407,8 @@ package string urlEncode(scope string[string] values) @safe pure string[string] a; assert(urlEncode(a) == ""); assert(urlEncode(["name1" : "value1"]) == "name1=value1"); - assert(urlEncode(["name1" : "value1", "name2" : "value2"]) == "name1=value1&name2=value2"); + auto enc = urlEncode(["name1" : "value1", "name2" : "value2"]); + assert(enc == "name1=value1&name2=value2" || enc == "name2=value2&name1=value1"); } /*************************** diff --git a/libphobos/src/std/uuid.d b/libphobos/src/std/uuid.d index c34b0a236..825e53dfe 100644 --- a/libphobos/src/std/uuid.d +++ b/libphobos/src/std/uuid.d @@ -880,13 +880,15 @@ public struct UUID const uint lo = (entry) & 0x0F; result[pos+1] = toChar!char(lo); } - foreach (i, c; result) + static if (!__traits(compiles, put(sink, result[])) || isSomeString!Writer) { - static if (__traits(compiles, put(sink, c))) - put(sink, c); - else + foreach (i, c; result) sink[i] = cast(typeof(sink[i]))c; } + else + { + put(sink, result[]); + } } /** @@ -1208,7 +1210,25 @@ public struct UUID @safe UUID randomUUID() { import std.random : rndGen; - return randomUUID(rndGen); + // A PRNG with fewer than `n` bytes of state cannot produce + // every distinct `n` byte sequence. + static if (typeof(rndGen).sizeof >= UUID.sizeof) + { + return randomUUID(rndGen); + } + else + { + import std.random : unpredictableSeed, Xorshift192; + static assert(Xorshift192.sizeof >= UUID.sizeof); + static Xorshift192 rng; + static bool initialized; + if (!initialized) + { + rng.seed(unpredictableSeed); + initialized = true; + } + return randomUUID(rng); + } } /// ditto @@ -1258,18 +1278,6 @@ if (isInputRange!RNG && isIntegral!(ElementType!RNG)) auto uuid3 = randomUUID(gen); } -/* - * Original boost.uuid used Mt19937, we don't want - * to use anything worse than that. If Random is changed - * to something else, this assert and the randomUUID function - * have to be updated. - */ -@safe unittest -{ - import std.random : rndGen, Mt19937; - static assert(is(typeof(rndGen) == Mt19937)); -} - @safe unittest { import std.random : Xorshift192, unpredictableSeed; diff --git a/libphobos/src/std/variant.d b/libphobos/src/std/variant.d index db98e1eb4..c7a1a0dbb 100644 --- a/libphobos/src/std/variant.d +++ b/libphobos/src/std/variant.d @@ -13,8 +13,8 @@ languages, and comfortable exploratory programming. A $(LREF Variant) object can hold a value of any type, with very few restrictions (such as `shared` types and noncopyable types). Setting the value is as immediate as assigning to the `Variant` object. To read back the value of -the appropriate type `T`, use the $(LREF get!T) call. To query whether a -`Variant` currently holds a value of type `T`, use $(LREF peek!T). To fetch the +the appropriate type `T`, use the $(LREF get) method. To query whether a +`Variant` currently holds a value of type `T`, use $(LREF peek). To fetch the exact type currently held, call $(LREF type), which returns the `TypeInfo` of the current value.