From: Iain Buclaw Date: Fri, 30 Jan 2026 16:30:33 +0000 (+0100) Subject: d: Merge upstream dmd, druntime e1f6680f50, phobos f87979028 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8b0ccc95f1a70415d50bb87397cf0b1761be1e40;p=thirdparty%2Fgcc.git d: Merge upstream dmd, druntime e1f6680f50, phobos f87979028 D front-end changes: - Import latest fixes from v2.112.0-beta.1. - Associative array operations are now lowered to templates defined in `core.internal.newaa'. D runtime changes: - Import latest fixes from v2.112.0-beta.1. Phobos changes: - Import latest fixes from v2.112.0-beta.1. gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd e1f6680f50. * Make-lang.in (D_FRONTEND_OBJS): Remove d/gluelayer.o, d/iasm.o, d/iasmgcc.o. Add d/iasm-package.o, d/iasm-gcc.o. (d/iasm-%.o): New recipe. * d-convert.cc (convert_expr): Remove library call to dynamic_cast. * d-lang.cc (d_init_options): Set errorSupplementLimit parameter. * decl.cc (DeclVisitor::visit): Update for new front-end interface. * expr.cc (ExprVisitor::visit (EqualExp *)): Update for front-end lowering expression using templates. (ExprVisitor::visit (InExp *)): Likewise. (ExprVisitor::visit (IndexExp *)): Likewise. (ExprVisitor::visit (CastExp *)): Likewise. (ExprVisitor::visit (RemoveExp *)): Likewise. (ExprVisitor::visit (AddrExp *)): Likewise. (ExprVisitor::visit (NewExp *)): Likewise. (ExprVisitor::visit (ArrayLiteralExp *)): Likewise. (ExprVisitor::visit (AssocArrayLiteralExp *)): Likewise. * imports.cc (ImportVisitor::visit (AliasDeclaration *)): Update for new front-end interface. * runtime.def (DYNAMIC_CAST): Remove. (INTERFACE_CAST): Remove. (ARRAYLITERALTX): Remove. (ADEQ2): Remove. (ASSOCARRAYLITERALTX): Remove. (AANEW): Remove. (AAEQUAL): Remove. (AAINX): Remove. (AAGETY): Remove. (AAGETRVALUEX): Remove. (AADELX): Remove. * typeinfo.cc (TypeInfoVisitor::visit): Layout xopEquals and xtoHash in TypeInfo_AssociativeArray. (create_typeinfo): Add new fields to internal typeinfo. (check_typeinfo_type): Print instantiation trace of error. * types.cc (TypeVisitor::visit (TypeStruct *)): Update for new front-end interface. libphobos/ChangeLog: * libdruntime/MERGE: Merge upstream druntime e1f6680f50. * libdruntime/Makefile.am (DRUNTIME_DSOURCES): Add core/internal/cast_.d. Remove rt/aaA.d, rt/adi.d, rt/cast_.d. (DRUNTIME_DSOURCES_POSIX): Add core/sys/posix/endian.d. * libdruntime/Makefile.in: Regenerate. * src/MERGE: Merge upstream phobos f87979028. * src/Makefile.am (PHOBOS_DSOURCES): Add std/internal/entropy.d. * src/Makefile.in: Regenerate. * testsuite/libphobos.aa/test_aa.d: Adjust test. * testsuite/libphobos.init_fini/custom_gc.d: Likewise. gcc/testsuite/ChangeLog: * gdc.dg/asm1.d: Adjust test. * gdc.dg/asm5.d: Likewise. * gdc.dg/pr100967.d: Likewise. * gdc.dg/rtti1.d: Likewise. * gdc.dg/rtti2.d: New test. --- diff --git a/gcc/d/Make-lang.in b/gcc/d/Make-lang.in index 9f048bb4e5d..9f91346290c 100644 --- a/gcc/d/Make-lang.in +++ b/gcc/d/Make-lang.in @@ -135,10 +135,9 @@ D_FRONTEND_OBJS = \ d/func.o \ d/funcsem.o \ d/globals.o \ - d/gluelayer.o \ d/hdrgen.o \ - d/iasm.o \ - d/iasmgcc.o \ + d/iasm-package.o \ + d/iasm-gcc.o \ d/id.o \ d/identifier.o \ d/impcnvtab.o \ @@ -420,6 +419,10 @@ d/common-%.o: d/dmd/common/%.d $(DCOMPILE) $(D_INCLUDES) $< $(DPOSTCOMPILE) +d/iasm-%.o: d/dmd/iasm/%.d + $(DCOMPILE) $(D_INCLUDES) $< + $(DPOSTCOMPILE) + d/mangle-%.o: d/dmd/mangle/%.d $(DCOMPILE) $(D_INCLUDES) $< $(DPOSTCOMPILE) diff --git a/gcc/d/d-convert.cc b/gcc/d/d-convert.cc index 81a1b3cd3cf..30874e72c0d 100644 --- a/gcc/d/d-convert.cc +++ b/gcc/d/d-convert.cc @@ -448,13 +448,6 @@ convert_expr (tree exp, Type *etype, Type *totype) break; } - - /* The offset can only be determined at run-time, do dynamic cast. */ - libcall_fn libcall = cdfrom->isInterfaceDeclaration () - ? LIBCALL_INTERFACE_CAST : LIBCALL_DYNAMIC_CAST; - - return build_libcall (libcall, totype, 2, exp, - build_address (get_classinfo_decl (cdto))); } /* else default conversion. */ break; diff --git a/gcc/d/d-lang.cc b/gcc/d/d-lang.cc index 4dc8eede74a..eed1ee97fe9 100644 --- a/gcc/d/d-lang.cc +++ b/gcc/d/d-lang.cc @@ -304,6 +304,7 @@ d_init_options (unsigned int, cl_decoded_option *decoded_options) global.params.useDeprecated = DIAGNOSTICinform; global.params.useWarnings = DIAGNOSTICoff; global.params.v.errorLimit = flag_max_errors; + global.params.v.errorSupplementLimit = flag_max_errors; global.params.v.messageStyle = MessageStyle::gnu; /* Extra GDC-specific options. */ diff --git a/gcc/d/decl.cc b/gcc/d/decl.cc index 0b915b5632a..8e740aedc99 100644 --- a/gcc/d/decl.cc +++ b/gcc/d/decl.cc @@ -449,7 +449,7 @@ public: if (dmd::isError (d)|| !d->members) return; - if (!d->needsCodegen ()) + if (!dmd::needsCodegen (d)) return; for (size_t i = 0; i < d->members->length; i++) @@ -766,7 +766,7 @@ public: Expression *ie = dmd::initializerToExpression (d->_init); add_stmt (build_expr (ie)); - Expression *e = d->type->defaultInitLiteral (d->loc); + Expression *e = dmd::defaultInitLiteral (d->type, d->loc); add_stmt (build_expr (e)); } @@ -775,7 +775,7 @@ public: if (d->aliasTuple) { - this->build_dsymbol (d->toAlias ()); + this->build_dsymbol (dmd::toAlias (d)); return; } @@ -834,7 +834,7 @@ public: DECL_INITIAL (decl) = layout_struct_initializer (ts->sym); else { - Expression *e = d->type->defaultInitLiteral (d->loc); + Expression *e = dmd::defaultInitLiteral (d->type, d->loc); DECL_INITIAL (decl) = build_expr (e, true); } } diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE index 62d90e5770b..a9235246e93 100644 --- a/gcc/d/dmd/MERGE +++ b/gcc/d/dmd/MERGE @@ -1,4 +1,4 @@ -1017635a9636ef6a7a4bda35b1e091875d829871 +e1f6680f50d147846316c2fa3363461a2aa7ac1d The first line of this file holds the git revision number of the last merge done from the dlang/dmd repository. diff --git a/gcc/d/dmd/README.md b/gcc/d/dmd/README.md index 2e93d26fe85..f10b162d274 100644 --- a/gcc/d/dmd/README.md +++ b/gcc/d/dmd/README.md @@ -35,6 +35,7 @@ Note that these groups have no strict meaning, the category assignments are a bi | [mars.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mars.d) | Argument parsing, path manipulation. | | [cli.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cli.d) | Define the command line interface | | [dmdparams.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dmdparams.d) | DMD-specific parameters | +| [dmsc.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dmsc.d) | Configures and initializes the back-end | | [globals.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/globals.d) | Define a structure storing command line options | | [dinifile.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dinifile.d) | Parse settings from .ini file (`sc.ini` / `dmd.conf`) | | [vsoptions.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/vsoptions.d) | Detect the Microsoft Visual Studio toolchain for linking | @@ -170,9 +171,10 @@ Note that these groups have no strict meaning, the category assignments are a bi | File | Purpose | |-------------------------------------------------------------------------|-------------------------------------------| -| [iasm.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasm.d) | Inline assembly depending on the compiler | -| [iasmdmd.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasmdmd.d) | Inline assembly for DMD | -| [iasmgcc.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasmgcc.d) | Inline assembly for GDC | +| [iasm/package.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasm/package.d) | Inline assembly depending on the compiler | +| [iasm/dmdx86.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasm/dmd.d) | Inline assembly for DMD X86_64 | +| [iasm/dmdaarch64.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasm/aarch64.d) | Inline assembly for DMD AArch64 | +| [iasm/gcc.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasm/gcc.d) | Inline assembly for GDC | **Other** @@ -208,27 +210,28 @@ Note that these groups have no strict meaning, the category assignments are a bi | [lib/scanmach.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/scanmach.d) | Extract symbol names from a library in Mach-O format | | [lib/scanmscoff.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/scanmscoff.d) | Extract symbol names from a library in COFF format | + +### ABI +| File | Purpose | +| [argtypes_x86.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/argtypes_x86.d) | Convert a D type into simple (register) types for the 32-bit x86 ABI | +| [argtypes_sysv_x64.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/argtypes_sysv_x64.d) | 'argtypes' for the x86_64 System V ABI | +| [argtypes_aarch64.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/argtypes_aarch64.d) | 'argtypes' for the AArch64 ABI | ### Code generation / back-end interfacing | File | Purpose | |---------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------| -| [dmsc.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dmsc.d) | Configures and initializes the back-end | -| [toobj.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/toobj.d) | Convert an AST that went through all semantic phases into an object file | -| [toir.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/toir.d) | Convert Dsymbols intermediate representation | -| [e2ir.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/e2ir.d) | Convert Expressions to intermediate representation | -| [s2ir.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/s2ir.d) | Convert Statements to intermediate representation | -| [stmtstate.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/stmtstate.d) | Used to help transform statement AST into flow graph | -| [toctype.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/toctype.d) | Convert a D type to a type the back-end understands | -| [tocsym.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/tocsym.d) | Convert a D symbol to a symbol the linker understands (with mangled name) | -| [argtypes_x86.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/argtypes_x86.d) | Convert a D type into simple (register) types for the 32-bit x86 ABI | -| [argtypes_sysv_x64.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/argtypes_sysv_x64.d) | 'argtypes' for the x86_64 System V ABI | -| [argtypes_aarch64.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/argtypes_aarch64.d) | 'argtypes' for the AArch64 ABI | -| [glue.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/glue.d) | Generate the object file for function declarations | -| [gluelayer.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/gluelayer.d) | Declarations for back-end functions that the front-end invokes | -| [todt.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/todt.d) | Convert initializers into structures that the back-end will add to the data segment | -| [tocvdebug.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/tovcdebug.d) | Generate debug info in the CV4 debug format. | -| [objc.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/objc.d) | Objective-C interfacing | -| [objc_glue.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/objc_glue.d) | Glue code for Objective-C interop. | +| [stmtstate.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/stmtstate.d) | Used to help transform statement AST into flow graph | +| [objc.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/objc.d) | Objective-C interfacing | +| [irgen/toobj.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/toobj.d) | Convert an AST that went through all semantic phases into an object file| +| [irgen/toir.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/toir.d) | Convert Dsymbols intermediate representation | +| [irgen/e2ir.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/e2ir.d) | Convert Expressions to intermediate representation | +| [irgen/s2ir.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/s2ir.d) | Convert Statements to intermediate representation | +| [irgen/toctype.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/toctype.d) | Convert a D type to a type the back-end understands | +| [irgen/tocsym.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/tocsym.d) | Convert a D symbol to a symbol the linker understands (with mangled name) | +| [irgen/package.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/package.d) | Generate the object file for function declarations | +| [irgen/todt.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/todt.d) | Convert initializers into structures that the back-end will add to the data segment | +| [irgen/tocvdebug.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/tovcdebug.d)| Generate debug info in the CV4 debug format. | +| [irgen/objc.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/objc.d)| irgen code for Objective-C interop. | **Name mangling** diff --git a/gcc/d/dmd/access.d b/gcc/d/dmd/access.d index eaaa288cb4a..1871f601b6e 100644 --- a/gcc/d/dmd/access.d +++ b/gcc/d/dmd/access.d @@ -20,6 +20,7 @@ import dmd.dmodule; import dmd.dscope; import dmd.dstruct; import dmd.dsymbol; +import dmd.dsymbolsem : toAlias; import dmd.errors; import dmd.expression; import dmd.funcsem : overloadApply; diff --git a/gcc/d/dmd/aggregate.d b/gcc/d/dmd/aggregate.d index 6359674d447..d547bb78626 100644 --- a/gcc/d/dmd/aggregate.d +++ b/gcc/d/dmd/aggregate.d @@ -25,19 +25,17 @@ import dmd.declaration; import dmd.dscope; import dmd.dstruct; import dmd.dsymbol; -import dmd.dsymbolsem : dsymbolSemantic, determineFields, search, determineSize, include; +import dmd.dsymbolsem : determineSize; import dmd.dtemplate; import dmd.errors; import dmd.expression; import dmd.func; -import dmd.globals; import dmd.hdrgen; import dmd.id; import dmd.identifier; import dmd.location; import dmd.mtype; import dmd.tokens; -import dmd.typesem : defaultInit, addMod, size; import dmd.visitor; /** @@ -171,7 +169,7 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol sc2.parent = this; sc2.inunion = isUnionDeclaration(); sc2.visibility = Visibility(Visibility.Kind.public_); - sc2.explicitVisibility = 0; + sc2.explicitVisibility = false; sc2.aligndecl = null; sc2.userAttribDecl = null; sc2.namespace = null; @@ -187,7 +185,7 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol return fields.length - isNested() - (vthis2 !is null); } - override final uinteger_t size(Loc loc) + override final ulong size(Loc loc) { //printf("+AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok); bool ok = determineSize(this, loc); @@ -195,86 +193,6 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol return ok ? structsize : SIZE_INVALID; } - /*************************************** - * Calculate field[i].overlapped and overlapUnsafe, and check that all of explicit - * field initializers have unique memory space on instance. - * Returns: - * true if any errors happen. - */ - extern (D) final bool checkOverlappedFields() - { - //printf("AggregateDeclaration::checkOverlappedFields() %s\n", toChars()); - assert(sizeok == Sizeok.done); - size_t nfields = fields.length; - if (isNested()) - { - auto cd = isClassDeclaration(); - if (!cd || !cd.baseClass || !cd.baseClass.isNested()) - nfields--; - if (vthis2 && !(cd && cd.baseClass && cd.baseClass.vthis2)) - nfields--; - } - bool errors = false; - - // Fill in missing any elements with default initializers - foreach (i; 0 .. nfields) - { - auto vd = fields[i]; - if (vd.errors) - { - errors = true; - continue; - } - - const vdIsVoidInit = vd._init && vd._init.isVoidInitializer(); - - // Find overlapped fields with the hole [vd.offset .. vd.offset.size()]. - foreach (j; 0 .. nfields) - { - if (i == j) - continue; - auto v2 = fields[j]; - if (v2.errors) - { - errors = true; - continue; - } - if (!vd.isOverlappedWith(v2)) - continue; - - // vd and v2 are overlapping. - vd.overlapped = true; - v2.overlapped = true; - - if (!MODimplicitConv(vd.type.mod, v2.type.mod)) - v2.overlapUnsafe = true; - if (!MODimplicitConv(v2.type.mod, vd.type.mod)) - vd.overlapUnsafe = true; - - if (i > j) - continue; - - if (!v2._init) - continue; - - if (v2._init.isVoidInitializer()) - continue; - - if (vd._init && !vdIsVoidInit && v2._init) - { - .error(loc, "overlapping default initialization for field `%s` and `%s`", v2.toChars(), vd.toChars()); - errors = true; - } - else if (v2._init && i < j) - { - .error(v2.loc, "union field `%s` with default initialization `%s` must be before field `%s`", - v2.toChars(), dmd.hdrgen.toChars(v2._init), vd.toChars()); - errors = true; - } - } - } - return errors; - } override final Type getType() { @@ -433,46 +351,6 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol return visibility.kind == Visibility.Kind.export_; } - /******************************************* - * Look for constructor declaration. - */ - extern (D) final Dsymbol searchCtor() - { - auto s = this.search(Loc.initial, Id.ctor); - if (s) - { - if (!(s.isCtorDeclaration() || - s.isTemplateDeclaration() || - s.isOverloadSet())) - { - error(s.loc, "%s name `__ctor` is not allowed", s.kind); - errorSupplemental(s.loc, "identifiers starting with `__` are reserved for internal use"); - errors = true; - s = null; - } - } - if (s && s.toParent() != this) - s = null; // search() looks through ancestor classes - if (s) - { - // Finish all constructors semantics to determine this.noDefaultCtor. - static int searchCtor(Dsymbol s, void*) - { - auto f = s.isCtorDeclaration(); - if (f && f.semanticRun == PASS.initial) - f.dsymbolSemantic(null); - return 0; - } - - for (size_t i = 0; i < members.length; i++) - { - auto sm = (*members)[i]; - sm.apply(&searchCtor, null); - } - } - return s; - } - override final Visibility visible() pure nothrow @nogc @safe { return visibility; @@ -499,39 +377,6 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol } } -/********************************* - * Iterate this dsymbol or members of this scoped dsymbol, then - * call `fp` with the found symbol and `params`. - * Params: - * symbol = the dsymbol or parent of members to call fp on - * fp = function pointer to process the iterated symbol. - * If it returns nonzero, the iteration will be aborted. - * ctx = context parameter passed to fp. - * Returns: - * nonzero if the iteration is aborted by the return value of fp, - * or 0 if it's completed. - */ -int apply(Dsymbol symbol, int function(Dsymbol, void*) fp, void* ctx) -{ - if (auto nd = symbol.isNspace()) - { - return nd.members.foreachDsymbol( (s) { return s && s.apply(fp, ctx); } ); - } - if (auto ad = symbol.isAttribDeclaration()) - { - return ad.include(ad._scope).foreachDsymbol( (s) { return s && s.apply(fp, ctx); } ); - } - if (auto tm = symbol.isTemplateMixin()) - { - if (tm._scope) // if fwd reference - dsymbolSemantic(tm, null); // try to resolve it - - return tm.members.foreachDsymbol( (s) { return s && s.apply(fp, ctx); } ); - } - - return fp(symbol, ctx); -} - /**************************** * Do byte or word alignment as necessary. * Align sizes of 0, as we may not know array sizes yet. diff --git a/gcc/d/dmd/aggregate.h b/gcc/d/dmd/aggregate.h index d89755cea27..3c4c84a0939 100644 --- a/gcc/d/dmd/aggregate.h +++ b/gcc/d/dmd/aggregate.h @@ -171,7 +171,6 @@ public: static StructDeclaration *create(Loc loc, Identifier *id, bool inObject); StructDeclaration *syntaxCopy(Dsymbol *s) override; const char *kind() const override; - bool isPOD(); bool zeroInit() const; // !=0 if initialize with 0 fill bool zeroInit(bool v); bool hasIdentityAssign() const; // true if has identity opAssign @@ -194,7 +193,6 @@ public: unsigned numArgTypes() const; Type *argType(unsigned index); - bool hasRegularCtor(bool ignoreDisabled = false); }; class UnionDeclaration final : public StructDeclaration @@ -273,7 +271,6 @@ public: ThreeState isabstract; // if abstract class Baseok baseok; // set the progress of base classes resolving ObjcClassDeclaration objc; // Data for a class declaration that is needed for the Objective-C integration - Symbol *cpp_type_info_ptr_sym; // cached instance of class Id.cpp_type_info_ptr static ClassDeclaration *create(Loc loc, Identifier *id, BaseClasses *baseclasses, Dsymbols *members, bool inObject); const char *toPrettyChars(bool QualifyTypes = false) override; diff --git a/gcc/d/dmd/arrayop.d b/gcc/d/dmd/arrayop.d index 7d25e185129..ea6f87f60ec 100644 --- a/gcc/d/dmd/arrayop.d +++ b/gcc/d/dmd/arrayop.d @@ -31,6 +31,7 @@ import dmd.location; import dmd.mtype; import dmd.common.outbuffer; import dmd.tokens; +import dmd.typesem : isAssignable; import dmd.visitor; /********************************************** diff --git a/gcc/d/dmd/astenums.d b/gcc/d/dmd/astenums.d index b71b6c40aeb..5cde5b334c5 100644 --- a/gcc/d/dmd/astenums.d +++ b/gcc/d/dmd/astenums.d @@ -19,12 +19,12 @@ enum Sizeok : ubyte } /// D Language version -enum Edition : ubyte +enum Edition : ushort { - none, - legacy, /// Before the introduction of editions - v2024, /// Experimental first new edition - latest = v2024 /// Newest edition that this compiler knows of + v2023 = 2023, /// Edition.min for default edition + v2024, /// first Edition + v2025, /// next Edition + /// use Edition.max for latest edition } enum Baseok : ubyte @@ -77,9 +77,10 @@ enum STC : ulong // transfer changes to declaration.h ref_ = 0x4_0000, /// `ref` scope_ = 0x8_0000, /// `scope` - scopeinferred = 0x20_0000, /// `scope` has been inferred and should not be part of mangling, `scope_` must also be set - return_ = 0x40_0000, /// 'return ref' or 'return scope' for function parameters - returnScope = 0x80_0000, /// if `ref return scope` then resolve to `ref` and `return scope` + scopeinferred = 0x10_0000, /// `scope` has been inferred and should not be part of mangling, `scope_` must also be set + return_ = 0x20_0000, /// 'return ref' or 'return scope' for function parameters + returnScope = 0x40_0000, /// if `ref return scope` then resolve to `ref` and `return scope` + returnRef = 0x80_0000, /// if `return ref` returninferred = 0x100_0000, /// `return` has been inferred and should not be part of mangling, `return_` must also be set immutable_ = 0x200_0000, /// `immutable` diff --git a/gcc/d/dmd/attrib.d b/gcc/d/dmd/attrib.d index a03e7bbdc0e..97dfa5ef683 100644 --- a/gcc/d/dmd/attrib.d +++ b/gcc/d/dmd/attrib.d @@ -32,10 +32,8 @@ import dmd.declaration; import dmd.dmodule; import dmd.dscope; import dmd.dsymbol; -import dmd.dsymbolsem : include; import dmd.expression; import dmd.func; -import dmd.globals; import dmd.hdrgen : visibilityToBuffer; import dmd.id; import dmd.identifier; @@ -80,7 +78,7 @@ extern (C++) abstract class AttribDeclaration : Dsymbol * the scope after it used. */ extern (D) static Scope* createNewScope(Scope* sc, STC stc, LINK linkage, - CPPMANGLE cppmangle, Visibility visibility, int explicitVisibility, + CPPMANGLE cppmangle, Visibility visibility, bool explicitVisibility, AlignDeclaration aligndecl, PragmaDeclaration inlining) { Scope* sc2 = sc; @@ -414,8 +412,7 @@ extern (C++) final class AlignDeclaration : AttribDeclaration this.dsym = DSYM.alignDeclaration; if (exp) { - exps = new Expressions(); - exps.push(exp); + exps = new Expressions(exp); } } diff --git a/gcc/d/dmd/canthrow.d b/gcc/d/dmd/canthrow.d index 96acf055293..bd4aa197ab9 100644 --- a/gcc/d/dmd/canthrow.d +++ b/gcc/d/dmd/canthrow.d @@ -20,7 +20,7 @@ import dmd.astenums; import dmd.blockexit : BE, checkThrow; import dmd.declaration; import dmd.dsymbol; -import dmd.dsymbolsem : include; +import dmd.dsymbolsem : include, toAlias; import dmd.errorsink; import dmd.expression; import dmd.expressionsem : errorSupplementalInferredAttr; diff --git a/gcc/d/dmd/clone.d b/gcc/d/dmd/clone.d index a21f5a05be7..f3f359ee312 100644 --- a/gcc/d/dmd/clone.d +++ b/gcc/d/dmd/clone.d @@ -300,8 +300,7 @@ FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) stc = (stc & ~STC.safe) | STC.trusted; } - auto fparams = new Parameters(); - fparams.push(new Parameter(loc, STC.nodtor, sd.type, Id.p, null, null)); + auto fparams = new Parameters(new Parameter(loc, STC.nodtor, sd.type, Id.p, null, null)); auto tf = new TypeFunction(ParameterList(fparams), sd.handleType(), LINK.d, stc | STC.ref_); auto fop = new FuncDeclaration(declLoc, Loc.initial, Id.opAssign, stc, tf); fop.storage_class |= STC.inference; @@ -574,8 +573,8 @@ FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc) scx.eSink = sc.eSink; /* const bool opEquals(ref const S s); */ - auto parameters = new Parameters(); - parameters.push(new Parameter(Loc.initial, STC.ref_ | STC.const_, sd.type, null, null, null)); + auto parameters = new Parameters(new Parameter(Loc.initial, STC.ref_ | STC.const_, sd.type, + null, null, null)); tfeqptr = new TypeFunction(ParameterList(parameters), Type.tbool, LINK.d); tfeqptr.mod = MODFlags.const_; tfeqptr = tfeqptr.typeSemantic(Loc.initial, &scx).isTypeFunction(); @@ -602,8 +601,7 @@ 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(loc, STC.ref_ | STC.const_, sd.type, Id.p, null, null)); + auto parameters = new Parameters(new Parameter(loc, STC.ref_ | STC.const_, sd.type, Id.p, null, null)); auto tf = new TypeFunction(ParameterList(parameters), Type.tbool, LINK.d, STC.const_); tf = tf.addSTC(STC.const_).toTypeFunction(); Identifier id = Id.xopEquals; @@ -649,8 +647,8 @@ FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) scx.eSink = sc.eSink; /* const int opCmp(ref const S s); */ - auto parameters = new Parameters(); - parameters.push(new Parameter(Loc.initial, STC.ref_ | STC.const_, sd.type, null, null, null)); + auto parameters = new Parameters(new Parameter(Loc.initial, STC.ref_ | STC.const_, sd.type, + null, null, null)); tfcmpptr = new TypeFunction(ParameterList(parameters), Type.tint32, LINK.d); tfcmpptr.mod = MODFlags.const_; tfcmpptr = tfcmpptr.typeSemantic(Loc.initial, &scx).isTypeFunction(); @@ -726,8 +724,7 @@ 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(loc, STC.ref_ | STC.const_, sd.type, Id.p, null, null)); + auto parameters = new Parameters(new Parameter(loc, STC.ref_ | STC.const_, sd.type, Id.p, null, null)); auto tf = new TypeFunction(ParameterList(parameters), Type.tint32, LINK.d, STC.const_); tf = tf.addSTC(STC.const_).toTypeFunction(); Identifier id = Id.xopCmp; @@ -854,8 +851,7 @@ FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc) //printf("StructDeclaration::buildXtoHash() %s\n", sd.toPrettyChars()); 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(loc, STC.ref_ | STC.const_, sd.type, Id.p, null, null)); + auto parameters = new Parameters(new Parameter(loc, STC.ref_ | STC.const_, sd.type, Id.p, null, null)); auto tf = new TypeFunction(ParameterList(parameters), Type.thash_t, LINK.d, STC.nothrow_ | STC.trusted); Identifier id = Id.xtoHash; auto fop = new FuncDeclaration(declLoc, Loc.initial, id, STC.static_, tf); @@ -872,6 +868,11 @@ FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc) // workaround https://issues.dlang.org/show_bug.cgi?id=17968 " static if(is(T* : const(.object.Object)*)) " ~ " h = h * 33 + typeid(const(.object.Object)).getHash(cast(const void*)&p.tupleof[i]);" ~ + // and another workaround for bitfields https://github.com/dlang/dmd/issues/20473 + " else static if (!__traits(compiles, &p.tupleof[i])) {" ~ + " auto t = p.tupleof[i];" ~ + " h = h * 33 + typeid(T).getHash(cast(const void*)&t);" ~ + " } " ~ " else " ~ " h = h * 33 + typeid(T).getHash(cast(const void*)&p.tupleof[i]);" ~ "return h;"; @@ -1561,10 +1562,9 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) */ private CtorDeclaration generateCtorDeclaration(StructDeclaration sd, const STC paramStc, const STC funcStc, bool move) { - auto fparams = new Parameters(); auto structType = sd.type; STC stc = move ? STC.none : STC.ref_; // the only difference between copy or move - fparams.push(new Parameter(Loc.initial, paramStc | stc, structType, Id.p, null, null)); + auto fparams = new Parameters(new Parameter(Loc.initial, paramStc | stc, structType, Id.p, null, null)); ParameterList pList = ParameterList(fparams); auto tf = new TypeFunction(pList, structType, LINK.d, STC.ref_); auto ccd = new CtorDeclaration(sd.loc, Loc.initial, STC.ref_, tf); diff --git a/gcc/d/dmd/common/bitfields.d b/gcc/d/dmd/common/bitfields.d index 72cb3e7d91c..5732f9d385f 100644 --- a/gcc/d/dmd/common/bitfields.d +++ b/gcc/d/dmd/common/bitfields.d @@ -10,18 +10,25 @@ */ module dmd.common.bitfields; +//version = Has_Bitfields; // does not work (yet) because hashOf doesn't work on bitfields +version(Has_Bitfields) + version = Debugger_friendly; // without Has_Bitfields, this uses more space by using S + /** * Generate code for bit fields inside a struct/class body * Params: * S = type of a struct with only boolean fields, which should become bit fields * T = type of bit fields variable, must have enough bits to store all booleans + * field = if provided, assume it is declared and initialized elsewhere + * bitOff = start using bits at the given offset * Returns: D code with a bit fields variable and getter / setter functions */ -extern (D) string generateBitFields(S, T)() +extern (D) string generateBitFields(S, T, string field = "", int bitOff = 0, int ID = __LINE__)() if (__traits(isUnsigned, T)) { import core.bitop: bsr; - + // if _fieldName provided, assume it declared and initialized elsewhere + enum fieldName = field.length == 0 ? "bitFields" : field; string result = "extern (C++) pure nothrow @nogc @safe final {"; struct BitInfo @@ -35,7 +42,7 @@ if (__traits(isUnsigned, T)) // Iterate over members to compute bit offset and bit size for each of them enum BitInfo bitInfo = () { BitInfo result; - int bitOffset = 0; + int bitOffset = bitOff; foreach (size_t i, mem; __traits(allMembers, S)) { alias memType = typeof(__traits(getMember, S, mem)); @@ -55,22 +62,60 @@ if (__traits(isUnsigned, T)) static assert(bitInfo.totalSize <= T.sizeof * 8, "sum of bit field size "~toString!(bitInfo.totalSize)~" exceeds storage type `"~T.stringof~"`"); + version(Debugger_friendly) + { + // unique name needed to allow same name as in base class using `alias`, but without overloading + string bitfieldsName = fieldName ~ toString!(ID); + string bitfieldsRead = T.stringof~" "~bitfieldsName~"() const pure { return 0"; + string bitfieldsWrite = "void "~bitfieldsName~"("~T.stringof~" v) {\n"; + } + foreach (size_t i, mem; __traits(allMembers, S)) { enum typeName = typeof(__traits(getMember, S, mem)).stringof; enum shift = toString!(bitInfo.offset[i]); enum sizeMask = toString!((1 << bitInfo.size[i]) - 1); // 0x01 for bool, 0xFF for ubyte etc. - result ~= " - "~typeName~" "~mem~"() const scope { return cast("~typeName~") ((bitFields >>> "~shift~") & "~sizeMask~"); } - "~typeName~" "~mem~"("~typeName~" v) scope + version(Debugger_friendly) + { + string memacc = mem; + bitfieldsRead ~= "\n| (cast("~T.stringof~")("~memacc~" & "~sizeMask~") << "~shift~")"; + bitfieldsWrite ~= memacc~" = cast("~typeName~")((v >> "~shift~") & "~sizeMask~");\n"; + result ~= typeName~" "~mem; + version(Has_Bitfields) + result ~= " : "~toString!(bitInfo.size[i]); + enum meminit = __traits(getMember, S.init, mem); + result ~= " = "~meminit.stringof~";\n"; + } + else { - bitFields &= ~("~sizeMask~" << "~shift~"); - bitFields |= v << "~shift~"; - return v; - }"; + result ~= " + "~typeName~" "~mem~"() const scope { return cast("~typeName~") (("~fieldName~" >>> "~shift~") & "~sizeMask~"); } + "~typeName~" "~mem~"("~typeName~" v) scope + { + "~fieldName~" &= ~("~sizeMask~" << "~shift~"); + "~fieldName~" |= v << "~shift~"; + return v; + }"; + } + } + version(Debugger_friendly) + { + bitfieldsRead ~= ";\n}\n"; + bitfieldsWrite ~= "}\n"; + if (field.length == 0) + result ~= "alias "~fieldName~" = "~bitfieldsName~";\n"; + result ~= bitfieldsRead ~ bitfieldsWrite; + result ~= "\n}\n"; + return result; + } + else + { + result ~= "\n}\n"; + enum TP initVal = bitInfo.initialValue; + if (field.length == 0) + result ~= " private "~T.stringof~" "~fieldName~" = " ~ toString!(initVal) ~ ";\n"; + return result; } - enum TP initVal = bitInfo.initialValue; - return result ~ "\n}\n private "~T.stringof~" bitFields = " ~ toString!(initVal) ~ ";\n"; } /// diff --git a/gcc/d/dmd/common/outbuffer.d b/gcc/d/dmd/common/outbuffer.d index 01fc3774002..a5340ef8058 100644 --- a/gcc/d/dmd/common/outbuffer.d +++ b/gcc/d/dmd/common/outbuffer.d @@ -230,9 +230,11 @@ struct OutBuffer offset += len; } - extern (C++) void write(const(void)* data, size_t nbytes) pure nothrow @system + alias put = write; // transition to output range which uses put() + + extern (C++) void write(scope const(void)* data, size_t nbytes) pure nothrow @system { - write(data[0 .. nbytes]); + put(data[0 .. nbytes]); } void write(scope const(void)[] buf) pure nothrow @trusted @@ -244,6 +246,23 @@ struct OutBuffer offset += buf.length; } + void write(scope string buf) pure nothrow @trusted // so write("hello") chooses this overload + { + if (doindent && !notlinehead) + indent(); + reserve(buf.length); + memcpy(this.data.ptr + offset, buf.ptr, buf.length); + offset += buf.length; + } + + extern (C++) void write(scope const(char)* s) pure nothrow @trusted + { + if (!s) + return; + import core.stdc.string : strlen; + put(s[0 .. strlen(s)]); + } + /** * Writes a 16 bit value, no reserve check. */ @@ -262,7 +281,7 @@ struct OutBuffer void write16(int v) nothrow { auto u = cast(ushort) v; - write(&u, u.sizeof); + put(&u, u.sizeof); } /** @@ -270,7 +289,7 @@ struct OutBuffer */ void write32(int v) nothrow @trusted { - write(&v, v.sizeof); + put(&v, v.sizeof); } /** @@ -278,34 +297,31 @@ struct OutBuffer */ @trusted void write64(long v) nothrow { - write(&v, v.sizeof); + put(&v, v.sizeof); } /// Buffer will NOT be zero-terminated extern (C++) void writestring(const(char)* s) pure nothrow @system { - if (!s) - return; - import core.stdc.string : strlen; - write(s[0 .. strlen(s)]); + put(s); } /// ditto void writestring(scope const(char)[] s) pure nothrow @safe { - write(s); + put(s); } /// ditto void writestring(scope string s) pure nothrow @safe { - write(s); + put(s); } /// Buffer will NOT be zero-terminated, followed by newline void writestringln(const(char)[] s) pure nothrow @safe { - writestring(s); + put(s); writenl(); } @@ -313,13 +329,13 @@ struct OutBuffer */ void writeStringz(const(char)* s) pure nothrow @system { - write(s[0 .. strlen(s)+1]); + put(s[0 .. strlen(s)+1]); } /// ditto void writeStringz(const(char)[] s) pure nothrow @safe { - write(s); + put(s); writeByte(0); } @@ -382,17 +398,26 @@ struct OutBuffer * Writes an 8 bit byte, no reserve check. */ extern (C++) nothrow @safe - void writeByten(int b) + void writeByten(ubyte b) { - this.data[offset++] = cast(ubyte) b; + this.data[offset++] = b; } - extern (C++) void writeByte(uint b) pure nothrow @safe + extern (C++) void writeByte(ubyte b) pure nothrow @safe { if (doindent && !notlinehead && b != '\n') indent(); reserve(1); - this.data[offset] = cast(ubyte)b; + this.data[offset] = b; + offset++; + } + + void write(ubyte b) pure nothrow @safe + { + if (doindent && !notlinehead && b != '\n') + indent(); + reserve(1); + this.data[offset] = b; offset++; } @@ -502,14 +527,17 @@ struct OutBuffer offset += 4; } - extern (C++) void write(const OutBuffer* buf) pure nothrow @trusted + extern (C++) void write(scope const OutBuffer* buf) pure nothrow @trusted { if (buf) - { - reserve(buf.offset); - memcpy(data.ptr + offset, buf.data.ptr, buf.offset); - offset += buf.offset; - } + put(*buf); + } + + void write(ref scope const OutBuffer buf) pure nothrow @trusted + { + reserve(buf.offset); + memcpy(data.ptr + offset, buf.data.ptr, buf.offset); + offset += buf.offset; } extern (C++) void fill0(size_t nbytes) pure nothrow @trusted @@ -871,7 +899,7 @@ unittest buf.prependbyte('x'); OutBuffer buf2; buf2.writestring("mmm"); - buf.write(&buf2); + buf.put(&buf2); char[] s = buf.extractSlice(); assert(s == "xdefabcmmm"); } diff --git a/gcc/d/dmd/common/outbuffer.h b/gcc/d/dmd/common/outbuffer.h index 62aca7a8014..9630f122015 100644 --- a/gcc/d/dmd/common/outbuffer.h +++ b/gcc/d/dmd/common/outbuffer.h @@ -53,7 +53,7 @@ public: void writestring(const char *string); void prependstring(const char *string); void writenl(); // write newline - void writeByte(unsigned b); + void writeByte(uint8_t b); void writeUTF8(unsigned b); void prependbyte(unsigned b); void writewchar(unsigned w); diff --git a/gcc/d/dmd/cond.d b/gcc/d/dmd/cond.d index 31c01f1853e..96c7db9086c 100644 --- a/gcc/d/dmd/cond.d +++ b/gcc/d/dmd/cond.d @@ -29,7 +29,6 @@ import dmd.identifier; import dmd.location; import dmd.mtype; import dmd.optimize; -import dmd.typesem; import dmd.common.outbuffer; import dmd.rootobject; import dmd.root.string; @@ -211,10 +210,9 @@ extern (C++) final class StaticForeach : RootObject auto sid = Identifier.generateId("Tuple"); auto sdecl = new StructDeclaration(loc, sid, false); sdecl.storage_class |= STC.static_; - sdecl.members = new Dsymbols(); auto fid = Identifier.idPool(tupleFieldName); auto ty = new TypeTypeof(loc, new TupleExp(loc, e)); - sdecl.members.push(new VarDeclaration(loc, ty, fid, null, STC.none)); + sdecl.members = new Dsymbols(new VarDeclaration(loc, ty, fid, null, STC.none)); auto r = cast(TypeStruct)sdecl.type; if (global.params.useTypeInfo && Type.dtypeinfo) r.vtinfo = TypeInfoStructDeclaration.create(r); // prevent typeinfo from going to object file diff --git a/gcc/d/dmd/constfold.d b/gcc/d/dmd/constfold.d index fad9c9ac44f..1e0024e92ce 100644 --- a/gcc/d/dmd/constfold.d +++ b/gcc/d/dmd/constfold.d @@ -25,6 +25,7 @@ import dmd.declaration; import dmd.dstruct; import dmd.errors; import dmd.expression; +import dmd.expressionsem : getField; import dmd.globals; import dmd.location; import dmd.mtype; @@ -1432,8 +1433,7 @@ UnionExp Cat(Loc loc, Type type, Expression e1, Expression e2) else { // Create an ArrayLiteralExp - auto elements = new Expressions(); - elements.push(e); + auto elements = new Expressions(e); emplaceExp!(ArrayLiteralExp)(&ue, e.loc, type, elements); } assert(ue.exp().type); @@ -1674,8 +1674,7 @@ UnionExp Cat(Loc loc, Type type, Expression e1, Expression e2) Type tb = t.toBasetype(); if (tb.ty == Tarray && tb.nextOf().equivalent(e.type)) { - auto expressions = new Expressions(); - expressions.push(e); + auto expressions = new Expressions(e); emplaceExp!(ArrayLiteralExp)(&ue, loc, t, expressions); e = ue.exp(); } diff --git a/gcc/d/dmd/cparse.d b/gcc/d/dmd/cparse.d index 70369ba20c7..4a6e5195f5f 100644 --- a/gcc/d/dmd/cparse.d +++ b/gcc/d/dmd/cparse.d @@ -655,8 +655,10 @@ final class CParser(AST) : Parser!AST case TOK.asm_: switch (peekNext()) { + case TOK.const_: case TOK.goto_: case TOK.inline: + case TOK.restrict: case TOK.volatile: case TOK.leftParenthesis: s = cparseGnuAsm(); @@ -1474,7 +1476,7 @@ final class CParser(AST) : Parser!AST * logical-OR-expression * logical-OR-expression ? expression : conditional-expression */ - private AST.Expression cparseCondExp() + AST.Expression cparseCondExp() { const loc = token.loc; @@ -1637,7 +1639,7 @@ final class CParser(AST) : Parser!AST */ private AST.Expression cparseConstantExp() { - return cparseAssignExp(); + return cparseCondExp(); } /***************************** @@ -3437,6 +3439,11 @@ final class CParser(AST) : Parser!AST cparseParens(); } } + else if (token.value == TOK._Noreturn) + { + specifier.noreturn = true; + nextToken(); + } else if (token.value == TOK.restrict) // ImportC assigns no semantics to `restrict`, so just ignore the keyword. nextToken(); else @@ -3485,19 +3492,51 @@ final class CParser(AST) : Parser!AST nextToken(); - // Consume all asm-qualifiers. As a future optimization, we could record - // the `inline` and `volatile` storage classes against the statement. - while (token.value == TOK.goto_ || - token.value == TOK.inline || - token.value == TOK.volatile) - nextToken(); + // Consume all asm-qualifiers. + enum AsmQualifiers { none = 0, goto_ = 1, inline = 2, volatile = 4, } + AsmQualifiers asmQualifiers; + while (1) + { + AsmQualifiers qual; + switch (token.value) + { + case TOK.goto_: + qual = AsmQualifiers.goto_; + goto Lcontinue; + + case TOK.inline: + qual = AsmQualifiers.inline; + goto Lcontinue; + + case TOK.volatile: + qual = AsmQualifiers.volatile; + goto Lcontinue; + + Lcontinue: + if (asmQualifiers & qual) + error(token.loc, "duplicate `asm` qualifier `%s`", token.toChars()); + else + asmQualifiers |= qual; + nextToken(); + continue; + + case TOK.const_: + case TOK.restrict: + error(token.loc, "`%s` is not a valid `asm` qualifier", token.toChars()); + nextToken(); + continue; + + default: + break; + } + break; + } check(TOK.leftParenthesis); if (token.value != TOK.string_) error("string literal expected for Assembler Template, not `%s`", token.toChars()); Token* toklist = null; Token** ptoklist = &toklist; - //Identifier label = null; auto statements = new AST.Statements(); int parens; @@ -3524,21 +3563,6 @@ final class CParser(AST) : Parser!AST error("matching `)` expected, not end of file"); break; - case TOK.colonColon: // treat as two separate : tokens for iasmgcc - *ptoklist = allocateToken(); - **ptoklist = this.token; - (*ptoklist).value = TOK.colon; - ptoklist = &(*ptoklist).next; - - *ptoklist = allocateToken(); - **ptoklist = this.token; - (*ptoklist).value = TOK.colon; - ptoklist = &(*ptoklist).next; - - *ptoklist = null; - nextToken(); - continue; - default: *ptoklist = allocateToken(); **ptoklist = this.token; @@ -3550,7 +3574,11 @@ final class CParser(AST) : Parser!AST if (toklist) { // Create AsmStatement from list of tokens we've saved - AST.Statement s = new AST.AsmStatement(token.loc, toklist); + auto s = new AST.AsmStatement(token.loc, toklist); + if (asmQualifiers & AsmQualifiers.volatile) + s.isVolatile = true; + if (asmQualifiers & AsmQualifiers.inline) + s.isInline = true; statements.push(s); } break; @@ -6069,7 +6097,7 @@ final class CParser(AST) : Parser!AST { //printf("addSym() %s\n", s.toChars()); if (auto v = s.isVarDeclaration()) - v.isCmacro(true); // mark it as coming from a C #define + v.isCmacro = true; // mark it as coming from a C #define if (auto td = s.isTemplateDeclaration()) td.isCmacro = true; // mark as coming from a C #define /* If it's already defined, replace the earlier diff --git a/gcc/d/dmd/ctfeexpr.d b/gcc/d/dmd/ctfeexpr.d index 2f577cea92f..58a00341d4e 100644 --- a/gcc/d/dmd/ctfeexpr.d +++ b/gcc/d/dmd/ctfeexpr.d @@ -26,7 +26,7 @@ import dmd.dtemplate; import dmd.errors; import dmd.expression; import dmd.func; -import dmd.globals; +import dmd.globals : dinteger_t, sinteger_t, uinteger_t; import dmd.location; import dmd.mtype; import dmd.root.bitarray; @@ -241,6 +241,7 @@ UnionExp copyLiteral(Expression e) AssocArrayLiteralExp r = ue.exp().isAssocArrayLiteralExp(); r.type = aae.type; r.lowering = aae.lowering; + r.loweringCtfe = aae.loweringCtfe; r.ownedByCtfe = OwnedBy.ctfe; return ue; } @@ -1459,7 +1460,7 @@ UnionExp ctfeCat(Loc loc, Type type, Expression e1, Expression e2) /* Given an AA literal 'ae', and a key 'e2': * Return ae[e2] if present, or NULL if not found. */ -Expression findKeyInAA(Loc loc, AssocArrayLiteralExp ae, Expression e2) +Expression findKeyInAA(Loc loc, AssocArrayLiteralExp ae, Expression e2, size_t* pidx = null) { /* Search the keys backwards, in case there are duplicate keys */ @@ -1470,6 +1471,8 @@ Expression findKeyInAA(Loc loc, AssocArrayLiteralExp ae, Expression e2) const int eq = ctfeEqual(loc, EXP.equal, ekey, e2); if (eq) { + if (pidx) + *pidx = i; return (*ae.values)[i]; } } @@ -1583,11 +1586,6 @@ Expression ctfeCast(UnionExp* pue, Loc loc, Type type, Type to, Expression e, bo */ void assignInPlace(Expression dest, Expression src) { - if (!(dest.op == EXP.structLiteral || dest.op == EXP.arrayLiteral || dest.op == EXP.string_)) - { - printf("invalid op %d %d\n", src.op, dest.op); - assert(0); - } Expressions* oldelems; Expressions* newelems; if (dest.op == EXP.structLiteral) @@ -1622,6 +1620,16 @@ void assignInPlace(Expression dest, Expression src) sliceAssignStringFromArrayLiteral(dest.isStringExp(), src.isArrayLiteralExp(), 0); return; } + else if (dest.op == EXP.int64 && src.op == EXP.int64) + { + dest.isIntegerExp().setInteger(src.isIntegerExp().getInteger()); + return; + } + else if (dest.op == EXP.float64 && src.op == EXP.float64) + { + dest.isRealExp().value = src.isRealExp().value; + return; + } else { printf("invalid op %d %d\n", src.op, dest.op); @@ -1829,6 +1837,9 @@ bool isCtfeValueValid(Expression newval) ( (e1.op == EXP.structLiteral || e1.op == EXP.arrayLiteral) && isCtfeValueValid(e1) || e1.op == EXP.variable || + e1.op == EXP.int64 || + e1.op == EXP.float64 || + e1.op == EXP.string_ || e1.op == EXP.dotVariable && isCtfeReferenceValid(e1) || e1.op == EXP.index && isCtfeReferenceValid(e1) || e1.op == EXP.slice && e1.type.toBasetype().ty == Tsarray diff --git a/gcc/d/dmd/cxxfrontend.d b/gcc/d/dmd/cxxfrontend.d index b61137ff20f..ce474c9bbef 100644 --- a/gcc/d/dmd/cxxfrontend.d +++ b/gcc/d/dmd/cxxfrontend.d @@ -26,12 +26,13 @@ import dmd.dtemplate /*: TemplateInstance, TemplateParameter, Tuple*/; import dmd.errorsink : ErrorSink; import dmd.expression /*: Expression*/; import dmd.func : FuncDeclaration; -import dmd.globals; +import dmd.globals : dinteger_t, uinteger_t, JsonFieldFlags; import dmd.identifier : Identifier; import dmd.init : Initializer, NeedInterpret; import dmd.location : Loc; import dmd.mtype /*: Covariant, Type, Parameter, ParameterList*/; import dmd.rootobject : RootObject; +import dmd.semantic3; import dmd.statement : Statement, AsmStatement, GccAsmStatement; // NB: At some point in the future, we can switch to shortened function syntax. @@ -132,7 +133,7 @@ void gendocfile(Module m, const char* ddoctext_ptr, size_t ddoctext_length, */ FuncDeclaration search_toString(StructDeclaration sd) { - return dmd.dstruct.search_toString(sd); + return dmd.semantic3.search_toString(sd); } /*********************************************************** @@ -205,6 +206,24 @@ void getLocalClasses(Module mod, ref ClassDeclarations aclasses) return dmd.dsymbolsem.getLocalClasses(mod, aclasses); } +Dsymbol toAlias(Dsymbol s) +{ + import dmd.dsymbolsem; + return dmd.dsymbolsem.toAlias(s); +} + +Dsymbol toAlias2(Dsymbol s) +{ + import dmd.dsymbolsem; + return dmd.dsymbolsem.toAlias2(s); +} + +bool isPOD(StructDeclaration sd) +{ + import dmd.dsymbolsem; + return dmd.dsymbolsem.isPOD(sd); +} + /*********************************************************** * dtemplate.d */ @@ -248,6 +267,11 @@ void printTemplateStats(bool listInstances, ErrorSink eSink) return dmd.dtemplate.printTemplateStats(listInstances, eSink); } +void printInstantiationTrace(TemplateInstance ti) +{ + return ti.printInstantiationTrace(); +} + /*********************************************************** * dtoh.d */ @@ -395,14 +419,14 @@ void asmSemantic(CAsmDeclaration d, Scope* sc) */ Statement gccAsmSemantic(GccAsmStatement s, Scope* sc) { - import dmd.iasmgcc; - return dmd.iasmgcc.gccAsmSemantic(s, sc); + import dmd.iasm.gcc; + return dmd.iasm.gcc.gccAsmSemantic(s, sc); } void gccAsmSemantic(CAsmDeclaration d, Scope* sc) { - import dmd.iasmgcc; - return dmd.iasmgcc.gccAsmSemantic(d, sc); + import dmd.iasm.gcc; + return dmd.iasm.gcc.gccAsmSemantic(d, sc); } /*********************************************************** @@ -721,6 +745,12 @@ MATCH constConv(Type from, Type to) return dmd.typesem.constConv(from, to); } +Expression defaultInitLiteral(Type t, Loc loc) +{ + import dmd.typesem; + return dmd.typesem.defaultInitLiteral(t, loc); +} + /*********************************************************** * typinf.d */ @@ -754,6 +784,20 @@ TypeInfoDeclaration getTypeInfoAssocArrayDeclaration(TypeAArray t, Scope* sc) return dmd.typinf.getTypeInfoAssocArrayDeclaration(t, sc); } +/** + * templatesem.d + */ +bool needsCodegen(TemplateInstance ti) +{ + import dmd.templatesem; + return dmd.templatesem.needsCodegen(ti); +} + +bool isDiscardable(TemplateInstance ti) +{ + import dmd.templatesem; + return dmd.templatesem.isDiscardable(ti); +} version (IN_LLVM) { /*********************************************************** diff --git a/gcc/d/dmd/dcast.d b/gcc/d/dmd/dcast.d index 9e35dde2969..4ac8b1f4471 100644 --- a/gcc/d/dmd/dcast.d +++ b/gcc/d/dmd/dcast.d @@ -2862,7 +2862,9 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) (*ae.keys)[i] = ex; } ae.type = t; - semanticTypeInfo(sc, ae.type); + ae.lowering = null; // we need a different lowering + ae.loweringCtfe = null; + ae.expressionSemantic(sc); return ae; } return visit(e); diff --git a/gcc/d/dmd/dclass.d b/gcc/d/dmd/dclass.d index 51ac939ade0..2129aa00cf6 100644 --- a/gcc/d/dmd/dclass.d +++ b/gcc/d/dmd/dclass.d @@ -19,7 +19,6 @@ import core.stdc.string; import dmd.aggregate; import dmd.arraytypes; import dmd.astenums; -import dmd.gluelayer; import dmd.declaration; import dmd.dscope; import dmd.dsymbol; @@ -194,8 +193,6 @@ extern (C++) class ClassDeclaration : AggregateDeclaration */ ObjcClassDeclaration objc; - 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) { objc = ObjcClassDeclaration(this); diff --git a/gcc/d/dmd/declaration.d b/gcc/d/dmd/declaration.d index 25e5c31dcc6..1bda9948cbf 100644 --- a/gcc/d/dmd/declaration.d +++ b/gcc/d/dmd/declaration.d @@ -22,18 +22,16 @@ import dmd.delegatize; import dmd.dscope; import dmd.dstruct; import dmd.dsymbol; -import dmd.dsymbolsem : dsymbolSemantic, aliasSemantic; +import dmd.dsymbolsem : toAlias; import dmd.dtemplate; import dmd.errors; import dmd.expression; import dmd.func; import dmd.globals; -import dmd.gluelayer; import dmd.hdrgen; import dmd.id; import dmd.identifier; import dmd.init; -import dmd.initsem : initializerToExpression, initializerSemantic; import dmd.intrange; import dmd.location; import dmd.mtype; @@ -42,7 +40,7 @@ import dmd.rootobject; import dmd.root.filename; import dmd.target; import dmd.tokens; -import dmd.typesem : toDsymbol, typeSemantic, size, hasPointers; +import dmd.typesem : typeSemantic, size; import dmd.visitor; version (IN_GCC) {} @@ -122,7 +120,7 @@ extern (C++) abstract class Declaration : Dsymbol return "declaration"; } - override final uinteger_t size(Loc loc) + override final ulong size(Loc loc) { assert(type); const sz = type.size(); @@ -358,21 +356,6 @@ extern (C++) final class TupleDeclaration : Declaration return tupletype; } - override Dsymbol toAlias2() - { - //printf("TupleDeclaration::toAlias2() '%s' objects = %s\n", toChars(), objects.toChars()); - for (size_t i = 0; i < objects.length; i++) - { - RootObject o = (*objects)[i]; - if (Dsymbol s = isDsymbol(o)) - { - s = s.toAlias2(); - (*objects)[i] = s; - } - } - return this; - } - override bool needThis() { //printf("TupleDeclaration::needThis(%s)\n", toChars()); @@ -601,117 +584,7 @@ extern (C++) final class AliasDeclaration : Declaration { if (type) return type; - return toAlias().getType(); - } - - override Dsymbol toAlias() - { - static if (0) - printf("[%s] AliasDeclaration::toAlias('%s', this = %p, aliassym: %s, kind: '%s', inuse = %d)\n", - loc.toChars(), toChars(), this, aliassym ? aliassym.toChars() : "", aliassym ? aliassym.kind() : "", inuse); - assert(this != aliassym); - //static int count; if (++count == 10) *(char*)0=0; - - Dsymbol err() - { - // Avoid breaking "recursive alias" state during errors gagged - if (global.gag) - return this; - aliassym = new AliasDeclaration(loc, ident, Type.terror); - type = Type.terror; - return aliassym; - } - // Reading the AliasDeclaration - if (!this.ignoreRead) - this.wasRead = true; // can never assign to this AliasDeclaration again - - if (inuse == 1 && type && _scope) - { - inuse = 2; - const olderrors = global.errors; - Dsymbol s = type.toDsymbol(_scope); - //printf("[%s] type = %s, s = %p, this = %p\n", loc.toChars(), type.toChars(), s, this); - if (global.errors != olderrors) - return err(); - if (s) - { - s = s.toAlias(); - if (global.errors != olderrors) - return err(); - aliassym = s; - inuse = 0; - } - else - { - Type t = type.typeSemantic(loc, _scope); - if (t.ty == Terror) - return err(); - if (global.errors != olderrors) - return err(); - //printf("t = %s\n", t.toChars()); - inuse = 0; - } - } - if (inuse) - { - .error(loc, "%s `%s` recursive alias declaration", kind, toPrettyChars); - return err(); - } - - if (semanticRun >= PASS.semanticdone) - { - // semantic is already done. - - // Do not see aliassym !is null, because of lambda aliases. - - // Do not see type.deco !is null, even so "alias T = const int;` needs - // semantic analysis to take the storage class `const` as type qualifier. - } - else - { - // stop AliasAssign tuple building - if (aliassym) - { - if (auto td = aliassym.isTupleDeclaration()) - { - if (td.building) - { - td.building = false; - semanticRun = PASS.semanticdone; - return td; - } - } - } - if (_import && _import._scope) - { - /* If this is an internal alias for selective/renamed import, - * load the module first. - */ - _import.dsymbolSemantic(null); - } - if (_scope) - { - aliasSemantic(this, _scope); - } - } - - inuse = 1; - Dsymbol s = aliassym ? aliassym.toAlias() : this; - inuse = 0; - return s; - } - - override Dsymbol toAlias2() - { - if (inuse) - { - .error(loc, "%s `%s` recursive alias declaration", kind, toPrettyChars); - return this; - } - inuse = 1; - Dsymbol s = aliassym ? aliassym.toAlias2() : this; - inuse = 0; - return s; + return toAlias(this).getType(); } override bool isOverloadable() const @@ -958,7 +831,7 @@ extern (C++) class VarDeclaration : Declaration { isdataseg = 2; // The Variables does not go into the datasegment - if (!canTakeAddressOf()) + if (!canTakeAddressOf() || (storage_class & STC.exptemp)) { return false; } @@ -1011,7 +884,7 @@ extern (C++) class VarDeclaration : Declaration auto bitoffset = offset * 8; auto vbitoffset = v.offset * 8; - // Bitsize of types are overridden by any bit-field widths. + // Bitsize of types are overridden by any bitfield widths. ulong tbitsize; if (auto bf = isBitFieldDeclaration()) { @@ -1051,49 +924,6 @@ extern (C++) class VarDeclaration : Declaration return edtor && !(storage_class & STC.nodtor); } - /******************************************* - * If variable has a constant expression initializer, get it. - * Otherwise, return null. - */ - extern (D) final Expression getConstInitializer(bool needFullType = true) - { - assert(type && _init); - - // Ungag errors when not speculative - const oldgag = global.gag; - if (global.gag) - { - Dsymbol sym = isMember(); - if (sym && !sym.isSpeculative()) - global.gag = 0; - } - - if (_scope) - { - inuse++; - _init = _init.initializerSemantic(_scope, type, INITinterpret); - import dmd.semantic2 : lowerStaticAAs; - lowerStaticAAs(this, _scope); - _scope = null; - inuse--; - } - - Expression e = _init.initializerToExpression(needFullType ? type : null); - global.gag = oldgag; - return e; - } - - override final Dsymbol toAlias() - { - //printf("VarDeclaration::toAlias('%s', this = %p, aliassym = %p)\n", toChars(), this, aliassym); - if ((!type || !type.deco) && _scope) - dsymbolSemantic(this, _scope); - - assert(this != aliasTuple); - Dsymbol s = aliasTuple ? aliasTuple.toAlias() : this; - return s; - } - override void accept(Visitor v) { v.visit(this); @@ -1396,6 +1226,8 @@ extern (C++) final class TypeInfoStaticArrayDeclaration : TypeInfoDeclaration extern (C++) final class TypeInfoAssociativeArrayDeclaration : TypeInfoDeclaration { Type entry; // type of TypeInfo_AssociativeArray.Entry!(t.index, t.next) + Declaration xopEqual; // implementation of TypeInfo_AssociativeArray.equals + Declaration xtoHash; // implementation of TypeInfo_AssociativeArray.getHash extern (D) this(Type tinfo) { diff --git a/gcc/d/dmd/declaration.h b/gcc/d/dmd/declaration.h index 74ea20cb56f..7a160770e14 100644 --- a/gcc/d/dmd/declaration.h +++ b/gcc/d/dmd/declaration.h @@ -71,9 +71,10 @@ namespace dmd #define STCref 0x40000ULL /// `ref` #define STCscope 0x80000ULL /// `scope` - #define STCscopeinferred 0x200000ULL /// `scope` has been inferred and should not be part of mangling, `scope` must also be set - #define STCreturn 0x400000ULL /// 'return ref' or 'return scope' for function parameters - #define STCreturnScope 0x800000ULL /// if `ref return scope` then resolve to `ref` and `return scope` + #define STCscopeinferred 0x100000ULL /// `scope` has been inferred and should not be part of mangling, `scope` must also be set + #define STCreturn 0x200000ULL /// 'return ref' or 'return scope' for function parameters + #define STCreturnScope 0x400000ULL /// if `ref return scope` then resolve to `ref` and `return scope` + #define STCreturnRef 0x800000ULL, /// if `return ref` #define STCreturninferred 0x1000000ULL /// `return` has been inferred and should not be part of mangling, `return` must also be set #define STCimmutable 0x2000000ULL /// `immutable` @@ -183,7 +184,6 @@ public: TupleDeclaration *syntaxCopy(Dsymbol *) override; const char *kind() const override; Type *getType() override; - Dsymbol *toAlias2() override; bool needThis() override; void accept(Visitor *v) override { v->visit(this); } @@ -203,8 +203,6 @@ public: bool overloadInsert(Dsymbol *s) override; const char *kind() const override; Type *getType() override; - Dsymbol *toAlias() override; - Dsymbol *toAlias2() override; bool isOverloadable() const override; void accept(Visitor *v) override { v->visit(this); } @@ -222,7 +220,6 @@ public: bool equals(const RootObject * const o) const override; bool overloadInsert(Dsymbol *s) override; - Dsymbol *toAlias() override; Dsymbol *isUnique(); bool isOverloadable() const override; @@ -300,7 +297,6 @@ public: bool isOverlappedWith(VarDeclaration *v); bool canTakeAddressOf(); bool needsScopeDtor(); - Dsymbol *toAlias() override final; // Eliminate need for dynamic_cast void accept(Visitor *v) override { v->visit(this); } }; @@ -395,6 +391,8 @@ class TypeInfoAssociativeArrayDeclaration final : public TypeInfoDeclaration { public: Type* entry; + Declaration* xopEqual; + Declaration* xtoHash; static TypeInfoAssociativeArrayDeclaration *create(Type *tinfo); @@ -727,7 +725,6 @@ public: virtual bool addPreInvariant(); virtual bool addPostInvariant(); const char *kind() const override; - bool isUnique(); bool needsClosure(); bool hasNestedFrameRefs(); ParameterList getParameterList(); diff --git a/gcc/d/dmd/delegatize.d b/gcc/d/dmd/delegatize.d index 1f5dca66b59..f37c83cbca7 100644 --- a/gcc/d/dmd/delegatize.d +++ b/gcc/d/dmd/delegatize.d @@ -180,6 +180,11 @@ private void lambdaSetParent(Expression e, FuncDeclaration fd) iz.accept(this); } } + override void visit(AssocArrayLiteralExp e) + { + if (e.lowering) + walkPostorder(e.lowering, this); + } } scope LambdaSetParent lsp = new LambdaSetParent(fd); diff --git a/gcc/d/dmd/denum.d b/gcc/d/dmd/denum.d index e1a4ab6d6a3..8b7d5e50123 100644 --- a/gcc/d/dmd/denum.d +++ b/gcc/d/dmd/denum.d @@ -18,7 +18,6 @@ import core.stdc.stdio; import dmd.astenums; import dmd.attrib; -import dmd.gluelayer; import dmd.declaration; import dmd.dsymbol; import dmd.expression; @@ -62,7 +61,7 @@ extern (C++) final class EnumDeclaration : ScopeDsymbol import dmd.common.bitfields : generateBitFields; mixin(generateBitFields!(BitFields, ubyte)); - Symbol* sinit; + void* sinit; extern (D) this(Loc loc, Identifier ident, Type memtype) { diff --git a/gcc/d/dmd/dimport.d b/gcc/d/dmd/dimport.d index bbd74a4da27..66810cb35fd 100644 --- a/gcc/d/dmd/dimport.d +++ b/gcc/d/dmd/dimport.d @@ -141,13 +141,6 @@ extern (C++) final class Import : Dsymbol scopesym.addAccessiblePackage(mod, visibility); // d } - override Dsymbol toAlias() - { - if (aliasId) - return mod; - return this; - } - override bool overloadInsert(Dsymbol s) { /* Allow multiple imports with the same package base, but disallow diff --git a/gcc/d/dmd/dinterpret.d b/gcc/d/dmd/dinterpret.d index 543c0e22e39..eaf3d993b01 100644 --- a/gcc/d/dmd/dinterpret.d +++ b/gcc/d/dmd/dinterpret.d @@ -50,7 +50,7 @@ import dmd.rootobject; import dmd.root.utf; import dmd.statement; import dmd.tokens; -import dmd.typesem : mutableOf, equivalent, pointerTo, sarrayOf, arrayOf, size; +import dmd.typesem : mutableOf, equivalent, pointerTo, sarrayOf, arrayOf, size, merge, defaultInitLiteral; import dmd.utils : arrayCastBigEndian; import dmd.visitor; @@ -569,7 +569,7 @@ private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterSta istatex.caller = istate; istatex.fd = fd; - if (fd.hasDualContext()) + if (fd.hasDualContext) { Expression arg0 = thisarg; if (arg0 && arg0.type.ty == Tstruct) @@ -722,7 +722,7 @@ private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterSta if (tf.isRef && e.op == EXP.variable && e.isVarExp().var == fd.vthis) e = thisarg; - if (tf.isRef && fd.hasDualContext() && e.op == EXP.index) + if (tf.isRef && fd.hasDualContext && e.op == EXP.index) { auto ie = e.isIndexExp(); auto pe = ie.e1.isPtrExp(); @@ -1745,7 +1745,7 @@ public: if (istate && istate.fd.vthis) { result = ctfeEmplaceExp!VarExp(e.loc, istate.fd.vthis); - if (istate.fd.hasDualContext()) + if (istate.fd.hasDualContext) { result = ctfeEmplaceExp!PtrExp(e.loc, result); result.type = Type.tvoidptr.sarrayOf(2); @@ -1761,7 +1761,7 @@ public: result = ctfeGlobals.stack.getThis(); if (result) { - if (istate && istate.fd.hasDualContext()) + if (istate && istate.fd.hasDualContext) { assert(result.op == EXP.address); result = result.isAddrExp().e1; @@ -2348,6 +2348,9 @@ public: if (ExpInitializer ie = v._init.isExpInitializer()) { result = interpretRegion(ie.exp, istate, goal); + if (result !is null && v.ctfeAdrOnStack != VarDeclaration.AdrOnStackNone) + if (!getValue(v)) + setValueWithoutChecking(v, result); // a temporary from extractSideEffects can be a ref return; } else if (v._init.isVoidInitializer()) @@ -2362,7 +2365,12 @@ public: { result = v._init.initializerToExpression(v.type); if (result !is null) + { + if (v.ctfeAdrOnStack != VarDeclaration.AdrOnStackNone) + if (!getValue(v)) + setValueWithoutChecking(v, result); // a temporary from extractSideEffects can be a ref return; + } } error(e.loc, "declaration `%s` is not yet implemented in CTFE", e.toChars()); result = CTFEExp.cantexp; @@ -2641,6 +2649,8 @@ public: valuesx !is e.values); auto aae = ctfeEmplaceExp!AssocArrayLiteralExp(e.loc, keysx, valuesx); aae.type = e.type; + aae.lowering = e.lowering; + aae.loweringCtfe = e.loweringCtfe; aae.ownedByCtfe = OwnedBy.ctfe; result = aae; } @@ -3406,131 +3416,10 @@ public: // --------------------------------------- // Interpret left hand side // --------------------------------------- - AssocArrayLiteralExp existingAA = null; - Expression lastIndex = null; Expression oldval = null; if (e1.op == EXP.index && e1.isIndexExp().e1.type.toBasetype().ty == Taarray) { - // --------------------------------------- - // Deal with AA index assignment - // --------------------------------------- - /* This needs special treatment if the AA doesn't exist yet. - * There are two special cases: - * (1) If the AA is itself an index of another AA, we may need to create - * multiple nested AA literals before we can insert the new value. - * (2) If the ultimate AA is null, no insertion happens at all. Instead, - * we create nested AA literals, and change it into a assignment. - */ - IndexExp ie = e1.isIndexExp(); - int depth = 0; // how many nested AA indices are there? - while (ie.e1.op == EXP.index && ie.e1.isIndexExp().e1.type.toBasetype().ty == Taarray) - { - assert(ie.modifiable); - ie = ie.e1.isIndexExp(); - ++depth; - } - - // Get the AA value to be modified. - Expression aggregate = interpretRegion(ie.e1, istate); - if (exceptionOrCant(aggregate)) - return; - if ((existingAA = aggregate.isAssocArrayLiteralExp()) !is null) - { - // Normal case, ultimate parent AA already exists - // We need to walk from the deepest index up, checking that an AA literal - // already exists on each level. - lastIndex = interpretRegion(e1.isIndexExp().e2, istate); - lastIndex = resolveSlice(lastIndex); // only happens with AA assignment - if (exceptionOrCant(lastIndex)) - return; - - while (depth > 0) - { - // Walk the syntax tree to find the indexExp at this depth - IndexExp xe = e1.isIndexExp(); - foreach (d; 0 .. depth) - xe = xe.e1.isIndexExp(); - - Expression ekey = interpretRegion(xe.e2, istate); - if (exceptionOrCant(ekey)) - return; - UnionExp ekeyTmp = void; - ekey = resolveSlice(ekey, &ekeyTmp); // only happens with AA assignment - - // Look up this index in it up in the existing AA, to get the next level of AA. - AssocArrayLiteralExp newAA = cast(AssocArrayLiteralExp)findKeyInAA(e.loc, existingAA, ekey); - if (exceptionOrCant(newAA)) - return; - if (!newAA) - { - // Doesn't exist yet, create an empty AA... - auto keysx = new Expressions(); - auto valuesx = new Expressions(); - newAA = ctfeEmplaceExp!AssocArrayLiteralExp(e.loc, keysx, valuesx); - newAA.type = xe.type; - newAA.ownedByCtfe = OwnedBy.ctfe; - //... and insert it into the existing AA. - existingAA.keys.push(ekey); - existingAA.values.push(newAA); - } - existingAA = newAA; - --depth; - } - - if (fp) - { - oldval = findKeyInAA(e.loc, existingAA, lastIndex); - if (!oldval) - oldval = copyLiteral(e.e1.type.defaultInitLiteral(e.loc)).copy(); - } - } - else - { - /* The AA is currently null. 'aggregate' is actually a reference to - * whatever contains it. It could be anything: var, dotvarexp, ... - * We rewrite the assignment from: - * aa[i][j] op= newval; - * into: - * aa = [i:[j:T.init]]; - * aa[j] op= newval; - */ - oldval = copyLiteral(e.e1.type.defaultInitLiteral(e.loc)).copy(); - - Expression newaae = oldval; - while (e1.op == EXP.index && e1.isIndexExp().e1.type.toBasetype().ty == Taarray) - { - Expression ekey = interpretRegion(e1.isIndexExp().e2, istate); - if (exceptionOrCant(ekey)) - return; - ekey = resolveSlice(ekey); // only happens with AA assignment - - auto keysx = new Expressions(); - auto valuesx = new Expressions(); - keysx.push(ekey); - valuesx.push(newaae); - - auto aae = ctfeEmplaceExp!AssocArrayLiteralExp(e.loc, keysx, valuesx); - aae.type = e1.isIndexExp().e1.type; - aae.ownedByCtfe = OwnedBy.ctfe; - if (!existingAA) - { - existingAA = aae; - lastIndex = ekey; - } - newaae = aae; - e1 = e1.isIndexExp().e1; - } - - // We must set to aggregate with newaae - e1 = interpretRegion(e1, istate, CTFEGoal.LValue); - if (exceptionOrCant(e1)) - return; - e1 = assignToLvalue(e, e1, newaae); - if (exceptionOrCant(e1)) - return; - } - assert(existingAA && lastIndex); - e1 = null; // stomp + assert(false, "indexing AA should have been lowered in semantic analysis"); } else if (e1.op == EXP.arrayLength) { @@ -3570,10 +3459,7 @@ public: if (e1.op == EXP.index && e1.isIndexExp().e1.type.toBasetype().ty == Taarray) { - IndexExp ie = e1.isIndexExp(); - assert(ie.e1.op == EXP.assocArrayLiteral); - existingAA = ie.e1.isAssocArrayLiteralExp(); - lastIndex = ie.e2; + assert(false, "indexing AA should have been lowered in semantic analysis"); } } @@ -3664,23 +3550,6 @@ public: } } - if (existingAA) - { - if (existingAA.ownedByCtfe != OwnedBy.ctfe) - { - error(e.loc, "cannot modify read-only constant `%s`", existingAA.toChars()); - result = CTFEExp.cantexp; - return; - } - - //printf("\t+L%d existingAA = %s, lastIndex = %s, oldval = %s, newval = %s\n", - // __LINE__, existingAA.toChars(), lastIndex.toChars(), oldval ? oldval.toChars() : NULL, newval.toChars()); - assignAssocArrayElement(e.loc, existingAA, lastIndex, newval); - - // Determine the return value - result = ctfeCast(pue, e.loc, e.type, e.type, fp && post ? oldval : newval); - return; - } if (e1.op == EXP.arrayLength) { /* Change the assignment from: @@ -3725,7 +3594,7 @@ public: if (newval == pue.exp()) newval = pue.copy(); - e1 = assignToLvalue(e, e1, newval); + e1 = assignToLvalue(e, e1, newval, istate); if (exceptionOrCant(e1)) return; @@ -3799,7 +3668,7 @@ public: /* Assignment to a CTFE reference. */ - if (Expression ex = assignToLvalue(e, e1, newval)) + if (Expression ex = assignToLvalue(e, e1, newval, istate)) result = ex; return; @@ -3807,7 +3676,7 @@ public: /* Set all sibling fields which overlap with v to VoidExp. */ - private void stompOverlappedFields(StructLiteralExp sle, VarDeclaration v) + private static void stompOverlappedFields(StructLiteralExp sle, VarDeclaration v) { if (!v.overlapped) return; @@ -3821,7 +3690,7 @@ public: } } - private Expression assignToLvalue(BinExp e, Expression e1, Expression newval) + private static Expression assignToLvalue(BinExp e, Expression e1, Expression newval, InterState* istate) { //printf("assignToLvalue() e: %s e1: %s newval: %s\n", e.toChars(), e1.toChars(), newval.toChars()); VarDeclaration vd = null; @@ -3915,7 +3784,8 @@ public: ArrayLiteralExp existingAE = aggregate.isArrayLiteralExp(); if (existingAE.ownedByCtfe != OwnedBy.ctfe) { - error(e.loc, "cannot modify read-only constant `%s`", existingAE.toChars()); + Expression literal = existingAE.aaLiteral ? existingAE.aaLiteral : existingAE; + error(e.loc, "cannot modify read-only constant `%s`", literal.toChars()); return CTFEExp.cantexp; } @@ -4933,7 +4803,7 @@ public: } } - if (fd && fd.semanticRun >= PASS.semantic3done && fd.hasSemantic3Errors()) + if (fd && fd.semanticRun >= PASS.semantic3done && fd.hasSemantic3Errors) { error(e.loc, "CTFE failed because of previous errors in `%s`", fd.toChars()); result = CTFEExp.cantexp; @@ -5239,16 +5109,6 @@ public: dinteger_t ofs; Expression agg = getAggregateFromPointer(e1, &ofs); - if (agg.op == EXP.null_) - { - error(e.loc, "cannot index through null pointer `%s`", e.e1.toChars()); - return false; - } - if (agg.op == EXP.int64) - { - error(e.loc, "cannot index through invalid pointer `%s` of value `%s`", e.e1.toChars(), e1.toChars()); - return false; - } // Pointer to a non-array variable if (agg.op == EXP.symbolOffset) { @@ -5267,9 +5127,15 @@ public: } else { + // agg is the value accessed, it is not dereferenced again, so offset 0 is always ok if (ofs + indx != 0) { - error(e.loc, "pointer index `[%lld]` lies outside memory block `[0..1]`", ofs + indx); + if (agg.op == EXP.null_) + error(e.loc, "cannot index through null pointer `%s`", e.e1.toChars()); + else if (agg.op == EXP.int64) + error(e.loc, "cannot index through invalid pointer `%s` of value `%s`", e.e1.toChars(), e1.toChars()); + else + error(e.loc, "pointer index `[%lld]` lies outside memory block `[0..1]`", ofs + indx); return false; } } @@ -5394,47 +5260,7 @@ public: if (e.e1.type.toBasetype().ty == Taarray) { - Expression e1 = interpretRegion(e.e1, istate); - if (exceptionOrCant(e1)) - return; - if (e1.op == EXP.null_) - { - if (goal == CTFEGoal.LValue && e1.type.ty == Taarray && e.modifiable) - { - assert(0); // does not reach here? - } - error(e.loc, "cannot index null array `%s`", e.e1.toChars()); - result = CTFEExp.cantexp; - return; - } - Expression e2 = interpretRegion(e.e2, istate); - if (exceptionOrCant(e2)) - return; - - if (goal == CTFEGoal.LValue) - { - // Pointer or reference of a scalar type - if (e1 == e.e1 && e2 == e.e2) - result = e; - else - { - emplaceExp!(IndexExp)(pue, e.loc, e1, e2); - result = pue.exp(); - result.type = e.type; - } - return; - } - - assert(e1.op == EXP.assocArrayLiteral); - UnionExp e2tmp = void; - e2 = resolveSlice(e2, &e2tmp); - result = findKeyInAA(e.loc, e1.isAssocArrayLiteralExp(), e2); - if (!result) - { - error(e.loc, "key `%s` not found in associative array `%s`", e2.toChars(), e.e1.toChars()); - result = CTFEExp.cantexp; - } - return; + assert(false, "indexing AA should have been lowered in semantic analysis"); } Expression agg; @@ -5662,50 +5488,6 @@ public: result.type = e.type; } - override void visit(InExp e) - { - debug (LOG) - { - printf("%s InExp::interpret() %s\n", e.loc.toChars(), e.toChars()); - } - Expression e1 = interpretRegion(e.e1, istate); - if (exceptionOrCant(e1)) - return; - Expression e2 = interpretRegion(e.e2, istate); - if (exceptionOrCant(e2)) - return; - if (e2.op == EXP.null_) - { - emplaceExp!(NullExp)(pue, e.loc, e.type); - result = pue.exp(); - return; - } - if (e2.op != EXP.assocArrayLiteral) - { - error(e.loc, "`%s` cannot be interpreted at compile time", e.toChars()); - result = CTFEExp.cantexp; - return; - } - - e1 = resolveSlice(e1); - result = findKeyInAA(e.loc, e2.isAssocArrayLiteralExp(), e1); - if (exceptionOrCant(result)) - return; - if (!result) - { - emplaceExp!(NullExp)(pue, e.loc, e.type); - result = pue.exp(); - } - else - { - // Create a CTFE pointer &aa[index] - result = ctfeEmplaceExp!IndexExp(e.loc, e2, e1); - result.type = e.type.nextOf(); - emplaceExp!(AddrExp)(pue, e.loc, result, e.type); - result = pue.exp(); - } - } - override void visit(CatExp e) { debug (LOG) @@ -6420,45 +6202,6 @@ public: } } - override void visit(RemoveExp e) - { - debug (LOG) - { - printf("%s RemoveExp::interpret() %s\n", e.loc.toChars(), e.toChars()); - } - Expression agg = interpret(e.e1, istate); - if (exceptionOrCant(agg)) - return; - Expression index = interpret(e.e2, istate); - if (exceptionOrCant(index)) - return; - if (agg.op == EXP.null_) - { - result = CTFEExp.voidexp; - return; - } - - AssocArrayLiteralExp aae = agg.isAssocArrayLiteralExp(); - Expressions* keysx = aae.keys; - Expressions* valuesx = aae.values; - size_t removed = 0; - foreach (j, evalue; *valuesx) - { - Expression ekey = (*keysx)[j]; - int eq = ctfeEqual(e.loc, EXP.equal, ekey, index); - if (eq) - ++removed; - else if (removed != 0) - { - (*keysx)[j - removed] = ekey; - (*valuesx)[j - removed] = evalue; - } - } - valuesx.length = valuesx.length - removed; - keysx.length = keysx.length - removed; - result = IntegerExp.createBool(removed != 0); - } - override void visit(ClassReferenceExp e) { //printf("ClassReferenceExp::interpret() %s\n", e.value.toChars()); @@ -6703,8 +6446,9 @@ ThrownExceptionExp chainExceptions(ThrownExceptionExp oldest, ThrownExceptionExp } // Little sanity check to make sure it's really a Throwable ClassReferenceExp boss = oldest.thrown; - const next = 5; // index of Throwable.next - assert((*boss.value.elements)[next].type.ty == Tclass); // Throwable.next + const next = 5; // index of Throwable._nextInChainPtr + with ((*boss.value.elements)[next].type) // Throwable._nextInChainPtr + assert(ty == Tpointer || ty == Tclass); ClassReferenceExp collateral = newest.thrown; if (collateral.originalClass().isErrorException() && !boss.originalClass().isErrorException()) { @@ -7150,6 +6894,53 @@ private Expression interpret_values(UnionExp* pue, InterState* istate, Expressio return pue.exp(); } +// signature is bool _d_aaDel(V[K] aa, K key) +private Expression interpret_aaDel(UnionExp* pue, InterState* istate, Expression aa, Expression key) +{ + Expression agg = interpret(aa, istate); + if (exceptionOrCantInterpret(agg)) + return agg; + Expression index = interpret(key, istate); + if (exceptionOrCantInterpret(index)) + return index; + if (agg.op == EXP.null_) + return CTFEExp.voidexp; //??? + + AssocArrayLiteralExp aae = agg.isAssocArrayLiteralExp(); + Expressions* keysx = aae.keys; + Expressions* valuesx = aae.values; + size_t removed = 0; + foreach (j, evalue; *valuesx) + { + Expression ekey = (*keysx)[j]; + int eq = ctfeEqual(aa.loc, EXP.equal, ekey, index); + if (eq) + ++removed; + else if (removed != 0) + { + (*keysx)[j - removed] = ekey; + (*valuesx)[j - removed] = evalue; + } + } + valuesx.length = valuesx.length - removed; + keysx.length = keysx.length - removed; + return IntegerExp.createBool(removed != 0); +} + +// signature is bool _d_aaDel(V[K] aa1, V[K] aa2) +private Expression interpret_aaEqual(UnionExp* pue, InterState* istate, Expression aa1, Expression aa2) +{ + Expression e1 = interpret(aa1, istate); + if (exceptionOrCantInterpret(e1)) + return e1; + Expression e2 = interpret(aa2, istate); + if (exceptionOrCantInterpret(e2)) + return e2; + + bool equal = ctfeEqual(aa1.loc, EXP.equal, e1, e2); + return IntegerExp.createBool(equal); +} + private Expression interpret_dup(UnionExp* pue, InterState* istate, Expression earg) { debug (LOG) @@ -7174,11 +6965,131 @@ private Expression interpret_dup(UnionExp* pue, InterState* istate, Expression e if (Expression e = evaluatePostblit(istate, (*aae.values)[i])) return e; } - aae.type = earg.type.mutableOf(); // repaint type from const(int[int]) to const(int)[int] + // repaint type from const(int[int]) to int[int] + if (auto taa = earg.type.toBasetype().isTypeAArray()) + { + auto aatype = new TypeAArray(taa.next.mutableOf(), taa.index); + aae.type = aatype.merge(); + } + else + aae.type = earg.type.mutableOf(); //printf("result is %s\n", aae.toChars()); return aae; } +// signature is bool V* _d_aaIn(V[K] aa, K key) +private Expression interpret_aaIn(UnionExp* pue, InterState* istate, Expression aa, Expression key) +{ + debug (LOG) + { + printf("%s _d_aaIn::interpret() %s in %s\n", aa.loc.toChars(), key.toChars(), aa.toChars()); + } + Expression eaa = interpretRegion(aa, istate); + if (exceptionOrCantInterpret(eaa)) + return eaa; + Expression ekey = interpretRegion(key, istate); + if (exceptionOrCantInterpret(ekey)) + return ekey; + + if (eaa.op != EXP.null_) + { + auto aalit = eaa.isAssocArrayLiteralExp(); + if (!aalit) + { + error(aa.loc, "`%s` cannot be interpreted at compile time", aa.toChars()); + return CTFEExp.cantexp; + } + + size_t idx; + auto result = findKeyInAA(aa.loc, aalit, ekey, &idx); + if (exceptionOrCantInterpret(result)) + return result; + if (result) + return pointerToAAValue(pue, aa, aalit, idx); + } + emplaceExp!(NullExp)(pue, aa.loc, aa.type.nextOf().pointerTo()); + return pue.exp(); +} + +// signature is V* _d_aaGetRvalueX(V[K] aa, K key) +private Expression interpret_aaGetRvalueX(UnionExp* pue, InterState* istate, Expression aa, Expression key) +{ + Expression e1 = interpret(aa, istate); + if (exceptionOrCantInterpret(e1)) + return e1; + Expression e2 = interpretRegion(key, istate); + if (exceptionOrCantInterpret(e2)) + return e2; + + auto aalit = e1.isAssocArrayLiteralExp(); + if (!aalit) + { + error(aa.loc, "cannot index null array `%s`", aa.toChars()); + return CTFEExp.cantexp; + } + size_t idx; + Expression result = findKeyInAA(aa.loc, aalit, e2, &idx); + if (!result) + { + error(aa.loc, "key `%s` not found in associative array `%s`", key.toChars(), aa.toChars()); + return CTFEExp.cantexp; + } + + return pointerToAAValue(pue, aa, aalit, idx); +} + +// signature is V* _d_aaGetY(ref V[K] aa, K key, out bool found) +private Expression interpret_aaGetY(UnionExp* pue, InterState* istate, Expression aa, Expression key, Expression found) +{ + Expression eaa = interpretRegion(aa, istate, CTFEGoal.LValue); + if (exceptionOrCantInterpret(eaa)) + return eaa; + Expression ekey = interpretRegion(key, istate); + if (exceptionOrCantInterpret(ekey)) + return ekey; + Expression efound = interpretRegion(found, istate, CTFEGoal.LValue); + if (exceptionOrCantInterpret(efound)) + return efound; + + auto ie = ctfeEmplaceExp!IndexExp(aa.loc, aa, key); // any BinExp for location in assignToLvalue + Expression evalaa = interpretRegion(eaa, istate); + auto aalit = evalaa.isAssocArrayLiteralExp(); + if (!aalit) + { + auto keysx = new Expressions(); + auto valuesx = new Expressions(); + aalit = ctfeEmplaceExp!AssocArrayLiteralExp(aa.loc, keysx, valuesx); + aalit.type = aa.type; + aalit.ownedByCtfe = OwnedBy.ctfe; + Interpreter.assignToLvalue(ie, eaa, aalit, istate); + } + size_t idx; + auto result = findKeyInAA(aa.loc, aalit, ekey, &idx); + if (found) + Interpreter.assignToLvalue(ie, efound, IntegerExp.createBool(result !is null), istate); + if (!result) + { + aalit.keys.push(ekey); + result = copyLiteral(aa.type.nextOf().defaultInitLiteral(aa.loc)).copy(); + idx = aalit.values.length; + aalit.values.push(result); + } + return pointerToAAValue(pue, aa, aalit, idx); +} + +private Expression pointerToAAValue(UnionExp* pue, Expression aa, AssocArrayLiteralExp aalit, size_t idx) +{ + auto arr = ctfeEmplaceExp!(ArrayLiteralExp)(aa.loc, aa.type.nextOf().arrayOf(), aalit.values); + arr.ownedByCtfe = aalit.ownedByCtfe; + arr.aaLiteral = aalit; + auto len = ctfeEmplaceExp!(IntegerExp)(aa.loc, idx, Type.tsize_t); + auto idxexp = ctfeEmplaceExp!(IndexExp)(aa.loc, arr, len); + idxexp.type = arr.type.nextOf(); + emplaceExp!(AddrExp)(pue, aa.loc, idxexp); + pue.exp().type = idxexp.type.pointerTo(); + return pue.exp(); +} + // signature is int delegate(ref Value) OR int delegate(ref Key, ref Value) private Expression interpret_aaApply(UnionExp* pue, InterState* istate, Expression aa, Expression deleg) { @@ -7223,7 +7134,10 @@ private Expression interpret_aaApply(UnionExp* pue, InterState* istate, Expressi if (wantRefValue) { Type t = evalue.type; - evalue = ctfeEmplaceExp!IndexExp(deleg.loc, ae, ekey); + auto arr = ctfeEmplaceExp!(ArrayLiteralExp)(aa.loc, t.arrayOf(), ae.values); + arr.ownedByCtfe = ae.ownedByCtfe; + auto idx = ctfeEmplaceExp!(IntegerExp)(aa.loc, i, Type.tsize_t); + evalue = ctfeEmplaceExp!(IndexExp)(aa.loc, arr, idx); evalue.type = t; } args[numParams - 1] = evalue; @@ -7481,7 +7395,7 @@ private Expression evaluateIfBuiltin(UnionExp* pue, InterState* istate, Loc loc, } if (!pthis) { - if (nargs == 1 || nargs == 3) + if (nargs >= 1 && nargs <= 3) { Expression firstarg = (*arguments)[0]; if (auto firstAAtype = firstarg.type.toBasetype().isTypeAArray()) @@ -7489,7 +7403,7 @@ private Expression evaluateIfBuiltin(UnionExp* pue, InterState* istate, Loc loc, const id = fd.ident; if (nargs == 1) { - if (id == Id.aaLen) + if (id == Id._d_aaLen) return interpret_length(pue, istate, firstarg); if (fd.toParent2().ident == Id.object) @@ -7504,12 +7418,25 @@ private Expression evaluateIfBuiltin(UnionExp* pue, InterState* istate, Loc loc, return interpret_dup(pue, istate, firstarg); } } + else if (nargs == 2) + { + if (id == Id._d_aaGetRvalueX) + return interpret_aaGetRvalueX(pue, istate, firstarg, (*arguments)[1]); + if (id == Id._d_aaIn) + return interpret_aaIn(pue, istate, firstarg, (*arguments)[1]); + if (id == Id._d_aaDel) + return interpret_aaDel(pue, istate, firstarg, (*arguments)[1]); + if (id == Id._d_aaEqual) + return interpret_aaEqual(pue, istate, firstarg, (*arguments)[1]); + if (id == Id._d_aaApply) + return interpret_aaApply(pue, istate, firstarg, (*arguments)[1]); + if (id == Id._d_aaApply2) + return interpret_aaApply(pue, istate, firstarg, (*arguments)[1]); + } else // (nargs == 3) { - if (id == Id._aaApply) - return interpret_aaApply(pue, istate, firstarg, (*arguments)[2]); - if (id == Id._aaApply2) - return interpret_aaApply(pue, istate, firstarg, (*arguments)[2]); + if (id == Id._d_aaGetY) + return interpret_aaGetY(pue, istate, firstarg, (*arguments)[1], (*arguments)[2]); } } } diff --git a/gcc/d/dmd/dmodule.d b/gcc/d/dmd/dmodule.d index 33539d4bf0b..08400433d09 100644 --- a/gcc/d/dmd/dmodule.d +++ b/gcc/d/dmd/dmodule.d @@ -30,15 +30,13 @@ import dmd.dmacro; import dmd.doc; import dmd.dscope; import dmd.dsymbol; -import dmd.dsymbolsem : dsymbolSemantic, importAll, load; +import dmd.dsymbolsem : dsymbolSemantic; import dmd.errors; import dmd.errorsink; import dmd.expression; -import dmd.expressionsem; import dmd.file_manager; import dmd.func; import dmd.globals; -import dmd.gluelayer; import dmd.id; import dmd.identifier; import dmd.location; @@ -412,6 +410,8 @@ extern (C++) final class Module : Package SearchOptFlags searchCacheFlags; // cached flags bool insearch; + bool isExplicitlyOutOfBinary; // Is this module known to be out of binary, and must be DllImport'd? + /** * A root module is one that will be compiled all the way to * object code. This field holds the root module that caused @@ -456,6 +456,7 @@ extern (C++) final class Module : Package else if (!FileName.equalsExt(srcfilename, mars_ext) && !FileName.equalsExt(srcfilename, hdr_ext) && !FileName.equalsExt(srcfilename, c_ext) && + !FileName.equalsExt(srcfilename, h_ext) && !FileName.equalsExt(srcfilename, i_ext) && !FileName.equalsExt(srcfilename, dd_ext)) { @@ -474,7 +475,7 @@ extern (C++) final class Module : Package if (doHdrGen) hdrfile = setOutfilename(global.params.dihdr.name, global.params.dihdr.dir, arg, hdr_ext); - this.edition = Edition.legacy; + this.edition = Edition.min; } extern (D) this(const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen) @@ -492,12 +493,6 @@ extern (C++) final class Module : Package return new Module(Loc.initial, filename, ident, doDocComment, doHdrGen); } - static const(char)* find(const(char)* filename) - { - ImportPathInfo pathThatFoundThis; // is this needed? In fact is this function needed still??? - return find(filename.toDString, pathThatFoundThis).ptr; - } - extern (D) static const(char)[] find(const(char)[] filename, out ImportPathInfo pathThatFoundThis) { ptrdiff_t whichPathFoundThis; @@ -532,6 +527,7 @@ extern (C++) final class Module : Package auto m = new Module(loc, filename, ident, 0, 0); // TODO: apply import path information (pathInfo) on to module + m.isExplicitlyOutOfBinary = importPathThatFindUs.isOutOfBinary; if (!m.read(loc)) return null; @@ -700,7 +696,8 @@ extern (C++) final class Module : Package const(ubyte)[] srctext; if (global.preprocess && - FileName.equalsExt(srcfile.toString(), c_ext) && + (FileName.equalsExt(srcfile.toString(), c_ext) || + FileName.equalsExt(srcfile.toString(), h_ext)) && FileName.exists(srcfile.toString())) { /* Apply C preprocessor to the .c file, returning the contents @@ -783,10 +780,12 @@ extern (C++) final class Module : Package DsymbolTable dst; Package ppack = null; - /* If it has the extension ".c", it is a "C" file. + /* If it has the extension ".c" or ".h", it is a "C" file. * If it has the extension ".i", it is a preprocessed "C" file. */ - if (FileName.equalsExt(arg, c_ext) || FileName.equalsExt(arg, i_ext)) + if (FileName.equalsExt(arg, c_ext) || + FileName.equalsExt(arg, h_ext) || + FileName.equalsExt(arg, i_ext)) { filetype = FileType.c; @@ -953,32 +952,6 @@ extern (C++) final class Module : Package return needmoduleinfo || global.params.cov; } - /******************************************* - * Print deprecation warning if we're deprecated, when - * this module is imported from scope sc. - * - * Params: - * sc = the scope into which we are imported - * loc = the location of the import statement - */ - void checkImportDeprecation(Loc loc, Scope* sc) - { - if (md && md.isdeprecated && !sc.isDeprecated) - { - Expression msg = md.msg; - if (StringExp se = msg ? msg.toStringExp() : null) - { - const slice = se.peekString(); - if (slice.length) - { - deprecation(loc, "%s `%s` is deprecated - %.*s", kind, toPrettyChars, cast(int)slice.length, slice.ptr); - return; - } - } - deprecation(loc, "%s `%s` is deprecated", kind, toPrettyChars); - } - } - override bool isPackageAccessible(Package p, Visibility visibility, SearchOptFlags flags = SearchOpt.all) { if (insearch) // don't follow import cycles @@ -1170,16 +1143,10 @@ extern (C++) final class Module : Package } // Back end - int doppelganger; // sub-module - Symbol* cov; // private uint[] __coverage; + void* cov; // private uint[] __coverage; uint[] covb; // bit array of valid code line numbers - Symbol* sictor; // module order independent constructor - Symbol* sctor; // module constructor - Symbol* sdtor; // module destructor - Symbol* ssharedctor; // module shared constructor - Symbol* sshareddtor; // module shared destructor - Symbol* stest; // module unit test - Symbol* sfilename; // symbol for filename + void* sfilename; // symbol for filename + bool hasCDtor; // this module has a (shared) module constructor or destructor and we are codegenning it uint[uint] ctfe_cov; /// coverage information from ctfe execution_count[line] @@ -1213,75 +1180,6 @@ extern (C++) final class Module : Package _escapetable = new Escape(); return _escapetable; } - - /**************************** - * A Singleton that loads core.stdc.config - * Returns: - * Module of core.stdc.config, null if couldn't find it - */ - extern (D) static Module loadCoreStdcConfig() - { - __gshared Module core_stdc_config; - auto pkgids = new Identifier[2]; - pkgids[0] = Id.core; - pkgids[1] = Id.stdc; - return loadModuleFromLibrary(core_stdc_config, pkgids, Id.config); - } - - /**************************** - * A Singleton that loads core.atomic - * Returns: - * Module of core.atomic, null if couldn't find it - */ - extern (D) static Module loadCoreAtomic() - { - __gshared Module core_atomic; - auto pkgids = new Identifier[1]; - pkgids[0] = Id.core; - return loadModuleFromLibrary(core_atomic, pkgids, Id.atomic); - } - - /**************************** - * A Singleton that loads std.math - * Returns: - * Module of std.math, null if couldn't find it - */ - extern (D) static Module loadStdMath() - { - __gshared Module std_math; - auto pkgids = new Identifier[1]; - pkgids[0] = Id.std; - return loadModuleFromLibrary(std_math, pkgids, Id.math); - } - - /********************************** - * Load a Module from the library. - * Params: - * mod = cached return value of this call - * pkgids = package identifiers - * modid = module id - * Returns: - * Module loaded, null if cannot load it - */ - extern (D) private static Module loadModuleFromLibrary(ref Module mod, Identifier[] pkgids, Identifier modid) - { - if (mod) - return mod; - - auto imp = new Import(Loc.initial, pkgids[], modid, null, true); - // Module.load will call fatal() if there's no module available. - // Gag the error here, pushing the error handling to the caller. - const errors = global.startGagging(); - imp.load(null); - if (imp.mod) - { - imp.mod.importAll(null); - imp.mod.dsymbolSemantic(null); - } - global.endGagging(errors); - mod = imp.mod; - return mod; - } } /*********************************************************** @@ -1386,7 +1284,7 @@ private const(char)[] processSource (const(ubyte)[] src, Module mod) dbuf.writeUTF8(u); } else - dbuf.writeByte(u); + dbuf.writeByte(cast(ubyte)u); } dbuf.writeByte(0); //add null terminator return dbuf.extractSlice(); @@ -1455,7 +1353,7 @@ private const(char)[] processSource (const(ubyte)[] src, Module mod) dbuf.writeUTF8(u); } else - dbuf.writeByte(u); + dbuf.writeByte(cast(ubyte)u); } dbuf.writeByte(0); //add a terminating null byte return dbuf.extractSlice(); @@ -1533,9 +1431,8 @@ FuncDeclaration findGetMembers(ScopeDsymbol dsym) { Scope sc; sc.eSink = global.errorSink; - auto parameters = new Parameters(); Parameters* p = new Parameter(STC.in_, Type.tchar.constOf().arrayOf(), null, null); - parameters.push(p); + auto parameters = new Parameters(p); Type tret = null; TypeFunction tf = new TypeFunction(parameters, tret, VarArg.none, LINK.d); tfgetmembers = tf.dsymbolSemantic(Loc.initial, &sc).isTypeFunction(); diff --git a/gcc/d/dmd/dscope.d b/gcc/d/dmd/dscope.d index c70e7d381b5..879ffc11268 100644 --- a/gcc/d/dmd/dscope.d +++ b/gcc/d/dmd/dscope.d @@ -55,7 +55,8 @@ enum Contract : ubyte ensure = 3, // out contract } -private extern (D) struct BitFields +/// Bitfield for settable/copyable flags, see `copyFlagsFrom`, `resetAllFlags` +private extern (D) struct FlagBitFields { bool ctor; /// constructor type bool noAccessCheck; /// don't do access checks @@ -74,6 +75,14 @@ private extern (D) struct BitFields bool ctfeBlock; /// inside a `if (__ctfe)` block } +private extern (D) struct NonFlagBitFields +{ + ubyte intypeof; /// in typeof(exp) + bool nofree; /// true if shouldn't free it + bool inLoop; /// true if inside a loop (where constructor calls aren't allowed) + bool inDefaultArg; /// true if inside a default argument (where __FILE__, etc are evaluated at the call site) + bool explicitVisibility; /// set if in an explicit visibility attribute +} /// State of -preview switches /// /// By making them part of a Scope, we reduce reliance on dmd.globals, @@ -140,10 +149,6 @@ extern (C++) struct Scope ForeachStatement fes; /// if nested function for ForeachStatement, this is it Scope* callsc; /// used for __FUNCTION__, __PRETTY_FUNCTION__ and __MODULE__ Dsymbol inunion; /// != null if processing members of a union - bool nofree; /// true if shouldn't free it - bool inLoop; /// true if inside a loop (where constructor calls aren't allowed) - bool inDefaultArg; /// true if inside a default argument (where __FILE__, etc are evaluated at the call site) - int intypeof; /// in typeof(exp) VarDeclaration lastVar; /// Previous symbol used to prevent goto-skips-init ErrorSink eSink; /// sink for error messages @@ -174,15 +179,15 @@ extern (C++) struct Scope /// visibility for class members Visibility visibility = Visibility(Visibility.Kind.public_); - int explicitVisibility; /// set if in an explicit visibility attribute STC stc; /// storage class DeprecatedDeclaration depdecl; /// customized deprecation message import dmd.common.bitfields : generateBitFields; - mixin(generateBitFields!(BitFields, ushort)); - + mixin(generateBitFields!(FlagBitFields, ushort)); + private ushort bitFields2; + mixin(generateBitFields!(NonFlagBitFields, ushort, "bitFields2")); Previews previews; // user defined attributes diff --git a/gcc/d/dmd/dstruct.d b/gcc/d/dmd/dstruct.d index 5aef3585caf..b565f159aec 100644 --- a/gcc/d/dmd/dstruct.d +++ b/gcc/d/dmd/dstruct.d @@ -23,13 +23,9 @@ import dmd.declaration; import dmd.dmodule; import dmd.dscope; import dmd.dsymbol; -import dmd.dsymbolsem : search, setFieldOffset; import dmd.dtemplate; -import dmd.errors; import dmd.expression; import dmd.func; -import dmd.funcsem; -import dmd.globals; import dmd.id; import dmd.identifier; import dmd.location; @@ -37,35 +33,9 @@ import dmd.mtype; import dmd.opover; import dmd.target; import dmd.tokens; -import dmd.typesem : isZeroInit, merge, size, hasPointers; import dmd.typinf; import dmd.visitor; -/*************************************** - * Search sd for a member function of the form: - * `extern (D) string toString();` - * Params: - * sd = struct declaration to search - * Returns: - * FuncDeclaration of `toString()` if found, `null` if not - */ -FuncDeclaration search_toString(StructDeclaration sd) -{ - Dsymbol s = search_function(sd, Id.tostring); - FuncDeclaration fd = s ? s.isFuncDeclaration() : null; - if (fd) - { - __gshared TypeFunction tftostring; - if (!tftostring) - { - tftostring = new TypeFunction(ParameterList(), Type.tstring, LINK.d); - tftostring = tftostring.merge().toTypeFunction(); - } - fd = fd.overloadExactMatch(tftostring); - } - return fd; -} - enum StructFlags : int { none = 0x0, @@ -151,100 +121,6 @@ extern (C++) class StructDeclaration : AggregateDeclaration return "struct"; } - /// Compute cached type properties for `TypeStruct` - extern(D) final void determineTypeProperties() - { - if (computedTypeProperties) - return; - foreach (vd; fields) - { - import dmd.dsymbolsem : hasPointers; - if (vd.storage_class & STC.ref_ || vd.hasPointers()) - { - hasPointerField = true; - hasUnsafeBitpatterns = true; - } - - if (vd._init && vd._init.isVoidInitializer() && vd.hasPointers()) - hasVoidInitPointers = true; - - if (vd.storage_class & STC.system || vd.type.hasUnsafeBitpatterns()) - hasUnsafeBitpatterns = true; - - if (!vd._init && vd.type.hasVoidInitPointers()) - hasVoidInitPointers = true; - - if (vd.type.hasInvariant()) - hasFieldWithInvariant = true; - } - computedTypeProperties = true; - } - - /*************************************** - * Determine if struct is POD (Plain Old Data). - * - * POD is defined as: - * $(OL - * $(LI not nested) - * $(LI no postblits, destructors, or assignment operators) - * $(LI no `ref` fields or fields that are themselves non-POD) - * ) - * The idea being these are compatible with C structs. - * - * Returns: - * true if struct is POD - */ - final bool isPOD() - { - // If we've already determined whether this struct is POD. - if (ispod != ThreeState.none) - return (ispod == ThreeState.yes); - - import dmd.clone; - - bool hasCpCtorLocal; - bool hasMoveCtorLocal; - bool needCopyCtor; - bool needMoveCtor; - needCopyOrMoveCtor(this, hasCpCtorLocal, hasMoveCtorLocal, needCopyCtor, needMoveCtor); - - if (enclosing || // is nested - search(this, loc, Id.postblit) || // has postblit - search(this, loc, Id.dtor) || // has destructor - /* This is commented out because otherwise buildkite vibe.d: - `canCAS!Task` fails to compile - */ - //hasMoveCtorLocal || // has move constructor - hasCpCtorLocal) // has copy constructor - { - ispod = ThreeState.no; - return false; - } - - // Recursively check all fields are POD. - for (size_t i = 0; i < fields.length; i++) - { - VarDeclaration v = fields[i]; - if (v.storage_class & STC.ref_) - { - ispod = ThreeState.no; - return false; - } - - if (auto ts = v.type.baseElemOf().isTypeStruct()) - { - if (!ts.sym.isPOD()) - { - ispod = ThreeState.no; - return false; - } - } - } - - ispod = ThreeState.yes; - return true; - } - /*************************************** * Determine if struct has copy construction (copy constructor or postblit) * Returns: @@ -269,54 +145,6 @@ extern (C++) class StructDeclaration : AggregateDeclaration { return index < numArgTypes() ? (*argTypes.arguments)[index].type : null; } - - - /*************************************** - * Verifies whether the struct declaration has a - * constructor that is not a copy constructor. - * Optionally, it can check whether the struct - * declaration has a regular constructor, that - * is not disabled. - * - * Params: - * ignoreDisabled = true to ignore disabled constructors - * Returns: - * true, if the struct has a regular (optionally, - * not disabled) constructor, false otherwise. - */ - final bool hasRegularCtor(bool ignoreDisabled = false) - { - if (!ctor) - return false; - - bool result; - overloadApply(ctor, (Dsymbol s) - { - if (auto td = s.isTemplateDeclaration()) - { - if (ignoreDisabled && td.onemember) - { - if (auto ctorDecl = td.onemember.isCtorDeclaration()) - { - if (ctorDecl.storage_class & STC.disable) - return 0; - } - } - result = true; - return 1; - } - if (auto ctorDecl = s.isCtorDeclaration()) - { - if (!ctorDecl.isCpCtor && (!ignoreDisabled || !(ctorDecl.storage_class & STC.disable))) - { - result = true; - return 1; - } - } - return 0; - }); - return result; - } } diff --git a/gcc/d/dmd/dsymbol.d b/gcc/d/dmd/dsymbol.d index e65068c7622..2059978ef8a 100644 --- a/gcc/d/dmd/dsymbol.d +++ b/gcc/d/dmd/dsymbol.d @@ -22,7 +22,6 @@ import dmd.arraytypes; import dmd.attrib; import dmd.astenums; import dmd.ast_node; -import dmd.gluelayer; import dmd.dclass; import dmd.declaration; import dmd.denum; @@ -31,11 +30,12 @@ import dmd.dmodule; import dmd.dversion; import dmd.dscope; import dmd.dstruct; +import dmd.dsymbolsem : toAlias; import dmd.dtemplate; import dmd.errors; import dmd.expression; +import dmd.expressionsem : getDsymbol; import dmd.func; -import dmd.globals; import dmd.id; import dmd.identifier; import dmd.init; @@ -106,22 +106,6 @@ void foreachDsymbol(Dsymbols* symbols, scope void delegate(Dsymbol) dg) } } - -struct Ungag -{ - uint oldgag; - - extern (D) this(uint old) nothrow @safe - { - this.oldgag = old; - } - - extern (C++) ~this() nothrow - { - global.gag = oldgag; - } -} - struct Visibility { /// @@ -334,7 +318,7 @@ extern (C++) class Dsymbol : ASTNode { Identifier ident; Dsymbol parent; - Symbol* csym; // symbol for code generator + void* csym; // symbol for code generator Scope* _scope; // !=null means context to use for semantic() private DsymbolAttributes* atts; /// attached attribute declarations const Loc loc; // where defined @@ -637,7 +621,7 @@ extern (C++) class Dsymbol : ASTNode static bool has2This(Dsymbol s) { if (auto f = s.isFuncDeclaration()) - return f.hasDualContext(); + return f.hasDualContext; if (auto ad = s.isAggregateDeclaration()) return ad.vthis2 !is null; return false; @@ -688,14 +672,6 @@ extern (C++) class Dsymbol : ASTNode return parent.isSpeculative(); } - final Ungag ungagSpeculative() const - { - const oldgag = global.gag; - if (global.gag && !isSpeculative() && !toParent2().isFuncDeclaration()) - global.gag = 0; - return Ungag(oldgag); - } - // kludge for template.isSymbol() override final DYNCAST dyncast() const { @@ -757,24 +733,6 @@ extern (C++) class Dsymbol : ASTNode return "symbol"; } - /********************************* - * If this symbol is really an alias for another, - * return that other. - * If needed, semantic() is invoked due to resolve forward reference. - */ - Dsymbol toAlias() - { - return this; - } - - /********************************* - * Resolve recursive tuple expansion in eponymous template. - */ - Dsymbol toAlias2() - { - return toAlias(); - } - bool overloadInsert(Dsymbol s) { //printf("Dsymbol::overloadInsert('%s')\n", s.toChars()); @@ -785,7 +743,7 @@ extern (C++) class Dsymbol : ASTNode * Returns: * SIZE_INVALID when the size cannot be determined */ - uinteger_t size(Loc loc) + ulong size(Loc loc) { .error(loc, "%s `%s` symbol `%s` has no size", kind, toPrettyChars, toChars()); return SIZE_INVALID; diff --git a/gcc/d/dmd/dsymbol.h b/gcc/d/dmd/dsymbol.h index 77e7dabcb6c..259817e6038 100644 --- a/gcc/d/dmd/dsymbol.h +++ b/gcc/d/dmd/dsymbol.h @@ -29,6 +29,7 @@ class AliasDeclaration; class AggregateDeclaration; class EnumDeclaration; class ClassDeclaration; +class StructDeclaration; class InterfaceDeclaration; class StructDeclaration; class UnionDeclaration; @@ -101,10 +102,13 @@ namespace dmd void dsymbolSemantic(Dsymbol *dsym, Scope *sc); void semantic2(Dsymbol *dsym, Scope *sc); void semantic3(Dsymbol *dsym, Scope* sc); + Dsymbol *toAlias(Dsymbol* s); + Dsymbol *toAlias2(Dsymbol* s); // in iasm.d void asmSemantic(CAsmDeclaration *ad, Scope *sc); // in iasmgcc.d void gccAsmSemantic(CAsmDeclaration *ad, Scope *sc); + bool isPOD(StructDeclaration *sd); } struct Visibility @@ -223,8 +227,6 @@ public: virtual Identifier *getIdent(); virtual const char *toPrettyChars(bool QualifyTypes = false); virtual const char *kind() const; - virtual Dsymbol *toAlias(); // resolve real symbol - virtual Dsymbol *toAlias2(); virtual bool overloadInsert(Dsymbol *s); virtual uinteger_t size(Loc loc); virtual bool isforwardRef(); diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d index 43fb0b72e4b..2c4996573d9 100644 --- a/gcc/d/dmd/dsymbolsem.d +++ b/gcc/d/dmd/dsymbolsem.d @@ -154,6 +154,199 @@ AlignDeclaration getAlignment(AlignDeclaration ad, Scope* sc) return ad; } + +/********************************* + * Resolve recursive tuple expansion in eponymous template. + */ +Dsymbol toAlias2(Dsymbol s) +{ + if (auto ad = s.isAliasDeclaration()) + { + if (ad.inuse) + { + .error(ad.loc, "%s `%s` recursive alias declaration", ad.kind, ad.toPrettyChars); + return ad; + } + ad.inuse = 1; + Dsymbol ds = ad.aliassym ? ad.aliassym.toAlias2() : ad; + ad.inuse = 0; + return ds; + } + if (auto td = s.isTupleDeclaration()) + { + //printf("TupleDeclaration::toAlias2() '%s' objects = %s\n", toChars(), objects.toChars()); + for (size_t i = 0; i < td.objects.length; i++) + { + RootObject o = (*td.objects)[i]; + if (Dsymbol ds = isDsymbol(o)) + { + ds = ds.toAlias2(); + (*td.objects)[i] = ds; + } + } + return td; + } + return toAlias(s); +} + +private Dsymbol toAliasImpl(AliasDeclaration ad) +{ + static if (0) + printf("[%s] AliasDeclaration::toAlias('%s', this = %p, aliassym: %s, kind: '%s', inuse = %d)\n", + ad.loc.toChars(), ad.toChars(), ad, ad.aliassym ? ad.aliassym.toChars() : "", ad.aliassym ? ad.aliassym.kind() : "", ad.inuse); + assert(ad != ad.aliassym); + //static int count; if (++count == 10) *(char*)0=0; + + Dsymbol err() + { + // Avoid breaking "recursive alias" state during errors gagged + if (global.gag) + return ad; + ad.aliassym = new AliasDeclaration(ad.loc, ad.ident, Type.terror); + ad.type = Type.terror; + return ad.aliassym; + } + // Reading the AliasDeclaration + if (!ad.ignoreRead) + ad.wasRead = true; // can never assign to this AliasDeclaration again + + if (ad.inuse == 1 && ad.type && ad._scope) + { + ad.inuse = 2; + const olderrors = global.errors; + Dsymbol s = ad.type.toDsymbol(ad._scope); + //printf("[%s] type = %s, s = %p, this = %p\n", loc.toChars(), type.toChars(), s, this); + if (global.errors != olderrors) + return err(); + if (s) + { + s = s.toAlias(); + if (global.errors != olderrors) + return err(); + ad.aliassym = s; + ad.inuse = 0; + } + else + { + Type t = ad.type.typeSemantic(ad.loc, ad._scope); + if (t.ty == Terror) + return err(); + if (global.errors != olderrors) + return err(); + //printf("t = %s\n", t.toChars()); + ad.inuse = 0; + } + } + if (ad.inuse) + { + .error(ad.loc, "%s `%s` recursive alias declaration", ad.kind, ad.toPrettyChars); + return err(); + } + + if (ad.semanticRun >= PASS.semanticdone) + { + // semantic is already done. + + // Do not see aliassym !is null, because of lambda aliases. + + // Do not see type.deco !is null, even so "alias T = const int;` needs + // semantic analysis to take the storage class `const` as type qualifier. + } + else + { + // stop AliasAssign tuple building + if (ad.aliassym) + { + if (auto td = ad.aliassym.isTupleDeclaration()) + { + if (td.building) + { + td.building = false; + ad.semanticRun = PASS.semanticdone; + return td; + } + } + } + if (ad._import && ad._import._scope) + { + /* If this is an internal alias for selective/renamed import, + * load the module first. + */ + ad._import.dsymbolSemantic(null); + } + if (ad._scope) + { + aliasSemantic(ad, ad._scope); + } + } + + ad.inuse = 1; + Dsymbol s = ad.aliassym ? ad.aliassym.toAlias() : ad; + ad.inuse = 0; + return s; +} + +/********************************* + * If this symbol is really an alias for another, + * return that other. + * If needed, semantic() is invoked due to resolve forward reference. + */ +Dsymbol toAlias(Dsymbol s) +{ + if (auto ad = s.isAliasDeclaration()) + { + return ad.toAliasImpl(); + } + if (auto imp = s.isImport()) + { + if (imp.aliasId) + return imp.mod; + return imp; + } + if (auto vd = s.isVarDeclaration()) + { + //printf("VarDeclaration::toAlias('%s', this = %p, aliassym = %p)\n", toChars(), this, aliassym); + if ((!vd.type || !vd.type.deco) && vd._scope) + dsymbolSemantic(vd, vd._scope); + + assert(vd != vd.aliasTuple); + return vd.aliasTuple ? vd.aliasTuple.toAlias() : vd; + } + // resolve real symbol + if (auto ti = s.isTemplateInstance()) + { + static if (LOG) + { + printf("TemplateInstance.toAlias()\n"); + } + if (!ti.inst) + { + // Maybe we can resolve it + if (ti._scope) + { + dsymbolSemantic(ti, ti._scope); + } + if (!ti.inst) + { + .error(ti.loc, "%s `%s` cannot resolve forward reference", ti.kind, ti.toPrettyChars); + ti.errors = true; + return ti; + } + } + + if (ti.inst != ti) + return ti.inst.toAlias(); + + if (ti.aliasdecl) + { + return ti.aliasdecl.toAlias(); + } + + return ti.inst; + } + return s; +} + const(char)* getMessage(DeprecatedDeclaration dd) { if (auto sc = dd._scope) @@ -244,6 +437,71 @@ package bool allowsContractWithoutBody(FuncDeclaration funcdecl) return true; } +/*************************************** + * Determine if struct is POD (Plain Old Data). + * + * POD is defined as: + * $(OL + * $(LI not nested) + * $(LI no postblits, destructors, or assignment operators) + * $(LI no `ref` fields or fields that are themselves non-POD) + * ) + * The idea being these are compatible with C structs. + * + * Returns: + * true if struct is POD + */ +bool isPOD(StructDeclaration sd) +{ + // If we've already determined whether this struct is POD. + if (sd.ispod != ThreeState.none) + return (sd.ispod == ThreeState.yes); + + import dmd.clone; + + bool hasCpCtorLocal; + bool hasMoveCtorLocal; + bool needCopyCtor; + bool needMoveCtor; + needCopyOrMoveCtor(sd, hasCpCtorLocal, hasMoveCtorLocal, needCopyCtor, needMoveCtor); + + if (sd.enclosing || // is nested + search(sd, sd.loc, Id.postblit) || // has postblit + search(sd, sd.loc, Id.dtor) || // has destructor + /* This is commented out because otherwise buildkite vibe.d: + `canCAS!Task` fails to compile + */ + //hasMoveCtorLocal || // has move constructor + hasCpCtorLocal) // has copy constructor + { + sd.ispod = ThreeState.no; + return false; + } + + // Recursively check all fields are POD. + for (size_t i = 0; i < sd.fields.length; i++) + { + VarDeclaration v = sd.fields[i]; + if (v.storage_class & STC.ref_) + { + sd.ispod = ThreeState.no; + return false; + } + + if (auto ts = v.type.baseElemOf().isTypeStruct()) + { + if (!ts.sym.isPOD()) + { + sd.ispod = ThreeState.no; + return false; + } + } + } + + sd.ispod = ThreeState.yes; + return true; +} + /* If sd has a copy constructor and ctor is an rvalue constructor, issue an error. @@ -351,7 +609,7 @@ Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool find * so resolve it ahead. */ { - int save = sc.intypeof; + ubyte save = sc.intypeof; sc.intypeof = 1; // bypass "need this" error check e = resolveProperties(sc, e); sc.intypeof = save; @@ -431,6 +689,55 @@ void deferDsymbolSemantic(Scope* sc, Dsymbol s, Scope* scx) Module.addDeferredSemantic(s); } +struct Ungag +{ + uint oldgag; + + extern (D) this(uint old) nothrow @safe + { + this.oldgag = old; + } + + extern (C++) ~this() nothrow + { + global.gag = oldgag; + } +} + +Ungag ungagSpeculative(const Dsymbol s) +{ + const oldgag = global.gag; + if (global.gag && !s.isSpeculative() && !s.toParent2().isFuncDeclaration()) + global.gag = 0; + return Ungag(oldgag); +} + +/******************************************* + * Print deprecation warning if we're deprecated, when + * this module is imported from scope sc. + * + * Params: + * m = the module + * sc = the scope into which we are imported + * loc = the location of the import statement + */ +private void checkImportDeprecation(Module m, Loc loc, Scope* sc) +{ + if (!m.md || !m.md.isdeprecated || sc.isDeprecated) + return; + + Expression msg = m.md.msg; + if (StringExp se = msg ? msg.toStringExp() : null) + { + const slice = se.peekString(); + if (slice.length) + { + deprecation(m.loc, "%s `%s` is deprecated - %.*s", m.kind, m.toPrettyChars, cast(int)slice.length, slice.ptr); + return; + } + } + deprecation(m.loc, "%s `%s` is deprecated", m.kind, m.toPrettyChars); +} private extern(C++) final class DsymbolSemanticVisitor : Visitor { @@ -779,8 +1086,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor ie = ie.expressionSemantic(sc); if (nelems > 0 && ie) { - auto iexps = new Expressions(); - iexps.push(ie); + auto iexps = new Expressions(ie); auto exps = new Expressions(); for (size_t pos = 0; pos < iexps.length; pos++) { @@ -1397,6 +1703,19 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor exp = ErrorExp.get(); } } + else if (exp.isBitField()) + { + if (dsym.storage_class & STC.autoref) + { + dsym.storage_class &= ~STC.ref_; + constructInit(false); + } + else + { + .error(dsym.loc, "bitfield `%s` cannot be assigned to `ref %s`", exp.toChars(), dsym.toChars()); + exp = ErrorExp.get(); + } + } else { constructInit(false); @@ -1592,7 +1911,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (!dsym.parent.isStructDeclaration() && !dsym.parent.isClassDeclaration()) { - .error(dsym.loc, "%s `%s` - bit-field must be member of struct, union, or class", dsym.kind, dsym.toPrettyChars); + .error(dsym.loc, "%s `%s` - bitfield must be member of struct, union, or class", dsym.kind, dsym.toPrettyChars); } sc = sc.startCTFE(); @@ -1602,18 +1921,18 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (!dsym.type.isIntegral()) { // C11 6.7.2.1-5 - error(width.loc, "bit-field type `%s` is not an integer type", dsym.type.toChars()); + error(width.loc, "bitfield type `%s` is not an integer type", dsym.type.toChars()); dsym.errors = true; } if (!width.isIntegerExp()) { - error(width.loc, "bit-field width `%s` is not an integer constant", dsym.width.toChars()); + error(width.loc, "bitfield width `%s` is not an integer constant", dsym.width.toChars()); dsym.errors = true; } const uwidth = width.toInteger(); // uwidth is unsigned if (uwidth == 0 && !dsym.isAnonymous()) { - error(width.loc, "bit-field `%s` has zero width", dsym.toChars()); + error(width.loc, "bitfield `%s` has zero width", dsym.toChars()); dsym.errors = true; } const sz = dsym.type.size(); @@ -1622,7 +1941,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor const max_width = sz * 8; if (uwidth > max_width) { - error(width.loc, "width `%lld` of bit-field `%s` does not fit in type `%s`", cast(long)uwidth, dsym.toChars(), dsym.type.toChars()); + error(width.loc, "width `%lld` of bitfield `%s` does not fit in type `%s`", cast(long)uwidth, dsym.toChars(), dsym.type.toChars()); dsym.errors = true; } dsym.fieldWidth = cast(uint)uwidth; @@ -2104,7 +2423,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor /* Run semantic on each argument, place results in tiargs[], * then find best match template with tiargs */ - if (!tm.findTempDecl(sc) || !tm.semanticTiargs(sc) || !tm.findBestMatch(sc, ArgumentList())) + if (!tm.findMixinTempDecl(sc) || !tm.semanticTiargs(sc) || !tm.findBestMatch(sc, ArgumentList())) { if (tm.semanticRun == PASS.initial) // forward reference had occurred { @@ -2686,9 +3005,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor auto v = new VarDeclaration(Loc.initial, Type.tint32, Id.gate, null); v.storage_class = STC.temp | STC.static_ | (isShared ? STC.shared_ : STC.none); - auto sa = new Statements(); Statement s = new ExpStatement(Loc.initial, v); - sa.push(s); + auto sa = new Statements(s); Expression e; if (isShared) @@ -3329,7 +3647,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } // - // For two structures or unions, corresponding bit-fields shall have the same widths. + // For two structures or unions, corresponding bitfields shall have the same widths. // BitFieldDeclaration bfa = a_field.isBitFieldDeclaration(); BitFieldDeclaration bfb = b_field.isBitFieldDeclaration(); @@ -4377,6 +4695,23 @@ private void attribAddMember(AttribDeclaration atb, Scope* sc, ScopeDsymbol sds) } } +/**************************************************** + * Declare parameters of template instance, initialize them with the + * template instance arguments. + */ +private void declareParameters(TemplateInstance ti, Scope* sc) +{ + TemplateDeclaration tempdecl = ti.tempdecl.isTemplateDeclaration(); + assert(tempdecl); + + //printf("TemplateInstance.declareParameters()\n"); + foreach (i, o; ti.tdtypes) // initializer for tp + { + TemplateParameter tp = (*tempdecl.parameters)[i]; + //printf("\ttdtypes[%d] = %p\n", i, o); + declareParameter(tempdecl, sc, tp, o); + } +} private extern(C++) class AddMemberVisitor : Visitor { alias visit = Visitor.visit; @@ -4764,6 +5099,173 @@ private bool isDRuntimeHook(Identifier id) id == Id._d_arrayappendcTX; } +/***************************************** + * Append `ti` to the specific module `ti.members[]` + */ +private Dsymbols* appendToModuleMember(TemplateInstance ti) +{ + Module mi = ti.minst; // instantiated . inserted module + + //printf("%s.appendToModuleMember() enclosing = %s mi = %s\n", + // toPrettyChars(), + // enclosing ? enclosing.toPrettyChars() : null, + // mi ? mi.toPrettyChars() : null); + if (global.params.allInst || !mi || mi.isRoot()) + { + /* If the instantiated module is speculative or root, insert to the + * member of a root module. Then: + * - semantic3 pass will get called on the instance members. + * - codegen pass will get a selection chance to do/skip it (needsCodegen()). + */ + static Dsymbol getStrictEnclosing(TemplateInstance ti) + { + do + { + if (ti.enclosing) + return ti.enclosing; + ti = ti.tempdecl.isInstantiated(); + } while (ti); + return null; + } + + Dsymbol enc = getStrictEnclosing(ti); + // insert target is made stable by using the module + // where tempdecl is declared. + mi = (enc ? enc : ti.tempdecl).getModule(); + if (!mi.isRoot()) + { + if (mi.importedFrom) + { + mi = mi.importedFrom; + assert(mi.isRoot()); + } + else + { + // This can happen when using the frontend as a library. + // Append it to the non-root module. + } + } + } + else + { + /* If the instantiated module is non-root, insert to the member of the + * non-root module. Then: + * - semantic3 pass won't be called on the instance. + * - codegen pass won't reach to the instance. + * Unless it is re-appended to a root module later (with changed minst). + */ + } + //printf("\t-. mi = %s\n", mi.toPrettyChars()); + + if (ti.memberOf) // already appended to some module + { + assert(mi.isRoot(), "can only re-append to a root module"); + if (ti.memberOf.isRoot()) + return null; // no need to move to another root module + } + + Dsymbols* a = mi.members; + a.push(ti); + ti.memberOf = mi; + if (mi.semanticRun >= PASS.semantic2done && mi.isRoot()) + Module.addDeferredSemantic2(ti); + if (mi.semanticRun >= PASS.semantic3done && mi.isRoot()) + Module.addDeferredSemantic3(ti); + return a; +} + +private void expandMembers(TemplateInstance ti,Scope* sc2) +{ + ti.members.foreachDsymbol( (s) { s.setScope (sc2); } ); + + ti.members.foreachDsymbol( (s) { s.importAll(sc2); } ); + + if (!ti.aliasdecl) + { + /* static if's are crucial to evaluating aliasdecl correctly. But + * evaluating the if/else bodies may require aliasdecl. + * So, evaluate the condition for static if's, but not their if/else bodies. + * Then try to set aliasdecl. + * Later do the if/else bodies. + * https://issues.dlang.org/show_bug.cgi?id=23598 + * It might be better to do this by attaching a lambda to the StaticIfDeclaration + * to do the oneMembers call after the sid.include(sc2) is run as part of dsymbolSemantic(). + */ + bool done; + void staticIfDg(Dsymbol s) + { + if (done || ti.aliasdecl) + return; + //printf("\t staticIfDg on '%s %s' in '%s'\n", s.kind(), s.toChars(), this.toChars()); + if (!s.isStaticIfDeclaration()) + { + //s.dsymbolSemantic(sc2); + done = true; + return; + } + auto sid = s.isStaticIfDeclaration(); + sid.include(sc2); + if (ti.members.length) + { + Dsymbol sa; + if (oneMembers(ti.members, sa, ti.tempdecl.ident) && sa) + ti.aliasdecl = sa; + } + done = true; + } + + ti.members.foreachDsymbol(&staticIfDg); + } + + void symbolDg(Dsymbol s) + { + //printf("\t semantic on '%s' %p kind %s in '%s'\n", s.toChars(), s, s.kind(), this.toChars()); + //printf("test: enclosing = %d, sc2.parent = %s\n", enclosing, sc2.parent.toChars()); + //if (enclosing) + // s.parent = sc.parent; + //printf("test3: enclosing = %d, s.parent = %s\n", enclosing, s.parent.toChars()); + s.dsymbolSemantic(sc2); + //printf("test4: enclosing = %d, s.parent = %s\n", enclosing, s.parent.toChars()); + Module.runDeferredSemantic(); + } + + ti.members.foreachDsymbol(&symbolDg); +} + +private void tryExpandMembers(TemplateInstance ti, Scope* sc2) +{ + __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 > global.recursionLimit) + { + global.gag = 0; // ensure error message gets printed + .error(ti.loc, "%s `%s` recursive expansion exceeded allowed nesting limit", ti.kind, ti.toPrettyChars); + fatal(); + } + + ti.expandMembers(sc2); + + nest--; +} + +private void trySemantic3(TemplateInstance ti, Scope* sc2) +{ + // extracted to a function to allow windows SEH to work without destructors in the same function + __gshared int nest; + //printf("%d\n", nest); + if (++nest > global.recursionLimit) + { + global.gag = 0; // ensure error message gets printed + .error(ti.loc, "%s `%s` recursive expansion exceeded allowed nesting limit", ti.kind, ti.toPrettyChars); + fatal(); + } + + semantic3(ti, sc2); + + --nest; +} + void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList argumentList) { //printf("[%s] TemplateInstance.dsymbolSemantic('%s', this=%p, gag = %d, sc = %p)\n", tempinst.loc.toChars(), tempinst.toChars(), tempinst, global.gag, sc); @@ -5080,8 +5582,7 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList if (STC stc = ModToStc(t.mod)) { //printf("t = %s, stc = x%llx\n", t.toChars(), stc); - auto s = new Dsymbols(); - s.push(new StorageClassDeclaration(stc, tempinst.members)); + auto s = new Dsymbols(new StorageClassDeclaration(stc, tempinst.members)); tempinst.members = s; } break; @@ -5677,6 +6178,196 @@ void aliasSemantic(AliasDeclaration ds, Scope* sc) normalRet(); } +/********************************************** + * Find template declaration corresponding to template instance. + * + * Returns: + * false if finding fails. + * Note: + * This function is reentrant against error occurrence. If returns false, + * any members of this object won't be modified, and repetition call will + * reproduce same error. + */ +bool findTempDecl(TemplateInstance ti, Scope* sc, WithScopeSymbol* pwithsym) +{ + if (pwithsym) + *pwithsym = null; + + if (ti.havetempdecl) + return true; + + //printf("TemplateInstance.findTempDecl() %s\n", toChars()); + if (!ti.tempdecl) + { + /* Given: + * foo!( ... ) + * figure out which TemplateDeclaration foo refers to. + */ + Identifier id = ti.name; + Dsymbol scopesym; + Dsymbol s = sc.search(ti.loc, id, scopesym); + if (!s) + { + s = sc.search_correct(id); + if (s) + .error(ti.loc, "%s `%s` template `%s` is not defined, did you mean %s?", ti.kind, ti.toPrettyChars(), id.toChars(), s.toChars()); + else + .error(ti.loc, "%s `%s` template `%s` is not defined", ti.kind, ti.toPrettyChars(), id.toChars()); + return false; + } + static if (LOG) + { + printf("It's an instance of '%s' kind '%s'\n", s.toChars(), s.kind()); + if (s.parent) + printf("s.parent = '%s'\n", s.parent.toChars()); + } + if (pwithsym) + *pwithsym = scopesym.isWithScopeSymbol(); + + /* We might have found an alias within a template when + * we really want the template. + */ + TemplateInstance ti2; + if (s.parent && (ti2 = s.parent.isTemplateInstance()) !is null) + { + if (ti2.tempdecl && ti2.tempdecl.ident == id) + { + /* This is so that one can refer to the enclosing + * template, even if it has the same name as a member + * of the template, if it has a !(arguments) + */ + TemplateDeclaration td = ti2.tempdecl.isTemplateDeclaration(); + assert(td); + if (td.overroot) // if not start of overloaded list of TemplateDeclaration's + td = td.overroot; // then get the start + s = td; + } + } + + // The template might originate from a selective import which implies that + // s is a lowered AliasDeclaration of the actual TemplateDeclaration. + // This is the last place where we see the deprecated alias because it is + // stripped below, so check if the selective import was deprecated. + // See https://issues.dlang.org/show_bug.cgi?id=20840. + if (s.isAliasDeclaration()) + s.checkDeprecated(ti.loc, sc); + + if (!ti.updateTempDecl(sc, s)) + { + return false; + } + } + assert(ti.tempdecl); + + // Look for forward references + auto tovers = ti.tempdecl.isOverloadSet(); + foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1) + { + Dsymbol dstart = tovers ? tovers.a[oi] : ti.tempdecl; + int r = overloadApply(dstart, (Dsymbol s) + { + auto td = s.isTemplateDeclaration(); + if (!td) + return 0; + + if (td.semanticRun == PASS.initial) + { + if (td._scope) + { + // Try to fix forward reference. Ungag errors while doing so. + auto ungag = td.ungagSpeculative(); + td.dsymbolSemantic(td._scope); + } + if (td.semanticRun == PASS.initial) + { + .error(ti.loc, "%s `%s` `%s` forward references template declaration `%s`", + ti.kind, ti.toPrettyChars(), ti.toChars(), td.toChars()); + return 1; + } + } + return 0; + }); + if (r) + return false; + } + return true; +} + +private bool findMixinTempDecl(TemplateMixin tm, Scope* sc) +{ + // Follow qualifications to find the TemplateDeclaration + if (!tm.tempdecl) + { + Expression e; + Type t; + Dsymbol s; + tm.tqual.resolve(tm.loc, sc, e, t, s); + if (!s) + { + .error(tm.loc, "%s `%s` is not defined", tm.kind, tm.toPrettyChars); + return false; + } + s = s.toAlias(); + tm.tempdecl = s.isTemplateDeclaration(); + OverloadSet os = s.isOverloadSet(); + + /* If an OverloadSet, look for a unique member that is a template declaration + */ + if (os) + { + Dsymbol ds = null; + foreach (i, sym; os.a) + { + Dsymbol s2 = sym.isTemplateDeclaration(); + if (s2) + { + if (ds) + { + tm.tempdecl = os; + break; + } + ds = s2; + } + } + } + if (!tm.tempdecl) + { + .error(tm.loc, "%s `%s` - `%s` is a %s, not a template", tm.kind, + tm.toPrettyChars, s.toChars(), s.kind()); + return false; + } + } + assert(tm.tempdecl); + + // Look for forward references + auto tovers = tm.tempdecl.isOverloadSet(); + foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1) + { + Dsymbol dstart = tovers ? tovers.a[oi] : tm.tempdecl; + int r = overloadApply(dstart, (Dsymbol s) + { + auto td = s.isTemplateDeclaration(); + if (!td) + return 0; + + if (td.semanticRun == PASS.initial) + { + if (td._scope) + td.dsymbolSemantic(td._scope); + else + { + tm.semanticRun = PASS.initial; + return 1; + } + } + return 0; + }); + if (r) + return false; + } + return true; +} + /******************** * Perform semantic on AliasAssignment. * Has a lot of similarities to aliasSemantic(). Perhaps they should share code. @@ -5939,7 +6630,7 @@ private TupleDeclaration aliasAssignInPlace(Scope* sc, TemplateInstance tempinst if (td) aliassym.aliassym = td; aliassym.semanticRun = PASS.semanticdone; - if (!TemplateInstance.semanticTiargs(tempinst.loc, sc, tempinst.tiargs, 0, td)) + if (!TemplateInstance_semanticTiargs(tempinst.loc, sc, tempinst.tiargs, 0, td)) { tempinst.errors = true; return null; @@ -5993,6 +6684,39 @@ private TupleDeclaration aliasAssignInPlace(Scope* sc, TemplateInstance tempinst return td; } +/********************************* + * Iterate this dsymbol or members of this scoped dsymbol, then + * call `fp` with the found symbol and `params`. + * Params: + * symbol = the dsymbol or parent of members to call fp on + * fp = function pointer to process the iterated symbol. + * If it returns nonzero, the iteration will be aborted. + * ctx = context parameter passed to fp. + * Returns: + * nonzero if the iteration is aborted by the return value of fp, + * or 0 if it's completed. + */ +int apply(Dsymbol symbol, int function(Dsymbol, void*) fp, void* ctx) +{ + if (auto nd = symbol.isNspace()) + { + return nd.members.foreachDsymbol( (s) { return s && s.apply(fp, ctx); } ); + } + if (auto ad = symbol.isAttribDeclaration()) + { + return ad.include(ad._scope).foreachDsymbol( (s) { return s && s.apply(fp, ctx); } ); + } + if (auto tm = symbol.isTemplateMixin()) + { + if (tm._scope) // if fwd reference + dsymbolSemantic(tm, null); // try to resolve it + + return tm.members.foreachDsymbol( (s) { return s && s.apply(fp, ctx); } ); + } + + return fp(symbol, ctx); +} + /*************************************** * Check if a template instance is a trivial AliasSeq but without other overloads. * We can only be 100% sure of being AliasSeq after running semanticTiargs() @@ -6109,12 +6833,81 @@ bool determineFields(AggregateDeclaration ad) return true; } +/**************************** + * A Singleton that loads core.stdc.config + * Returns: + * Module of core.stdc.config, null if couldn't find it + */ +Module loadCoreStdcConfig() +{ + __gshared Module core_stdc_config; + auto pkgids = new Identifier[2]; + pkgids[0] = Id.core; + pkgids[1] = Id.stdc; + return loadModuleFromLibrary(core_stdc_config, pkgids, Id.config); +} + +/**************************** + * A Singleton that loads core.atomic + * Returns: + * Module of core.atomic, null if couldn't find it + */ +private Module loadCoreAtomic() +{ + __gshared Module core_atomic; + auto pkgids = new Identifier[1]; + pkgids[0] = Id.core; + return loadModuleFromLibrary(core_atomic, pkgids, Id.atomic); +} + +/**************************** + * A Singleton that loads std.math + * Returns: + * Module of std.math, null if couldn't find it + */ +Module loadStdMath() +{ + __gshared Module std_math; + auto pkgids = new Identifier[1]; + pkgids[0] = Id.std; + return loadModuleFromLibrary(std_math, pkgids, Id.math); +} + +/********************************** + * Load a Module from the library. + * Params: + * mod = cached return value of this call + * pkgids = package identifiers + * modid = module id + * Returns: + * Module loaded, null if cannot load it + */ +extern (D) private static Module loadModuleFromLibrary(ref Module mod, Identifier[] pkgids, Identifier modid) +{ + if (mod) + return mod; + + auto imp = new Import(Loc.initial, pkgids[], modid, null, true); + // Module.load will call fatal() if there's no module available. + // Gag the error here, pushing the error handling to the caller. + const errors = global.startGagging(); + imp.load(null); + if (imp.mod) + { + imp.mod.importAll(null); + imp.mod.dsymbolSemantic(null); + } + global.endGagging(errors); + mod = imp.mod; + return mod; +} + /// Do an atomic operation (currently tailored to [shared] static ctors|dtors) needs private CallExp doAtomicOp (string op, Identifier var, Expression arg) { assert(op == "-=" || op == "+="); - Module mod = Module.loadCoreAtomic(); + Module mod = loadCoreAtomic(); if (!mod) return null; // core.atomic couldn't be loaded @@ -6488,7 +7281,7 @@ private extern(C++) class SearchVisitor : Visitor { //printf("ArrayScopeSymbol::search('%s', flags = %d)\n", ident.toChars(), flags); if (ident != Id.dollar) - return setResult(null); + return visit(cast(ScopeDsymbol)ass); VarDeclaration* pvar; Expression ce; @@ -6608,10 +7401,9 @@ private extern(C++) class SearchVisitor : Visitor { assert(0); } - auto tiargs = new Objects(); Expression edim = new IntegerExp(Loc.initial, dim, Type.tsize_t); edim = edim.expressionSemantic(ass._scope); - tiargs.push(edim); + auto tiargs = new Objects(edim); e = new DotTemplateInstanceExp(loc, ce, td.ident, tiargs); } else @@ -7406,7 +8198,7 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor const style = target.c.bitFieldStyle; if (style != TargetC.BitFieldStyle.MS && style != TargetC.BitFieldStyle.Gcc_Clang) - assert(0, "unsupported bit-field style"); + assert(0, "unsupported bitfield style"); const isMicrosoftStyle = style == TargetC.BitFieldStyle.MS; const contributesToAggregateAlignment = target.c.contributesToAggregateAlignment(bfd); @@ -7476,7 +8268,7 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor } else if (!isMicrosoftStyle) { - // If the bit-field spans more units of alignment than its type + // If the bitfield spans more units of alignment than its type // and is at the alignment boundary, start a new field at the // next alignment boundary. This affects when offsetof reports // a higher number and bitoffsetof starts at zero again. @@ -7622,14 +8414,56 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor isunion); // Add to the anon fields the base offset of this anonymous aggregate - //printf("anon fields, anonoffset = %d\n", anonoffset); - foreach (const i; fieldstart .. ad.fields.length) + //printf("anon fields, anonoffset = %d\n", anond.anonoffset); + if (anond.anonoffset) + anond.decl.foreachDsymbol( (s) => s.adjustBaseOffset(anond.anonoffset) ); + } + } +} + +// Adds `offset` as the new base offset of all field members in `d`. +private void adjustBaseOffset(Dsymbol d, uint offset) +{ + switch (d.dsym) + { + case DSYM.nspace: + auto ns = cast(Nspace)d; + ns.members.foreachDsymbol( s => s.adjustBaseOffset(offset) ); + break; + + case DSYM.templateMixin: + auto tm = cast(TemplateMixin)d; + tm.members.foreachDsymbol( s => s.adjustBaseOffset(offset) ); + break; + + case DSYM.anonDeclaration: + auto ad = cast(AnonDeclaration)d; + if (ad.decl) + ad.decl.foreachDsymbol( s => s.adjustBaseOffset(offset) ); + break; + + case DSYM.bitFieldDeclaration: + auto bfd = cast(BitFieldDeclaration)d; + bfd.offset += offset; + //printf("\t%s %d : %d\n", bfd.toChars(), bfd.offset, bfd.bitOffset); + break; + + default: + if (auto vd = d.isVarDeclaration()) { - VarDeclaration v = ad.fields[i]; - //printf("\t[%d] %s %d\n", i, v.toChars(), v.offset); - v.offset += anond.anonoffset; + if (vd.aliasTuple) + vd.aliasTuple.foreachVar( s => s.adjustBaseOffset(offset) ); + else if (vd.isField()) + { + vd.offset += offset; + //printf("\t%s %d\n", vd.toChars(), vd.offset); + } } - } + else if (auto atd = d.isAttribDeclaration()) + { + atd.include(null).foreachDsymbol( s => s.adjustBaseOffset(offset) ); + } + break; } } @@ -7989,8 +8823,6 @@ private Expression callScopeDtor(VarDeclaration vd, Scope* sc) const sz = vd.type.size(); assert(sz != SIZE_INVALID); - if (!sz) - return null; if (vd.type.toBasetype().ty == Tstruct) { @@ -8644,6 +9476,87 @@ bool _isZeroInit(Expression exp) } } +/*************************************** + * Calculate `ad.field[i].overlapped` and `overlapUnsafe`, and check that all of explicit + * field initializers have unique memory space on instance. + * Returns: + * true if any errors happen. + */ +private bool checkOverlappedFields(AggregateDeclaration ad) +{ + //printf("AggregateDeclaration::checkOverlappedFields() %s\n", toChars()); + assert(ad.sizeok == Sizeok.done); + size_t nfields = ad.fields.length; + if (ad.isNested()) + { + auto cd = ad.isClassDeclaration(); + if (!cd || !cd.baseClass || !cd.baseClass.isNested()) + nfields--; + if (ad.vthis2 && !(cd && cd.baseClass && cd.baseClass.vthis2)) + nfields--; + } + bool errors = false; + + // Fill in missing any elements with default initializers + foreach (i; 0 .. nfields) + { + auto vd = ad.fields[i]; + if (vd.errors) + { + errors = true; + continue; + } + + const vdIsVoidInit = vd._init && vd._init.isVoidInitializer(); + + // Find overlapped fields with the hole [vd.offset .. vd.offset.size()]. + foreach (j; 0 .. nfields) + { + if (i == j) + continue; + auto v2 = ad.fields[j]; + if (v2.errors) + { + errors = true; + continue; + } + if (!vd.isOverlappedWith(v2)) + continue; + + // vd and v2 are overlapping. + vd.overlapped = true; + v2.overlapped = true; + + if (!MODimplicitConv(vd.type.mod, v2.type.mod)) + v2.overlapUnsafe = true; + if (!MODimplicitConv(v2.type.mod, vd.type.mod)) + vd.overlapUnsafe = true; + + if (i > j) + continue; + + if (!v2._init) + continue; + + if (v2._init.isVoidInitializer()) + continue; + + if (vd._init && !vdIsVoidInit && v2._init) + { + .error(ad.loc, "overlapping default initialization for field `%s` and `%s`", v2.toChars(), vd.toChars()); + errors = true; + } + else if (v2._init && i < j) + { + .error(v2.loc, "union field `%s` with default initialization `%s` must be before field `%s`", + v2.toChars(), dmd.hdrgen.toChars(v2._init), vd.toChars()); + errors = true; + } + } + } + return errors; +} + private extern(C++) class FinalizeSizeVisitor : Visitor { alias visit = Visitor.visit; diff --git a/gcc/d/dmd/dtemplate.d b/gcc/d/dmd/dtemplate.d index 16e192da20f..b2a61dda441 100644 --- a/gcc/d/dmd/dtemplate.d +++ b/gcc/d/dmd/dtemplate.d @@ -53,13 +53,11 @@ import dmd.dinterpret; import dmd.dmodule; import dmd.dscope; import dmd.dsymbol; -import dmd.dsymbolsem : dsymbolSemantic, checkDeprecated, aliasSemantic, search, search_correct, setScope, importAll, include, hasStaticCtorOrDtor, oneMembers; +import dmd.dsymbolsem : aliasSemantic, oneMembers, toAlias; import dmd.errors; import dmd.errorsink; import dmd.expression; -import dmd.expressionsem : resolveLoc, expressionSemantic, resolveProperties, checkValue; import dmd.func; -import dmd.funcsem : functionSemantic, leastAsSpecialized, overloadApply; import dmd.globals; import dmd.hdrgen; import dmd.id; @@ -74,16 +72,11 @@ import dmd.optimize; import dmd.root.array; import dmd.common.outbuffer; import dmd.rootobject; -import dmd.semantic3 : semantic3; -import dmd.templatesem : matchWithInstance, formatParamsWithTiargs, leastAsSpecialized, declareParameter; +import dmd.templatesem : getExpression, TemplateInstance_semanticTiargs; import dmd.tokens; -import dmd.typesem : hasPointers, typeSemantic, merge, merge2, resolve, toDsymbol, - addStorageClass, isBaseOf, equivalent, sarrayOf, constOf, mutableOf, unSharedOf, - unqualify, aliasthisOf, castMod, substWildTo, addMod; +import dmd.typesem : typeSemantic, isBaseOf, resolveNamedArgs; import dmd.visitor; -import dmd.templateparamsem; - //debug = FindExistingInstance; // print debug stats of findExistingInstance private enum LOG = false; @@ -202,76 +195,6 @@ inout(Type) getType(inout RootObject o) } -/*********************************** - * If oarg represents a Dsymbol, return that Dsymbol - * Params: - * oarg = argument to check - * Returns: - * Dsymbol if a symbol, null if not - */ -Dsymbol getDsymbol(RootObject oarg) -{ - //printf("getDsymbol()\n"); - //printf("e %p s %p t %p v %p\n", isExpression(oarg), isDsymbol(oarg), isType(oarg), isTuple(oarg)); - if (auto ea = isExpression(oarg)) - { - // Try to convert Expression to symbol - if (auto ve = ea.isVarExp()) - return ve.var; - if (auto fe = ea.isFuncExp()) - return fe.td ? fe.td : fe.fd; - if (auto te = ea.isTemplateExp()) - return te.td; - if (auto te = ea.isScopeExp()) - return te.sds; - return null; - } - // Try to convert Type to symbol - if (auto ta = isType(oarg)) - return ta.toDsymbol(null); - return isDsymbol(oarg); // if already a symbol -} - - -private Expression getValue(ref Dsymbol s) -{ - if (s) - { - if (VarDeclaration v = s.isVarDeclaration()) - { - if (v.storage_class & STC.manifest) - return v.getConstInitializer(); - } - } - return null; -} - -/*********************** - * Try to get value from manifest constant - */ -private Expression getValue(Expression e) -{ - if (!e) - return null; - if (auto ve = e.isVarExp()) - { - if (auto v = ve.var.isVarDeclaration()) - { - if (v.storage_class & STC.manifest) - { - e = v.getConstInitializer(); - } - } - } - return e; -} - -private Expression getExpression(RootObject o) -{ - auto s = isDsymbol(o); - return s ? .getValue(s) : .getValue(isExpression(o)); -} - /****************************** * See if two objects match * Params: @@ -279,7 +202,7 @@ private Expression getExpression(RootObject o) * o2 = second object * Returns: true if they match */ -private bool match(RootObject o1, RootObject o2) +bool match(RootObject o1, RootObject o2) { enum log = false; @@ -766,66 +689,6 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol return visibility; } - /**************************** - * Destructively get the error message from the last constraint evaluation - * Params: - * tip = tip to show after printing all overloads - */ - const(char)* getConstraintEvalError(ref const(char)* tip) - { - import dmd.staticcond; - - // there will be a full tree view in verbose mode, and more compact list in the usual - const full = global.params.v.verbose; - uint count; - const msg = visualizeStaticCondition(constraint, lastConstraint, lastConstraintNegs[], full, count); - scope (exit) - { - lastConstraint = null; - lastConstraintTiargs = null; - lastConstraintNegs.setDim(0); - } - if (!msg) - return null; - - OutBuffer buf; - - assert(parameters && lastConstraintTiargs); - if (parameters.length > 0) - { - formatParamsWithTiargs(*parameters, *lastConstraintTiargs, isVariadic() !is null, buf); - buf.writenl(); - } - if (!full) - { - // choosing singular/plural - const s = (count == 1) ? - " must satisfy the following constraint:" : - " must satisfy one of the following constraints:"; - buf.writestring(s); - buf.writenl(); - // the constraints - buf.writeByte('`'); - buf.writestring(msg); - buf.writeByte('`'); - } - else - { - buf.writestring(" whose parameters have the following constraints:"); - buf.writenl(); - const sep = " `~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`"; - buf.writestring(sep); - buf.writenl(); - // the constraints - buf.writeByte('`'); - buf.writestring(msg); - buf.writeByte('`'); - buf.writestring(sep); - tip = "not satisfied constraints are marked with `>`"; - } - return buf.extractChars(); - } - debug (FindExistingInstance) { __gshared uint nFound, nNotFound, nAdded, nRemoved; @@ -947,4068 +810,1165 @@ extern (C++) final class TypeDeduced : Type argexps.push(e); tparams.push(tparam); } - - MATCH matchAll(Type tt) - { - MATCH match = MATCH.exact; - foreach (j, e; argexps) - { - assert(e); - if (e == emptyArrayElement) - continue; - - Type t = tt.addMod(tparams[j].mod).substWildTo(MODFlags.const_); - - MATCH m = e.implicitConvTo(t); - if (match > m) - match = m; - if (match == MATCH.nomatch) - break; - } - return match; - } } - /* ======================== Type ============================================ */ -/**** - * Given an identifier, figure out which TemplateParameter it is. - * Return IDX_NOTFOUND if not found. +/*********************************************************** + * Check whether the type t representation relies on one or more the template parameters. + * Params: + * t = Tested type, if null, returns false. + * tparams = Template parameters. + * iStart = Start index of tparams to limit the tested parameters. If it's + * nonzero, tparams[0..iStart] will be excluded from the test target. */ -private size_t templateIdentifierLookup(Identifier id, TemplateParameters* parameters) +bool reliesOnTident(Type t, TemplateParameters* tparams, size_t iStart = 0) { - for (size_t i = 0; i < parameters.length; i++) - { - TemplateParameter tp = (*parameters)[i]; - if (tp.ident.equals(id)) - return i; - } - return IDX_NOTFOUND; + return reliesOnTemplateParameters(t, (*tparams)[0 .. tparams.length]); } -size_t templateParameterLookup(Type tparam, TemplateParameters* parameters) +/*********************************************************** + * Check whether the type t representation relies on one or more the template parameters. + * Params: + * t = Tested type, if null, returns false. + * tparams = Template parameters. + */ +bool reliesOnTemplateParameters(Type t, TemplateParameter[] tparams) { - if (TypeIdentifier tident = tparam.isTypeIdentifier()) + bool visitVector(TypeVector t) { - //printf("\ttident = '%s'\n", tident.toChars()); - return templateIdentifierLookup(tident.ident, parameters); + return t.basetype.reliesOnTemplateParameters(tparams); } - return IDX_NOTFOUND; -} - -private auto X(T, U)(T m, U n) -{ - return (m << 4) | n; -} - -ubyte deduceWildHelper(Type t, Type* at, Type tparam) -{ - if ((tparam.mod & MODFlags.wild) == 0) - return 0; - *at = null; + bool visitAArray(TypeAArray t) + { + return t.next.reliesOnTemplateParameters(tparams) || + t.index.reliesOnTemplateParameters(tparams); + } - switch (X(tparam.mod, t.mod)) + bool visitFunction(TypeFunction t) { - case X(MODFlags.wild, 0): - case X(MODFlags.wild, MODFlags.const_): - case X(MODFlags.wild, MODFlags.shared_): - case X(MODFlags.wild, MODFlags.shared_ | MODFlags.const_): - case X(MODFlags.wild, MODFlags.immutable_): - case X(MODFlags.wildconst, 0): - case X(MODFlags.wildconst, MODFlags.const_): - case X(MODFlags.wildconst, MODFlags.shared_): - case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_): - case X(MODFlags.wildconst, MODFlags.immutable_): - case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_): - case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.const_): - case X(MODFlags.shared_ | MODFlags.wild, MODFlags.immutable_): - case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_): - case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_): - case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.immutable_): - { - ubyte wm = (t.mod & ~MODFlags.shared_); - if (wm == 0) - wm = MODFlags.mutable; - ubyte m = (t.mod & (MODFlags.const_ | MODFlags.immutable_)) | (tparam.mod & t.mod & MODFlags.shared_); - *at = t.unqualify(m); - return wm; - } - case X(MODFlags.wild, MODFlags.wild): - case X(MODFlags.wild, MODFlags.wildconst): - case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wild): - case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst): - case X(MODFlags.wildconst, MODFlags.wild): - case X(MODFlags.wildconst, MODFlags.wildconst): - case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild): - case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst): - case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wild): - case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst): - case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild): - case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst): + foreach (i, fparam; t.parameterList) { - *at = t.unqualify(tparam.mod & t.mod); - return MODFlags.wild; + if (fparam.type.reliesOnTemplateParameters(tparams)) + return true; } - default: - return 0; + return t.next.reliesOnTemplateParameters(tparams); } -} - -/** - * Returns the common type of the 2 types. - */ -private Type rawTypeMerge(Type t1, Type t2) -{ - if (t1.equals(t2)) - return t1; - if (t1.equivalent(t2)) - return t1.castMod(MODmerge(t1.mod, t2.mod)); - - auto t1b = t1.toBasetype(); - auto t2b = t2.toBasetype(); - if (t1b.equals(t2b)) - return t1b; - if (t1b.equivalent(t2b)) - return t1b.castMod(MODmerge(t1b.mod, t2b.mod)); - - auto ty = implicitConvCommonTy(t1b.ty, t2b.ty); - if (ty != Terror) - return Type.basic[ty]; - - return null; -} -MATCH deduceTypeHelper(Type t, out Type at, Type tparam) -{ - // 9*9 == 81 cases - switch (X(tparam.mod, t.mod)) + bool visitIdentifier(TypeIdentifier t) { - case X(0, 0): - case X(0, MODFlags.const_): - case X(0, MODFlags.wild): - case X(0, MODFlags.wildconst): - case X(0, MODFlags.shared_): - case X(0, MODFlags.shared_ | MODFlags.const_): - case X(0, MODFlags.shared_ | MODFlags.wild): - case X(0, MODFlags.shared_ | MODFlags.wildconst): - case X(0, MODFlags.immutable_): - // foo(U) T => T - // foo(U) const(T) => const(T) - // foo(U) inout(T) => inout(T) - // foo(U) inout(const(T)) => inout(const(T)) - // foo(U) shared(T) => shared(T) - // foo(U) shared(const(T)) => shared(const(T)) - // foo(U) shared(inout(T)) => shared(inout(T)) - // foo(U) shared(inout(const(T))) => shared(inout(const(T))) - // foo(U) immutable(T) => immutable(T) - { - at = t; - return MATCH.exact; - } - case X(MODFlags.const_, MODFlags.const_): - case X(MODFlags.wild, MODFlags.wild): - case X(MODFlags.wildconst, MODFlags.wildconst): - case X(MODFlags.shared_, MODFlags.shared_): - case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_ | MODFlags.const_): - case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wild): - case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst): - case X(MODFlags.immutable_, MODFlags.immutable_): - // foo(const(U)) const(T) => T - // foo(inout(U)) inout(T) => T - // foo(inout(const(U))) inout(const(T)) => T - // foo(shared(U)) shared(T) => T - // foo(shared(const(U))) shared(const(T)) => T - // foo(shared(inout(U))) shared(inout(T)) => T - // foo(shared(inout(const(U)))) shared(inout(const(T))) => T - // foo(immutable(U)) immutable(T) => T - { - at = t.mutableOf().unSharedOf(); - return MATCH.exact; - } - case X(MODFlags.const_, MODFlags.shared_ | MODFlags.const_): - case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wild): - case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst): - // foo(const(U)) shared(const(T)) => shared(T) - // foo(inout(U)) shared(inout(T)) => shared(T) - // foo(inout(const(U))) shared(inout(const(T))) => shared(T) - { - at = t.mutableOf(); - return MATCH.exact; - } - case X(MODFlags.const_, 0): - case X(MODFlags.const_, MODFlags.wild): - case X(MODFlags.const_, MODFlags.wildconst): - case X(MODFlags.const_, MODFlags.shared_ | MODFlags.wild): - case X(MODFlags.const_, MODFlags.shared_ | MODFlags.wildconst): - case X(MODFlags.const_, MODFlags.immutable_): - case X(MODFlags.shared_ | MODFlags.const_, MODFlags.immutable_): - // foo(const(U)) T => T - // foo(const(U)) inout(T) => T - // foo(const(U)) inout(const(T)) => T - // foo(const(U)) shared(inout(T)) => shared(T) - // foo(const(U)) shared(inout(const(T))) => shared(T) - // foo(const(U)) immutable(T) => T - // foo(shared(const(U))) immutable(T) => T - { - at = t.mutableOf(); - return MATCH.constant; - } - case X(MODFlags.const_, MODFlags.shared_): - // foo(const(U)) shared(T) => shared(T) - { - at = t; - return MATCH.constant; - } - case X(MODFlags.shared_, MODFlags.shared_ | MODFlags.const_): - case X(MODFlags.shared_, MODFlags.shared_ | MODFlags.wild): - case X(MODFlags.shared_, MODFlags.shared_ | MODFlags.wildconst): - // foo(shared(U)) shared(const(T)) => const(T) - // foo(shared(U)) shared(inout(T)) => inout(T) - // foo(shared(U)) shared(inout(const(T))) => inout(const(T)) - { - at = t.unSharedOf(); - return MATCH.exact; - } - case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_): - // foo(shared(const(U))) shared(T) => T - { - at = t.unSharedOf(); - return MATCH.constant; - } - case X(MODFlags.wildconst, MODFlags.immutable_): - case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_ | MODFlags.wildconst): - case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.immutable_): - case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild): - // foo(inout(const(U))) immutable(T) => T - // foo(shared(const(U))) shared(inout(const(T))) => T - // foo(shared(inout(const(U)))) immutable(T) => T - // foo(shared(inout(const(U)))) shared(inout(T)) => T + foreach (tp; tparams) { - at = t.unSharedOf().mutableOf(); - return MATCH.constant; + if (tp.ident.equals(t.ident)) + return true; } - case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_ | MODFlags.wild): - // foo(shared(const(U))) shared(inout(T)) => T + return false; + } + + bool visitInstance(TypeInstance t) + { + foreach (tp; tparams) { - at = t.unSharedOf().mutableOf(); - return MATCH.constant; + if (t.tempinst.name == tp.ident) + return true; } - case X(MODFlags.wild, 0): - case X(MODFlags.wild, MODFlags.const_): - case X(MODFlags.wild, MODFlags.wildconst): - case X(MODFlags.wild, MODFlags.immutable_): - case X(MODFlags.wild, MODFlags.shared_): - case X(MODFlags.wild, MODFlags.shared_ | MODFlags.const_): - case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst): - case X(MODFlags.wildconst, 0): - case X(MODFlags.wildconst, MODFlags.const_): - case X(MODFlags.wildconst, MODFlags.wild): - case X(MODFlags.wildconst, MODFlags.shared_): - case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_): - case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild): - case X(MODFlags.shared_, 0): - case X(MODFlags.shared_, MODFlags.const_): - case X(MODFlags.shared_, MODFlags.wild): - case X(MODFlags.shared_, MODFlags.wildconst): - case X(MODFlags.shared_, MODFlags.immutable_): - case X(MODFlags.shared_ | MODFlags.const_, 0): - case X(MODFlags.shared_ | MODFlags.const_, MODFlags.const_): - case X(MODFlags.shared_ | MODFlags.const_, MODFlags.wild): - case X(MODFlags.shared_ | MODFlags.const_, MODFlags.wildconst): - case X(MODFlags.shared_ | MODFlags.wild, 0): - case X(MODFlags.shared_ | MODFlags.wild, MODFlags.const_): - case X(MODFlags.shared_ | MODFlags.wild, MODFlags.wild): - case X(MODFlags.shared_ | MODFlags.wild, MODFlags.wildconst): - case X(MODFlags.shared_ | MODFlags.wild, MODFlags.immutable_): - case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_): - case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.const_): - case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst): - case X(MODFlags.shared_ | MODFlags.wildconst, 0): - case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.const_): - case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.wild): - case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.wildconst): - case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_): - case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_): - case X(MODFlags.immutable_, 0): - case X(MODFlags.immutable_, MODFlags.const_): - case X(MODFlags.immutable_, MODFlags.wild): - case X(MODFlags.immutable_, MODFlags.wildconst): - case X(MODFlags.immutable_, MODFlags.shared_): - case X(MODFlags.immutable_, MODFlags.shared_ | MODFlags.const_): - case X(MODFlags.immutable_, MODFlags.shared_ | MODFlags.wild): - case X(MODFlags.immutable_, MODFlags.shared_ | MODFlags.wildconst): - // foo(inout(U)) T => nomatch - // foo(inout(U)) const(T) => nomatch - // foo(inout(U)) inout(const(T)) => nomatch - // foo(inout(U)) immutable(T) => nomatch - // foo(inout(U)) shared(T) => nomatch - // foo(inout(U)) shared(const(T)) => nomatch - // foo(inout(U)) shared(inout(const(T))) => nomatch - // foo(inout(const(U))) T => nomatch - // foo(inout(const(U))) const(T) => nomatch - // foo(inout(const(U))) inout(T) => nomatch - // foo(inout(const(U))) shared(T) => nomatch - // foo(inout(const(U))) shared(const(T)) => nomatch - // foo(inout(const(U))) shared(inout(T)) => nomatch - // foo(shared(U)) T => nomatch - // foo(shared(U)) const(T) => nomatch - // foo(shared(U)) inout(T) => nomatch - // foo(shared(U)) inout(const(T)) => nomatch - // foo(shared(U)) immutable(T) => nomatch - // foo(shared(const(U))) T => nomatch - // foo(shared(const(U))) const(T) => nomatch - // foo(shared(const(U))) inout(T) => nomatch - // foo(shared(const(U))) inout(const(T)) => nomatch - // foo(shared(inout(U))) T => nomatch - // foo(shared(inout(U))) const(T) => nomatch - // foo(shared(inout(U))) inout(T) => nomatch - // foo(shared(inout(U))) inout(const(T)) => nomatch - // foo(shared(inout(U))) immutable(T) => nomatch - // foo(shared(inout(U))) shared(T) => nomatch - // foo(shared(inout(U))) shared(const(T)) => nomatch - // foo(shared(inout(U))) shared(inout(const(T))) => nomatch - // foo(shared(inout(const(U)))) T => nomatch - // foo(shared(inout(const(U)))) const(T) => nomatch - // foo(shared(inout(const(U)))) inout(T) => nomatch - // foo(shared(inout(const(U)))) inout(const(T)) => nomatch - // foo(shared(inout(const(U)))) shared(T) => nomatch - // foo(shared(inout(const(U)))) shared(const(T)) => nomatch - // foo(immutable(U)) T => nomatch - // foo(immutable(U)) const(T) => nomatch - // foo(immutable(U)) inout(T) => nomatch - // foo(immutable(U)) inout(const(T)) => nomatch - // foo(immutable(U)) shared(T) => nomatch - // foo(immutable(U)) shared(const(T)) => nomatch - // foo(immutable(U)) shared(inout(T)) => nomatch - // foo(immutable(U)) shared(inout(const(T))) => nomatch - return MATCH.nomatch; - default: - assert(0); + if (t.tempinst.tiargs) + foreach (arg; *t.tempinst.tiargs) + { + if (Type ta = isType(arg)) + { + if (ta.reliesOnTemplateParameters(tparams)) + return true; + } + } + + return false; + } + + bool visitTypeof(TypeTypeof t) + { + //printf("TypeTypeof.reliesOnTemplateParameters('%s')\n", t.toChars()); + return t.exp.reliesOnTemplateParameters(tparams); + } + + bool visitTuple(TypeTuple t) + { + if (t.arguments) + foreach (arg; *t.arguments) + { + if (arg.type.reliesOnTemplateParameters(tparams)) + return true; + } + + return false; + } + + if (!t) + return false; + + Type tb = t.toBasetype(); + switch (tb.ty) + { + case Tvector: return visitVector(tb.isTypeVector()); + case Taarray: return visitAArray(tb.isTypeAArray()); + case Tfunction: return visitFunction(tb.isTypeFunction()); + case Tident: return visitIdentifier(tb.isTypeIdentifier()); + case Tinstance: return visitInstance(tb.isTypeInstance()); + case Ttypeof: return visitTypeof(tb.isTypeTypeof()); + case Ttuple: return visitTuple(tb.isTypeTuple()); + case Tenum: return false; + default: return tb.nextOf().reliesOnTemplateParameters(tparams); } } -__gshared Expression emptyArrayElement = null; - -/* These form the heart of template argument deduction. - * Given 'this' being the type argument to the template instance, - * it is matched against the template declaration parameter specialization - * 'tparam' to determine the type to be used for the parameter. - * Example: - * template Foo(T:T*) // template declaration - * Foo!(int*) // template instantiation - * Input: - * this = int* - * tparam = T* - * parameters = [ T:T* ] // Array of TemplateParameter's - * Output: - * dedtypes = [ int ] // Array of Expression/Type's +/*********************************************************** + * Check whether the expression representation relies on one or more the template parameters. + * Params: + * e = expression to test + * tparams = Template parameters. + * Returns: + * true if it does */ -MATCH deduceType(RootObject o, Scope* sc, Type tparam, ref TemplateParameters parameters, ref Objects dedtypes, uint* wm = null, size_t inferStart = 0, bool ignoreAliasThis = false) +private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparams) { - extern (C++) final class DeduceType : Visitor + extern (C++) final class ReliesOnTemplateParameters : Visitor { alias visit = Visitor.visit; public: - MATCH result; + TemplateParameter[] tparams; + bool result; - extern (D) this() @safe + extern (D) this(TemplateParameter[] tparams) @safe { - result = MATCH.nomatch; + this.tparams = tparams; } - override void visit(Type t) + override void visit(Expression e) { - if (!tparam) - goto Lnomatch; - - if (t == tparam) - goto Lexact; + //printf("Expression.reliesOnTemplateParameters('%s')\n", e.toChars()); + } - if (tparam.ty == Tident) + override void visit(IdentifierExp e) + { + //printf("IdentifierExp.reliesOnTemplateParameters('%s')\n", e.toChars()); + foreach (tp; tparams) { - // Determine which parameter tparam is - size_t i = templateParameterLookup(tparam, ¶meters); - if (i == IDX_NOTFOUND) + if (e.ident == tp.ident) { - if (!sc) - goto Lnomatch; - - /* Need a loc to go with the semantic routine. - */ - Loc loc; - if (parameters.length) - { - TemplateParameter tp = parameters[0]; - loc = tp.loc; - } - - /* BUG: what if tparam is a template instance, that - * has as an argument another Tident? - */ - tparam = tparam.typeSemantic(loc, sc); - assert(tparam.ty != Tident); - result = deduceType(t, sc, tparam, parameters, dedtypes, wm); + result = true; return; } + } + } - TemplateParameter tp = parameters[i]; - - TypeIdentifier tident = tparam.isTypeIdentifier(); - if (tident.idents.length > 0) + override void visit(TupleExp e) + { + //printf("TupleExp.reliesOnTemplateParameters('%s')\n", e.toChars()); + if (e.exps) + { + foreach (ea; *e.exps) { - //printf("matching %s to %s\n", tparam.toChars(), t.toChars()); - Dsymbol s = t.toDsymbol(sc); - for (size_t j = tident.idents.length; j-- > 0;) - { - RootObject id = tident.idents[j]; - if (id.dyncast() == DYNCAST.identifier) - { - if (!s || !s.parent) - goto Lnomatch; - Dsymbol s2 = s.parent.search(Loc.initial, cast(Identifier)id); - if (!s2) - goto Lnomatch; - s2 = s2.toAlias(); - //printf("[%d] s = %s %s, s2 = %s %s\n", j, s.kind(), s.toChars(), s2.kind(), s2.toChars()); - if (s != s2) - { - if (Type tx = s2.getType()) - { - if (s != tx.toDsymbol(sc)) - goto Lnomatch; - } - else - goto Lnomatch; - } - s = s.parent; - } - else - goto Lnomatch; - } - //printf("[e] s = %s\n", s?s.toChars():"(null)"); - if (tp.isTemplateTypeParameter()) - { - Type tt = s.getType(); - if (!tt) - goto Lnomatch; - Type at = cast(Type)dedtypes[i]; - if (at && at.ty == Tnone) - at = (cast(TypeDeduced)at).tded; - if (!at || tt.equals(at)) - { - dedtypes[i] = tt; - goto Lexact; - } - } - if (tp.isTemplateAliasParameter()) - { - Dsymbol s2 = cast(Dsymbol)dedtypes[i]; - if (!s2 || s == s2) - { - dedtypes[i] = s; - goto Lexact; - } - } - goto Lnomatch; + ea.accept(this); + if (result) + return; } + } + } - // Found the corresponding parameter tp - /+ - https://issues.dlang.org/show_bug.cgi?id=23578 - To pattern match: - static if (is(S!int == S!av, alias av)) - - We eventually need to deduce `int` (Tint32 [0]) and `av` (Tident). - Previously this would not get pattern matched at all, but now we check if the - template parameter `av` came from. - - This note has been left to serve as a hint for further explorers into - how IsExp matching works. - +/ - if (auto ta = tp.isTemplateAliasParameter()) - { - dedtypes[i] = t; - goto Lexact; - } - // (23578) - ensure previous behaviour for non-alias template params - if (!tp.isTemplateTypeParameter()) + override void visit(ArrayLiteralExp e) + { + //printf("ArrayLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars()); + if (e.elements) + { + foreach (el; *e.elements) { - goto Lnomatch; + el.accept(this); + if (result) + return; } + } + } - Type at = cast(Type)dedtypes[i]; - Type tt; - if (ubyte wx = wm ? deduceWildHelper(t, &tt, tparam) : 0) - { - // type vs (none) - if (!at) - { - dedtypes[i] = tt; - *wm |= wx; - result = MATCH.constant; - return; - } - - // type vs expressions - if (at.ty == Tnone) - { - auto xt = cast(TypeDeduced)at; - result = xt.matchAll(tt); - if (result > MATCH.nomatch) - { - dedtypes[i] = tt; - if (result > MATCH.constant) - result = MATCH.constant; // limit level for inout matches - } - return; - } - - // type vs type - if (tt.equals(at)) - { - dedtypes[i] = tt; // Prefer current type match - goto Lconst; - } - if (tt.implicitConvTo(at.constOf())) - { - dedtypes[i] = at.constOf().mutableOf(); - *wm |= MODFlags.const_; - goto Lconst; - } - if (at.implicitConvTo(tt.constOf())) - { - dedtypes[i] = tt.constOf().mutableOf(); - *wm |= MODFlags.const_; - goto Lconst; - } - goto Lnomatch; - } - else if (MATCH m = deduceTypeHelper(t, tt, tparam)) - { - // type vs (none) - if (!at) - { - dedtypes[i] = tt; - result = m; - return; - } - - // type vs expressions - if (at.ty == Tnone) - { - auto xt = cast(TypeDeduced)at; - result = xt.matchAll(tt); - if (result > MATCH.nomatch) - { - dedtypes[i] = tt; - } - return; - } - - // type vs type - if (tt.equals(at)) - { - goto Lexact; - } - if (tt.ty == Tclass && at.ty == Tclass) - { - result = tt.implicitConvTo(at); - return; - } - if (tt.ty == Tsarray && at.ty == Tarray && tt.nextOf().implicitConvTo(at.nextOf()) >= MATCH.constant) - { - goto Lexact; - } - } - goto Lnomatch; - } - - if (tparam.ty == Ttypeof) - { - /* Need a loc to go with the semantic routine. - */ - Loc loc; - if (parameters.length) - { - TemplateParameter tp = parameters[0]; - loc = tp.loc; - } - - tparam = tparam.typeSemantic(loc, sc); - } - if (t.ty != tparam.ty) + override void visit(AssocArrayLiteralExp e) + { + //printf("AssocArrayLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars()); + foreach (ek; *e.keys) { - if (Dsymbol sym = t.toDsymbol(sc)) - { - if (sym.isforwardRef() && !tparam.deco) - goto Lnomatch; - } - - MATCH m = t.implicitConvTo(tparam); - if (m == MATCH.nomatch && !ignoreAliasThis) - { - if (auto tc = t.isTypeClass()) - { - if (tc.sym.aliasthis && !(tc.att & AliasThisRec.tracingDT)) - { - if (auto ato = t.aliasthisOf()) - { - tc.att = cast(AliasThisRec)(tc.att | AliasThisRec.tracingDT); - m = deduceType(ato, sc, tparam, parameters, dedtypes, wm); - tc.att = cast(AliasThisRec)(tc.att & ~AliasThisRec.tracingDT); - } - } - } - else if (auto ts = t.isTypeStruct()) - { - if (ts.sym.aliasthis && !(ts.att & AliasThisRec.tracingDT)) - { - if (auto ato = t.aliasthisOf()) - { - ts.att = cast(AliasThisRec)(ts.att | AliasThisRec.tracingDT); - m = deduceType(ato, sc, tparam, parameters, dedtypes, wm); - ts.att = cast(AliasThisRec)(ts.att & ~AliasThisRec.tracingDT); - } - } - } - } - result = m; - return; + ek.accept(this); + if (result) + return; } - - if (t.nextOf()) + foreach (ev; *e.values) { - if (tparam.deco && !tparam.hasWild()) - { - result = t.implicitConvTo(tparam); + ev.accept(this); + if (result) return; - } - - Type tpn = tparam.nextOf(); - if (wm && t.ty == Taarray && tparam.isWild()) - { - // https://issues.dlang.org/show_bug.cgi?id=12403 - // In IFTI, stop inout matching on transitive part of AA types. - tpn = tpn.substWildTo(MODFlags.mutable); - } - - result = deduceType(t.nextOf(), sc, tpn, parameters, dedtypes, wm); - return; } - - Lexact: - result = MATCH.exact; - return; - - Lnomatch: - result = MATCH.nomatch; - return; - - Lconst: - result = MATCH.constant; } - override void visit(TypeVector t) + override void visit(StructLiteralExp e) { - if (auto tp = tparam.isTypeVector()) + //printf("StructLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars()); + if (e.elements) { - result = deduceType(t.basetype, sc, tp.basetype, parameters, dedtypes, wm); - return; + foreach (ea; *e.elements) + { + ea.accept(this); + if (result) + return; + } } - visit(cast(Type)t); } - override void visit(TypeDArray t) + override void visit(TypeExp e) { - visit(cast(Type)t); + //printf("TypeExp.reliesOnTemplateParameters('%s')\n", e.toChars()); + result = e.type.reliesOnTemplateParameters(tparams); } - override void visit(TypeSArray t) + override void visit(NewExp e) { - // Extra check that array dimensions must match - if (!tparam) - { - visit(cast(Type)t); - return; - } - - if (tparam.ty == Tarray) - { - MATCH m = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm); - result = (m >= MATCH.constant) ? MATCH.convert : MATCH.nomatch; - return; - } - - TemplateParameter tp = null; - Expression edim = null; - size_t i; - if (auto tsa = tparam.isTypeSArray()) + //printf("NewExp.reliesOnTemplateParameters('%s')\n", e.toChars()); + if (e.placement) + e.placement.accept(this); + if (e.thisexp) + e.thisexp.accept(this); + result = e.newtype.reliesOnTemplateParameters(tparams); + if (!result && e.arguments) { - if (tsa.dim.isVarExp() && tsa.dim.isVarExp().var.storage_class & STC.templateparameter) + foreach (ea; *e.arguments) { - Identifier id = tsa.dim.isVarExp().var.ident; - i = templateIdentifierLookup(id, ¶meters); - assert(i != IDX_NOTFOUND); - tp = parameters[i]; + ea.accept(this); + if (result) + return; } - else - edim = tsa.dim; } - else if (auto taa = tparam.isTypeAArray()) - { - i = templateParameterLookup(taa.index, ¶meters); - if (i != IDX_NOTFOUND) - tp = parameters[i]; - else - { - Loc loc; - // The "type" (it hasn't been resolved yet) of the function parameter - // does not have a location but the parameter it is related to does, - // so we use that for the resolution (better error message). - if (inferStart < parameters.length) - { - TemplateParameter loctp = parameters[inferStart]; - loc = loctp.loc; - } + } - Expression e; - Type tx; - Dsymbol s; - taa.index.resolve(loc, sc, e, tx, s); - edim = s ? getValue(s) : getValue(e); - } - } - if ((tp && tp.matchArg(sc, t.dim, i, ¶meters, dedtypes, null)) || - (edim && edim.isIntegerExp() && edim.toInteger() == t.dim.toInteger()) - ) - { - result = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm); - return; - } + override void visit(NewAnonClassExp e) + { + //printf("NewAnonClassExp.reliesOnTemplateParameters('%s')\n", e.toChars()); + result = true; + } + + override void visit(FuncExp e) + { + //printf("FuncExp.reliesOnTemplateParameters('%s')\n", e.toChars()); + result = true; + } - visit(cast(Type)t); + override void visit(TypeidExp e) + { + //printf("TypeidExp.reliesOnTemplateParameters('%s')\n", e.toChars()); + if (auto ea = isExpression(e.obj)) + ea.accept(this); + else if (auto ta = isType(e.obj)) + result = ta.reliesOnTemplateParameters(tparams); } - override void visit(TypeAArray t) + override void visit(TraitsExp e) { - // Extra check that index type must match - if (tparam && tparam.ty == Taarray) + //printf("TraitsExp.reliesOnTemplateParameters('%s')\n", e.toChars()); + if (e.args) { - TypeAArray tp = tparam.isTypeAArray(); - if (!deduceType(t.index, sc, tp.index, parameters, dedtypes)) + foreach (oa; *e.args) { - result = MATCH.nomatch; - return; + if (auto ea = isExpression(oa)) + ea.accept(this); + else if (auto ta = isType(oa)) + result = ta.reliesOnTemplateParameters(tparams); + if (result) + return; } } - visit(cast(Type)t); } - override void visit(TypeFunction t) + override void visit(IsExp e) { - // Extra check that function characteristics must match - if (!tparam) - return visit(cast(Type)t); - - auto tp = tparam.isTypeFunction(); - if (!tp) - { - visit(cast(Type)t); - return; - } + //printf("IsExp.reliesOnTemplateParameters('%s')\n", e.toChars()); + result = e.targ.reliesOnTemplateParameters(tparams); + } - if (t.parameterList.varargs != tp.parameterList.varargs || t.linkage != tp.linkage) - { - result = MATCH.nomatch; - return; - } + override void visit(UnaExp e) + { + //printf("UnaExp.reliesOnTemplateParameters('%s')\n", e.toChars()); + e.e1.accept(this); + } - foreach (fparam; *tp.parameterList.parameters) + override void visit(DotTemplateInstanceExp e) + { + //printf("DotTemplateInstanceExp.reliesOnTemplateParameters('%s')\n", e.toChars()); + visit(e.isUnaExp()); + if (!result && e.ti.tiargs) { - // https://issues.dlang.org/show_bug.cgi?id=2579 - // Apply function parameter storage classes to parameter types - fparam.type = fparam.type.addStorageClass(fparam.storageClass); - fparam.storageClass &= ~STC.TYPECTOR; - - // https://issues.dlang.org/show_bug.cgi?id=15243 - // Resolve parameter type if it's not related with template parameters - if (!reliesOnTemplateParameters(fparam.type, parameters[inferStart .. parameters.length])) + foreach (oa; *e.ti.tiargs) { - auto tx = fparam.type.typeSemantic(Loc.initial, sc); - if (tx.ty == Terror) - { - result = MATCH.nomatch; + if (auto ea = isExpression(oa)) + ea.accept(this); + else if (auto ta = isType(oa)) + result = ta.reliesOnTemplateParameters(tparams); + if (result) return; - } - fparam.type = tx; } } + } - const size_t nfargs = t.parameterList.length; - size_t nfparams = tp.parameterList.length; - - /* See if tuple match - */ - if (nfparams > 0 && nfargs >= nfparams - 1) + override void visit(CallExp e) + { + //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars()); + visit(e.isUnaExp()); + if (!result && e.arguments) { - /* See if 'A' of the template parameter matches 'A' - * of the type of the last function parameter. - */ - Parameter fparam = tp.parameterList[nfparams - 1]; - assert(fparam); - assert(fparam.type); - if (fparam.type.ty != Tident) - goto L1; - TypeIdentifier tid = fparam.type.isTypeIdentifier(); - if (tid.idents.length) - goto L1; - - /* Look through parameters to find tuple matching tid.ident - */ - size_t tupi = 0; - for (; 1; tupi++) - { - if (tupi == parameters.length) - goto L1; - TemplateParameter tx = parameters[tupi]; - TemplateTupleParameter tup = tx.isTemplateTupleParameter(); - if (tup && tup.ident.equals(tid.ident)) - break; - } - - /* The types of the function arguments [nfparams - 1 .. nfargs] - * now form the tuple argument. - */ - size_t tuple_dim = nfargs - (nfparams - 1); - - /* See if existing tuple, and whether it matches or not - */ - RootObject o = dedtypes[tupi]; - if (o) + foreach (ea; *e.arguments) { - // Existing deduced argument must be a tuple, and must match - Tuple tup = isTuple(o); - if (!tup || tup.objects.length != tuple_dim) - { - result = MATCH.nomatch; + ea.accept(this); + if (result) return; - } - for (size_t i = 0; i < tuple_dim; i++) - { - Parameter arg = t.parameterList[nfparams - 1 + i]; - if (!arg.type.equals(tup.objects[i])) - { - result = MATCH.nomatch; - return; - } - } - } - else - { - // Create new tuple - auto tup = new Tuple(tuple_dim); - for (size_t i = 0; i < tuple_dim; i++) - { - Parameter arg = t.parameterList[nfparams - 1 + i]; - tup.objects[i] = arg.type; - } - dedtypes[tupi] = tup; } - nfparams--; // don't consider the last parameter for type deduction - goto L2; - } - - L1: - if (nfargs != nfparams) - { - result = MATCH.nomatch; - return; } - L2: - assert(nfparams <= tp.parameterList.length); - foreach (i, ap; tp.parameterList) - { - if (i == nfparams) - break; - - Parameter a = t.parameterList[i]; + } - if (!a.isCovariant(t.isRef, ap) || - !deduceType(a.type, sc, ap.type, parameters, dedtypes)) - { - result = MATCH.nomatch; - return; - } - } + override void visit(CastExp e) + { + //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars()); + visit(e.isUnaExp()); + // e.to can be null for cast() with no type + if (!result && e.to) + result = e.to.reliesOnTemplateParameters(tparams); + } - visit(cast(Type)t); + override void visit(SliceExp e) + { + //printf("SliceExp.reliesOnTemplateParameters('%s')\n", e.toChars()); + visit(e.isUnaExp()); + if (!result && e.lwr) + e.lwr.accept(this); + if (!result && e.upr) + e.upr.accept(this); } - override void visit(TypeIdentifier t) + override void visit(IntervalExp e) { - // Extra check - if (tparam && tparam.ty == Tident) - { - TypeIdentifier tp = tparam.isTypeIdentifier(); - for (size_t i = 0; i < t.idents.length; i++) - { - RootObject id1 = t.idents[i]; - RootObject id2 = tp.idents[i]; - if (!id1.equals(id2)) - { - result = MATCH.nomatch; - return; - } - } - } - visit(cast(Type)t); + //printf("IntervalExp.reliesOnTemplateParameters('%s')\n", e.toChars()); + e.lwr.accept(this); + if (!result) + e.upr.accept(this); } - override void visit(TypeInstance t) + override void visit(ArrayExp e) { - // Extra check - if (!tparam || tparam.ty != Tinstance || !t.tempinst.tempdecl) + //printf("ArrayExp.reliesOnTemplateParameters('%s')\n", e.toChars()); + visit(e.isUnaExp()); + if (!result && e.arguments) { - visit(cast(Type)t); - return; + foreach (ea; *e.arguments) + ea.accept(this); } + } - TemplateDeclaration tempdecl = t.tempinst.tempdecl.isTemplateDeclaration(); - assert(tempdecl); - - TypeInstance tp = tparam.isTypeInstance(); + override void visit(BinExp e) + { + //printf("BinExp.reliesOnTemplateParameters('%s')\n", e.toChars()); + e.e1.accept(this); + if (!result) + e.e2.accept(this); + } - //printf("tempinst.tempdecl = %p\n", tempdecl); - //printf("tp.tempinst.tempdecl = %p\n", tp.tempinst.tempdecl); - if (!tp.tempinst.tempdecl) - { - //printf("tp.tempinst.name = '%s'\n", tp.tempinst.name.toChars()); - - /* Handle case of: - * template Foo(T : sa!(T), alias sa) - */ - size_t i = templateIdentifierLookup(tp.tempinst.name, ¶meters); - if (i == IDX_NOTFOUND) - { - /* Didn't find it as a parameter identifier. Try looking - * it up and seeing if is an alias. - * https://issues.dlang.org/show_bug.cgi?id=1454 - */ - auto tid = new TypeIdentifier(tp.loc, tp.tempinst.name); - Type tx; - Expression e; - Dsymbol s; - tid.resolve(tp.loc, sc, e, tx, s); - if (tx) - { - s = tx.toDsymbol(sc); - if (TemplateInstance ti = s ? s.parent.isTemplateInstance() : null) - { - // https://issues.dlang.org/show_bug.cgi?id=14290 - // Try to match with ti.tempecl, - // only when ti is an enclosing instance. - Dsymbol p = sc.parent; - while (p && p != ti) - p = p.parent; - if (p) - s = ti.tempdecl; - } - } - if (s) - { - s = s.toAlias(); - TemplateDeclaration td = s.isTemplateDeclaration(); - if (td) - { - if (td.overroot) - td = td.overroot; - for (; td; td = td.overnext) - { - if (td == tempdecl) - goto L2; - } - } - } - goto Lnomatch; - } + override void visit(CondExp e) + { + //printf("BinExp.reliesOnTemplateParameters('%s')\n", e.toChars()); + e.econd.accept(this); + if (!result) + visit(e.isBinExp()); + } + } - TemplateParameter tpx = parameters[i]; - if (!tpx.matchArg(sc, tempdecl, i, ¶meters, dedtypes, null)) - goto Lnomatch; - } - else if (tempdecl != tp.tempinst.tempdecl) - goto Lnomatch; + scope ReliesOnTemplateParameters v = new ReliesOnTemplateParameters(tparams); + e.accept(v); + return v.result; +} - L2: - if (!resolveTemplateInstantiation(sc, ¶meters, t.tempinst.tiargs, &t.tempinst.tdtypes, tempdecl, tp, &dedtypes)) - goto Lnomatch; +/*********************************************************** + * https://dlang.org/spec/template.html#TemplateParameter + */ +extern (C++) class TemplateParameter : ASTNode +{ + Loc loc; + Identifier ident; - visit(cast(Type)t); - return; + /* True if this is a part of precedent parameter specialization pattern. + * + * template A(T : X!TL, alias X, TL...) {} + * // X and TL are dependent template parameter + * + * A dependent template parameter should return MATCH.exact in matchArg() + * to respect the match level of the corresponding precedent parameter. + */ + bool dependent; - Lnomatch: - //printf("no match\n"); - result = MATCH.nomatch; - } + /* ======================== TemplateParameter =============================== */ + extern (D) this(Loc loc, Identifier ident) @safe + { + this.loc = loc; + this.ident = ident; + } - override void visit(TypeStruct t) - { - /* If this struct is a template struct, and we're matching - * it against a template instance, convert the struct type - * to a template instance, too, and try again. - */ - TemplateInstance ti = t.sym.parent.isTemplateInstance(); + TemplateTypeParameter isTemplateTypeParameter() + { + return null; + } - if (tparam && tparam.ty == Tinstance) - { - if (ti && ti.toAlias() == t.sym) - { - auto tx = new TypeInstance(Loc.initial, ti); - auto m = deduceType(tx, sc, tparam, parameters, dedtypes, wm); - // if we have a no match we still need to check alias this - if (m != MATCH.nomatch) - { - result = m; - return; - } - } + TemplateValueParameter isTemplateValueParameter() + { + return null; + } - /* Match things like: - * S!(T).foo - */ - TypeInstance tpi = tparam.isTypeInstance(); - if (tpi.idents.length) - { - RootObject id = tpi.idents[tpi.idents.length - 1]; - if (id.dyncast() == DYNCAST.identifier && t.sym.ident.equals(cast(Identifier)id)) - { - Type tparent = t.sym.parent.getType(); - if (tparent) - { - /* Slice off the .foo in S!(T).foo - */ - tpi.idents.length--; - result = deduceType(tparent, sc, tpi, parameters, dedtypes, wm); - tpi.idents.length++; - return; - } - } - } - } + TemplateAliasParameter isTemplateAliasParameter() + { + return null; + } - // Extra check - if (tparam && tparam.ty == Tstruct) - { - TypeStruct tp = tparam.isTypeStruct(); + TemplateThisParameter isTemplateThisParameter() + { + return null; + } - //printf("\t%d\n", cast(MATCH) t.implicitConvTo(tp)); - if (wm && t.deduceWild(tparam, false)) - { - result = MATCH.constant; - return; - } - result = t.implicitConvTo(tp); - return; - } - visit(cast(Type)t); - } + TemplateTupleParameter isTemplateTupleParameter() + { + return null; + } - override void visit(TypeEnum t) - { - // Extra check - if (tparam && tparam.ty == Tenum) - { - TypeEnum tp = tparam.isTypeEnum(); - if (t.sym == tp.sym) - visit(cast(Type)t); - else - result = MATCH.nomatch; - return; - } - Type tb = t.toBasetype(); - if (tb.ty == tparam.ty || tb.ty == Tsarray && tparam.ty == Taarray) - { - result = deduceType(tb, sc, tparam, parameters, dedtypes, wm); - if (result == MATCH.exact) - result = MATCH.convert; - return; - } - visit(cast(Type)t); - } + abstract TemplateParameter syntaxCopy(); - override void visit(TypeClass t) - { - //printf("TypeClass.deduceType(this = %s)\n", t.toChars()); + abstract bool declareParameter(Scope* sc); - /* If this class is a template class, and we're matching - * it against a template instance, convert the class type - * to a template instance, too, and try again. - */ - TemplateInstance ti = t.sym.parent.isTemplateInstance(); + abstract void print(RootObject oarg, RootObject oded); - if (tparam && tparam.ty == Tinstance) - { - if (ti && ti.toAlias() == t.sym) - { - auto tx = new TypeInstance(Loc.initial, ti); - MATCH m = deduceType(tx, sc, tparam, parameters, dedtypes, wm); - // Even if the match fails, there is still a chance it could match - // a base class. - if (m != MATCH.nomatch) - { - result = m; - return; - } - } + abstract RootObject specialization(); - /* Match things like: - * S!(T).foo - */ - TypeInstance tpi = tparam.isTypeInstance(); - if (tpi.idents.length) - { - RootObject id = tpi.idents[tpi.idents.length - 1]; - if (id.dyncast() == DYNCAST.identifier && t.sym.ident.equals(cast(Identifier)id)) - { - Type tparent = t.sym.parent.getType(); - if (tparent) - { - /* Slice off the .foo in S!(T).foo - */ - tpi.idents.length--; - result = deduceType(tparent, sc, tpi, parameters, dedtypes, wm); - tpi.idents.length++; - return; - } - } - } + abstract bool hasDefaultArg(); - // If it matches exactly or via implicit conversion, we're done - visit(cast(Type)t); - if (result != MATCH.nomatch) - return; + override const(char)* toChars() const + { + return this.ident.toChars(); + } - /* There is still a chance to match via implicit conversion to - * a base class or interface. Because there could be more than one such - * match, we need to check them all. - */ + override DYNCAST dyncast() const + { + return DYNCAST.templateparameter; + } - int numBaseClassMatches = 0; // Have we found an interface match? + override void accept(Visitor v) + { + v.visit(this); + } +} - // Our best guess at dedtypes - auto best = new Objects(dedtypes.length); +/*********************************************************** + * https://dlang.org/spec/template.html#TemplateTypeParameter + * Syntax: + * ident : specType = defaultType + */ +extern (C++) class TemplateTypeParameter : TemplateParameter +{ + Type specType; // if !=null, this is the type specialization + Type defaultType; - ClassDeclaration s = t.sym; - while (s && s.baseclasses.length > 0) - { - // Test the base class - deduceBaseClassParameters(*(*s.baseclasses)[0], sc, tparam, parameters, dedtypes, *best, numBaseClassMatches); + extern (D) __gshared Type tdummy = null; - // Test the interfaces inherited by the base class - foreach (b; s.interfaces) - { - deduceBaseClassParameters(*b, sc, tparam, parameters, dedtypes, *best, numBaseClassMatches); - } - s = (*s.baseclasses)[0].sym; - } + extern (D) this(Loc loc, Identifier ident, Type specType, Type defaultType) @safe + { + super(loc, ident); + this.specType = specType; + this.defaultType = defaultType; + } - if (numBaseClassMatches == 0) - { - result = MATCH.nomatch; - return; - } + override final TemplateTypeParameter isTemplateTypeParameter() + { + return this; + } - // If we got at least one match, copy the known types into dedtypes - memcpy(dedtypes.tdata(), best.tdata(), best.length * (void*).sizeof); - result = MATCH.convert; - return; - } + override TemplateTypeParameter syntaxCopy() + { + return new TemplateTypeParameter(loc, ident, specType ? specType.syntaxCopy() : null, defaultType ? defaultType.syntaxCopy() : null); + } - // Extra check - if (tparam && tparam.ty == Tclass) - { - TypeClass tp = tparam.isTypeClass(); + override final bool declareParameter(Scope* sc) + { + //printf("TemplateTypeParameter.declareParameter('%s')\n", ident.toChars()); + auto ti = new TypeIdentifier(loc, ident); + Declaration ad = new AliasDeclaration(loc, ident, ti); + return sc.insert(ad) !is null; + } - //printf("\t%d\n", cast(MATCH) t.implicitConvTo(tp)); - if (wm && t.deduceWild(tparam, false)) - { - result = MATCH.constant; - return; - } - result = t.implicitConvTo(tp); - return; - } - visit(cast(Type)t); - } + override final void print(RootObject oarg, RootObject oded) + { + printf(" %s\n", ident.toChars()); - override void visit(Expression e) - { - //printf("Expression.deduceType(e = %s)\n", e.toChars()); - size_t i = templateParameterLookup(tparam, ¶meters); - if (i == IDX_NOTFOUND || tparam.isTypeIdentifier().idents.length > 0) - { - if (e == emptyArrayElement && tparam.ty == Tarray) - { - Type tn = (cast(TypeNext)tparam).next; - result = deduceType(emptyArrayElement, sc, tn, parameters, dedtypes, wm); - return; - } - e.type.accept(this); - return; - } + Type t = isType(oarg); + Type ta = isType(oded); + assert(ta); - TemplateTypeParameter tp = parameters[i].isTemplateTypeParameter(); - if (!tp) - return; // nomatch + if (specType) + printf("\tSpecialization: %s\n", specType.toChars()); + if (defaultType) + printf("\tDefault: %s\n", defaultType.toChars()); + printf("\tParameter: %s\n", t ? t.toChars() : "NULL"); + printf("\tDeduced Type: %s\n", ta.toChars()); + } - if (e == emptyArrayElement) - { - if (dedtypes[i]) - { - result = MATCH.exact; - return; - } - if (tp.defaultType) - { - tp.defaultType.accept(this); - return; - } - } + override final RootObject specialization() + { + return specType; + } - /* Returns `true` if `t` is a reference type, or an array of reference types - */ - bool isTopRef(Type t) - { - auto tb = t.baseElemOf(); - return tb.ty == Tclass || - tb.ty == Taarray || - tb.ty == Tstruct && tb.hasPointers(); - } + override final bool hasDefaultArg() + { + return defaultType !is null; + } - Type at = cast(Type)dedtypes[i]; - Type tt; - if (ubyte wx = deduceWildHelper(e.type, &tt, tparam)) - { - *wm |= wx; - result = MATCH.constant; - } - else if (MATCH m = deduceTypeHelper(e.type, tt, tparam)) - { - result = m; - } - else if (!isTopRef(e.type)) - { - /* https://issues.dlang.org/show_bug.cgi?id=15653 - * In IFTI, recognize top-qualifier conversions - * through the value copy, e.g. - * int --> immutable(int) - * immutable(string[]) --> immutable(string)[] - */ - tt = e.type.mutableOf(); - result = MATCH.convert; - } - else - return; // nomatch + override void accept(Visitor v) + { + v.visit(this); + } +} - // expression vs (none) - if (!at) - { - dedtypes[i] = new TypeDeduced(tt, e, tparam); - return; - } +/*********************************************************** + * https://dlang.org/spec/template.html#TemplateThisParameter + * Syntax: + * this ident : specType = defaultType + */ +extern (C++) final class TemplateThisParameter : TemplateTypeParameter +{ + extern (D) this(Loc loc, Identifier ident, Type specType, Type defaultType) @safe + { + super(loc, ident, specType, defaultType); + } - TypeDeduced xt = null; - if (at.ty == Tnone) - { - xt = cast(TypeDeduced)at; - at = xt.tded; - } + override TemplateThisParameter isTemplateThisParameter() + { + return this; + } - // From previous matched expressions to current deduced type - MATCH match1 = xt ? xt.matchAll(tt) : MATCH.nomatch; + override TemplateThisParameter syntaxCopy() + { + return new TemplateThisParameter(loc, ident, specType ? specType.syntaxCopy() : null, defaultType ? defaultType.syntaxCopy() : null); + } - // From current expressions to previous deduced type - Type pt = at.addMod(tparam.mod); - if (*wm) - pt = pt.substWildTo(*wm); - MATCH match2 = e.implicitConvTo(pt); + override void accept(Visitor v) + { + v.visit(this); + } +} - if (match1 > MATCH.nomatch && match2 > MATCH.nomatch) - { - if (at.implicitConvTo(tt) == MATCH.nomatch) - match1 = MATCH.nomatch; // Prefer at - else if (tt.implicitConvTo(at) == MATCH.nomatch) - match2 = MATCH.nomatch; // Prefer tt - else if (tt.isTypeBasic() && tt.ty == at.ty && tt.mod != at.mod) - { - if (!tt.isMutable() && !at.isMutable()) - tt = tt.mutableOf().addMod(MODmerge(tt.mod, at.mod)); - else if (tt.isMutable()) - { - if (at.mod == 0) // Prefer unshared - match1 = MATCH.nomatch; - else - match2 = MATCH.nomatch; - } - else if (at.isMutable()) - { - if (tt.mod == 0) // Prefer unshared - match2 = MATCH.nomatch; - else - match1 = MATCH.nomatch; - } - //printf("tt = %s, at = %s\n", tt.toChars(), at.toChars()); - } - else - { - match1 = MATCH.nomatch; - match2 = MATCH.nomatch; - } - } - if (match1 > MATCH.nomatch) - { - // Prefer current match: tt - if (xt) - xt.update(tt, e, tparam); - else - dedtypes[i] = tt; - result = match1; - return; - } - if (match2 > MATCH.nomatch) - { - // Prefer previous match: (*dedtypes)[i] - if (xt) - xt.update(e, tparam); - result = match2; - return; - } - - /* Deduce common type - */ - if (Type t = rawTypeMerge(at, tt)) - { - if (xt) - xt.update(t, e, tparam); - else - dedtypes[i] = t; - - pt = tt.addMod(tparam.mod); - if (*wm) - pt = pt.substWildTo(*wm); - result = e.implicitConvTo(pt); - return; - } - - result = MATCH.nomatch; - } - - MATCH deduceEmptyArrayElement() - { - if (!emptyArrayElement) - { - emptyArrayElement = new IdentifierExp(Loc.initial, Id.p); // dummy - emptyArrayElement.type = Type.tvoid; - } - assert(tparam.ty == Tarray); - - Type tn = (cast(TypeNext)tparam).next; - return deduceType(emptyArrayElement, sc, tn, parameters, dedtypes, wm); - } +/*********************************************************** + * https://dlang.org/spec/template.html#TemplateValueParameter + * Syntax: + * valType ident : specValue = defaultValue + */ +extern (C++) final class TemplateValueParameter : TemplateParameter +{ + Type valType; + Expression specValue; + Expression defaultValue; - override void visit(NullExp e) - { - if (tparam.ty == Tarray && e.type.ty == Tnull) - { - // tparam:T[] <- e:null (void[]) - result = deduceEmptyArrayElement(); - return; - } - visit(cast(Expression)e); - } + extern (D) __gshared Expression[void*] edummies; - override void visit(StringExp e) - { - Type taai; - if (e.type.ty == Tarray && (tparam.ty == Tsarray || tparam.ty == Taarray && (taai = (cast(TypeAArray)tparam).index).ty == Tident && (cast(TypeIdentifier)taai).idents.length == 0)) - { - // Consider compile-time known boundaries - e.type.nextOf().sarrayOf(e.len).accept(this); - return; - } - visit(cast(Expression)e); - } + extern (D) this(Loc loc, Identifier ident, Type valType, + Expression specValue, Expression defaultValue) @safe + { + super(loc, ident); + this.valType = valType; + this.specValue = specValue; + this.defaultValue = defaultValue; + } - override void visit(ArrayLiteralExp e) - { - // https://issues.dlang.org/show_bug.cgi?id=20092 - if (e.elements && e.elements.length && e.type.toBasetype().nextOf().ty == Tvoid) - { - result = deduceEmptyArrayElement(); - return; - } - if ((!e.elements || !e.elements.length) && e.type.toBasetype().nextOf().ty == Tvoid && tparam.ty == Tarray) - { - // tparam:T[] <- e:[] (void[]) - result = deduceEmptyArrayElement(); - return; - } + override TemplateValueParameter isTemplateValueParameter() + { + return this; + } - if (tparam.ty == Tarray && e.elements && e.elements.length) - { - Type tn = (cast(TypeDArray)tparam).next; - result = MATCH.exact; - if (e.basis) - { - MATCH m = deduceType(e.basis, sc, tn, parameters, dedtypes, wm); - if (m < result) - result = m; - } - foreach (el; *e.elements) - { - if (result == MATCH.nomatch) - break; - if (!el) - continue; - MATCH m = deduceType(el, sc, tn, parameters, dedtypes, wm); - if (m < result) - result = m; - } - return; - } + override TemplateValueParameter syntaxCopy() + { + return new TemplateValueParameter(loc, ident, + valType.syntaxCopy(), + specValue ? specValue.syntaxCopy() : null, + defaultValue ? defaultValue.syntaxCopy() : null); + } - Type taai; - if (e.type.ty == Tarray && (tparam.ty == Tsarray || tparam.ty == Taarray && (taai = (cast(TypeAArray)tparam).index).ty == Tident && (cast(TypeIdentifier)taai).idents.length == 0)) - { - // Consider compile-time known boundaries - e.type.nextOf().sarrayOf(e.elements.length).accept(this); - return; - } - visit(cast(Expression)e); - } + override bool declareParameter(Scope* sc) + { + /* + Do type semantic earlier. - override void visit(AssocArrayLiteralExp e) - { - if (tparam.ty == Taarray && e.keys && e.keys.length) - { - TypeAArray taa = cast(TypeAArray)tparam; - result = MATCH.exact; - foreach (i, key; *e.keys) - { - MATCH m1 = deduceType(key, sc, taa.index, parameters, dedtypes, wm); - if (m1 < result) - result = m1; - if (result == MATCH.nomatch) - break; - MATCH m2 = deduceType((*e.values)[i], sc, taa.next, parameters, dedtypes, wm); - if (m2 < result) - result = m2; - if (result == MATCH.nomatch) - break; - } - return; - } - visit(cast(Expression)e); - } + This means for certain erroneous value parameters + their "type" can be known earlier and thus a better + error message given. - override void visit(FuncExp e) - { - //printf("e.type = %s, tparam = %s\n", e.type.toChars(), tparam.toChars()); - if (e.td) - { - Type to = tparam; - if (!to.nextOf()) - return; - auto tof = to.nextOf().isTypeFunction(); - if (!tof) - return; + For example: + `template test(x* x) {}` + now yields "undefined identifier" rather than the opaque + "variable `x` is used as a type". + */ + if (valType) + valType = valType.typeSemantic(loc, sc); + auto v = new VarDeclaration(loc, valType, ident, null); + v.storage_class = STC.templateparameter; + return sc.insert(v) !is null; + } - // Parameter types inference from 'tof' - assert(e.td._scope); - TypeFunction tf = e.fd.type.isTypeFunction(); - //printf("\ttof = %s\n", tof.toChars()); - //printf("\ttf = %s\n", tf.toChars()); - const dim = tf.parameterList.length; + override void print(RootObject oarg, RootObject oded) + { + printf(" %s\n", ident.toChars()); + Expression ea = isExpression(oded); + if (specValue) + printf("\tSpecialization: %s\n", specValue.toChars()); + printf("\tParameter Value: %s\n", ea ? ea.toChars() : "NULL"); + } - if (tof.parameterList.length != dim || tof.parameterList.varargs != tf.parameterList.varargs) - return; + override RootObject specialization() + { + return specValue; + } - auto tiargs = new Objects(); - tiargs.reserve(e.td.parameters.length); + override bool hasDefaultArg() + { + return defaultValue !is null; + } - foreach (tp; *e.td.parameters) - { - size_t u = 0; - foreach (i, p; tf.parameterList) - { - if (p.type.ty == Tident && (cast(TypeIdentifier)p.type).ident == tp.ident) - break; - ++u; - } - assert(u < dim); - Parameter pto = tof.parameterList[u]; - if (!pto) - break; - Type t = pto.type.syntaxCopy(); // https://issues.dlang.org/show_bug.cgi?id=11774 - if (reliesOnTemplateParameters(t, parameters[inferStart .. parameters.length])) - return; - t = t.typeSemantic(e.loc, sc); - if (t.ty == Terror) - return; - tiargs.push(t); - } + override void accept(Visitor v) + { + v.visit(this); + } +} - // Set target of return type inference - if (!tf.next && tof.next) - e.fd.treq = tparam; +/*********************************************************** + * https://dlang.org/spec/template.html#TemplateAliasParameter + * Syntax: + * specType ident : specAlias = defaultAlias + */ +extern (C++) final class TemplateAliasParameter : TemplateParameter +{ + Type specType; + RootObject specAlias; + RootObject defaultAlias; - auto ti = new TemplateInstance(e.loc, e.td, tiargs); - Expression ex = (new ScopeExp(e.loc, ti)).expressionSemantic(e.td._scope); + extern (D) __gshared Dsymbol sdummy = null; - // Reset inference target for the later re-semantic - e.fd.treq = null; + extern (D) this(Loc loc, Identifier ident, Type specType, RootObject specAlias, RootObject defaultAlias) @safe + { + super(loc, ident); + this.specType = specType; + this.specAlias = specAlias; + this.defaultAlias = defaultAlias; + } - if (ex.op == EXP.error) - return; - if (ex.op != EXP.function_) - return; - visit(ex.type); - return; - } + override TemplateAliasParameter isTemplateAliasParameter() + { + return this; + } - Type t = e.type; + override TemplateAliasParameter syntaxCopy() + { + return new TemplateAliasParameter(loc, ident, specType ? specType.syntaxCopy() : null, objectSyntaxCopy(specAlias), objectSyntaxCopy(defaultAlias)); + } - if (t.ty == Tdelegate && tparam.ty == Tpointer) - return; + override bool declareParameter(Scope* sc) + { + auto ti = new TypeIdentifier(loc, ident); + Declaration ad = new AliasDeclaration(loc, ident, ti); + return sc.insert(ad) !is null; + } - // Allow conversion from implicit function pointer to delegate - if (e.tok == TOK.reserved && t.ty == Tpointer && tparam.ty == Tdelegate) - { - TypeFunction tf = t.nextOf().isTypeFunction(); - t = (new TypeDelegate(tf)).merge(); - } - //printf("tparam = %s <= e.type = %s, t = %s\n", tparam.toChars(), e.type.toChars(), t.toChars()); - visit(t); - } + override void print(RootObject oarg, RootObject oded) + { + printf(" %s\n", ident.toChars()); + Dsymbol sa = isDsymbol(oded); + assert(sa); + printf("\tParameter alias: %s\n", sa.toChars()); + } - override void visit(SliceExp e) - { - Type taai; - if (e.type.ty == Tarray && (tparam.ty == Tsarray || tparam.ty == Taarray && (taai = (cast(TypeAArray)tparam).index).ty == Tident && (cast(TypeIdentifier)taai).idents.length == 0)) - { - // Consider compile-time known boundaries - if (Type tsa = toStaticArrayType(e)) - { - tsa.accept(this); - if (result > MATCH.convert) - result = MATCH.convert; // match with implicit conversion at most - return; - } - } - visit(cast(Expression)e); - } + override RootObject specialization() + { + return specAlias; + } - override void visit(CommaExp e) - { - e.e2.accept(this); - } + override bool hasDefaultArg() + { + return defaultAlias !is null; } - scope DeduceType v = new DeduceType(); - if (Type t = isType(o)) - t.accept(v); - else if (Expression e = isExpression(o)) + override void accept(Visitor v) { - assert(wm); - e.accept(v); + v.visit(this); } - else - assert(0); - return v.result; } - -/* Helper for TypeClass.deduceType(). - * Classes can match with implicit conversion to a base class or interface. - * This is complicated, because there may be more than one base class which - * matches. In such cases, one or more parameters remain ambiguous. - * For example, - * - * interface I(X, Y) {} - * class C : I(uint, double), I(char, double) {} - * C x; - * foo(T, U)( I!(T, U) x) - * - * deduces that U is double, but T remains ambiguous (could be char or uint). - * - * Given a baseclass b, and initial deduced types 'dedtypes', this function - * tries to match tparam with b, and also tries all base interfaces of b. - * If a match occurs, numBaseClassMatches is incremented, and the new deduced - * types are ANDed with the current 'best' estimate for dedtypes. +/*********************************************************** + * https://dlang.org/spec/template.html#TemplateSequenceParameter + * Syntax: + * ident ... */ -private void deduceBaseClassParameters(ref BaseClass b, Scope* sc, Type tparam, ref TemplateParameters parameters, ref Objects dedtypes, ref Objects best, ref int numBaseClassMatches) +extern (C++) final class TemplateTupleParameter : TemplateParameter { - if (TemplateInstance parti = b.sym ? b.sym.parent.isTemplateInstance() : null) + extern (D) this(Loc loc, Identifier ident) @safe { - // Make a temporary copy of dedtypes so we don't destroy it - auto tmpdedtypes = new Objects(dedtypes.length); - memcpy(tmpdedtypes.tdata(), dedtypes.tdata(), dedtypes.length * (void*).sizeof); + super(loc, ident); + } - auto t = new TypeInstance(Loc.initial, parti); - MATCH m = deduceType(t, sc, tparam, parameters, *tmpdedtypes); - if (m > MATCH.nomatch) - { - // If this is the first ever match, it becomes our best estimate - if (numBaseClassMatches == 0) - memcpy(best.tdata(), tmpdedtypes.tdata(), tmpdedtypes.length * (void*).sizeof); - else - for (size_t k = 0; k < tmpdedtypes.length; ++k) - { - // If we've found more than one possible type for a parameter, - // mark it as unknown. - if ((*tmpdedtypes)[k] != best[k]) - best[k] = dedtypes[k]; - } - ++numBaseClassMatches; - } + override TemplateTupleParameter isTemplateTupleParameter() + { + return this; } - // Now recursively test the inherited interfaces - foreach (ref bi; b.baseInterfaces) + override TemplateTupleParameter syntaxCopy() { - deduceBaseClassParameters(bi, sc, tparam, parameters, dedtypes, best, numBaseClassMatches); + return new TemplateTupleParameter(loc, ident); } -} -/******************** - * Match template `parameters` to the target template instance. - * Example: - * struct Temp(U, int Z) {} - * void foo(T)(Temp!(T, 3)); - * foo(Temp!(int, 3)()); - * Input: - * sc = context - * parameters = template params of foo -> [T] - * tiargs = .tiargs -> [int, 3] - * tdtypes = .tdtypes -> [int, 3] - * tempdecl = -> [T, Z] - * tp = - * Output: - * dedtypes = deduced params of `foo(Temp!(int, 3)())` -> [int] - */ -private bool resolveTemplateInstantiation(Scope* sc, TemplateParameters* parameters, Objects* tiargs, Objects* tdtypes, TemplateDeclaration tempdecl, TypeInstance tp, Objects* dedtypes) -{ - for (size_t i = 0; 1; i++) + override bool declareParameter(Scope* sc) { - //printf("\ttest: tempinst.tiargs[%zu]\n", i); - RootObject o1 = null; - if (i < tiargs.length) - o1 = (*tiargs)[i]; - else if (i < tdtypes.length && i < tp.tempinst.tiargs.length) - { - // Pick up default arg - o1 = (*tdtypes)[i]; - } - else if (i >= tp.tempinst.tiargs.length) - break; - //printf("\ttest: o1 = %s\n", o1.toChars()); - if (i >= tp.tempinst.tiargs.length) - { - size_t dim = tempdecl.parameters.length - (tempdecl.isVariadic() ? 1 : 0); - while (i < dim && ((*tempdecl.parameters)[i].dependent || (*tempdecl.parameters)[i].hasDefaultArg())) - { - i++; - } - if (i >= dim) - break; // match if all remained parameters are dependent - return false; - } + auto ti = new TypeIdentifier(loc, ident); + Declaration ad = new AliasDeclaration(loc, ident, ti); + return sc.insert(ad) !is null; + } - RootObject o2 = (*tp.tempinst.tiargs)[i]; - Type t2 = isType(o2); - //printf("\ttest: o2 = %s\n", o2.toChars()); - size_t j = (t2 && t2.ty == Tident && i == tp.tempinst.tiargs.length - 1) - ? templateParameterLookup(t2, parameters) : IDX_NOTFOUND; - if (j != IDX_NOTFOUND && j == parameters.length - 1 && - (*parameters)[j].isTemplateTupleParameter()) - { - /* Given: - * struct A(B...) {} - * alias A!(int, float) X; - * static if (is(X Y == A!(Z), Z...)) {} - * deduce that Z is a tuple(int, float) - */ - - /* Create tuple from remaining args - */ - size_t vtdim = (tempdecl.isVariadic() ? tiargs.length : tdtypes.length) - i; - auto vt = new Tuple(vtdim); - for (size_t k = 0; k < vtdim; k++) - { - RootObject o; - if (k < tiargs.length) - o = (*tiargs)[i + k]; - else // Pick up default arg - o = (*tdtypes)[i + k]; - vt.objects[k] = o; - } + override void print(RootObject oarg, RootObject oded) + { + printf(" %s... [", ident.toChars()); + Tuple v = isTuple(oded); + assert(v); - Tuple v = cast(Tuple)(*dedtypes)[j]; - if (v) - { - if (!match(v, vt)) - return false; - } - else - (*dedtypes)[j] = vt; - break; - } - else if (!o1) - break; - - Type t1 = isType(o1); - Dsymbol s1 = isDsymbol(o1); - Dsymbol s2 = isDsymbol(o2); - Expression e1 = s1 ? getValue(s1) : getValue(isExpression(o1)); - Expression e2 = isExpression(o2); - version (none) + //printf("|%d| ", v.objects.length); + foreach (i, o; v.objects) { - Tuple v1 = isTuple(o1); - Tuple v2 = isTuple(o2); - if (t1) - printf("t1 = %s\n", t1.toChars()); - if (t2) - printf("t2 = %s\n", t2.toChars()); - if (e1) - printf("e1 = %s\n", e1.toChars()); - if (e2) - printf("e2 = %s\n", e2.toChars()); - if (s1) - printf("s1 = %s\n", s1.toChars()); - if (s2) - printf("s2 = %s\n", s2.toChars()); - if (v1) - printf("v1 = %s\n", v1.toChars()); - if (v2) - printf("v2 = %s\n", v2.toChars()); - } + if (i) + printf(", "); - if (t1 && t2) - { - if (!deduceType(t1, sc, t2, *parameters, *dedtypes)) - return false; - } - else if (e1 && e2) - { - Le: - e1 = e1.ctfeInterpret(); + Dsymbol sa = isDsymbol(o); + if (sa) + printf("alias: %s", sa.toChars()); + Type ta = isType(o); + if (ta) + printf("type: %s", ta.toChars()); + Expression ea = isExpression(o); + if (ea) + printf("exp: %s", ea.toChars()); - /* If it is one of the template parameters for this template, - * we should not attempt to interpret it. It already has a value. - */ - if (e2.op == EXP.variable && (e2.isVarExp().var.storage_class & STC.templateparameter)) - { - /* - * (T:Number!(e2), int e2) - */ - j = templateIdentifierLookup(e2.isVarExp().var.ident, parameters); - if (j != IDX_NOTFOUND) - goto L1; - // The template parameter was not from this template - // (it may be from a parent template, for example) - } + assert(!isTuple(o)); // no nested Tuple arguments + } + printf("]\n"); + } - e2 = e2.expressionSemantic(sc); // https://issues.dlang.org/show_bug.cgi?id=13417 - e2 = e2.ctfeInterpret(); + override RootObject specialization() + { + return null; + } - //printf("e1 = %s, type = %s %d\n", e1.toChars(), e1.type.toChars(), e1.type.ty); - //printf("e2 = %s, type = %s %d\n", e2.toChars(), e2.type.toChars(), e2.type.ty); - if (!e1.equals(e2)) - { - if (!e2.implicitConvTo(e1.type)) - return false; + override bool hasDefaultArg() + { + return false; + } - e2 = e2.implicitCastTo(sc, e1.type); - e2 = e2.ctfeInterpret(); - if (!e1.equals(e2)) - return false; - } - } - else if (e1 && t2 && t2.ty == Tident) - { - j = templateParameterLookup(t2, parameters); - L1: - if (j == IDX_NOTFOUND) - { - t2.resolve((cast(TypeIdentifier)t2).loc, sc, e2, t2, s2); - if (e2) - goto Le; - return false; - } - if (!(*parameters)[j].matchArg(sc, e1, j, parameters, *dedtypes, null)) - return false; - } - else if (s1 && s2) - { - Ls: - if (!s1.equals(s2)) - return false; - } - else if (s1 && t2 && t2.ty == Tident) - { - j = templateParameterLookup(t2, parameters); - if (j == IDX_NOTFOUND) - { - t2.resolve((cast(TypeIdentifier)t2).loc, sc, e2, t2, s2); - if (s2) - goto Ls; - return false; - } - if (!(*parameters)[j].matchArg(sc, s1, j, parameters, *dedtypes, null)) - return false; - } - else - return false; + override void accept(Visitor v) + { + v.visit(this); } - return true; } - /*********************************************************** - * Check whether the type t representation relies on one or more the template parameters. - * Params: - * t = Tested type, if null, returns false. - * tparams = Template parameters. - * iStart = Start index of tparams to limit the tested parameters. If it's - * nonzero, tparams[0..iStart] will be excluded from the test target. + * https://dlang.org/spec/template.html#explicit_tmp_instantiation + * Given: + * foo!(args) => + * name = foo + * tiargs = args */ -bool reliesOnTident(Type t, TemplateParameters* tparams, size_t iStart = 0) +extern (C++) class TemplateInstance : ScopeDsymbol { - return reliesOnTemplateParameters(t, (*tparams)[0 .. tparams.length]); -} + Identifier name; -/*********************************************************** - * Check whether the type t representation relies on one or more the template parameters. - * Params: - * t = Tested type, if null, returns false. - * tparams = Template parameters. - */ -bool reliesOnTemplateParameters(Type t, TemplateParameter[] tparams) -{ - bool visitVector(TypeVector t) + // Array of Types/Expressions of template + // instance arguments [int*, char, 10*10] + Objects* tiargs; + + // Array of Types/Expressions corresponding + // to TemplateDeclaration.parameters + // [int, char, 100] + Objects tdtypes; + + // Modules imported by this template instance + Modules importedModules; + + Dsymbol tempdecl; // referenced by foo.bar.abc + Dsymbol enclosing; // if referencing local symbols, this is the context + Dsymbol aliasdecl; // !=null if instance is an alias for its sole member + + /** + If this is not null and it has a value that is not the current object, + then this field points to an existing template instance + and that object has been duplicated into us. + + If this object is a duplicate, + the ``memberOf`` field will be set to a root module (passed on CLI). + + This information is useful to deduplicate analysis that may occur + after semantic 3 has completed. + + See_Also: memberOf + */ + TemplateInstance inst; + + ScopeDsymbol argsym; // argument symbol table + size_t hash; // cached result of toHash() + + /// For function template, these are the function fnames(name and loc of it) and arguments + /// Relevant because different resolutions of `auto ref` parameters + /// create different template instances even with the same template arguments + Expressions* fargs; + ArgumentLabels* fnames; + + TemplateInstances* deferred; + + /** + If this is not null then this template instance appears in a root module's members. + + Note: This is not useful for determining duplication status of this template instance. + Use the field ``inst`` for determining if a template instance has been duplicated into this object. + + See_Also: inst + */ + Module memberOf; + + // Used to determine the instance needs code generation. + // Note that these are inaccurate until semantic analysis phase completed. + TemplateInstance tinst; // enclosing template instance + TemplateInstance tnext; // non-first instantiated instances + Module minst; // the top module that instantiated this instance + + private ushort _nest; // for recursive pretty printing detection, 3 MSBs reserved for flags (below) + ubyte inuse; // for recursive expansion detection + + private enum Flag : uint { - return t.basetype.reliesOnTemplateParameters(tparams); + semantictiargsdone = 1u << (_nest.sizeof * 8 - 1), // MSB of _nest + havetempdecl = semantictiargsdone >> 1, + gagged = semantictiargsdone >> 2, + available = gagged - 1 // always last flag minus one, 1s for all available bits } - bool visitAArray(TypeAArray t) + extern(D) final @safe @property pure nothrow @nogc { - return t.next.reliesOnTemplateParameters(tparams) || - t.index.reliesOnTemplateParameters(tparams); + ushort nest() const { return _nest & Flag.available; } + void nestUp() { assert(nest() < Flag.available); ++_nest; } + void nestDown() { assert(nest() > 0); --_nest; } + /// has semanticTiargs() been done? + bool semantictiargsdone() const { return (_nest & Flag.semantictiargsdone) != 0; } + void semantictiargsdone(bool x) + { + if (x) _nest |= Flag.semantictiargsdone; + else _nest &= ~Flag.semantictiargsdone; + } + /// if used second constructor + bool havetempdecl() const { return (_nest & Flag.havetempdecl) != 0; } + void havetempdecl(bool x) + { + if (x) _nest |= Flag.havetempdecl; + else _nest &= ~Flag.havetempdecl; + } + /// if the instantiation is done with error gagging + bool gagged() const { return (_nest & Flag.gagged) != 0; } + void gagged(bool x) + { + if (x) _nest |= Flag.gagged; + else _nest &= ~Flag.gagged; + } } - bool visitFunction(TypeFunction t) + extern (D) this(Loc loc, Identifier ident, Objects* tiargs) scope { - foreach (i, fparam; t.parameterList) + super(loc, null); + static if (LOG) { - if (fparam.type.reliesOnTemplateParameters(tparams)) - return true; + printf("TemplateInstance(this = %p, ident = '%s')\n", this, ident ? ident.toChars() : "null"); } - return t.next.reliesOnTemplateParameters(tparams); + this.dsym = DSYM.templateInstance; + this.name = ident; + this.tiargs = tiargs; } - bool visitIdentifier(TypeIdentifier t) + /***************** + * This constructor is only called when we figured out which function + * template to instantiate. + */ + extern (D) this(Loc loc, TemplateDeclaration td, Objects* tiargs) scope { - foreach (tp; tparams) + super(loc, null); + static if (LOG) { - if (tp.ident.equals(t.ident)) - return true; + printf("TemplateInstance(this = %p, tempdecl = '%s')\n", this, td.toChars()); } - return false; + this.dsym = DSYM.templateInstance; + this.name = td.ident; + this.tiargs = tiargs; + this.tempdecl = td; + this.semantictiargsdone = true; + this.havetempdecl = true; + assert(tempdecl._scope); } - bool visitInstance(TypeInstance t) + extern (D) static Objects* arraySyntaxCopy(Objects* objs) { - foreach (tp; tparams) + Objects* a = null; + if (objs) { - if (t.tempinst.name == tp.ident) - return true; + a = new Objects(objs.length); + foreach (i, o; *objs) + (*a)[i] = objectSyntaxCopy(o); } - - if (t.tempinst.tiargs) - foreach (arg; *t.tempinst.tiargs) - { - if (Type ta = isType(arg)) - { - if (ta.reliesOnTemplateParameters(tparams)) - return true; - } - } - - return false; + return a; } - bool visitTypeof(TypeTypeof t) + override TemplateInstance syntaxCopy(Dsymbol s) { - //printf("TypeTypeof.reliesOnTemplateParameters('%s')\n", t.toChars()); - return t.exp.reliesOnTemplateParameters(tparams); + TemplateInstance ti = s ? cast(TemplateInstance)s : new TemplateInstance(loc, name, null); + ti.tiargs = arraySyntaxCopy(tiargs); + TemplateDeclaration td; + if (inst && tempdecl && (td = tempdecl.isTemplateDeclaration()) !is null) + td.ScopeDsymbol.syntaxCopy(ti); + else + ScopeDsymbol.syntaxCopy(ti); + return ti; } - bool visitTuple(TypeTuple t) + override const(char)* kind() const { - if (t.arguments) - foreach (arg; *t.arguments) - { - if (arg.type.reliesOnTemplateParameters(tparams)) - return true; - } + return "template instance"; + } - return false; - } - - if (!t) - return false; - - Type tb = t.toBasetype(); - switch (tb.ty) - { - case Tvector: return visitVector(tb.isTypeVector()); - case Taarray: return visitAArray(tb.isTypeAArray()); - case Tfunction: return visitFunction(tb.isTypeFunction()); - case Tident: return visitIdentifier(tb.isTypeIdentifier()); - case Tinstance: return visitInstance(tb.isTypeInstance()); - case Ttypeof: return visitTypeof(tb.isTypeTypeof()); - case Ttuple: return visitTuple(tb.isTypeTuple()); - case Tenum: return false; - default: return tb.nextOf().reliesOnTemplateParameters(tparams); - } -} - -/*********************************************************** - * Check whether the expression representation relies on one or more the template parameters. - * Params: - * e = expression to test - * tparams = Template parameters. - * Returns: - * true if it does - */ -private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparams) -{ - extern (C++) final class ReliesOnTemplateParameters : Visitor - { - alias visit = Visitor.visit; - public: - TemplateParameter[] tparams; - bool result; - - extern (D) this(TemplateParameter[] tparams) @safe - { - this.tparams = tparams; - } - - override void visit(Expression e) - { - //printf("Expression.reliesOnTemplateParameters('%s')\n", e.toChars()); - } - - override void visit(IdentifierExp e) - { - //printf("IdentifierExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - foreach (tp; tparams) - { - if (e.ident == tp.ident) - { - result = true; - return; - } - } - } - - override void visit(TupleExp e) - { - //printf("TupleExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - if (e.exps) - { - foreach (ea; *e.exps) - { - ea.accept(this); - if (result) - return; - } - } - } - - override void visit(ArrayLiteralExp e) - { - //printf("ArrayLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - if (e.elements) - { - foreach (el; *e.elements) - { - el.accept(this); - if (result) - return; - } - } - } - - override void visit(AssocArrayLiteralExp e) - { - //printf("AssocArrayLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - foreach (ek; *e.keys) - { - ek.accept(this); - if (result) - return; - } - foreach (ev; *e.values) - { - ev.accept(this); - if (result) - return; - } - } - - override void visit(StructLiteralExp e) - { - //printf("StructLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - if (e.elements) - { - foreach (ea; *e.elements) - { - ea.accept(this); - if (result) - return; - } - } - } - - override void visit(TypeExp e) - { - //printf("TypeExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - result = e.type.reliesOnTemplateParameters(tparams); - } - - override void visit(NewExp e) - { - //printf("NewExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - if (e.placement) - e.placement.accept(this); - if (e.thisexp) - e.thisexp.accept(this); - result = e.newtype.reliesOnTemplateParameters(tparams); - if (!result && e.arguments) - { - foreach (ea; *e.arguments) - { - ea.accept(this); - if (result) - return; - } - } - } - - override void visit(NewAnonClassExp e) - { - //printf("NewAnonClassExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - result = true; - } - - override void visit(FuncExp e) - { - //printf("FuncExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - result = true; - } - - override void visit(TypeidExp e) - { - //printf("TypeidExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - if (auto ea = isExpression(e.obj)) - ea.accept(this); - else if (auto ta = isType(e.obj)) - result = ta.reliesOnTemplateParameters(tparams); - } - - override void visit(TraitsExp e) - { - //printf("TraitsExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - if (e.args) - { - foreach (oa; *e.args) - { - if (auto ea = isExpression(oa)) - ea.accept(this); - else if (auto ta = isType(oa)) - result = ta.reliesOnTemplateParameters(tparams); - if (result) - return; - } - } - } - - override void visit(IsExp e) - { - //printf("IsExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - result = e.targ.reliesOnTemplateParameters(tparams); - } - - override void visit(UnaExp e) - { - //printf("UnaExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - e.e1.accept(this); - } - - override void visit(DotTemplateInstanceExp e) - { - //printf("DotTemplateInstanceExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - visit(e.isUnaExp()); - if (!result && e.ti.tiargs) - { - foreach (oa; *e.ti.tiargs) - { - if (auto ea = isExpression(oa)) - ea.accept(this); - else if (auto ta = isType(oa)) - result = ta.reliesOnTemplateParameters(tparams); - if (result) - return; - } - } - } - - override void visit(CallExp e) - { - //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - visit(e.isUnaExp()); - if (!result && e.arguments) - { - foreach (ea; *e.arguments) - { - ea.accept(this); - if (result) - return; - } - } - } - - override void visit(CastExp e) - { - //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - visit(e.isUnaExp()); - // e.to can be null for cast() with no type - if (!result && e.to) - result = e.to.reliesOnTemplateParameters(tparams); - } - - override void visit(SliceExp e) - { - //printf("SliceExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - visit(e.isUnaExp()); - if (!result && e.lwr) - e.lwr.accept(this); - if (!result && e.upr) - e.upr.accept(this); - } - - override void visit(IntervalExp e) - { - //printf("IntervalExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - e.lwr.accept(this); - if (!result) - e.upr.accept(this); - } - - override void visit(ArrayExp e) - { - //printf("ArrayExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - visit(e.isUnaExp()); - if (!result && e.arguments) - { - foreach (ea; *e.arguments) - ea.accept(this); - } - } - - override void visit(BinExp e) - { - //printf("BinExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - e.e1.accept(this); - if (!result) - e.e2.accept(this); - } - - override void visit(CondExp e) - { - //printf("BinExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - e.econd.accept(this); - if (!result) - visit(e.isBinExp()); - } - } - - scope ReliesOnTemplateParameters v = new ReliesOnTemplateParameters(tparams); - e.accept(v); - return v.result; -} - -/*********************************************************** - * https://dlang.org/spec/template.html#TemplateParameter - */ -extern (C++) class TemplateParameter : ASTNode -{ - Loc loc; - Identifier ident; - - /* True if this is a part of precedent parameter specialization pattern. - * - * template A(T : X!TL, alias X, TL...) {} - * // X and TL are dependent template parameter - * - * A dependent template parameter should return MATCH.exact in matchArg() - * to respect the match level of the corresponding precedent parameter. - */ - bool dependent; - - /* ======================== TemplateParameter =============================== */ - extern (D) this(Loc loc, Identifier ident) @safe - { - this.loc = loc; - this.ident = ident; - } - - TemplateTypeParameter isTemplateTypeParameter() - { - return null; - } - - TemplateValueParameter isTemplateValueParameter() - { - return null; - } - - TemplateAliasParameter isTemplateAliasParameter() - { - return null; - } - - TemplateThisParameter isTemplateThisParameter() - { - return null; - } - - TemplateTupleParameter isTemplateTupleParameter() - { - return null; - } - - abstract TemplateParameter syntaxCopy(); - - abstract bool declareParameter(Scope* sc); - - abstract void print(RootObject oarg, RootObject oded); - - abstract RootObject specialization(); - - abstract RootObject defaultArg(Loc instLoc, Scope* sc); - - abstract bool hasDefaultArg(); - - override const(char)* toChars() const - { - return this.ident.toChars(); - } - - override DYNCAST dyncast() const - { - return DYNCAST.templateparameter; - } - - override void accept(Visitor v) - { - v.visit(this); - } -} - -/*********************************************************** - * https://dlang.org/spec/template.html#TemplateTypeParameter - * Syntax: - * ident : specType = defaultType - */ -extern (C++) class TemplateTypeParameter : TemplateParameter -{ - Type specType; // if !=null, this is the type specialization - Type defaultType; - - extern (D) __gshared Type tdummy = null; - - extern (D) this(Loc loc, Identifier ident, Type specType, Type defaultType) @safe - { - super(loc, ident); - this.specType = specType; - this.defaultType = defaultType; - } - - override final TemplateTypeParameter isTemplateTypeParameter() - { - return this; - } - - override TemplateTypeParameter syntaxCopy() - { - return new TemplateTypeParameter(loc, ident, specType ? specType.syntaxCopy() : null, defaultType ? defaultType.syntaxCopy() : null); - } - - override final bool declareParameter(Scope* sc) - { - //printf("TemplateTypeParameter.declareParameter('%s')\n", ident.toChars()); - auto ti = new TypeIdentifier(loc, ident); - Declaration ad = new AliasDeclaration(loc, ident, ti); - return sc.insert(ad) !is null; - } - - override final void print(RootObject oarg, RootObject oded) - { - printf(" %s\n", ident.toChars()); - - Type t = isType(oarg); - Type ta = isType(oded); - assert(ta); - - if (specType) - printf("\tSpecialization: %s\n", specType.toChars()); - if (defaultType) - printf("\tDefault: %s\n", defaultType.toChars()); - printf("\tParameter: %s\n", t ? t.toChars() : "NULL"); - printf("\tDeduced Type: %s\n", ta.toChars()); - } - - override final RootObject specialization() - { - return specType; - } - - override final RootObject defaultArg(Loc instLoc, Scope* sc) - { - Type t = defaultType; - if (t) - { - t = t.syntaxCopy(); - t = t.typeSemantic(loc, sc); // use the parameter loc - } - return t; - } - - override final bool hasDefaultArg() - { - return defaultType !is null; - } - - override void accept(Visitor v) - { - v.visit(this); - } -} - -/*********************************************************** - * https://dlang.org/spec/template.html#TemplateThisParameter - * Syntax: - * this ident : specType = defaultType - */ -extern (C++) final class TemplateThisParameter : TemplateTypeParameter -{ - extern (D) this(Loc loc, Identifier ident, Type specType, Type defaultType) @safe - { - super(loc, ident, specType, defaultType); - } - - override TemplateThisParameter isTemplateThisParameter() - { - return this; - } - - override TemplateThisParameter syntaxCopy() - { - return new TemplateThisParameter(loc, ident, specType ? specType.syntaxCopy() : null, defaultType ? defaultType.syntaxCopy() : null); - } - - override void accept(Visitor v) - { - v.visit(this); - } -} - -/*********************************************************** - * https://dlang.org/spec/template.html#TemplateValueParameter - * Syntax: - * valType ident : specValue = defaultValue - */ -extern (C++) final class TemplateValueParameter : TemplateParameter -{ - Type valType; - Expression specValue; - Expression defaultValue; - - extern (D) __gshared Expression[void*] edummies; - - extern (D) this(Loc loc, Identifier ident, Type valType, - Expression specValue, Expression defaultValue) @safe - { - super(loc, ident); - this.valType = valType; - this.specValue = specValue; - this.defaultValue = defaultValue; - } - - override TemplateValueParameter isTemplateValueParameter() - { - return this; - } - - override TemplateValueParameter syntaxCopy() - { - return new TemplateValueParameter(loc, ident, - valType.syntaxCopy(), - specValue ? specValue.syntaxCopy() : null, - defaultValue ? defaultValue.syntaxCopy() : null); - } - - override bool declareParameter(Scope* sc) - { - /* - Do type semantic earlier. - - This means for certain erroneous value parameters - their "type" can be known earlier and thus a better - error message given. - - For example: - `template test(x* x) {}` - now yields "undefined identifier" rather than the opaque - "variable `x` is used as a type". - */ - if (valType) - valType = valType.typeSemantic(loc, sc); - auto v = new VarDeclaration(loc, valType, ident, null); - v.storage_class = STC.templateparameter; - return sc.insert(v) !is null; - } - - override void print(RootObject oarg, RootObject oded) - { - printf(" %s\n", ident.toChars()); - Expression ea = isExpression(oded); - if (specValue) - printf("\tSpecialization: %s\n", specValue.toChars()); - printf("\tParameter Value: %s\n", ea ? ea.toChars() : "NULL"); - } - - override RootObject specialization() - { - return specValue; - } - - override RootObject defaultArg(Loc instLoc, Scope* sc) - { - Expression e = defaultValue; - if (!e) - return null; - - e = e.syntaxCopy(); - Scope* sc2 = sc.push(); - sc2.inDefaultArg = true; - e = e.expressionSemantic(sc2); - sc2.pop(); - if (e is null) - return null; - if (auto te = e.isTemplateExp()) - { - assert(sc && sc.tinst); - if (te.td == sc.tinst.tempdecl) - { - // defaultValue is a reference to its template declaration - // i.e: `template T(int arg = T)` - // Raise error now before calling resolveProperties otherwise we'll - // start looping on the expansion of the template instance. - auto td = sc.tinst.tempdecl; - .error(td.loc, "%s `%s` recursive template expansion", td.kind, td.toPrettyChars); - return ErrorExp.get(); - } - } - if ((e = resolveProperties(sc, e)) is null) - return null; - e = e.resolveLoc(instLoc, sc); // use the instantiated loc - e = e.optimize(WANTvalue); - - return e; - } - - override bool hasDefaultArg() - { - return defaultValue !is null; - } - - override void accept(Visitor v) - { - v.visit(this); - } -} - -/*********************************************************** - * https://dlang.org/spec/template.html#TemplateAliasParameter - * Syntax: - * specType ident : specAlias = defaultAlias - */ -extern (C++) final class TemplateAliasParameter : TemplateParameter -{ - Type specType; - RootObject specAlias; - RootObject defaultAlias; - - extern (D) __gshared Dsymbol sdummy = null; - - extern (D) this(Loc loc, Identifier ident, Type specType, RootObject specAlias, RootObject defaultAlias) @safe - { - super(loc, ident); - this.specType = specType; - this.specAlias = specAlias; - this.defaultAlias = defaultAlias; - } - - override TemplateAliasParameter isTemplateAliasParameter() - { - return this; - } - - override TemplateAliasParameter syntaxCopy() - { - return new TemplateAliasParameter(loc, ident, specType ? specType.syntaxCopy() : null, objectSyntaxCopy(specAlias), objectSyntaxCopy(defaultAlias)); - } - - override bool declareParameter(Scope* sc) - { - auto ti = new TypeIdentifier(loc, ident); - Declaration ad = new AliasDeclaration(loc, ident, ti); - return sc.insert(ad) !is null; - } - - override void print(RootObject oarg, RootObject oded) - { - printf(" %s\n", ident.toChars()); - Dsymbol sa = isDsymbol(oded); - assert(sa); - printf("\tParameter alias: %s\n", sa.toChars()); - } - - override RootObject specialization() - { - return specAlias; - } - - override RootObject defaultArg(Loc instLoc, Scope* sc) - { - RootObject da = defaultAlias; - if (auto ta = isType(defaultAlias)) - { - switch (ta.ty) - { - // If the default arg is a template, instantiate for each type - case Tinstance : - // same if the default arg is a mixin, traits, typeof - // since the content might rely on a previous parameter - // (https://issues.dlang.org/show_bug.cgi?id=23686) - case Tmixin, Ttypeof, Ttraits : - da = ta.syntaxCopy(); - break; - default: - } - } - - RootObject o = aliasParameterSemantic(loc, sc, da, null); // use the parameter loc - return o; - } - - override bool hasDefaultArg() - { - return defaultAlias !is null; - } - - override void accept(Visitor v) - { - v.visit(this); - } -} - -/*********************************************************** - * https://dlang.org/spec/template.html#TemplateSequenceParameter - * Syntax: - * ident ... - */ -extern (C++) final class TemplateTupleParameter : TemplateParameter -{ - extern (D) this(Loc loc, Identifier ident) @safe - { - super(loc, ident); - } - - override TemplateTupleParameter isTemplateTupleParameter() - { - return this; - } - - override TemplateTupleParameter syntaxCopy() - { - return new TemplateTupleParameter(loc, ident); - } - - override bool declareParameter(Scope* sc) - { - auto ti = new TypeIdentifier(loc, ident); - Declaration ad = new AliasDeclaration(loc, ident, ti); - return sc.insert(ad) !is null; - } - - override void print(RootObject oarg, RootObject oded) - { - printf(" %s... [", ident.toChars()); - Tuple v = isTuple(oded); - assert(v); - - //printf("|%d| ", v.objects.length); - foreach (i, o; v.objects) - { - if (i) - printf(", "); - - Dsymbol sa = isDsymbol(o); - if (sa) - printf("alias: %s", sa.toChars()); - Type ta = isType(o); - if (ta) - printf("type: %s", ta.toChars()); - Expression ea = isExpression(o); - if (ea) - printf("exp: %s", ea.toChars()); - - assert(!isTuple(o)); // no nested Tuple arguments - } - printf("]\n"); - } - - override RootObject specialization() - { - return null; - } - - override RootObject defaultArg(Loc instLoc, Scope* sc) - { - return null; - } - - override bool hasDefaultArg() - { - return false; - } - - override void accept(Visitor v) - { - v.visit(this); - } -} - -/*********************************************************** - * https://dlang.org/spec/template.html#explicit_tmp_instantiation - * Given: - * foo!(args) => - * name = foo - * tiargs = args - */ -extern (C++) class TemplateInstance : ScopeDsymbol -{ - Identifier name; - - // Array of Types/Expressions of template - // instance arguments [int*, char, 10*10] - Objects* tiargs; - - // Array of Types/Expressions corresponding - // to TemplateDeclaration.parameters - // [int, char, 100] - Objects tdtypes; - - // Modules imported by this template instance - Modules importedModules; - - Dsymbol tempdecl; // referenced by foo.bar.abc - Dsymbol enclosing; // if referencing local symbols, this is the context - Dsymbol aliasdecl; // !=null if instance is an alias for its sole member - - /** - If this is not null and it has a value that is not the current object, - then this field points to an existing template instance - and that object has been duplicated into us. - - If this object is a duplicate, - the ``memberOf`` field will be set to a root module (passed on CLI). - - This information is useful to deduplicate analysis that may occur - after semantic 3 has completed. - - See_Also: memberOf - */ - TemplateInstance inst; - - ScopeDsymbol argsym; // argument symbol table - size_t hash; // cached result of toHash() - - /// For function template, these are the function fnames(name and loc of it) and arguments - /// Relevant because different resolutions of `auto ref` parameters - /// create different template instances even with the same template arguments - Expressions* fargs; - ArgumentLabels* fnames; - - TemplateInstances* deferred; - - /** - If this is not null then this template instance appears in a root module's members. - - Note: This is not useful for determining duplication status of this template instance. - Use the field ``inst`` for determining if a template instance has been duplicated into this object. - - See_Also: inst - */ - Module memberOf; - - // Used to determine the instance needs code generation. - // Note that these are inaccurate until semantic analysis phase completed. - TemplateInstance tinst; // enclosing template instance - TemplateInstance tnext; // non-first instantiated instances - Module minst; // the top module that instantiated this instance - - private ushort _nest; // for recursive pretty printing detection, 3 MSBs reserved for flags (below) - ubyte inuse; // for recursive expansion detection - - private enum Flag : uint - { - semantictiargsdone = 1u << (_nest.sizeof * 8 - 1), // MSB of _nest - havetempdecl = semantictiargsdone >> 1, - gagged = semantictiargsdone >> 2, - available = gagged - 1 // always last flag minus one, 1s for all available bits - } - - extern(D) final @safe @property pure nothrow @nogc - { - ushort nest() const { return _nest & Flag.available; } - void nestUp() { assert(nest() < Flag.available); ++_nest; } - void nestDown() { assert(nest() > 0); --_nest; } - /// has semanticTiargs() been done? - bool semantictiargsdone() const { return (_nest & Flag.semantictiargsdone) != 0; } - void semantictiargsdone(bool x) - { - if (x) _nest |= Flag.semantictiargsdone; - else _nest &= ~Flag.semantictiargsdone; - } - /// if used second constructor - bool havetempdecl() const { return (_nest & Flag.havetempdecl) != 0; } - void havetempdecl(bool x) - { - if (x) _nest |= Flag.havetempdecl; - else _nest &= ~Flag.havetempdecl; - } - /// if the instantiation is done with error gagging - bool gagged() const { return (_nest & Flag.gagged) != 0; } - void gagged(bool x) - { - if (x) _nest |= Flag.gagged; - else _nest &= ~Flag.gagged; - } - } - - extern (D) this(Loc loc, Identifier ident, Objects* tiargs) scope - { - super(loc, null); - static if (LOG) - { - printf("TemplateInstance(this = %p, ident = '%s')\n", this, ident ? ident.toChars() : "null"); - } - this.dsym = DSYM.templateInstance; - this.name = ident; - this.tiargs = tiargs; - } - - /***************** - * This constructor is only called when we figured out which function - * template to instantiate. - */ - extern (D) this(Loc loc, TemplateDeclaration td, Objects* tiargs) scope - { - super(loc, null); - static if (LOG) - { - printf("TemplateInstance(this = %p, tempdecl = '%s')\n", this, td.toChars()); - } - this.dsym = DSYM.templateInstance; - this.name = td.ident; - this.tiargs = tiargs; - this.tempdecl = td; - this.semantictiargsdone = true; - this.havetempdecl = true; - assert(tempdecl._scope); - } - - extern (D) static Objects* arraySyntaxCopy(Objects* objs) - { - Objects* a = null; - if (objs) - { - a = new Objects(objs.length); - foreach (i, o; *objs) - (*a)[i] = objectSyntaxCopy(o); - } - return a; - } - - override TemplateInstance syntaxCopy(Dsymbol s) - { - TemplateInstance ti = s ? cast(TemplateInstance)s : new TemplateInstance(loc, name, null); - ti.tiargs = arraySyntaxCopy(tiargs); - TemplateDeclaration td; - if (inst && tempdecl && (td = tempdecl.isTemplateDeclaration()) !is null) - td.ScopeDsymbol.syntaxCopy(ti); - else - ScopeDsymbol.syntaxCopy(ti); - return ti; - } - - // resolve real symbol - override final Dsymbol toAlias() - { - static if (LOG) - { - printf("TemplateInstance.toAlias()\n"); - } - if (!inst) - { - // Maybe we can resolve it - if (_scope) - { - dsymbolSemantic(this, _scope); - } - if (!inst) - { - .error(loc, "%s `%s` cannot resolve forward reference", kind, toPrettyChars); - errors = true; - return this; - } - } - - if (inst != this) - return inst.toAlias(); - - if (aliasdecl) - { - return aliasdecl.toAlias(); - } - - return inst; - } - - override const(char)* kind() const - { - return "template instance"; - } - - override final const(char)* toPrettyCharsHelper() - { - OutBuffer buf; - toCBufferInstance(this, buf, true); - return buf.extractChars(); - } - - /************************************** - * Given an error instantiating the TemplateInstance, - * give the nested TemplateInstance instantiations that got - * us here. Those are a list threaded into the nested scopes. - * Params: - * cl = classification of this trace as printing either errors or deprecations - * max_shown = maximum number of trace elements printed (controlled with -v/-verror-limit) - */ - extern(D) final void printInstantiationTrace(Classification cl = Classification.error, - const(uint) max_shown = global.params.v.errorSupplementCount()) - { - if (global.gag) - return; - - // Print full trace for verbose mode, otherwise only short traces - const(char)* format = "instantiated from here: `%s`"; - - // This returns a function pointer - scope printFn = () { - final switch (cl) - { - case Classification.error: - return &errorSupplemental; - case Classification.deprecation: - return &deprecationSupplemental; - case Classification.gagged, Classification.tip, Classification.warning: - assert(0); - } - }(); - - // determine instantiation depth and number of recursive instantiations - int n_instantiations = 1; - int n_totalrecursions = 0; - for (TemplateInstance cur = this; cur; cur = cur.tinst) - { - ++n_instantiations; - // Set error here as we don't want it to depend on the number of - // entries that are being printed. - if (cl == Classification.error || - (cl == Classification.warning && global.params.useWarnings == DiagnosticReporting.error) || - (cl == Classification.deprecation && global.params.useDeprecated == DiagnosticReporting.error)) - cur.errors = true; - - // If two instantiations use the same declaration, they are recursive. - // (this works even if they are instantiated from different places in the - // same template). - // In principle, we could also check for multiple-template recursion, but it's - // probably not worthwhile. - if (cur.tinst && cur.tempdecl && cur.tinst.tempdecl && cur.tempdecl.loc.equals(cur.tinst.tempdecl.loc)) - ++n_totalrecursions; - } - - if (n_instantiations <= max_shown) - { - for (TemplateInstance cur = this; cur; cur = cur.tinst) - printFn(cur.loc, format, cur.toErrMsg()); - } - else if (n_instantiations - n_totalrecursions <= max_shown) - { - // By collapsing recursive instantiations into a single line, - // we can stay under the limit. - int recursionDepth = 0; - for (TemplateInstance cur = this; cur; cur = cur.tinst) - { - if (cur.tinst && cur.tempdecl && cur.tinst.tempdecl && cur.tempdecl.loc.equals(cur.tinst.tempdecl.loc)) - { - ++recursionDepth; - } - else - { - if (recursionDepth) - printFn(cur.loc, "%d recursive instantiations from here: `%s`", recursionDepth + 2, cur.toChars()); - else - printFn(cur.loc, format, cur.toChars()); - recursionDepth = 0; - } - } - } - else - { - // Even after collapsing the recursions, the depth is too deep. - // Just display the first few and last few instantiations. - uint i = 0; - for (TemplateInstance cur = this; cur; cur = cur.tinst) - { - if (i == max_shown / 2) - printFn(cur.loc, "... (%d instantiations, -v to show) ...", n_instantiations - max_shown); - - if (i < max_shown / 2 || i >= n_instantiations - max_shown + max_shown / 2) - printFn(cur.loc, format, cur.toChars()); - ++i; - } - } - } - - /************************************* - * Lazily generate identifier for template instance. - * This is because 75% of the ident's are never needed. - */ - override final Identifier getIdent() - { - if (!ident && inst && !errors) - ident = genIdent(tiargs); // need an identifier for name mangling purposes. - return ident; - } - - /************************************* - * Compare proposed template instantiation with existing template instantiation. - * Note that this is not commutative because of the auto ref check. - * Params: - * ti = existing template instantiation - * Returns: - * true for match - */ - final bool equalsx(TemplateInstance ti) - { - //printf("this = %p, ti = %p\n", this, ti); - assert(tdtypes.length == ti.tdtypes.length); - - // Nesting must match - if (enclosing != ti.enclosing) - { - //printf("test2 enclosing %s ti.enclosing %s\n", enclosing ? enclosing.toChars() : "", ti.enclosing ? ti.enclosing.toChars() : ""); - return false; - } - //printf("parent = %s, ti.parent = %s\n", parent.toPrettyChars(), ti.parent.toPrettyChars()); - - if (!arrayObjectMatch(tdtypes, ti.tdtypes)) - return false; - - /* Template functions may have different instantiations based on - * "auto ref" parameters. - */ - auto fd = ti.toAlias().isFuncDeclaration(); - if (!fd) - return true; - if (fd.errors) - return true; - - auto resolvedArgs = fd.type.isTypeFunction().resolveNamedArgs( - ArgumentList(this.fargs, this.fnames), null); - - // resolvedArgs can be null when there's an error: fail_compilation/fail14669.d - // In that case, equalsx returns true to prevent endless template instantiations - // However, it can also mean the function was explicitly instantiated - // without function arguments: fail_compilation/fail14669 - // Hence the following check: - if (this.fargs && !resolvedArgs) - return true; - - Expression[] args = resolvedArgs ? (*resolvedArgs)[] : []; - - auto fparameters = fd.getParameterList(); - size_t nfparams = fparameters.length; // Num function parameters - for (size_t j = 0; j < nfparams; j++) - { - Parameter fparam = fparameters[j]; - if (!(fparam.storageClass & STC.autoref) ) // if "auto ref" - continue; - - Expression farg = (j < args.length) ? args[j] : fparam.defaultArg; - // resolveNamedArgs strips trailing nulls / default params - // when it doesn't anymore, the ternary can be replaced with: - // assert(j < resolvedArgs.length); - if (!farg) - farg = fparam.defaultArg; - if (!farg) - return false; - if (farg.isLvalue()) - { - if (!(fparam.storageClass & STC.ref_)) - return false; // auto ref's don't match - } - else - { - if (fparam.storageClass & STC.ref_) - return false; // auto ref's don't match - } - } - return true; - } - - extern (D) final size_t toHash() - { - if (!hash) - { - hash = cast(size_t)cast(void*)enclosing; - hash += arrayObjectHash(tdtypes); - hash += hash == 0; - } - return hash; - } - - /** - Returns: true if the instances' innards are discardable. - - The idea of this function is to see if the template instantiation - can be 100% replaced with its eponymous member. All other members - can be discarded, even in the compiler to free memory (for example, - the template could be expanded in a region allocator, deemed trivial, - the end result copied back out independently and the entire region freed), - and can be elided entirely from the binary. - - The current implementation affects code that generally looks like: - - --- - template foo(args...) { - some_basic_type_or_string helper() { .... } - enum foo = helper(); - } - --- - - since it was the easiest starting point of implementation but it can and - should be expanded more later. - */ - final bool isDiscardable() - { - if (aliasdecl is null) - return false; - - auto v = aliasdecl.isVarDeclaration(); - if (v is null) - return false; - - if (!(v.storage_class & STC.manifest)) - return false; - - // Currently only doing basic types here because it is the easiest proof-of-concept - // implementation with minimal risk of side effects, but it could likely be - // expanded to any type that already exists outside this particular instance. - if (!(v.type.equals(Type.tstring) || (v.type.isTypeBasic() !is null))) - return false; - - // Static ctors and dtors, even in an eponymous enum template, are still run, - // so if any of them are in here, we'd better not assume it is trivial lest - // we break useful code - foreach(member; *members) - { - if(member.hasStaticCtorOrDtor()) - return false; - if(member.isStaticDtorDeclaration()) - return false; - if(member.isStaticCtorDeclaration()) - return false; - } - - // but if it passes through this gauntlet... it should be fine. D code will - // see only the eponymous member, outside stuff can never access it, even through - // reflection; the outside world ought to be none the wiser. Even dmd should be - // able to simply free the memory of everything except the final result. - - return true; - } - - - /*********************************************** - * Returns true if this is not instantiated in non-root module, and - * is a part of non-speculative instantiatiation. - * - * Note: minst does not stabilize until semantic analysis is completed, - * so don't call this function during semantic analysis to return precise result. - */ - final bool needsCodegen() - { - //printf("needsCodegen() %s\n", toChars()); - - // minst is finalized after the 1st invocation. - // tnext is only needed for the 1st invocation and - // cleared for further invocations. - TemplateInstance tnext = this.tnext; - TemplateInstance tinst = this.tinst; - this.tnext = null; - - // Don't do codegen if the instance has errors, - // is a dummy instance (see evaluateConstraint), - // or is determined to be discardable. - if (errors || inst is null || inst.isDiscardable()) - { - minst = null; // mark as speculative - return false; - } - - // This should only be called on the primary instantiation. - assert(this is inst); - - if (global.params.allInst) - { - // Do codegen if there is an instantiation from a root module, to maximize link-ability. - static ThreeState needsCodegenAllInst(TemplateInstance tithis, TemplateInstance tinst) - { - // Do codegen if `this` is instantiated from a root module. - if (tithis.minst && tithis.minst.isRoot()) - return ThreeState.yes; - - // Do codegen if the ancestor needs it. - if (tinst && tinst.inst && tinst.inst.needsCodegen()) - { - tithis.minst = tinst.inst.minst; // cache result - assert(tithis.minst); - assert(tithis.minst.isRoot()); - return ThreeState.yes; - } - return ThreeState.none; - } - - if (const needsCodegen = needsCodegenAllInst(this, tinst)) - return needsCodegen == ThreeState.yes ? true : false; - - // Do codegen if a sibling needs it. - for (; tnext; tnext = tnext.tnext) - { - const needsCodegen = needsCodegenAllInst(tnext, tnext.tinst); - if (needsCodegen == ThreeState.yes) - { - minst = tnext.minst; // cache result - assert(minst); - assert(minst.isRoot()); - return true; - } - else if (!minst && tnext.minst) - { - minst = tnext.minst; // cache result from non-speculative sibling - // continue searching - } - else if (needsCodegen != ThreeState.none) - break; - } - - // Elide codegen because there's no instantiation from any root modules. - return false; - } - - // Prefer instantiations from non-root modules, to minimize object code size. - - /* If a TemplateInstance is ever instantiated from a non-root module, - * we do not have to generate code for it, - * because it will be generated when the non-root module is compiled. - * - * But, if the non-root 'minst' imports any root modules, it might still need codegen. - * - * The problem is if A imports B, and B imports A, and both A - * and B instantiate the same template, does the compilation of A - * or the compilation of B do the actual instantiation? - * - * See https://issues.dlang.org/show_bug.cgi?id=2500. - * - * => Elide codegen if there is at least one instantiation from a non-root module - * which doesn't import any root modules. - */ - static ThreeState needsCodegenRootOnly(TemplateInstance tithis, TemplateInstance tinst) - { - // If the ancestor isn't speculative, - // 1. do codegen if the ancestor needs it - // 2. elide codegen if the ancestor doesn't need it (non-root instantiation of ancestor incl. subtree) - if (tinst && tinst.inst) - { - tinst = tinst.inst; - const needsCodegen = tinst.needsCodegen(); // sets tinst.minst - if (tinst.minst) // not speculative - { - tithis.minst = tinst.minst; // cache result - return needsCodegen ? ThreeState.yes : ThreeState.no; - } - } - - // Elide codegen if `this` doesn't need it. - if (tithis.minst && !tithis.minst.isRoot() && !tithis.minst.rootImports()) - return ThreeState.no; - - return ThreeState.none; - } - - if (const needsCodegen = needsCodegenRootOnly(this, tinst)) - return needsCodegen == ThreeState.yes ? true : false; - - // Elide codegen if a (non-speculative) sibling doesn't need it. - for (; tnext; tnext = tnext.tnext) - { - const needsCodegen = needsCodegenRootOnly(tnext, tnext.tinst); // sets tnext.minst - if (tnext.minst) // not speculative - { - if (needsCodegen == ThreeState.no) - { - minst = tnext.minst; // cache result - assert(!minst.isRoot() && !minst.rootImports()); - return false; - } - else if (!minst) - { - minst = tnext.minst; // cache result from non-speculative sibling - // continue searching - } - else if (needsCodegen != ThreeState.none) - break; - } - } - - // Unless `this` is still speculative (=> all further siblings speculative too), - // do codegen because we found no guaranteed-codegen'd non-root instantiation. - return minst !is null; - } - - /********************************************** - * Find template declaration corresponding to template instance. - * - * Returns: - * false if finding fails. - * Note: - * This function is reentrant against error occurrence. If returns false, - * any members of this object won't be modified, and repetition call will - * reproduce same error. - */ - extern (D) final bool findTempDecl(Scope* sc, WithScopeSymbol* pwithsym) + override final const(char)* toPrettyCharsHelper() { - if (pwithsym) - *pwithsym = null; - - if (havetempdecl) - return true; - - //printf("TemplateInstance.findTempDecl() %s\n", toChars()); - if (!tempdecl) - { - /* Given: - * foo!( ... ) - * figure out which TemplateDeclaration foo refers to. - */ - Identifier id = name; - Dsymbol scopesym; - Dsymbol s = sc.search(loc, id, scopesym); - if (!s) - { - s = sc.search_correct(id); - if (s) - .error(loc, "%s `%s` template `%s` is not defined, did you mean %s?", kind, toPrettyChars, id.toChars(), s.toChars()); - else - .error(loc, "%s `%s` template `%s` is not defined", kind, toPrettyChars, id.toChars()); - return false; - } - static if (LOG) - { - printf("It's an instance of '%s' kind '%s'\n", s.toChars(), s.kind()); - if (s.parent) - printf("s.parent = '%s'\n", s.parent.toChars()); - } - if (pwithsym) - *pwithsym = scopesym.isWithScopeSymbol(); - - /* We might have found an alias within a template when - * we really want the template. - */ - TemplateInstance ti; - if (s.parent && (ti = s.parent.isTemplateInstance()) !is null) - { - if (ti.tempdecl && ti.tempdecl.ident == id) - { - /* This is so that one can refer to the enclosing - * template, even if it has the same name as a member - * of the template, if it has a !(arguments) - */ - TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration(); - assert(td); - if (td.overroot) // if not start of overloaded list of TemplateDeclaration's - td = td.overroot; // then get the start - s = td; - } - } - - // The template might originate from a selective import which implies that - // s is a lowered AliasDeclaration of the actual TemplateDeclaration. - // This is the last place where we see the deprecated alias because it is - // stripped below, so check if the selective import was deprecated. - // See https://issues.dlang.org/show_bug.cgi?id=20840. - if (s.isAliasDeclaration()) - s.checkDeprecated(this.loc, sc); - - if (!updateTempDecl(sc, s)) - { - return false; - } - } - assert(tempdecl); - - // Look for forward references - auto tovers = tempdecl.isOverloadSet(); - foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1) - { - Dsymbol dstart = tovers ? tovers.a[oi] : tempdecl; - int r = overloadApply(dstart, (Dsymbol s) - { - auto td = s.isTemplateDeclaration(); - if (!td) - return 0; - - if (td.semanticRun == PASS.initial) - { - if (td._scope) - { - // Try to fix forward reference. Ungag errors while doing so. - Ungag ungag = td.ungagSpeculative(); - td.dsymbolSemantic(td._scope); - } - if (td.semanticRun == PASS.initial) - { - .error(loc, "%s `%s` `%s` forward references template declaration `%s`", kind, toPrettyChars, - toChars(), td.toChars()); - return 1; - } - } - return 0; - }); - if (r) - return false; - } - return true; + OutBuffer buf; + toCBufferInstance(this, buf, true); + return buf.extractChars(); } - /********************************************** - * Confirm s is a valid template, then store it. - * Input: - * sc - * s candidate symbol of template. It may be: - * TemplateDeclaration - * FuncDeclaration with findTemplateDeclRoot() != NULL - * OverloadSet which contains candidates - * Returns: - * true if updating succeeds. + /************************************** + * Given an error instantiating the TemplateInstance, + * give the nested TemplateInstance instantiations that got + * us here. Those are a list threaded into the nested scopes. + * Params: + * cl = classification of this trace as printing either errors or deprecations + * max_shown = maximum number of trace elements printed (controlled with -v/-verror-limit) */ - extern (D) final bool updateTempDecl(Scope* sc, Dsymbol s) + extern(D) final void printInstantiationTrace(Classification cl = Classification.error, + const(uint) max_shown = global.params.v.errorSupplementCount()) { - if (!s) - return tempdecl !is null; - - Identifier id = name; - s = s.toAlias(); - - /* If an OverloadSet, look for a unique member that is a template declaration - */ - if (OverloadSet os = s.isOverloadSet()) - { - s = null; - foreach (s2; os.a) - { - if (FuncDeclaration f = s2.isFuncDeclaration()) - s2 = f.findTemplateDeclRoot(); - else - s2 = s2.isTemplateDeclaration(); - if (s2) - { - if (s) - { - tempdecl = os; - return true; - } - s = s2; - } - } - if (!s) - { - .error(loc, "%s `%s` template `%s` is not defined", kind, toPrettyChars, id.toChars()); - return false; - } - } - - if (OverDeclaration od = s.isOverDeclaration()) - { - tempdecl = od; // TODO: more strict check - return true; - } - - /* It should be a TemplateDeclaration, not some other symbol - */ - if (FuncDeclaration f = s.isFuncDeclaration()) - tempdecl = f.findTemplateDeclRoot(); - else - tempdecl = s.isTemplateDeclaration(); - - // We're done - if (tempdecl) - return true; + if (global.gag) + return; - // Error already issued, just return `false` - if (!s.parent && global.errors) - return false; + // Print full trace for verbose mode, otherwise only short traces + const(char)* format = "instantiated from here: `%s`"; - if (!s.parent && s.getType()) - { - Dsymbol s2 = s.getType().toDsymbol(sc); - if (!s2) - { - .error(loc, "`%s` is not a valid template instance, because `%s` is not a template declaration but a type (`%s == %s`)", toChars(), id.toChars(), id.toChars(), s.getType.kind()); - return false; - } - // because s can be the alias created for a TemplateParameter - const AliasDeclaration ad = s.isAliasDeclaration(); - version (none) + // This returns a function pointer + scope printFn = () { + final switch (cl) { - if (ad && ad.isAliasedTemplateParameter()) - printf("`%s` is an alias created from a template parameter\n", s.toChars()); + case Classification.error: + return &errorSupplemental; + case Classification.deprecation: + return &deprecationSupplemental; + case Classification.gagged, Classification.tip, Classification.warning: + assert(0); } - if (!ad || !ad.isAliasedTemplateParameter()) - s = s2; - } - - TemplateInstance ti = s.parent ? s.parent.isTemplateInstance() : null; - - /* This avoids the VarDeclaration.toAlias() which runs semantic() too soon - */ - static bool matchId(TemplateInstance ti, Identifier id) - { - if (ti.aliasdecl && ti.aliasdecl.isVarDeclaration()) - return ti.aliasdecl.isVarDeclaration().ident == id; - return ti.toAlias().ident == id; - } - - if (ti && (ti.name == s.ident || matchId(ti, s.ident)) && ti.tempdecl) - { - /* This is so that one can refer to the enclosing - * template, even if it has the same name as a member - * of the template, if it has a !(arguments) - */ - TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration(); - assert(td); - if (td.overroot) // if not start of overloaded list of TemplateDeclaration's - td = td.overroot; // then get the start - tempdecl = td; - return true; - } - else - { - .error(loc, "%s `%s` `%s` is not a template declaration, it is a %s", kind, toPrettyChars, id.toChars(), s.kind()); - return false; - } - } - - /********************************** - * Run semantic of tiargs as arguments of template. - * Input: - * loc - * sc - * tiargs array of template arguments - * flags 1: replace const variables with their initializers - * 2: don't devolve Parameter to Type - * atd tuple being optimized. If found, it's not expanded here - * but in AliasAssign semantic. - * Returns: - * false if one or more arguments have errors. - */ - extern (D) static bool semanticTiargs(Loc loc, Scope* sc, Objects* tiargs, int flags, TupleDeclaration atd = null) - { - // Run semantic on each argument, place results in tiargs[] - //printf("+TemplateInstance.semanticTiargs()\n"); - if (!tiargs) - return true; - bool err = false; - - // The arguments are not treated as part of a default argument, - // because they are evaluated at compile time. - const inCondition = sc.condition; - sc = sc.push(); - sc.inDefaultArg = false; - - // https://issues.dlang.org/show_bug.cgi?id=24699 - sc.condition = inCondition; + }(); - for (size_t j = 0; j < tiargs.length; j++) + // determine instantiation depth and number of recursive instantiations + int n_instantiations = 1; + int n_totalrecursions = 0; + for (TemplateInstance cur = this; cur; cur = cur.tinst) { - RootObject o = (*tiargs)[j]; - Type ta = isType(o); - Expression ea = isExpression(o); - Dsymbol sa = isDsymbol(o); - - //printf("1: (*tiargs)[%d] = %p, s=%p, v=%p, ea=%p, ta=%p\n", j, o, isDsymbol(o), isTuple(o), ea, ta); - if (ta) - { - //printf("type %s\n", ta.toChars()); - - // It might really be an Expression or an Alias - ta.resolve(loc, sc, ea, ta, sa, (flags & 1) != 0); - if (ea) - goto Lexpr; - if (sa) - goto Ldsym; - if (ta is null) - { - assert(global.errors); - ta = Type.terror; - } - - Ltype: - if (TypeTuple tt = ta.isTypeTuple()) - { - // Expand tuple - size_t dim = tt.arguments.length; - tiargs.remove(j); - if (dim) - { - tiargs.reserve(dim); - foreach (i, arg; *tt.arguments) - { - if (flags & 2 && (arg.storageClass & STC.parameter)) - tiargs.insert(j + i, arg); - else - tiargs.insert(j + i, arg.type); - } - } - j--; - continue; - } - if (ta.ty == Terror) - { - err = true; - continue; - } - (*tiargs)[j] = ta.merge2(); - } - else if (ea) - { - Lexpr: - //printf("+[%d] ea = %s %s\n", j, EXPtoString(ea.op).ptr, ea.toChars()); - if (flags & 1) // only used by __traits - { - ea = ea.expressionSemantic(sc); - - // must not interpret the args, excepting template parameters - if (!ea.isVarExp() || (ea.isVarExp().var.storage_class & STC.templateparameter)) - { - ea = ea.optimize(WANTvalue); - } - } - else - { - sc = sc.startCTFE(); - ea = ea.expressionSemantic(sc); - sc = sc.endCTFE(); - - if (auto varExp = ea.isVarExp()) - { - /* If the parameter is a function that is not called - * explicitly, i.e. `foo!func` as opposed to `foo!func()`, - * then it is a dsymbol, not the return value of `func()` - */ - Declaration vd = varExp.var; - if (auto fd = vd.isFuncDeclaration()) - { - sa = fd; - goto Ldsym; - } - /* Otherwise skip substituting a const var with - * its initializer. The problem is the initializer won't - * match with an 'alias' parameter. Instead, do the - * const substitution in TemplateValueParameter.matchArg(). - */ - } - else if (definitelyValueParameter(ea)) - { - if (ea.checkValue()) // check void expression - ea = ErrorExp.get(); - const olderrs = global.errors; - ea = ea.ctfeInterpret(); - if (global.errors != olderrs) - ea = ErrorExp.get(); - } - } - //printf("-[%d] ea = %s %s\n", j, EXPtoString(ea.op).ptr, ea.toChars()); - if (TupleExp te = ea.isTupleExp()) - { - // Expand tuple - size_t dim = te.exps.length; - tiargs.remove(j); - if (dim) - { - tiargs.reserve(dim); - foreach (i, exp; *te.exps) - tiargs.insert(j + i, exp); - } - j--; - continue; - } - if (ea.op == EXP.error) - { - err = true; - continue; - } - (*tiargs)[j] = ea; + ++n_instantiations; + // Set error here as we don't want it to depend on the number of + // entries that are being printed. + if (cl == Classification.error || + (cl == Classification.warning && global.params.useWarnings == DiagnosticReporting.error) || + (cl == Classification.deprecation && global.params.useDeprecated == DiagnosticReporting.error)) + cur.errors = true; - if (ea.op == EXP.type) - { - ta = ea.type; - goto Ltype; - } - if (ea.op == EXP.scope_) - { - sa = ea.isScopeExp().sds; - goto Ldsym; - } - if (FuncExp fe = ea.isFuncExp()) - { - /* A function literal, that is passed to template and - * already semanticed as function pointer, never requires - * outer frame. So convert it to global function is valid. - */ - if (fe.fd.tok == TOK.reserved && fe.type.ty == Tpointer) - { - // change to non-nested - fe.fd.tok = TOK.function_; - fe.fd.vthis = null; - } - else if (fe.td) - { - /* If template argument is a template lambda, - * get template declaration itself. */ - //sa = fe.td; - //goto Ldsym; - } - } - if (ea.op == EXP.dotVariable && !(flags & 1)) - { - // translate expression to dsymbol. - sa = ea.isDotVarExp().var; - goto Ldsym; - } - if (auto te = ea.isTemplateExp()) - { - sa = te.td; - goto Ldsym; - } - if (ea.op == EXP.dotTemplateDeclaration && !(flags & 1)) - { - // translate expression to dsymbol. - sa = ea.isDotTemplateExp().td; - goto Ldsym; - } - if (auto de = ea.isDotExp()) - { - if (auto se = de.e2.isScopeExp()) - { - sa = se.sds; - goto Ldsym; - } - } - } - else if (sa) - { - Ldsym: - //printf("dsym %s %s\n", sa.kind(), sa.toChars()); - if (sa.errors) - { - err = true; - continue; - } + // If two instantiations use the same declaration, they are recursive. + // (this works even if they are instantiated from different places in the + // same template). + // In principle, we could also check for multiple-template recursion, but it's + // probably not worthwhile. + if (cur.tinst && cur.tempdecl && cur.tinst.tempdecl && cur.tempdecl.loc.equals(cur.tinst.tempdecl.loc)) + ++n_totalrecursions; + } - TupleDeclaration d = sa.toAlias().isTupleDeclaration(); - if (d) - { - if (d is atd) - { - (*tiargs)[j] = d; - continue; - } - // Expand tuple - tiargs.remove(j); - tiargs.insert(j, d.objects); - j--; - continue; - } - if (FuncAliasDeclaration fa = sa.isFuncAliasDeclaration()) + if (n_instantiations <= max_shown) + { + for (TemplateInstance cur = this; cur; cur = cur.tinst) + printFn(cur.loc, format, cur.toErrMsg()); + } + else if (n_instantiations - n_totalrecursions <= max_shown) + { + // By collapsing recursive instantiations into a single line, + // we can stay under the limit. + int recursionDepth = 0; + for (TemplateInstance cur = this; cur; cur = cur.tinst) + { + if (cur.tinst && cur.tempdecl && cur.tinst.tempdecl && cur.tempdecl.loc.equals(cur.tinst.tempdecl.loc)) { - FuncDeclaration f = fa.toAliasFunc(); - if (!fa.hasOverloads && f.isUnique()) - { - // Strip FuncAlias only when the aliased function - // does not have any overloads. - sa = f; - } + ++recursionDepth; } - (*tiargs)[j] = sa; - - TemplateDeclaration td = sa.isTemplateDeclaration(); - if (td && td.semanticRun == PASS.initial && td.literal) + else { - td.dsymbolSemantic(sc); + if (recursionDepth) + printFn(cur.loc, "%d recursive instantiations from here: `%s`", recursionDepth + 2, cur.toChars()); + else + printFn(cur.loc, format, cur.toChars()); + recursionDepth = 0; } - FuncDeclaration fd = sa.isFuncDeclaration(); - if (fd) - functionSemantic(fd); - } - else if (isParameter(o)) - { - } - else - { - assert(0); } - //printf("1: (*tiargs)[%d] = %p\n", j, (*tiargs)[j]); } - sc.pop(); - version (none) + else { - printf("-TemplateInstance.semanticTiargs()\n"); - for (size_t j = 0; j < tiargs.length; j++) + // Even after collapsing the recursions, the depth is too deep. + // Just display the first few and last few instantiations. + uint i = 0; + for (TemplateInstance cur = this; cur; cur = cur.tinst) { - RootObject o = (*tiargs)[j]; - Type ta = isType(o); - Expression ea = isExpression(o); - Dsymbol sa = isDsymbol(o); - Tuple va = isTuple(o); - printf("\ttiargs[%d] = ta %p, ea %p, sa %p, va %p\n", j, ta, ea, sa, va); + if (i == max_shown / 2) + printFn(cur.loc, "... (%d instantiations, -v to show) ...", n_instantiations - max_shown); + + if (i < max_shown / 2 || i >= n_instantiations - max_shown + max_shown / 2) + printFn(cur.loc, format, cur.toChars()); + ++i; } } - return !err; } - /********************************** - * Run semantic on the elements of tiargs. - * Input: - * sc - * Returns: - * false if one or more arguments have errors. - * Note: - * This function is reentrant against error occurrence. If returns false, - * all elements of tiargs won't be modified. + /************************************* + * Lazily generate identifier for template instance. + * This is because 75% of the ident's are never needed. */ - extern (D) final bool semanticTiargs(Scope* sc) + override final Identifier getIdent() { - //printf("+TemplateInstance.semanticTiargs() %s\n", toChars()); - if (semantictiargsdone) - return true; - if (semanticTiargs(loc, sc, tiargs, 0)) - { - // cache the result iff semantic analysis succeeded entirely - semantictiargsdone = 1; - return true; - } - return false; + if (!ident && inst && !errors) + ident = genIdent(tiargs); // need an identifier for name mangling purposes. + return ident; } - /********************************** - * Find the TemplateDeclaration that matches this TemplateInstance best. - * + /************************************* + * Compare proposed template instantiation with existing template instantiation. + * Note that this is not commutative because of the auto ref check. * Params: - * sc = the scope this TemplateInstance resides in - * argumentList = function arguments in case of a template function - * + * ti = existing template instantiation * Returns: - * `true` if a match was found, `false` otherwise + * true for match */ - extern (D) final bool findBestMatch(Scope* sc, ArgumentList argumentList) + final bool equalsx(TemplateInstance ti) { - if (havetempdecl) - { - TemplateDeclaration tempdecl = this.tempdecl.isTemplateDeclaration(); - assert(tempdecl); - assert(tempdecl._scope); - // Deduce tdtypes - tdtypes.setDim(tempdecl.parameters.length); - if (!matchWithInstance(sc, tempdecl, this, tdtypes, argumentList, 2)) - { - .error(loc, "%s `%s` incompatible arguments for template instantiation", kind, toPrettyChars); - return false; - } - // TODO: Normalizing tiargs for https://issues.dlang.org/show_bug.cgi?id=7469 is necessary? - return true; - } + //printf("this = %p, ti = %p\n", this, ti); + assert(tdtypes.length == ti.tdtypes.length); - static if (LOG) + // Nesting must match + if (enclosing != ti.enclosing) { - printf("TemplateInstance.findBestMatch()\n"); + //printf("test2 enclosing %s ti.enclosing %s\n", enclosing ? enclosing.toChars() : "", ti.enclosing ? ti.enclosing.toChars() : ""); + return false; } + //printf("parent = %s, ti.parent = %s\n", parent.toPrettyChars(), ti.parent.toPrettyChars()); - const errs = global.errors; - TemplateDeclaration td_last = null; - Objects dedtypes; + if (!arrayObjectMatch(tdtypes, ti.tdtypes)) + return false; - /* Since there can be multiple TemplateDeclaration's with the same - * name, look for the best match. + /* Template functions may have different instantiations based on + * "auto ref" parameters. */ - auto tovers = tempdecl.isOverloadSet(); - foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1) - { - TemplateDeclaration td_best; - TemplateDeclaration td_ambig; - MATCH m_best = MATCH.nomatch; - - Dsymbol dstart = tovers ? tovers.a[oi] : tempdecl; - overloadApply(dstart, (Dsymbol s) - { - auto td = s.isTemplateDeclaration(); - if (!td) - return 0; - if (td == td_best) // skip duplicates - return 0; - - //printf("td = %s\n", td.toPrettyChars()); - // If more arguments than parameters, - // then this is no match. - if (td.parameters.length < tiargs.length) - { - if (!td.isVariadic()) - return 0; - } - - dedtypes.setDim(td.parameters.length); - dedtypes.zero(); - assert(td.semanticRun != PASS.initial); - - MATCH m = matchWithInstance(sc, td, this, dedtypes, argumentList, 0); - //printf("matchWithInstance = %d\n", m); - if (m == MATCH.nomatch) // no match at all - return 0; - if (m < m_best) goto Ltd_best; - if (m > m_best) goto Ltd; - - // Disambiguate by picking the most specialized TemplateDeclaration - { - MATCH c1 = leastAsSpecialized(sc, td, td_best, argumentList); - MATCH c2 = leastAsSpecialized(sc, td_best, td, argumentList); - //printf("c1 = %d, c2 = %d\n", c1, c2); - if (c1 > c2) goto Ltd; - if (c1 < c2) goto Ltd_best; - } + auto fd = ti.toAlias().isFuncDeclaration(); + if (!fd) + return true; + if (fd.errors) + return true; - td_ambig = td; - return 0; - - Ltd_best: - // td_best is the best match so far - td_ambig = null; - return 0; - - Ltd: - // td is the new best match - td_ambig = null; - td_best = td; - m_best = m; - tdtypes.setDim(dedtypes.length); - memcpy(tdtypes.tdata(), dedtypes.tdata(), tdtypes.length * (void*).sizeof); - return 0; - }); - - if (td_ambig) - { - .error(loc, "%s `%s.%s` matches more than one template declaration:", - td_best.kind(), td_best.parent.toPrettyChars(), td_best.ident.toChars()); - .errorSupplemental(td_best.loc, "`%s`\nand:", td_best.toChars()); - .errorSupplemental(td_ambig.loc, "`%s`", td_ambig.toChars()); - return false; - } - if (td_best) - { - if (!td_last) - td_last = td_best; - else if (td_last != td_best) - { - ScopeDsymbol.multiplyDefined(loc, td_last, td_best); - return false; - } - } - } + auto resolvedArgs = fd.type.isTypeFunction().resolveNamedArgs( + ArgumentList(this.fargs, this.fnames), null); - if (td_last) - { - /* https://issues.dlang.org/show_bug.cgi?id=7469 - * Normalize tiargs by using corresponding deduced - * template value parameters and tuples for the correct mangling. - * - * By doing this before hasNestedArgs, CTFEable local variable will be - * accepted as a value parameter. For example: - * - * void foo() { - * struct S(int n) {} // non-global template - * const int num = 1; // CTFEable local variable - * S!num s; // S!1 is instantiated, not S!num - * } - */ - size_t dim = td_last.parameters.length - (td_last.isVariadic() ? 1 : 0); - for (size_t i = 0; i < dim; i++) - { - if (tiargs.length <= i) - tiargs.push(tdtypes[i]); - assert(i < tiargs.length); + // resolvedArgs can be null when there's an error: fail_compilation/fail14669.d + // In that case, equalsx returns true to prevent endless template instantiations + // However, it can also mean the function was explicitly instantiated + // without function arguments: fail_compilation/fail14669 + // Hence the following check: + if (this.fargs && !resolvedArgs) + return true; - auto tvp = (*td_last.parameters)[i].isTemplateValueParameter(); - if (!tvp) - continue; - assert(tdtypes[i]); - // tdtypes[i] is already normalized to the required type in matchArg + Expression[] args = resolvedArgs ? (*resolvedArgs)[] : []; - (*tiargs)[i] = tdtypes[i]; - } - if (td_last.isVariadic() && tiargs.length == dim && tdtypes[dim]) - { - Tuple va = isTuple(tdtypes[dim]); - assert(va); - tiargs.pushSlice(va.objects[]); - } - } - else if (errors && inst) - { - // instantiation was failed with error reporting - assert(global.errors); - return false; - } - else + auto fparameters = fd.getParameterList(); + size_t nfparams = fparameters.length; // Num function parameters + for (size_t j = 0; j < nfparams; j++) { - auto tdecl = tempdecl.isTemplateDeclaration(); + Parameter fparam = fparameters[j]; + if (!(fparam.storageClass & STC.autoref) ) // if "auto ref" + continue; - if (errs != global.errors) - errorSupplemental(loc, "while looking for match for `%s`", toChars()); - else if (tdecl && !tdecl.overnext) + Expression farg = (j < args.length) ? args[j] : fparam.defaultArg; + // resolveNamedArgs strips trailing nulls / default params + // when it doesn't anymore, the ternary can be replaced with: + // assert(j < resolvedArgs.length); + if (!farg) + farg = fparam.defaultArg; + if (!farg) + return false; + if (farg.isLvalue()) { - // Only one template, so we can give better error message - const(char)* msg = "does not match template declaration"; - const(char)* tip; - OutBuffer buf; - HdrGenState hgs; - hgs.skipConstraints = true; - toCharsMaybeConstraints(tdecl, buf, hgs); - const tmsg = buf.peekChars(); - const cmsg = tdecl.getConstraintEvalError(tip); - if (cmsg) - { - .error(loc, "%s `%s` %s `%s`\n%s", kind, toPrettyChars, msg, tmsg, cmsg); - if (tip) - .tip(tip); - } - else - { - .error(loc, "%s `%s` %s `%s`", kind, toPrettyChars, msg, tmsg); - - if (tdecl.parameters.length == tiargs.length) - { - // https://issues.dlang.org/show_bug.cgi?id=7352 - // print additional information, e.g. `foo` is not a type - foreach (i, param; *tdecl.parameters) - { - MATCH match = param.matchArg(loc, sc, tiargs, i, tdecl.parameters, dedtypes, null); - auto arg = (*tiargs)[i]; - auto sym = arg.isDsymbol; - auto exp = arg.isExpression; - - if (exp) - exp = exp.optimize(WANTvalue); - - if (match == MATCH.nomatch && - ((sym && sym.isFuncDeclaration) || - (exp && exp.isVarExp))) - { - if (param.isTemplateTypeParameter) - errorSupplemental(loc, "`%s` is not a type", arg.toChars); - else if (auto tvp = param.isTemplateValueParameter) - errorSupplemental(loc, "`%s` is not of a value of type `%s`", - arg.toChars, tvp.valType.toChars); - - } - } - } - } + if (!(fparam.storageClass & STC.ref_)) + return false; // auto ref's don't match } else { - .error(loc, "%s `%s` does not match any template declaration", kind(), toPrettyChars()); - bool found; - overloadApply(tempdecl, (s){ - if (!found) - errorSupplemental(loc, "Candidates are:"); - found = true; - errorSupplemental(s.loc, "%s", s.toChars()); - return 0; - }); + if (fparam.storageClass & STC.ref_) + return false; // auto ref's don't match } - return false; } + return true; + } - /* The best match is td_last - */ - tempdecl = td_last; + extern (D) final size_t toHash() + { + if (!hash) + { + hash = cast(size_t)cast(void*)enclosing; + hash += arrayObjectHash(tdtypes); + hash += hash == 0; + } + return hash; + } - static if (LOG) + /********************************** + * Run semantic on the elements of tiargs. + * Input: + * sc + * Returns: + * false if one or more arguments have errors. + * Note: + * This function is reentrant against error occurrence. If returns false, + * all elements of tiargs won't be modified. + */ + extern (D) final bool semanticTiargs(Scope* sc) + { + //printf("+TemplateInstance.semanticTiargs() %s\n", toChars()); + if (semantictiargsdone) + return true; + if (TemplateInstance_semanticTiargs(loc, sc, tiargs, 0)) { - printf("\tIt's a match with template declaration '%s'\n", tempdecl.toChars()); + // cache the result iff semantic analysis succeeded entirely + semantictiargsdone = 1; + return true; } - return (errs == global.errors); + return false; } /***************************************** @@ -5121,108 +2081,15 @@ extern (C++) class TemplateInstance : ScopeDsymbol L1: //printf("\tnested inside %s as it references %s\n", enclosing.toChars(), sa.toChars()); nested |= 1; - } - } - else if (va) - { - nested |= cast(int)hasNestedArgs(&va.objects, isstatic); - } - } - //printf("-TemplateInstance.hasNestedArgs('%s') = %d\n", tempdecl.ident.toChars(), nested); - return nested != 0; - } - - /***************************************** - * Append 'this' to the specific module members[] - */ - extern (D) final Dsymbols* appendToModuleMember() - { - Module mi = minst; // instantiated . inserted module - - //printf("%s.appendToModuleMember() enclosing = %s mi = %s\n", - // toPrettyChars(), - // enclosing ? enclosing.toPrettyChars() : null, - // mi ? mi.toPrettyChars() : null); - if (global.params.allInst || !mi || mi.isRoot()) - { - /* If the instantiated module is speculative or root, insert to the - * member of a root module. Then: - * - semantic3 pass will get called on the instance members. - * - codegen pass will get a selection chance to do/skip it (needsCodegen()). - */ - static Dsymbol getStrictEnclosing(TemplateInstance ti) - { - do - { - if (ti.enclosing) - return ti.enclosing; - ti = ti.tempdecl.isInstantiated(); - } while (ti); - return null; - } - - Dsymbol enc = getStrictEnclosing(this); - // insert target is made stable by using the module - // where tempdecl is declared. - mi = (enc ? enc : tempdecl).getModule(); - if (!mi.isRoot()) - { - if (mi.importedFrom) - { - mi = mi.importedFrom; - assert(mi.isRoot()); - } - else - { - // This can happen when using the frontend as a library. - // Append it to the non-root module. - } - } - } - else - { - /* If the instantiated module is non-root, insert to the member of the - * non-root module. Then: - * - semantic3 pass won't be called on the instance. - * - codegen pass won't reach to the instance. - * Unless it is re-appended to a root module later (with changed minst). - */ - } - //printf("\t-. mi = %s\n", mi.toPrettyChars()); - - if (memberOf) // already appended to some module - { - assert(mi.isRoot(), "can only re-append to a root module"); - if (memberOf.isRoot()) - return null; // no need to move to another root module - } - - Dsymbols* a = mi.members; - a.push(this); - memberOf = mi; - if (mi.semanticRun >= PASS.semantic2done && mi.isRoot()) - Module.addDeferredSemantic2(this); - if (mi.semanticRun >= PASS.semantic3done && mi.isRoot()) - Module.addDeferredSemantic3(this); - return a; - } - - /**************************************************** - * Declare parameters of template instance, initialize them with the - * template instance arguments. - */ - extern (D) final void declareParameters(Scope* sc) - { - TemplateDeclaration tempdecl = this.tempdecl.isTemplateDeclaration(); - assert(tempdecl); - - //printf("TemplateInstance.declareParameters()\n"); - foreach (i, o; tdtypes) // initializer for tp - { - TemplateParameter tp = (*tempdecl.parameters)[i]; - //printf("\ttdtypes[%d] = %p\n", i, o); - declareParameter(tempdecl, sc, tp, o); + } + } + else if (va) + { + nested |= cast(int)hasNestedArgs(&va.objects, isstatic); + } } + //printf("-TemplateInstance.hasNestedArgs('%s') = %d\n", tempdecl.ident.toChars(), nested); + return nested != 0; } /**************************************** @@ -5240,166 +2107,12 @@ extern (C++) class TemplateInstance : ScopeDsymbol return Identifier.idPool(buf[]); } - extern (D) final void expandMembers(Scope* sc2) - { - members.foreachDsymbol( (s) { s.setScope (sc2); } ); - - members.foreachDsymbol( (s) { s.importAll(sc2); } ); - - if (!aliasdecl) - { - /* static if's are crucial to evaluating aliasdecl correctly. But - * evaluating the if/else bodies may require aliasdecl. - * So, evaluate the condition for static if's, but not their if/else bodies. - * Then try to set aliasdecl. - * Later do the if/else bodies. - * https://issues.dlang.org/show_bug.cgi?id=23598 - * It might be better to do this by attaching a lambda to the StaticIfDeclaration - * to do the oneMembers call after the sid.include(sc2) is run as part of dsymbolSemantic(). - */ - bool done; - void staticIfDg(Dsymbol s) - { - if (done || aliasdecl) - return; - //printf("\t staticIfDg on '%s %s' in '%s'\n", s.kind(), s.toChars(), this.toChars()); - if (!s.isStaticIfDeclaration()) - { - //s.dsymbolSemantic(sc2); - done = true; - return; - } - auto sid = s.isStaticIfDeclaration(); - sid.include(sc2); - if (members.length) - { - Dsymbol sa; - if (oneMembers(members, sa, tempdecl.ident) && sa) - aliasdecl = sa; - } - done = true; - } - - members.foreachDsymbol(&staticIfDg); - } - - void symbolDg(Dsymbol s) - { - //printf("\t semantic on '%s' %p kind %s in '%s'\n", s.toChars(), s, s.kind(), this.toChars()); - //printf("test: enclosing = %d, sc2.parent = %s\n", enclosing, sc2.parent.toChars()); - //if (enclosing) - // s.parent = sc.parent; - //printf("test3: enclosing = %d, s.parent = %s\n", enclosing, s.parent.toChars()); - s.dsymbolSemantic(sc2); - //printf("test4: enclosing = %d, s.parent = %s\n", enclosing, s.parent.toChars()); - Module.runDeferredSemantic(); - } - - members.foreachDsymbol(&symbolDg); - } - - extern (D) final void tryExpandMembers(Scope* sc2) - { - __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 > global.recursionLimit) - { - global.gag = 0; // ensure error message gets printed - .error(loc, "%s `%s` recursive expansion exceeded allowed nesting limit", kind, toPrettyChars); - fatal(); - } - - expandMembers(sc2); - - nest--; - } - - extern (D) final void trySemantic3(Scope* sc2) - { - // extracted to a function to allow windows SEH to work without destructors in the same function - __gshared int nest; - //printf("%d\n", nest); - if (++nest > global.recursionLimit) - { - global.gag = 0; // ensure error message gets printed - .error(loc, "%s `%s` recursive expansion exceeded allowed nesting limit", kind, toPrettyChars); - fatal(); - } - - semantic3(this, sc2); - - --nest; - } - override void accept(Visitor v) { v.visit(this); } } -/************************************** - * IsExpression can evaluate the specified type speculatively, and even if - * it instantiates any symbols, they are normally unnecessary for the - * final executable. - * However, if those symbols leak to the actual code, compiler should remark - * them as non-speculative to generate their code and link to the final executable. - */ -void unSpeculative(Scope* sc, RootObject o) -{ - if (!o) - return; - - if (Tuple tup = isTuple(o)) - { - foreach (obj; tup.objects) - { - unSpeculative(sc, obj); - } - return; - } - - Dsymbol s = getDsymbol(o); - if (!s) - return; - - if (Declaration d = s.isDeclaration()) - { - if (VarDeclaration vd = d.isVarDeclaration()) - o = vd.type; - else if (AliasDeclaration ad = d.isAliasDeclaration()) - { - o = ad.getType(); - if (!o) - o = ad.toAlias(); - } - else - o = d.toAlias(); - - s = getDsymbol(o); - if (!s) - return; - } - - if (TemplateInstance ti = s.isTemplateInstance()) - { - // If the instance is already non-speculative, - // or it is leaked to the speculative scope. - if (ti.minst !is null || sc.minst is null) - return; - - // Remark as non-speculative instance. - ti.minst = sc.minst; - if (!ti.tinst) - ti.tinst = sc.tinst; - - unSpeculative(sc, ti.tempdecl); - } - - if (TemplateInstance ti = s.isInstantiated()) - unSpeculative(sc, ti); -} - /********************************** * Return true if e could be valid only as a template value parameter. * Return false if it might be an alias or tuple. @@ -5491,80 +2204,6 @@ extern (C++) final class TemplateMixin : TemplateInstance return "mixin"; } - extern (D) bool findTempDecl(Scope* sc) - { - // Follow qualifications to find the TemplateDeclaration - if (!tempdecl) - { - Expression e; - Type t; - Dsymbol s; - tqual.resolve(loc, sc, e, t, s); - if (!s) - { - .error(loc, "%s `%s` is not defined", kind, toPrettyChars); - return false; - } - s = s.toAlias(); - tempdecl = s.isTemplateDeclaration(); - OverloadSet os = s.isOverloadSet(); - - /* If an OverloadSet, look for a unique member that is a template declaration - */ - if (os) - { - Dsymbol ds = null; - foreach (i, sym; os.a) - { - Dsymbol s2 = sym.isTemplateDeclaration(); - if (s2) - { - if (ds) - { - tempdecl = os; - break; - } - ds = s2; - } - } - } - if (!tempdecl) - { - .error(loc, "%s `%s` - `%s` is a %s, not a template", kind, toPrettyChars, s.toChars(), s.kind()); - return false; - } - } - assert(tempdecl); - - // Look for forward references - auto tovers = tempdecl.isOverloadSet(); - foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1) - { - Dsymbol dstart = tovers ? tovers.a[oi] : tempdecl; - int r = overloadApply(dstart, (Dsymbol s) - { - auto td = s.isTemplateDeclaration(); - if (!td) - return 0; - - if (td.semanticRun == PASS.initial) - { - if (td._scope) - td.dsymbolSemantic(td._scope); - else - { - semanticRun = PASS.initial; - return 1; - } - } - return 0; - }); - if (r) - return false; - } - return true; - } - override void accept(Visitor v) { v.visit(this); @@ -5629,466 +2268,6 @@ struct TemplateInstanceBox } } -/******************************************* - * Match to a particular TemplateParameter. - * Input: - * instLoc location that the template is instantiated. - * tiargs[] actual arguments to template instance - * i i'th argument - * parameters[] template parameters - * dedtypes[] deduced arguments to template instance - * *psparam set to symbol declared and initialized to dedtypes[i] - */ -MATCH matchArg(TemplateParameter tp, Loc instLoc, Scope* sc, Objects* tiargs, size_t i, TemplateParameters* parameters, ref Objects dedtypes, Declaration* psparam) -{ - MATCH matchArgNoMatch() - { - if (psparam) - *psparam = null; - return MATCH.nomatch; - } - - MATCH matchArgParameter() - { - RootObject oarg; - - if (i < tiargs.length) - oarg = (*tiargs)[i]; - else - { - // Get default argument instead - oarg = tp.defaultArg(instLoc, sc); - if (!oarg) - { - assert(i < dedtypes.length); - // It might have already been deduced - oarg = dedtypes[i]; - if (!oarg) - return matchArgNoMatch(); - } - } - return tp.matchArg(sc, oarg, i, parameters, dedtypes, psparam); - } - - MATCH matchArgTuple(TemplateTupleParameter ttp) - { - /* The rest of the actual arguments (tiargs[]) form the match - * for the variadic parameter. - */ - assert(i + 1 == dedtypes.length); // must be the last one - Tuple ovar; - - if (Tuple u = isTuple(dedtypes[i])) - { - // It has already been deduced - ovar = u; - } - else if (i + 1 == tiargs.length && isTuple((*tiargs)[i])) - ovar = isTuple((*tiargs)[i]); - else - { - ovar = new Tuple(); - //printf("ovar = %p\n", ovar); - if (i < tiargs.length) - { - //printf("i = %d, tiargs.length = %d\n", i, tiargs.length); - ovar.objects.setDim(tiargs.length - i); - foreach (j, ref obj; ovar.objects) - obj = (*tiargs)[i + j]; - } - } - return ttp.matchArg(sc, ovar, i, parameters, dedtypes, psparam); - } - - if (auto ttp = tp.isTemplateTupleParameter()) - return matchArgTuple(ttp); - - return matchArgParameter(); -} - -MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, TemplateParameters* parameters, ref Objects dedtypes, Declaration* psparam) -{ - MATCH matchArgNoMatch() - { - //printf("\tm = %d\n", MATCH.nomatch); - if (psparam) - *psparam = null; - return MATCH.nomatch; - } - - MATCH matchArgType(TemplateTypeParameter ttp) - { - //printf("TemplateTypeParameter.matchArg('%s')\n", ttp.ident.toChars()); - MATCH m = MATCH.exact; - Type ta = isType(oarg); - if (!ta) - { - //printf("%s %p %p %p\n", oarg.toChars(), isExpression(oarg), isDsymbol(oarg), isTuple(oarg)); - return matchArgNoMatch(); - } - //printf("ta is %s\n", ta.toChars()); - - if (ttp.specType) - { - if (!ta || ta == TemplateTypeParameter.tdummy) - return matchArgNoMatch(); - - //printf("\tcalling deduceType(): ta is %s, specType is %s\n", ta.toChars(), ttp.specType.toChars()); - MATCH m2 = deduceType(ta, sc, ttp.specType, *parameters, dedtypes); - if (m2 == MATCH.nomatch) - { - //printf("\tfailed deduceType\n"); - return matchArgNoMatch(); - } - - if (m2 < m) - m = m2; - if (dedtypes[i]) - { - Type t = cast(Type)dedtypes[i]; - - if (ttp.dependent && !t.equals(ta)) // https://issues.dlang.org/show_bug.cgi?id=14357 - return matchArgNoMatch(); - - /* This is a self-dependent parameter. For example: - * template X(T : T*) {} - * template X(T : S!T, alias S) {} - */ - //printf("t = %s ta = %s\n", t.toChars(), ta.toChars()); - ta = t; - } - } - else - { - if (dedtypes[i]) - { - // Must match already deduced type - Type t = cast(Type)dedtypes[i]; - - if (!t.equals(ta)) - { - //printf("t = %s ta = %s\n", t.toChars(), ta.toChars()); - return matchArgNoMatch(); - } - } - else - { - // So that matches with specializations are better - m = MATCH.convert; - } - } - dedtypes[i] = ta; - - if (psparam) - *psparam = new AliasDeclaration(ttp.loc, ttp.ident, ta); - //printf("\tm = %d\n", m); - return ttp.dependent ? MATCH.exact : m; - } - - MATCH matchArgValue(TemplateValueParameter tvp) - { - //printf("TemplateValueParameter.matchArg('%s')\n", tvp.ident.toChars()); - MATCH m = MATCH.exact; - - Expression ei = isExpression(oarg); - Type vt; - - if (!ei && oarg) - { - Dsymbol si = isDsymbol(oarg); - FuncDeclaration f = si ? si.isFuncDeclaration() : null; - if (!f || !f.fbody || f.needThis()) - return matchArgNoMatch(); - - ei = new VarExp(tvp.loc, f); - ei = ei.expressionSemantic(sc); - - /* If a function is really property-like, and then - * it's CTFEable, ei will be a literal expression. - */ - const olderrors = global.startGagging(); - ei = resolveProperties(sc, ei); - ei = ei.ctfeInterpret(); - if (global.endGagging(olderrors) || ei.op == EXP.error) - return matchArgNoMatch(); - - /* https://issues.dlang.org/show_bug.cgi?id=14520 - * A property-like function can match to both - * TemplateAlias and ValueParameter. But for template overloads, - * it should always prefer alias parameter to be consistent - * template match result. - * - * template X(alias f) { enum X = 1; } - * template X(int val) { enum X = 2; } - * int f1() { return 0; } // CTFEable - * int f2(); // body-less function is not CTFEable - * enum x1 = X!f1; // should be 1 - * enum x2 = X!f2; // should be 1 - * - * e.g. The x1 value must be same even if the f1 definition will be moved - * into di while stripping body code. - */ - m = MATCH.convert; - } - - if (ei && ei.op == EXP.variable) - { - // Resolve const variables that we had skipped earlier - ei = ei.ctfeInterpret(); - } - - //printf("\tvalType: %s, ty = %d\n", tvp.valType.toChars(), tvp.valType.ty); - vt = tvp.valType.typeSemantic(tvp.loc, sc); - //printf("ei: %s, ei.type: %s\n", ei.toChars(), ei.type.toChars()); - //printf("vt = %s\n", vt.toChars()); - - if (ei.type) - { - MATCH m2 = ei.implicitConvTo(vt); - //printf("m: %d\n", m); - if (m2 < m) - m = m2; - if (m == MATCH.nomatch) - return matchArgNoMatch(); - ei = ei.implicitCastTo(sc, vt); - ei = ei.ctfeInterpret(); - } - - if (tvp.specValue) - { - if (ei is null || (cast(void*)ei.type in TemplateValueParameter.edummies && - TemplateValueParameter.edummies[cast(void*)ei.type] == ei)) - return matchArgNoMatch(); - - Expression e = tvp.specValue; - - sc = sc.startCTFE(); - e = e.expressionSemantic(sc); - e = resolveProperties(sc, e); - sc = sc.endCTFE(); - e = e.implicitCastTo(sc, vt); - e = e.ctfeInterpret(); - - ei = ei.syntaxCopy(); - sc = sc.startCTFE(); - ei = ei.expressionSemantic(sc); - sc = sc.endCTFE(); - ei = ei.implicitCastTo(sc, vt); - ei = ei.ctfeInterpret(); - //printf("\tei: %s, %s\n", ei.toChars(), ei.type.toChars()); - //printf("\te : %s, %s\n", e.toChars(), e.type.toChars()); - if (!ei.equals(e)) - return matchArgNoMatch(); - } - else - { - if (dedtypes[i]) - { - // Must match already deduced value - Expression e = cast(Expression)dedtypes[i]; - if (!ei || !ei.equals(e)) - return matchArgNoMatch(); - } - } - dedtypes[i] = ei; - - if (psparam) - { - Initializer _init = new ExpInitializer(tvp.loc, ei); - Declaration sparam = new VarDeclaration(tvp.loc, vt, tvp.ident, _init); - sparam.storage_class = STC.manifest; - *psparam = sparam; - } - return tvp.dependent ? MATCH.exact : m; - } - - MATCH matchArgAlias(TemplateAliasParameter tap) - { - //printf("TemplateAliasParameter.matchArg('%s')\n", tap.ident.toChars()); - MATCH m = MATCH.exact; - Type ta = isType(oarg); - RootObject sa = ta && !ta.deco ? null : getDsymbol(oarg); - Expression ea = isExpression(oarg); - if (ea) - { - if (auto te = ea.isThisExp()) - sa = te.var; - else if (auto se = ea.isSuperExp()) - sa = se.var; - else if (auto se = ea.isScopeExp()) - sa = se.sds; - } - if (sa) - { - if ((cast(Dsymbol)sa).isAggregateDeclaration()) - m = MATCH.convert; - - /* specType means the alias must be a declaration with a type - * that matches specType. - */ - if (tap.specType) - { - tap.specType = typeSemantic(tap.specType, tap.loc, sc); - Declaration d = (cast(Dsymbol)sa).isDeclaration(); - if (!d) - return matchArgNoMatch(); - if (!d.type.equals(tap.specType)) - return matchArgNoMatch(); - } - } - else - { - sa = oarg; - if (ea) - { - if (tap.specType) - { - if (!ea.type.equals(tap.specType)) - return matchArgNoMatch(); - } - } - else if (ta && ta.ty == Tinstance && !tap.specAlias) - { - /* Specialized parameter should be preferred - * match to the template type parameter. - * template X(alias a) {} // a == this - * template X(alias a : B!A, alias B, A...) {} // B!A => ta - */ - } - else if (sa && sa == TemplateTypeParameter.tdummy) - { - /* https://issues.dlang.org/show_bug.cgi?id=2025 - * Aggregate Types should preferentially - * match to the template type parameter. - * template X(alias a) {} // a == this - * template X(T) {} // T => sa - */ - } - else if (ta && ta.ty != Tident) - { - /* Match any type that's not a TypeIdentifier to alias parameters, - * but prefer type parameter. - * template X(alias a) { } // a == ta - * - * TypeIdentifiers are excluded because they might be not yet resolved aliases. - */ - m = MATCH.convert; - } - else - return matchArgNoMatch(); - } - - if (tap.specAlias) - { - if (sa == TemplateAliasParameter.sdummy) - return matchArgNoMatch(); - // check specialization if template arg is a symbol - Dsymbol sx = isDsymbol(sa); - if (sa != tap.specAlias && sx) - { - Type talias = isType(tap.specAlias); - if (!talias) - return matchArgNoMatch(); - - TemplateInstance ti = sx.isTemplateInstance(); - if (!ti && sx.parent) - { - ti = sx.parent.isTemplateInstance(); - if (ti && ti.name != sx.ident) - return matchArgNoMatch(); - } - if (!ti) - return matchArgNoMatch(); - - Type t = new TypeInstance(Loc.initial, ti); - MATCH m2 = deduceType(t, sc, talias, *parameters, dedtypes); - if (m2 == MATCH.nomatch) - return matchArgNoMatch(); - } - // check specialization if template arg is a type - else if (ta) - { - if (Type tspec = isType(tap.specAlias)) - { - MATCH m2 = ta.implicitConvTo(tspec); - if (m2 == MATCH.nomatch) - return matchArgNoMatch(); - } - else - { - error(tap.loc, "template parameter specialization for a type must be a type and not `%s`", - tap.specAlias.toChars()); - return matchArgNoMatch(); - } - } - } - else if (dedtypes[i]) - { - // Must match already deduced symbol - RootObject si = dedtypes[i]; - if (!sa || si != sa) - return matchArgNoMatch(); - } - dedtypes[i] = sa; - - if (psparam) - { - if (Dsymbol s = isDsymbol(sa)) - { - *psparam = new AliasDeclaration(tap.loc, tap.ident, s); - } - else if (Type t = isType(sa)) - { - *psparam = new AliasDeclaration(tap.loc, tap.ident, t); - } - else - { - assert(ea); - - // Declare manifest constant - Initializer _init = new ExpInitializer(tap.loc, ea); - auto v = new VarDeclaration(tap.loc, null, tap.ident, _init); - v.storage_class = STC.manifest; - v.dsymbolSemantic(sc); - *psparam = v; - } - } - return tap.dependent ? MATCH.exact : m; - } - - MATCH matchArgTuple(TemplateTupleParameter ttp) - { - //printf("TemplateTupleParameter.matchArg('%s')\n", ttp.ident.toChars()); - Tuple ovar = isTuple(oarg); - if (!ovar) - return MATCH.nomatch; - if (dedtypes[i]) - { - Tuple tup = isTuple(dedtypes[i]); - if (!tup) - return MATCH.nomatch; - if (!match(tup, ovar)) - return MATCH.nomatch; - } - dedtypes[i] = ovar; - - if (psparam) - *psparam = new TupleDeclaration(ttp.loc, ttp.ident, &ovar.objects); - return ttp.dependent ? MATCH.exact : MATCH.convert; - } - - if (auto ttp = tp.isTemplateTypeParameter()) - return matchArgType(ttp); - if (auto tvp = tp.isTemplateValueParameter()) - return matchArgValue(tvp); - if (auto tap = tp.isTemplateAliasParameter()) - return matchArgAlias(tap); - if (auto ttp = tp.isTemplateTupleParameter()) - return matchArgTuple(ttp); - assert(0); -} - - /*********************************************** * Collect and print statistics on template instantiations. */ diff --git a/gcc/d/dmd/dtoh.d b/gcc/d/dmd/dtoh.d index 3946c25c9f0..4cb7d1ac5dd 100644 --- a/gcc/d/dmd/dtoh.d +++ b/gcc/d/dmd/dtoh.d @@ -895,7 +895,7 @@ public: { // Omit redundant declarations - the slot was already // reserved in the base class - if (fd.isVirtual() && fd.isIntroducing()) + if (fd.isVirtual() && fd.isIntroducing) { // Hide placeholders because they are not ABI compatible writeProtection(AST.Visibility.Kind.private_); @@ -1164,7 +1164,7 @@ public: auto fd = ad.aliassym.isFuncDeclaration(); - if (fd && (fd.isGenerated() || fd.isDtorDeclaration())) + if (fd && (fd.isGenerated || fd.isDtorDeclaration())) { // Ignore. It's taken care of while visiting FuncDeclaration return; @@ -1197,7 +1197,7 @@ public: // Print prefix of the base class if this function originates from a superclass // because alias might be resolved through multiple classes, e.g. // e.g. for alias visit = typeof(super).visit in the visitors - if (!fd.isIntroducing()) + if (!fd.isIntroducing) printPrefix(ad.toParent().isClassDeclaration().baseClass); else printPrefix(pd); diff --git a/gcc/d/dmd/dversion.d b/gcc/d/dmd/dversion.d index 26528e9f517..1561ebb0b7c 100644 --- a/gcc/d/dmd/dversion.d +++ b/gcc/d/dmd/dversion.d @@ -20,7 +20,6 @@ import dmd.dmodule; import dmd.dscope; import dmd.dsymbol; import dmd.dsymbolsem; -import dmd.globals; import dmd.identifier; import dmd.location; import dmd.common.outbuffer; diff --git a/gcc/d/dmd/enum.h b/gcc/d/dmd/enum.h index 6d5d3022a7f..2b4d3b52d89 100644 --- a/gcc/d/dmd/enum.h +++ b/gcc/d/dmd/enum.h @@ -59,7 +59,7 @@ public: bool isSpecial() const; - Symbol *sinit; + void *sinit; void accept(Visitor *v) override { v->visit(this); } }; diff --git a/gcc/d/dmd/enumsem.d b/gcc/d/dmd/enumsem.d index 6d325e44c96..cdf8a61bd88 100644 --- a/gcc/d/dmd/enumsem.d +++ b/gcc/d/dmd/enumsem.d @@ -240,7 +240,9 @@ void enumSemantic(Scope* sc, EnumDeclaration ed) em.dsymbolSemantic(em._scope); }); - if (global.params.useTypeInfo && Type.dtypeinfo && !ed.inNonRoot()) + if (ed.errors) + ed.memtype = Type.terror; // avoid infinite recursion in toBaseType + else if (global.params.useTypeInfo && Type.dtypeinfo && !ed.inNonRoot()) semanticTypeInfo(sc, ed.memtype); //printf("ed.defaultval = %lld\n", ed.defaultval); diff --git a/gcc/d/dmd/escape.d b/gcc/d/dmd/escape.d index a0c5472375e..d3473f691d5 100644 --- a/gcc/d/dmd/escape.d +++ b/gcc/d/dmd/escape.d @@ -1557,7 +1557,7 @@ void escapeExp(Expression e, ref scope EscapeByResults er, int deref) void visitThis(ThisExp e) { // Special case because `__this2` isn't `ref` internally - if (deref == -1 && e.var && e.var.toParent2().isFuncDeclaration().hasDualContext()) + if (deref == -1 && e.var && e.var.toParent2().isFuncDeclaration().hasDualContext) { escapeByValue(e, er); return; diff --git a/gcc/d/dmd/expression.d b/gcc/d/dmd/expression.d index c29484d9e7b..4d873ccecdc 100644 --- a/gcc/d/dmd/expression.d +++ b/gcc/d/dmd/expression.d @@ -21,6 +21,7 @@ import dmd.aggregate; import dmd.arraytypes; import dmd.astenums; import dmd.ast_node; +import dmd.ctfeexpr : isCtfeReferenceValid; import dmd.dcast : implicitConvTo; import dmd.dclass; import dmd.declaration; @@ -31,6 +32,7 @@ import dmd.dsymbol; import dmd.dtemplate; import dmd.errors; import dmd.errorsink; +import dmd.expressionsem : getDsymbol; import dmd.func; import dmd.globals; import dmd.hdrgen; @@ -49,7 +51,7 @@ import dmd.root.string; import dmd.root.utf; import dmd.target; import dmd.tokens; -import dmd.typesem; +import dmd.typesem : toHeadMutable, size, mutableOf, unSharedOf; import dmd.visitor; enum LOGSEMANTIC = false; @@ -527,20 +529,6 @@ extern (C++) abstract class Expression : ASTNode return false; } - /****************************** - * Take address of expression. - */ - final Expression addressOf() - { - //printf("Expression::addressOf()\n"); - debug - { - assert(op == EXP.error || isLvalue()); - } - Expression e = new AddrExp(loc, this, type.pointerTo()); - return e; - } - /****************************** * If this is a reference, dereference it. */ @@ -1906,6 +1894,12 @@ extern (C++) final class ArrayLiteralExp : Expression Expressions* elements; + Expression lowering; + + // aaLiteral is set if this is an array of values of an AA literal + // only used during CTFE to show the original AA in error messages instead + AssocArrayLiteralExp aaLiteral; + extern (D) this(Loc loc, Type type, Expressions* elements) @safe { super(loc, EXP.arrayLiteral); @@ -1917,8 +1911,7 @@ extern (C++) final class ArrayLiteralExp : Expression { super(loc, EXP.arrayLiteral); this.type = type; - elements = new Expressions(); - elements.push(e); + elements = new Expressions(e); } extern (D) this(Loc loc, Type type, Expression basis, Expressions* elements) @safe @@ -2009,7 +2002,7 @@ extern (C++) final class ArrayLiteralExp : Expression if (ch.op != EXP.int64) return null; if (sz == 1) - buf.writeByte(cast(uint)ch.toInteger()); + buf.writeByte(cast(ubyte)ch.toInteger()); else if (sz == 2) buf.writeword(cast(uint)ch.toInteger()); else @@ -2059,8 +2052,9 @@ extern (C++) final class AssocArrayLiteralExp : Expression Expressions* keys; Expressions* values; - /// Lower to core.internal.newaa for static initializaton - Expression lowering; + + Expression lowering; // call to _d_assocarrayliteralTX() + Expression loweringCtfe; // result of interpreting lowering for static initializaton extern (D) this(Loc loc, Expressions* keys, Expressions* values) @safe { @@ -2217,56 +2211,6 @@ extern (C++) final class StructLiteralExp : Expression return exp; } - /************************************** - * Gets expression at offset of type. - * Returns NULL if not found. - */ - extern (D) Expression getField(Type type, uint offset) - { - //printf("StructLiteralExp::getField(this = %s, type = %s, offset = %u)\n", - // /*toChars()*/"", type.toChars(), offset); - Expression e = null; - int i = getFieldIndex(type, offset); - - if (i != -1) - { - //printf("\ti = %d\n", i); - if (i >= sd.nonHiddenFields()) - return null; - - assert(i < elements.length); - e = (*elements)[i]; - if (e) - { - //printf("e = %s, e.type = %s\n", e.toChars(), e.type.toChars()); - - /* If type is a static array, and e is an initializer for that array, - * then the field initializer should be an array literal of e. - */ - auto tsa = type.isTypeSArray(); - if (tsa && e.type.castMod(0) != type.castMod(0)) - { - const length = cast(size_t)tsa.dim.toInteger(); - auto z = new Expressions(length); - foreach (ref q; *z) - q = e.copy(); - e = new ArrayLiteralExp(loc, type, z); - } - else - { - e = e.copy(); - e.type = type; - } - if (useStaticInit && e.type.needsNested()) - if (auto se = e.isStructLiteralExp()) - { - se.useStaticInit = true; - } - } - } - return e; - } - /************************************ * Get index of field. * Returns -1 if not found. @@ -3312,6 +3256,7 @@ extern (C++) final class CallExp : UnaExp bool ignoreAttributes; /// don't enforce attributes (e.g. call @gc function in @nogc code) bool isUfcsRewrite; /// the first argument was pushed in here by a UFCS rewrite VarDeclaration vthis2; // container for multi-context + Expression loweredFrom; // set if this is the result of a lowering /// Puts the `arguments` and `names` into an `ArgumentList` for easily passing them around. /// The fields are still separate for backwards compatibility @@ -3486,6 +3431,13 @@ extern (C++) final class AddrExp : UnaExp type = t; } + override Optional!bool toBool() + { + if (isCtfeReferenceValid(e1)) + return typeof(return)(true); + return UnaExp.toBool(); + } + override void accept(Visitor v) { v.visit(this); @@ -3574,6 +3526,8 @@ extern (C++) final class ComExp : UnaExp */ extern (C++) final class NotExp : UnaExp { + Expression loweredFrom; // for lowering of `aa1 != aa2` to `!_d_aaEqual(aa1, aa2)` + extern (D) this(Loc loc, Expression e) @safe { super(loc, EXP.not, e); @@ -3618,6 +3572,7 @@ extern (C++) final class CastExp : UnaExp Type to; // type to cast to ubyte mod = cast(ubyte)~0; // MODxxxxx bool trusted; // assume cast is safe + Expression lowering; extern (D) this(Loc loc, Expression e, Type t) @safe { @@ -3797,6 +3752,7 @@ extern (C++) final class ArrayExp : UnaExp size_t currentDimension; // for opDollar VarDeclaration lengthVar; + bool modifiable = false; // is this expected to be an lvalue in an AssignExp? propagate to IndexExp extern (D) this(Loc loc, Expression e1, Expression index = null) { @@ -3994,6 +3950,7 @@ extern (C++) final class DelegateFuncptrExp : UnaExp extern (C++) final class IndexExp : BinExp { VarDeclaration lengthVar; + Expression loweredFrom; // for associative array lowering to _d_aaGetY or _d_aaGetRvalueX bool modifiable = false; // assume it is an rvalue bool indexIsInBounds; // true if 0 <= e2 && e2 <= e1.length - 1 @@ -4030,29 +3987,6 @@ extern (C++) final class IndexExp : BinExp return true; } - extern (D) Expression markSettingAAElem() - { - if (e1.type.toBasetype().ty == Taarray) - { - Type t2b = e2.type.toBasetype(); - if (t2b.ty == Tarray && t2b.nextOf().isMutable()) - { - error(loc, "associative arrays can only be assigned values with immutable keys, not `%s`", e2.type.toChars()); - return ErrorExp.get(); - } - modifiable = true; - - if (auto ie = e1.isIndexExp()) - { - Expression ex = ie.markSettingAAElem(); - if (ex.op == EXP.error) - return ex; - assert(ex == e1); - } - } - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -4783,7 +4717,6 @@ extern (C++) final class RemoveExp : BinExp extern (D) this(Loc loc, Expression e1, Expression e2) { super(loc, EXP.remove, e1, e2); - type = Type.tbool; } override void accept(Visitor v) @@ -4801,6 +4734,7 @@ extern (C++) final class RemoveExp : BinExp */ extern (C++) final class EqualExp : BinExp { + Expression lowering; extern (D) this(EXP op, Loc loc, Expression e1, Expression e2) @safe { super(loc, op, e1, e2); diff --git a/gcc/d/dmd/expression.h b/gcc/d/dmd/expression.h index 6b4298b3308..7c92ebe66c4 100644 --- a/gcc/d/dmd/expression.h +++ b/gcc/d/dmd/expression.h @@ -407,6 +407,8 @@ public: d_bool onstack; Expression *basis; Expressions *elements; + Expression *lowering; + AssocArrayLiteralExp* aaLiteral; // set if this is an array of keys/values of an AA literal static ArrayLiteralExp *create(Loc loc, Expressions *elements); ArrayLiteralExp *syntaxCopy() override; @@ -425,6 +427,7 @@ public: Expressions *keys; Expressions *values; Expression* lowering; + Expression* loweringCtfe; bool equals(const RootObject * const o) const override; AssocArrayLiteralExp *syntaxCopy() override; @@ -833,6 +836,7 @@ public: d_bool ignoreAttributes; // don't enforce attributes (e.g. call @gc function in @nogc code) d_bool isUfcsRewrite; // the first argument was pushed in here by a UFCS rewrite VarDeclaration *vthis2; // container for multi-context + Expression* loweredFrom; // set if this is the result of a lowering static CallExp *create(Loc loc, Expression *e, Expressions *exps); static CallExp *create(Loc loc, Expression *e); @@ -848,6 +852,7 @@ public: class AddrExp final : public UnaExp { public: + Optional toBool() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -880,6 +885,7 @@ public: class NotExp final : public UnaExp { public: + Expression* loweredFrom; // for lowering of `aa1 != aa2` to `!_d_aaEqual(aa1, aa2)` void accept(Visitor *v) override { v->visit(this); } }; @@ -897,6 +903,7 @@ public: Type *to; // type to cast to unsigned char mod; // MODxxxxx d_bool trusted; // assume cast is safe + Expression* lowering; CastExp *syntaxCopy() override; bool isLvalue() override; @@ -986,6 +993,7 @@ public: Expressions *arguments; // Array of Expression's size_t currentDimension; // for opDollar VarDeclaration *lengthVar; + d_bool modifiable; ArrayExp *syntaxCopy() override; bool isLvalue() override; @@ -1016,6 +1024,7 @@ class IndexExp final : public BinExp { public: VarDeclaration *lengthVar; + Expression* loweredFrom; // for associative array lowering to _d_aaGetY or _d_aaGetRvalueX d_bool modifiable; d_bool indexIsInBounds; // true if 0 <= e2 && e2 <= e1.length - 1 @@ -1279,6 +1288,8 @@ public: class EqualExp final : public BinExp { public: + Expression* lowering; + void accept(Visitor *v) override { v->visit(this); } }; diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d index 2576d7c0d05..68fe3dca41c 100644 --- a/gcc/d/dmd/expressionsem.d +++ b/gcc/d/dmd/expressionsem.d @@ -1,4 +1,4 @@ -/** +/*** * Semantic analysis of expressions. * * Specification: ($LINK2 https://dlang.org/spec/expression.html, Expressions) @@ -29,6 +29,7 @@ import dmd.cond; import dmd.ctorflow; import dmd.dscope; import dmd.dsymbol; +import dmd.dsymbolsem; import dmd.declaration; import dmd.dclass; import dmd.dcast; @@ -39,7 +40,7 @@ import dmd.dimport; import dmd.dinterpret; import dmd.dmodule; import dmd.dstruct; -import dmd.dsymbolsem; +import dmd.dsymbolsem : findTempDecl; import dmd.dtemplate; import dmd.errors; import dmd.errorsink; @@ -82,7 +83,7 @@ import dmd.semantic3; import dmd.sideeffect; import dmd.safe; import dmd.target; -import dmd.templatesem : matchWithInstance; +import dmd.templatesem : matchWithInstance, deduceType, matchArg, updateTempDecl; import dmd.tokens; import dmd.traits; import dmd.typesem; @@ -202,6 +203,54 @@ bool expressionsToString(ref OutBuffer buf, Scope* sc, Expressions* exps, return false; } +/*************************************** + * Verifies whether the struct declaration has a + * constructor that is not a copy constructor. + * Optionally, it can check whether the struct + * declaration has a regular constructor, that + * is not disabled. + * + * Params: + * sd = struct declaration + * ignoreDisabled = true to ignore disabled constructors + * Returns: + * true, if the struct has a regular (optionally, + * not disabled) constructor, false otherwise. + */ +bool hasRegularCtor(StructDeclaration sd, bool ignoreDisabled) +{ + if (!sd.ctor) + return false; + + bool result; + overloadApply(sd.ctor, (Dsymbol s) + { + if (auto td = s.isTemplateDeclaration()) + { + if (ignoreDisabled && td.onemember) + { + if (auto ctorDecl = td.onemember.isCtorDeclaration()) + { + if (ctorDecl.storage_class & STC.disable) + return 0; + } + } + result = true; + return 1; + } + if (auto ctorDecl = s.isCtorDeclaration()) + { + if (!ctorDecl.isCpCtor && !ctorDecl.isMoveCtor && (!ignoreDisabled || !(ctorDecl.storage_class & STC.disable))) + { + result = true; + return 1; + } + } + return 0; + }); + return result; +} + /***************************************** * Determine if `this` is available by walking up the enclosing * scopes until a function is found. @@ -228,7 +277,7 @@ FuncDeclaration hasThis(Scope* sc) { return null; } - if (!fd.isNested() || fd.isThis() || (fd.hasDualContext() && fd.isMember2())) + if (!fd.isNested() || fd.isThis() || (fd.hasDualContext && fd.isMember2())) break; Dsymbol parent = fd.parent; @@ -245,7 +294,7 @@ FuncDeclaration hasThis(Scope* sc) fd = parent.isFuncDeclaration(); } - if (!fd.isThis() && !(fd.hasDualContext() && fd.isMember2())) + if (!fd.isThis() && !(fd.hasDualContext && fd.isMember2())) { return null; } @@ -410,54 +459,6 @@ extern (D) Expression incompatibleTypes(BinExp e, Scope* sc = null) return ErrorExp.get(); } -private Expression reorderSettingAAElem(BinExp exp, Scope* sc) -{ - BinExp be = exp; - - auto ie = be.e1.isIndexExp(); - if (!ie) - return be; - if (ie.e1.type.toBasetype().ty != Taarray) - return be; - - /* Fix evaluation order of setting AA element - * https://issues.dlang.org/show_bug.cgi?id=3825 - * Rewrite: - * aa[k1][k2][k3] op= val; - * as: - * auto ref __aatmp = aa; - * auto ref __aakey3 = k1, __aakey2 = k2, __aakey1 = k3; - * auto ref __aaval = val; - * __aatmp[__aakey3][__aakey2][__aakey1] op= __aaval; // assignment - */ - - Expression e0; - while (1) - { - Expression de; - ie.e2 = extractSideEffect(sc, "__aakey", de, ie.e2); - e0 = Expression.combine(de, e0); - - auto ie1 = ie.e1.isIndexExp(); - if (!ie1 || - ie1.e1.type.toBasetype().ty != Taarray) - { - break; - } - ie = ie1; - } - assert(ie.e1.type.toBasetype().ty == Taarray); - - Expression de; - ie.e1 = extractSideEffect(sc, "__aatmp", de, ie.e1); - e0 = Expression.combine(de, e0); - - be.e2 = extractSideEffect(sc, "__aaval", e0, be.e2, true); - - //printf("-e0 = %s, be = %s\n", e0.toChars(), be.toChars()); - return Expression.combine(e0, be); -} - private Expression checkOpAssignTypes(BinExp binExp, Scope* sc) { auto e1 = binExp.e1; @@ -612,20 +613,15 @@ private Expression extractOpDollarSideEffect(Scope* sc, UnaExp ue) // https://issues.dlang.org/show_bug.cgi?id=12585 // Extract the side effect part if ue.e1 is comma. - if (sc.ctfe ? hasSideEffect(e1) : !isTrivialExp(e1)) // match logic in extractSideEffect() - { - /* 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.isVarExp()); - e1.isVarExp().var.storage_class |= STC.exptemp; // lifetime limited to expression - } + /* 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); ue.e1 = e1; return e0; } @@ -663,6 +659,72 @@ TupleDeclaration isAliasThisTuple(Expression e) } } +/****************************** + * Take address of expression. + */ +Expression addressOf(Expression e) +{ + //printf("Expression::addressOf() %s\n", e.toChars()); + debug + { + assert(e.op == EXP.error || e.isLvalue()); + } + return new AddrExp(e.loc, e, e.type.pointerTo()); +} + +/************************************** + * Gets expression at offset of type. + * Returns NULL if not found. + */ +Expression getField(StructLiteralExp sle, Type type, uint offset) +{ + //printf("StructLiteralExp::getField(this = %s, type = %s, offset = %u)\n", + // /*toChars()*/"", type.toChars(), offset); + Expression e2 = null; + const int i = sle.getFieldIndex(type, offset); + + if (i == -1) + return null; + + //printf("\ti = %d\n", i); + if (i >= sle.sd.nonHiddenFields()) + return null; + + assert(i < sle.elements.length); + e2 = (*sle.elements)[i]; + if (!e2) + return null; + + //printf("e = %s, e.type = %s\n", e.toChars(), e.type.toChars()); + + /* If type is a static array, and e is an initializer for that array, + * then the field initializer should be an array literal of e. + */ + auto tsa = type.isTypeSArray(); + if (tsa && e2.type.castMod(0) != type.castMod(0)) + { + const length = cast(size_t)tsa.dim.toInteger(); + auto z = new Expressions(length); + foreach (ref q; *z) + q = e2.copy(); + e2 = new ArrayLiteralExp(sle.loc, type, z); + } + else + { + e2 = e2.copy(); + e2.type = type; + } + if (sle.useStaticInit && e2.type.needsNested()) + if (auto se = e2.isStructLiteralExp()) + { + se.useStaticInit = true; + } + + return e2; +} + + + /************************************** * Runs semantic on ae.arguments. Declares temporary variables * if '$' was used. @@ -712,14 +774,11 @@ Expression resolveOpDollar(Scope* sc, ArrayExp ae, out Expression pe0) if (auto ie = e.isIntervalExp()) { - auto tiargs = new Objects(); Expression edim = new IntegerExp(ae.loc, i, Type.tsize_t); edim = edim.expressionSemantic(sc); - tiargs.push(edim); + auto tiargs = new Objects(edim); - auto fargs = new Expressions(2); - (*fargs)[0] = ie.lwr; - (*fargs)[1] = ie.upr; + auto fargs = new Expressions(ie.lwr, ie.upr); const xerrors = global.startGagging(); sc = sc.push(); @@ -1246,7 +1305,8 @@ private Expression resolveUFCS(Scope* sc, CallExp ce) semanticTypeInfo(sc, taa.index); - return new RemoveExp(loc, eleft, key); + e = new RemoveExp(loc, eleft, key); + return e.expressionSemantic(sc); } } else @@ -1817,7 +1877,7 @@ L1: if (e1.op == EXP.this_) { FuncDeclaration f = hasThis(sc); - if (f && f.hasDualContext()) + if (f && f.hasDualContext) { if (f.followInstantiationContext(ad)) { @@ -2000,7 +2060,7 @@ void checkOverriddenDtor(FuncDeclaration f, Scope* sc, Loc loc, scope bool function(DtorDeclaration) check, const string checkName) { auto dd = f.isDtorDeclaration(); - if (!dd || !dd.isGenerated()) + if (!dd || !dd.isGenerated) return; // DtorDeclaration without parents should fail at an earlier stage @@ -2017,7 +2077,7 @@ void checkOverriddenDtor(FuncDeclaration f, Scope* sc, Loc loc, } dd.loc.errorSupplemental("%s`%s.~this` is %.*s because of the following field's destructors:", - dd.isGenerated() ? "generated " : "".ptr, + dd.isGenerated ? "generated " : "".ptr, ad.toChars, cast(int) checkName.length, checkName.ptr); @@ -2048,7 +2108,7 @@ void checkOverriddenDtor(FuncDeclaration f, Scope* sc, Loc loc, { field.loc.errorSupplemental(" - %s %s", field.type.toChars(), field.toChars()); - if (fieldSd.dtor.isGenerated()) + if (fieldSd.dtor.isGenerated) fieldSd.dtor.checkOverriddenDtor(sc, loc, check, checkName); else fieldSd.dtor.loc.errorSupplemental(" %.*s `%s.~this` is declared here", @@ -2351,7 +2411,9 @@ private bool checkNogc(FuncDeclaration f, ref Loc loc, Scope* sc) // so don't print anything to avoid double error messages. if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX - || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT)) + || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT + || f.ident == Id._d_assocarrayliteralTX || f.ident == Id._d_arrayliteralTX + || f.ident == Id._d_aaGetY)) { error(loc, "`@nogc` %s `%s` cannot call non-@nogc %s `%s`", sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars()); @@ -2373,7 +2435,7 @@ private bool checkNogc(FuncDeclaration f, ref Loc loc, Scope* sc) * t = struct type, or static array of struct type to check * loc = error message location * sc = scope in which attributes are checked - * Returns: true if there's an error + * Returns: true if there is an error */ private bool checkPostblit(Type t, ref Loc loc, Scope* sc) { @@ -2441,8 +2503,7 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = return ErrorExp.get(); e2 = resolveProperties(sc, e2); - Expressions* a = new Expressions(); - a.push(e2); + Expressions* a = new Expressions(e2); for (size_t i = 0; i < os.a.length; i++) { @@ -2561,8 +2622,7 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = return ErrorExp.get(); e2 = resolveProperties(sc, e2); - Expressions* a = new Expressions(); - a.push(e2); + Expressions* a = new Expressions(e2); FuncDeclaration fd = resolveFuncCall(loc, sc, s, tiargs, tthis, ArgumentList(a), FuncResolveFlag.quiet); if (fd && fd.type) @@ -3032,7 +3092,9 @@ private bool functionParameters(Loc loc, Scope* sc, */ if (fd && fd.inlining == PINLINE.always) { - if (sc._module) + if (sc.minst) + sc.minst.hasAlwaysInlines = true; + else if (sc._module) sc._module.hasAlwaysInlines = true; if (sc.func) sc.func.hasAlwaysInlines = true; @@ -3754,8 +3816,8 @@ private bool functionParameters(Loc loc, Scope* sc, int offset; if (!tret.implicitConvTo(tthis) && !(MODimplicitConv(tret.mod, tthis.mod) && tret.isBaseOf(tthis, &offset) && offset == 0)) { - const(char)* s1 = tret.isNaked() ? " mutable" : tret.modToChars(); - const(char)* s2 = tthis.isNaked() ? " mutable" : tthis.modToChars(); + const(char)* s1 = tret.isNaked ? " mutable" : tret.modToChars(); + const(char)* s2 = tthis.isNaked ? " mutable" : tthis.modToChars(); .error(loc, "`inout` constructor `%s` creates%s object, not%s", fd.toPrettyChars(), s1, s2); err = true; } @@ -3807,6 +3869,326 @@ Package resolveIsPackage(Dsymbol sym) return pkg; } +/** + * Performs the lowering of a CastExp to a call to `.object._d_cast`, + * populating the `lowering` field of the CastExp. + * This is only done for casts between classes/interfaces. + * + * Params: + * cex = the CastExp to lower + * sc = the current scope + */ +private void lowerCastExp(CastExp cex, Scope* sc) +{ + Type t1b = cex.e1.type.toBasetype(); + Type tob = cex.to.toBasetype(); + + if (t1b.ty != Tclass || tob.ty != Tclass) + return; + + ClassDeclaration cdfrom = t1b.isClassHandle(); + ClassDeclaration cdto = tob.isClassHandle(); + + int offset; + if ((cdto.isBaseOf(cdfrom, &offset) && offset != ClassDeclaration.OFFSET_RUNTIME) + || cdfrom.classKind == ClassKind.cpp || cdto.classKind == ClassKind.cpp) + return; + + Identifier hook = Id._d_cast; + if (!verifyHookExist(cex.loc, *sc, hook, "d_cast", Id.object)) + return; + + // Lower to .object._d_cast!(To)(exp.e1) + Expression lowering = new IdentifierExp(cex.loc, Id.empty); + lowering = new DotIdExp(cex.loc, lowering, Id.object); + + // Unqualify the type being casted to, avoiding multiple instantiations + auto unqual_tob = tob.unqualify(MODFlags.wild | MODFlags.const_ | + MODFlags.immutable_ | MODFlags.shared_); + auto tiargs = new Objects(unqual_tob); + lowering = new DotTemplateInstanceExp(cex.loc, lowering, hook, tiargs); + + // Unqualify the type being casted from to avoid multiple instantiations + auto unqual_t1b = t1b.unqualify(MODFlags.wild | MODFlags.const_ | + MODFlags.immutable_ | MODFlags.shared_); + Expression e1c = cex.e1.copy(); + e1c.type = unqual_t1b; + auto arguments = new Expressions(e1c); + + lowering = new CallExp(cex.loc, lowering, arguments); + + cex.lowering = lowering.expressionSemantic(sc); +} + +/** + * Visitor to check if a type is suitable for comparison using `memcmp`. + * Currently used when lowering `EqualExp`. + */ +private extern(C++) final class IsMemcmpableVisitor : Visitor +{ + alias visit = Visitor.visit; + public: + bool result = false; + + override void visit(Type t) + { + result = t.ty == Tvoid || (t.isScalar() && !t.isFloating()); + } + + override void visit(TypeStruct ts) + { + result = false; + + if (ts.sym.hasIdentityEquals) + return; // has custom opEquals + + if (!ts.sym.members) + { + result = true; + return; + } + + /* We recursively check all variable declaration within the struct. + * The recursiveness is needed to handle cases like this: + * struct Test { + * nothrow: + * int[] contents; + * } + * Here a `StorageClassDeclaration` symbol will be created, which wraps the variable declaration. + */ + static bool visitAllMembers(Dsymbols* members, TypeStruct root, IsMemcmpableVisitor v) + { + if (members is null) + return true; + + foreach (m; *members) + { + if (auto vd = m.isVarDeclaration()) + { + if (vd.type is null) + continue; + + auto tbvd = vd.type.toBasetype(); + if (tbvd !is root) + tbvd.accept(v); + + if (!v.result) + return false; + } + else if (auto ad = m.isAttribDeclaration()) + { + if(!visitAllMembers(ad.decl, root, v)) + return false; + } + } + return true; + } + result = visitAllMembers(ts.sym.members, ts, this); + } + + override void visit(TypeSArray tsa) + { + tsa.nextOf().toBasetype().accept(this); + } +} + +/********************************************* + * In the current function 'sc.func', we are calling 'fd'. + * 1. Check to see if the current function can call 'fd' , issue error if not. + * 2. If the current function is not the parent of 'fd' , then add + * the current function to the list of siblings of 'fd' . + * 3. If the current function is a literal, and it's accessing an uplevel scope, + * then mark it as a delegate. + * Returns true if error occurs. + */ +private bool checkNestedFuncReference(FuncDeclaration fd, Scope* sc, Loc loc) +{ + //printf("FuncDeclaration::checkNestedFuncReference() %s\n", toPrettyChars()); + if (auto fld = fd.isFuncLiteralDeclaration()) + { + if (fld.tok == TOK.reserved) + { + fld.tok = TOK.function_; + fld.vthis = null; + } + } + if (!fd.parent || fd.parent == sc.parent) + return false; + if (fd.ident == Id.require || fd.ident == Id.ensure) + return false; + if (!fd.isThis() && !fd.isNested()) + return false; + // The current function + FuncDeclaration fdthis = sc.parent.isFuncDeclaration(); + if (!fdthis) + return false; // out of function scope + Dsymbol p = fd.toParentLocal(); + Dsymbol p2 = fd.toParent2(); + // Function literals from fdthis to p must be delegates + ensureStaticLinkTo(fdthis, p); + if (p != p2) + ensureStaticLinkTo(fdthis, p2); + if (!fd.isNested()) + return false; + + // The function that this function is in + bool checkEnclosing(FuncDeclaration fdv) + { + if (!fdv) + return false; + if (fdv == fdthis) + return false; + //printf("this = %s in [%s]\n", this.toChars(), this.loc.toChars()); + //printf("fdv = %s in [%s]\n", fdv .toChars(), fdv .loc.toChars()); + //printf("fdthis = %s in [%s]\n", fdthis.toChars(), fdthis.loc.toChars()); + // Add this function to the list of those which called us + if (fdthis != fd) + { + bool found = false; + for (size_t i = 0; i < fd.siblingCallers.length; ++i) + { + if (fd.siblingCallers[i] == fdthis) + found = true; + } + if (!found) + { + //printf("\tadding sibling %s to %s\n", fdthis.toPrettyChars(), toPrettyChars()); + if (!sc.intypeof && !sc.traitsCompiles) + { + fd.siblingCallers.push(fdthis); + fd.computedEscapingSiblings = false; + } + } + } + const lv = fdthis.getLevelAndCheck(loc, sc, fdv, fd); + if (lv == fd.LevelError) + return true; // error + if (lv == -1) + return false; // downlevel call + if (lv == 0) + return false; // same level call + return false; // Uplevel call + } + if (checkEnclosing(p.isFuncDeclaration())) + return true; + if (checkEnclosing(p == p2 ? null : p2.isFuncDeclaration())) + return true; + return false; +} + +Expression lowerArrayLiteral(ArrayLiteralExp ale, Scope* sc) +{ + const dim = ale.elements ? ale.elements.length : 0; + + Identifier hook = global.params.tracegc ? Id._d_arrayliteralTXTrace : Id._d_arrayliteralTX; + if (!verifyHookExist(ale.loc, *sc, hook, "creating array literals")) + return null; + + Expression lowering = new IdentifierExp(ale.loc, Id.empty); + lowering = new DotIdExp(ale.loc, lowering, Id.object); + // Remove `inout`, `const`, `immutable` and `shared` to reduce template instances + auto t = ale.type.nextOf().unqualify(MODFlags.wild | MODFlags.const_ | MODFlags.immutable_ | MODFlags.shared_); + auto tiargs = new Objects(t); + lowering = new DotTemplateInstanceExp(ale.loc, lowering, hook, tiargs); + + auto arguments = new Expressions(new IntegerExp(dim)); + lowering = new CallExp(ale.loc, lowering, arguments); + ale.lowering = lowering.expressionSemantic(sc); + + return ale.lowering; +} + +/*********************************** + * If oarg represents a Dsymbol, return that Dsymbol + * Params: + * oarg = argument to check + * Returns: + * Dsymbol if a symbol, null if not + */ +Dsymbol getDsymbol(RootObject oarg) +{ + //printf("getDsymbol()\n"); + //printf("e %p s %p t %p v %p\n", isExpression(oarg), isDsymbol(oarg), isType(oarg), isTuple(oarg)); + if (auto ea = isExpression(oarg)) + { + // Try to convert Expression to symbol + if (auto ve = ea.isVarExp()) + return ve.var; + if (auto fe = ea.isFuncExp()) + return fe.td ? fe.td : fe.fd; + if (auto te = ea.isTemplateExp()) + return te.td; + if (auto te = ea.isScopeExp()) + return te.sds; + return null; + } + // Try to convert Type to symbol + if (auto ta = isType(oarg)) + return ta.toDsymbol(null); + return isDsymbol(oarg); // if already a symbol +} + +/************************************** + * IsExpression can evaluate the specified type speculatively, and even if + * it instantiates any symbols, they are normally unnecessary for the + * final executable. + * However, if those symbols leak to the actual code, compiler should remark + * them as non-speculative to generate their code and link to the final executable. + */ +private void unSpeculative(Scope* sc, RootObject o) +{ + if (!o) + return; + + if (Tuple tup = isTuple(o)) + { + foreach (obj; tup.objects) + { + unSpeculative(sc, obj); + } + return; + } + + Dsymbol s = getDsymbol(o); + if (!s) + return; + + if (Declaration d = s.isDeclaration()) + { + if (VarDeclaration vd = d.isVarDeclaration()) + o = vd.type; + else if (AliasDeclaration ad = d.isAliasDeclaration()) + { + o = ad.getType(); + if (!o) + o = ad.toAlias(); + } + else + o = d.toAlias(); + + s = getDsymbol(o); + if (!s) + return; + } + + if (TemplateInstance ti = s.isTemplateInstance()) + { + // If the instance is already non-speculative, + // or it is leaked to the speculative scope. + if (ti.minst !is null || sc.minst is null) + return; + + // Remark as non-speculative instance. + ti.minst = sc.minst; + if (!ti.tinst) + ti.tinst = sc.tinst; + + unSpeculative(sc, ti.tempdecl); + } + + if (TemplateInstance ti = s.isInstantiated()) + unSpeculative(sc, ti); +} private extern (C++) final class ExpressionSemanticVisitor : Visitor { @@ -3921,6 +4303,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("IdentifierExp::semantic('%s')\n", exp.ident.toChars()); } + if (exp.rvalue) + { + if (sc.setUnsafe(false, exp.loc, "moving variable `%s` with `__rvalue`", exp)) + { + setError(); + } + } scope (exit) result.rvalue = exp.rvalue; Dsymbol scopesym; @@ -4318,8 +4707,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Expression makeNonTemplateItem(Identifier which) { Expression id = new IdentifierExp(e.loc, Id.empty); id = new DotIdExp(e.loc, id, Id.object); - auto moduleNameArgs = new Objects(); - moduleNameArgs.push(new StringExp(e.loc, "core.interpolation")); + auto moduleNameArgs = new Objects(new StringExp(e.loc, "core.interpolation")); id = new DotTemplateInstanceExp(e.loc, id, Id.imported, moduleNameArgs); id = new DotIdExp(e.loc, id, which); id = new CallExp(e.loc, id, new Expressions()); @@ -4329,21 +4717,19 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Expression makeTemplateItem(Identifier which, string arg) { Expression id = new IdentifierExp(e.loc, Id.empty); id = new DotIdExp(e.loc, id, Id.object); - auto moduleNameArgs = new Objects(); - moduleNameArgs.push(new StringExp(e.loc, "core.interpolation")); + auto moduleNameArgs = new Objects(new StringExp(e.loc, "core.interpolation")); id = new DotTemplateInstanceExp(e.loc, id, Id.imported, moduleNameArgs); - auto tiargs = new Objects(); + auto templateStringArg = new StringExp(e.loc, arg); // banning those instead of forwarding them // templateStringArg.postfix = e.postfix; // forward the postfix to these literals - tiargs.push(templateStringArg); + auto tiargs = new Objects(templateStringArg); id = new DotTemplateInstanceExp(e.loc, id, which, tiargs); id = new CallExp(e.loc, id, new Expressions()); return id; } - auto arguments = new Expressions(); - arguments.push(makeNonTemplateItem(Id.InterpolationHeader)); + auto arguments = new Expressions(makeNonTemplateItem(Id.InterpolationHeader)); foreach (idx, str; e.interpolatedSet.parts) { @@ -4355,8 +4741,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor else { arguments.push(makeTemplateItem(Id.InterpolatedExpression, str)); - Expressions* mix = new Expressions(); - mix.push(new StringExp(e.loc, str)); + Expressions* mix = new Expressions(new StringExp(e.loc, str)); // FIXME: i'd rather not use MixinExp but idk how to do it lol arguments.push(new MixinExp(e.loc, mix)); } @@ -4557,6 +4942,34 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = e; } + void tryLowerAALiteral(AssocArrayLiteralExp aaExp) + { + auto hookId = Id._d_assocarrayliteralTX; + if (!verifyHookExist(aaExp.loc, *sc, hookId, "initializing associative arrays", Id.object)) + return; + + auto aaType = aaExp.type.toBasetype().isTypeAArray(); + assert(aaType); + Expression hookFunc = new IdentifierExp(aaExp.loc, Id.empty); + hookFunc = new DotIdExp(aaExp.loc, hookFunc, Id.object); + auto keytype = aaType.index.substWildTo(MODFlags.const_); + auto valtype = aaType.nextOf().substWildTo(MODFlags.const_); + auto tiargs = new Objects(keytype, valtype); + hookFunc = new DotTemplateInstanceExp(aaExp.loc, hookFunc, hookId, tiargs); + auto ale1 = new ArrayLiteralExp(aaExp.loc, keytype.arrayOf(), aaExp.keys); + auto ale2 = new ArrayLiteralExp(aaExp.loc, valtype.arrayOf(), aaExp.values); + lowerArrayLiteral(ale1, sc); + lowerArrayLiteral(ale2, sc); + auto arguments = new Expressions(ale1, ale2); + + Expression loweredExp = new CallExp(aaExp.loc, hookFunc, arguments); + loweredExp = loweredExp.expressionSemantic(sc); + loweredExp = resolveProperties(sc, loweredExp); + aaExp.lowering = loweredExp; + + semanticTypeInfo(sc, loweredExp.type); + } + override void visit(AssocArrayLiteralExp e) { static if (LOGSEMANTIC) @@ -4567,6 +4980,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { // already done, but we might have missed generating type info semanticTypeInfo(sc, e.type); + if (!e.lowering) + tryLowerAALiteral(e); result = e; return; } @@ -4593,6 +5008,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor e.type = new TypeAArray(tvalue, tkey); e.type = e.type.typeSemantic(e.loc, sc); + tryLowerAALiteral(e); semanticTypeInfo(sc, e.type); if (checkAssocArrayLiteralEscape(*sc, e, false)) @@ -4866,14 +5282,45 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ Expression id = new IdentifierExp(ne.loc, Id.empty); id = new DotIdExp(ne.loc, id, Id.object); - auto tiargs = new Objects(); /* * Remove `inout`, `const`, `immutable` and `shared` to reduce the * number of generated `_d_newitemT` instances. */ auto t = ne.type.nextOf.unqualify(MODFlags.wild | MODFlags.const_ | MODFlags.immutable_ | MODFlags.shared_); - tiargs.push(t); + auto tiargs = new Objects(t); + id = new DotTemplateInstanceExp(ne.loc, id, hook, tiargs); + + auto arguments = new Expressions(); + id = new CallExp(ne.loc, id, arguments); + + ne.lowering = id.expressionSemantic(sc); + } + + /** + * Sets the `lowering` field of a `NewExp` to a call to `_d_newAA` unless + * compiling with `-betterC` or within `__traits(compiles)`. + * + * Params: + * ne = the `NewExp` to lower + */ + private void tryLowerToNewAA(NewExp ne) + { + if (!global.params.useGC || !sc.needsCodegen()) + return; + + Identifier hook = Id._d_aaNew; + if (!verifyHookExist(ne.loc, *sc, hook, "new AA")) + return; + + /* Lower the memory allocation and initialization of `new V[K]` to + * `_d_newAA!(V[K])()`. + */ + Expression id = new IdentifierExp(ne.loc, Id.empty); + id = new DotIdExp(ne.loc, id, Id.object); + auto taa = ne.type.isTypeAArray(); + assert(taa); + auto tiargs = new Objects(taa.index, taa.next); id = new DotTemplateInstanceExp(ne.loc, id, hook, tiargs); auto arguments = new Expressions(); @@ -4907,7 +5354,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { return setError(); } - if (!exp.placement.type.isNaked()) + if (!exp.placement.type.isNaked) { error(p.loc, "PlacementExpression `%s` of type `%s` be unshared and mutable", p.toChars(), toChars(p.type)); return setError(); @@ -4977,8 +5424,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor else { // --> new T[](edim) - exp.arguments = new Expressions(); - exp.arguments.push(edim); + exp.arguments = new Expressions(edim); exp.type = exp.type.arrayOf(); } } @@ -5226,8 +5672,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Expression id = new IdentifierExp(exp.loc, Id.empty); id = new DotIdExp(exp.loc, id, Id.object); - auto tiargs = new Objects(); - tiargs.push(exp.newtype); + auto tiargs = new Objects(exp.newtype); id = new DotTemplateInstanceExp(exp.loc, id, Id._d_newThrowable, tiargs); @@ -5252,9 +5697,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Expression id = new IdentifierExp(exp.loc, Id.empty); id = new DotIdExp(exp.loc, id, Id.object); - auto tiargs = new Objects(); auto t = exp.newtype.unqualify(MODFlags.wild); // remove `inout` - tiargs.push(t); + auto tiargs = new Objects(t); id = new DotTemplateInstanceExp(exp.loc, id, hook, tiargs); auto arguments = new Expressions(); id = new CallExp(exp.loc, id, arguments); @@ -5288,7 +5732,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // If the new expression has arguments, we either should call a // regular constructor of a copy constructor if the first argument // is the same type as the struct - if (nargs && (sd.hasRegularCtor() || (sd.ctor && (*exp.arguments)[0].type.mutableOf() == sd.type.mutableOf()))) + if (nargs && (sd.hasRegularCtor(false) || (sd.ctor && (*exp.arguments)[0].type.mutableOf() == sd.type.mutableOf()))) { FuncDeclaration f = resolveFuncCall(exp.loc, sc, sd.ctor, null, tb, exp.argumentList, FuncResolveFlag.standard); if (!f || f.errors) @@ -5440,19 +5884,17 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ Expression lowering = new IdentifierExp(exp.loc, Id.empty); lowering = new DotIdExp(exp.loc, lowering, Id.object); - auto tiargs = new Objects(); + /* Remove `inout`, `const`, `immutable` and `shared` to reduce * the number of generated `_d_newarrayT` instances. */ const isShared = exp.type.nextOf.isShared(); auto t = exp.type.nextOf.unqualify(MODFlags.wild | MODFlags.const_ | MODFlags.immutable_ | MODFlags.shared_); - tiargs.push(t); + auto tiargs = new Objects(t); lowering = new DotTemplateInstanceExp(exp.loc, lowering, hook, tiargs); - auto arguments = new Expressions(); - arguments.push((*exp.arguments)[0]); - arguments.push(new IntegerExp(exp.loc, isShared, Type.tbool)); + auto arguments = new Expressions((*exp.arguments)[0], new IntegerExp(exp.loc, isShared, Type.tbool)); lowering = new CallExp(exp.loc, lowering, arguments); exp.lowering = lowering.expressionSemantic(sc); @@ -5476,15 +5918,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor auto unqualTbn = tbn.unqualify(MODFlags.wild | MODFlags.const_ | MODFlags.immutable_ | MODFlags.shared_); - auto tiargs = new Objects(); - tiargs.push(exp.type); - tiargs.push(unqualTbn); + auto tiargs = new Objects(exp.type, unqualTbn); lowering = new DotTemplateInstanceExp(exp.loc, lowering, hook, tiargs); - auto arguments = new Expressions(); - - arguments.push(new ArrayLiteralExp(exp.loc, Type.tsize_t.sarrayOf(nargs), exp.arguments)); - arguments.push(new IntegerExp(exp.loc, tbn.isShared(), Type.tbool)); + auto arguments = new Expressions(new ArrayLiteralExp(exp.loc, Type.tsize_t.sarrayOf(nargs), exp.arguments), + new IntegerExp(exp.loc, tbn.isShared(), Type.tbool)); lowering = new CallExp(exp.loc, lowering, arguments); exp.lowering = lowering.expressionSemantic(sc); @@ -5529,6 +5967,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor error(exp.loc, "`new` cannot take arguments for an associative array"); return setError(); } + tryLowerToNewAA(exp); } else { @@ -5702,7 +6141,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor symtab = sds.symtab; } assert(symtab); - Identifier id = Identifier.generateIdWithLoc(s, exp.loc, cast(string) toDString(sc.parent.toPrettyChars())); + Identifier id = Identifier.generateIdWithLoc(s, exp.loc, cast(const void*) sc.parent); exp.fd.ident = id; if (exp.td) exp.td.ident = id; @@ -5916,11 +6355,28 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { if (TypeFunction tf = exp.f ? cast(TypeFunction)exp.f.type : null) { - result.rvalue = tf.isRvalue; - if (tf.isRvalue && !tf.isRef) + if (tf.isRvalue) { - error(exp.f.loc, "`__rvalue` only valid on functions that return by `ref`"); - setError(); + if(!tf.isRef) + { + error(exp.f.loc, "`__rvalue` only valid on functions that return by `ref`"); + setError(); + } + else if (sc.setUnsafe(false, exp.loc, "calling `__rvalue`-annotated function `%s`", exp.f)) + { + setError(); + } + else + { + result.rvalue = true; + } + } + else if (exp.rvalue && tf.isRef) + { + if (sc.setUnsafe(false, exp.loc, "moving result of `ref` function `%s` with `__rvalue`", exp.f)) + { + setError(); + } } } } @@ -6213,7 +6669,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (sd.ctor) { auto ctor = sd.ctor.isCtorDeclaration(); - if (ctor && (ctor.isCpCtor || ctor.isMoveCtor) && ctor.isGenerated()) + if (ctor && (ctor.isCpCtor || ctor.isMoveCtor) && ctor.isGenerated) sd.ctor = null; } @@ -6227,7 +6683,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor If all constructors are copy constructors, then try default construction. */ - if (!sd.hasRegularCtor && + if (!sd.hasRegularCtor(false) && // https://issues.dlang.org/show_bug.cgi?id=22639 // we might still have a copy constructor that could be called (*exp.arguments)[0].type.mutableOf != sd.type.mutableOf()) @@ -6898,8 +7354,21 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!exp.type) { exp.e1 = e1org; // https://issues.dlang.org/show_bug.cgi?id=10922 - // avoid recursive expression printing - error(exp.loc, "forward reference to inferred return type of function call `%s`", exp.toErrMsg()); + // avoid recursive expression printing + FuncDeclaration fd = null; + if (auto dve = exp.e1.isDotVarExp()) + fd = cast(FuncDeclaration)dve.var; + else if (auto ve = exp.e1.isVarExp()) + fd = cast(FuncDeclaration)ve.var; + + if (fd && fd.inferRetType && sc && sc.func == fd) + { + error(exp.loc, "can't infer return type in function `%s`", fd.toChars()); + } + else + { + error(exp.loc, "forward reference to inferred return type of function call `%s`", exp.toErrMsg()); + } return setError(); } @@ -6911,7 +7380,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (tf.next.isBaseOf(t, &offset) && offset) { exp.type = tf.next; - result = Expression.combine(argprefix, exp.castTo(sc, t)); + auto casted_exp = exp.castTo(sc, t); + if (auto cex = casted_exp.isCastExp()) + { + lowerCastExp(cex, sc); + } + result = Expression.combine(argprefix, casted_exp); return; } } @@ -6960,7 +7434,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } // declare dual-context container - if (exp.f && exp.f.hasDualContext() && !sc.intypeof && sc.func) + if (exp.f && exp.f.hasDualContext && !sc.intypeof && sc.func) { // check access to second `this` if (AggregateDeclaration ad2 = exp.f.isMember2()) @@ -7700,7 +8174,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } assert(e.op == EXP.assign || e == exp); - result = (cast(BinExp)e).reorderSettingAAElem(sc); + result = e; } private Expression compileIt(MixinExp exp, Scope* sc) @@ -7917,7 +8391,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // Defensively assume that function calls may have side effects even // though it's not detected by hasSideEffect (e.g. `debug puts("Hello")` ) // Rewriting CallExp's also avoids some issues with the inliner/debug generation - if (op.hasSideEffect(true)) + if (op.hasSideEffect(true) || op.isAssocArrayLiteralExp()) { // Don't create an invalid temporary for void-expressions // Further semantic will issue an appropriate error @@ -8003,7 +8477,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor isEqualsCallExpression) { es = new Expressions(3); - tiargs = new Objects(1); if (isEqualsCallExpression) { @@ -8042,7 +8515,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Expression comp = new StringExp(loc, isEqualsCallExpression ? "==" : EXPtoString(exp.e1.op)); comp = comp.expressionSemantic(sc); (*es)[0] = comp; - (*tiargs)[0] = (*es)[1].type; + tiargs = new Objects((*es)[1].type); } // Format exp.e1 before any additional boolean conversion @@ -8050,7 +8523,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor else if (op != EXP.andAnd && op != EXP.orOr) { es = new Expressions(2); - tiargs = new Objects(1); if (auto ne = exp.e1.isNotExp()) { @@ -8074,7 +8546,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor (*es)[1] = maybePromoteToTmp(exp.e1); } - (*tiargs)[0] = (*es)[1].type; + tiargs = new Objects((*es)[1].type); // Passing __ctfe to auto ref infers ref and aborts compilation: // "cannot modify compiler-generated variable __ctfe" @@ -8185,6 +8657,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("DotIdExp::semantic(this = %p, '%s')\n", exp, exp.toChars()); printAST(exp); } + scope (exit) + { + if (result) + result.rvalue = exp.rvalue; + else + setError(); + } if (sc.inCfile) { @@ -8467,7 +8946,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } result = e; // declare dual-context container - if (f.hasDualContext() && !sc.intypeof && sc.func) + if (f.hasDualContext && !sc.intypeof && sc.func) { // check access to second `this` if (AggregateDeclaration ad2 = f.isMember2()) @@ -8641,7 +9120,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor error(exp.loc, "cannot take address of `%s`", exp.e1.toErrMsg()); return setError(); } - if (!checkAddressable(exp, sc)) + if (!checkAddressable(exp, sc, "take address of")) return setError(); bool hasOverloads; @@ -9299,13 +9778,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Expression id = new IdentifierExp(exp.loc, Id.empty); auto dotid = new DotIdExp(exp.loc, id, Id.object); - auto tiargs = new Objects(); - tiargs.push(tFrom); - tiargs.push(tTo); + auto tiargs = new Objects(tFrom, tTo); auto dt = new DotTemplateInstanceExp(exp.loc, dotid, Id.__ArrayCast, tiargs); - auto arguments = new Expressions(); - arguments.push(exp.e1); + auto arguments = new Expressions(exp.e1); Expression ce = new CallExp(exp.loc, dt, arguments); result = expressionSemantic(ce, sc); @@ -9328,6 +9804,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor ex.type = exp.to; } } + + if (auto cex = ex.isCastExp()) + { + lowerCastExp(cex, sc); + } + result = ex; } @@ -9966,7 +10448,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } if (t1b.isStaticOrDynamicArray()) { - if (!checkAddressable(exp, sc)) + if (!checkAddressable(exp, sc, "index through")) return setError(); } @@ -10053,9 +10535,16 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } semanticTypeInfo(sc, taa); - checkNewEscape(*sc, exp.e2, false); + bool escape = checkNewEscape(*sc, exp.e2, false); + if (escape) + return setError(); exp.type = taa.next; + if (!exp.modifiable) + { + result = lowerAAIndexRead(exp, sc); + return; + } break; } case Ttuple: @@ -10161,6 +10650,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } + if (auto ae = exp.e1.isArrayExp()) + markArrayExpModifiable(ae); + if (Expression ex = binSemantic(exp, sc)) { result = ex; @@ -10174,6 +10666,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } exp.e1 = e1x; + Type[2] aliasThisStop; + if (auto res = rewriteIndexAssign(exp, sc, aliasThisStop)) + { + result = res; + return; + } if (exp.e1.checkReadModifyWrite(exp.op)) return setError(); @@ -10243,6 +10741,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(PreExp exp) { + if (auto ae = exp.e1.isArrayExp()) + markArrayExpModifiable(ae); + // printf("PreExp::semantic('%s')\n", toChars()); if (Expression e = exp.opOverloadUnary(sc)) { @@ -10333,6 +10834,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (auto ae = exp.e1.isArrayExp()) { Expression res; + markArrayExpModifiable(ae); ae.e1 = ae.e1.expressionSemantic(sc); ae.e1 = resolveProperties(sc, ae.e1); @@ -10408,8 +10910,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor /* Rewrite (a[i..j] = e2) as: * a.opSliceAssign(e2, i, j) */ - auto a = new Expressions(); - a.push(exp.e2); + auto a = new Expressions(exp.e2); if (ie) { a.push(ie.lwr); @@ -10600,8 +11101,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Expression e0; Expression ev = extractSideEffect(sc, "__tup", e0, e2x); - auto iexps = new Expressions(); - iexps.push(ev); + auto iexps = new Expressions(ev); for (size_t u = 0; u < iexps.length; u++) { Lexpand: @@ -10675,18 +11175,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor auto t = exp.type; exp = new ConstructExp(exp.loc, exp.e1, exp.e2); exp.type = t; - - // https://issues.dlang.org/show_bug.cgi?id=13515 - // set Index::modifiable flag for complex AA element initialization - if (auto ie1 = exp.e1.isIndexExp()) - { - Expression e1x = ie1.markSettingAAElem(); - if (e1x.op == EXP.error) - { - result = e1x; - return; - } - } } else if (exp.op == EXP.construct && exp.e1.op == EXP.variable && (cast(VarExp)exp.e1).var.storage_class & (STC.out_ | STC.ref_)) @@ -10702,6 +11190,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor checkUnsafeAccess(sc, exp.e2, true, true); // Initializer must always be checked + if (auto res = rewriteIndexAssign(exp, sc, aliasThisStop)) + { + result = res; + return; + } + /* If it is an assignment from a 'foreign' type, * check for operator overloading. */ @@ -10991,91 +11485,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } else if (exp.op == EXP.assign) { - if (e1x.op == EXP.index && (cast(IndexExp)e1x).e1.type.toBasetype().ty == Taarray) - { - /* - * Rewrite: - * aa[key] = e2; - * as: - * ref __aatmp = aa; - * ref __aakey = key; - * ref __aaval = e2; - * (__aakey in __aatmp - * ? __aatmp[__aakey].opAssign(__aaval) - * : ConstructExp(__aatmp[__aakey], __aaval)); - */ - // ensure we keep the expr modifiable - Expression esetting = (cast(IndexExp)e1x).markSettingAAElem(); - if (esetting.op == EXP.error) - { - result = esetting; - return; - } - assert(esetting.op == EXP.index); - IndexExp ie = cast(IndexExp) esetting; - Type t2 = e2x.type.toBasetype(); - - Expression e0 = null; - Expression ea = extractSideEffect(sc, "__aatmp", e0, ie.e1); - Expression ek = extractSideEffect(sc, "__aakey", e0, ie.e2); - Expression ev = extractSideEffect(sc, "__aaval", e0, e2x); - - AssignExp ae = cast(AssignExp)exp.copy(); - ae.e1 = new IndexExp(exp.loc, ea, ek); - ae.e1 = ae.e1.expressionSemantic(sc); - ae.e1 = ae.e1.optimize(WANTvalue); - ae.e2 = ev; - if (Expression e = ae.opOverloadAssign(sc, aliasThisStop)) - { - Expression ey = null; - if (t2.ty == Tstruct && sd == t2.toDsymbol(sc)) - { - ey = ev; - } - else if (!ev.implicitConvTo(ie.type) && sd.ctor) - { - // Look for implicit constructor call - // Rewrite as S().ctor(e2) - ey = new StructLiteralExp(exp.loc, sd, null); - ey = new DotIdExp(exp.loc, ey, Id.ctor); - ey = new CallExp(exp.loc, ey, ev); - ey = ey.trySemantic(sc); - } - if (ey) - { - Expression ex; - ex = new IndexExp(exp.loc, ea, ek); - ex = ex.expressionSemantic(sc); - ex = ex.modifiableLvalue(sc); // allocate new slot - ex = ex.optimize(WANTvalue); - - ey = new ConstructExp(exp.loc, ex, ey); - ey = ey.expressionSemantic(sc); - if (ey.op == EXP.error) - { - result = ey; - return; - } - ex = e; - - // https://issues.dlang.org/show_bug.cgi?id=14144 - // The whole expression should have the common type - // of opAssign() return and assigned AA entry. - // Even if there's no common type, expression should be typed as void. - if (!typeMerge(sc, EXP.question, ex, ey)) - { - ex = new CastExp(ex.loc, ex, Type.tvoid); - ey = new CastExp(ey.loc, ey, Type.tvoid); - } - e = new CondExp(exp.loc, new InExp(exp.loc, ek, ea), ex, ey); - } - e = Expression.combine(e0, e); - e = e.expressionSemantic(sc); - result = e; - return; - } - } - else if (Expression e = exp.isAssignExp().opOverloadAssign(sc, aliasThisStop)) + if (Expression e = exp.isAssignExp().opOverloadAssign(sc, aliasThisStop)) { result = e; return; @@ -11239,7 +11649,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { // Ensure e1 is a modifiable lvalue - auto ale1x = ale.e1.modifiableLvalueImpl(sc, exp.e1); + auto ale1x = ale.e1.modifiableLvalue(sc, exp.e1); if (ale1x.op == EXP.error) return setResult(ale1x); ale.e1 = ale1x; @@ -11283,9 +11693,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor id = id.expressionSemantic(sc); // Generate call: _d_arraysetlengthT(e1, e2) - auto arguments = new Expressions(); - arguments.push(ale.e1); // array - arguments.push(exp.e2); // new length + auto arguments = new Expressions(ale.e1, // array + exp.e2); // new length Expression ce = new CallExp(ale.loc, id, arguments).expressionSemantic(sc); auto res = new LoweredAssignExp(exp, ce); @@ -11319,7 +11728,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor se = cast(SliceExp)se.e1; if (se.e1.op == EXP.question && se.e1.type.toBasetype().ty == Tsarray) { - se.e1 = se.e1.modifiableLvalueImpl(sc, exp.e1); + se.e1 = se.e1.modifiableLvalue(sc, exp.e1); if (se.e1.op == EXP.error) return setResult(se.e1); } @@ -11343,7 +11752,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // Try to do a decent error message with the expression // before it gets constant folded if (exp.op == EXP.assign) - e1x = e1x.modifiableLvalueImpl(sc, e1old); + e1x = e1x.modifiableLvalue(sc, e1old); e1x = e1x.optimize(WANTvalue, /*keepLvalue*/ true); @@ -11490,7 +11899,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor t1.isStaticOrDynamicArray() && t1.nextOf().toBasetype().ty == Tvoid) { - if (t2.nextOf().implicitConvTo(t1.nextOf())) + auto t2n = t2.nextOf(); + if (!t2n) + { + // filling not allowed + error(exp.loc, "cannot copy `%s` to `%s`", + t2.toChars(), t1.toChars()); + } + else if (t2n.implicitConvTo(t1.nextOf())) { if (sc.setUnsafe(false, exp.loc, "copying `%s` to `%s`", t2, t1)) return setError(); @@ -11568,16 +11984,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.type = exp.e1.type; assert(exp.type); auto assignElem = exp.e2; - auto res = exp.op == EXP.assign ? exp.reorderSettingAAElem(sc) : exp; - /* https://issues.dlang.org/show_bug.cgi?id=22366 - * - * `reorderSettingAAElem` creates a tree of comma expressions, however, - * `checkAssignExp` expects only AssignExps. - */ - if (res == exp) // no `AA[k] = v` rewrite was performed - checkAssignEscape(*sc, res, false, false); - else - checkNewEscape(*sc, assignElem, false); // assigning to AA puts it on heap + Expression res = exp; + checkAssignEscape(*sc, res, false, false); if (auto ae = res.isConstructExp()) { @@ -11639,8 +12047,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor id = new DotIdExp(exp.loc, id, Id.object); id = new DotIdExp(exp.loc, id, func); - auto arguments = new Expressions(); - arguments.push(new CastExp(ae.loc, ae.e1, t1e.arrayOf).expressionSemantic(sc)); + auto arguments = new Expressions(new CastExp(ae.loc, ae.e1, t1e.arrayOf).expressionSemantic(sc)); if (lowerToArrayCtor) { arguments.push(new CastExp(ae.loc, rhs, t2b.nextOf.arrayOf).expressionSemantic(sc)); @@ -11721,9 +12128,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor id = new DotIdExp(ae.loc, id, Id.object); id = new DotIdExp(ae.loc, id, func); - auto arguments = new Expressions(); - arguments.push(new CastExp(ae.loc, ae.e1, ae.e1.type.nextOf.arrayOf) - .expressionSemantic(sc)); + auto arguments = new Expressions(new CastExp(ae.loc, ae.e1, ae.e1.type.nextOf.arrayOf) + .expressionSemantic(sc)); Expression eValue2, value2 = ae.e2; if (isArrayAssign && value2.isLvalue()) @@ -11752,14 +12158,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Expression ce = new CallExp(ae.loc, id, arguments); res = Expression.combine(eValue2, ce).expressionSemantic(sc); if (isArrayAssign) - res = Expression.combine(res, ae.e1).expressionSemantic(sc); + res = Expression.combine(res, ae.e1).expressionSemantic(sc).checkGC(sc); if (global.params.v.verbose) message("lowered %s =>\n %s", ae.toChars(), res.toChars()); res = new LoweredAssignExp(ae, res); res.type = ae.type; - res = res.checkGC(sc); return res; } @@ -11814,7 +12219,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if ((exp.e1.type.isIntegral() || exp.e1.type.isFloating()) && (exp.e2.type.isIntegral() || exp.e2.type.isFloating())) { Expression e0 = null; - Expression e = exp.reorderSettingAAElem(sc); + Expression e = exp; e = Expression.extractLast(e, e0); assert(e == exp); @@ -11961,10 +12366,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.type = exp.e1.type; auto assignElem = exp.e2; - auto res = exp.reorderSettingAAElem(sc); - if (res != exp) // `AA[k] = v` rewrite was performed - checkNewEscape(*sc, assignElem, false); - else if (exp.op == EXP.concatenateElemAssign || exp.op == EXP.concatenateDcharAssign) + auto res = exp; + if (exp.op == EXP.concatenateElemAssign || exp.op == EXP.concatenateDcharAssign) checkAssignEscape(*sc, res, false, false); result = res; @@ -12000,9 +12403,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor id = new DotIdExp(exp.loc, id, Id.object); id = new DotIdExp(exp.loc, id, hook); - auto arguments = new Expressions(); - arguments.push(exp.e1); - arguments.push(exp.e2); + auto arguments = new Expressions(exp.e1, exp.e2); Expression ce = new CallExp(exp.loc, id, arguments); exp.lowering = ce.expressionSemantic(sc); @@ -12034,12 +12435,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor id = new DotIdExp(exp.loc, id, Id.object); id = new DotIdExp(exp.loc, id, hook); - auto arguments = new Expressions(); Expression eValue1; Expression value1 = extractSideEffect(sc, "__appendtmp", eValue1, exp.e1); - arguments.push(value1); - arguments.push(new IntegerExp(exp.loc, 1, Type.tsize_t)); + auto arguments = new Expressions(value1, new IntegerExp(exp.loc, 1, Type.tsize_t)); Expression ce = new CallExp(exp.loc, id, arguments); @@ -12051,14 +12450,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor * for expressions like `a ~= a[$-1]`. Here, $ will be modified * by calling `_d_arrayappendcTX`, so we need to save `a[$-1]` in * a temporary variable. + * `__appendtmp*` will be destroyed together with the array `exp.e1`. */ - value2 = extractSideEffect(sc, "__appendtmp", eValue2, value2, true); - - // `__appendtmp*` will be destroyed together with the array `exp.e1`. - auto vd = eValue2.isDeclarationExp().declaration.isVarDeclaration(); - vd.storage_class |= STC.nodtor; - // Be more explicit that this "declaration" is local to the expression - vd.storage_class |= STC.exptemp; + value2 = extractSideEffect(sc, "__appendtmp", eValue2, value2, true, STC.nodtor | STC.exptemp); } auto ale = new ArrayLengthExp(exp.loc, value1); @@ -12392,11 +12786,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Expression id = new IdentifierExp(exp.loc, Id.empty); id = new DotIdExp(exp.loc, id, Id.object); - auto tiargs = new Objects(); - tiargs.push(exp.type); + auto tiargs = new Objects(exp.type); id = new DotTemplateInstanceExp(exp.loc, id, hook, tiargs); id = new CallExp(exp.loc, id, arguments); - return id.expressionSemantic(sc); + return id.expressionSemantic(sc).checkGC(sc); } void trySetCatExpLowering(Expression exp) @@ -12845,7 +13238,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } - Module mmath = Module.loadStdMath(); + Module mmath = loadStdMath(); if (!mmath) { error(e.loc, "`%s` requires `std.math` for `^^` operators", e.toErrMsg()); @@ -13172,6 +13565,59 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } + /** + * lowers an `InExp` to a call to `_d_aaIn!(V[K])`. + * + * Params: + * ie = the `InExp` to lower + * Returns: + * the lowered expression or an ErrorExp + */ + private Expression lowerToAAIn(InExp ie) + { + Identifier hook = Id._d_aaIn; + if (!verifyHookExist(ie.loc, *sc, hook, "key in AA")) + return ErrorExp.get(); + + Expression id = new IdentifierExp(ie.loc, Id.empty); + id = new DotIdExp(ie.loc, id, Id.object); + auto tiargs = new Objects(); + id = new DotIdExp(ie.loc, id, hook); + + Expression e1; + Expression ekey = extractSideEffect(sc, "__aakey", e1, ie.e1); + auto arguments = new Expressions(ie.e2, ekey); + auto ce = new CallExp(ie.loc, id, arguments); + ce.loweredFrom = ie; + e1 = Expression.combine(e1, ce); + return e1.expressionSemantic(sc); + } + + /** + * lowers a `RemoveExp` to a call to `_d_aaDel!(V[K])`. + * + * Params: + * re = the `RemoveExp` to lower + * Returns: + * the lowered expression or an ErrorExp + */ + private Expression lowerToAADel(RemoveExp re) + { + Identifier hook = Id._d_aaDel; + if (!verifyHookExist(re.loc, *sc, hook, "remove key in AA")) + return ErrorExp.get(); + + Expression id = new IdentifierExp(re.loc, Id.empty); + id = new DotIdExp(re.loc, id, Id.object); + auto tiargs = new Objects(); + id = new DotIdExp(re.loc, id, hook); + + auto arguments = new Expressions(re.e1, re.e2); + auto ce = new CallExp(re.loc, id, arguments); + ce.loweredFrom = re; + return ce.expressionSemantic(sc); + } + override void visit(InExp exp) { if (Expression e = exp.opOverloadBinary(sc, aliasThisStop)) @@ -13194,14 +13640,19 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.e1 = exp.e1.implicitCastTo(sc, ta.index); } + // for backward compatibility, type accepted during semantic, but errors in glue layer + if (exp.e2.isTypeExp()) + { + exp.type = ta.nextOf().pointerTo(); + break; + } + result = lowerToAAIn(exp); + // even though the glue layer only needs the type info of the index, // this might be the first time an AA literal is accessed, so check // the full type info semanticTypeInfo(sc, ta); - - // Return type is pointer to value - exp.type = ta.nextOf().pointerTo(); - break; + return; } case Terror: @@ -13229,7 +13680,39 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = ex; return; } - result = e; + + result = lowerToAADel(e); + } + + /** + * lower a `EqualExp` to a call to `_d_aaEqual!(aa1, aa2)`. + * + * Params: + * ee = the `EqualExp` to lower + * Returns: + * the lowered expression or an ErrorExp + */ + private Expression lowerToAAEqual(EqualExp ee) + { + Identifier hook = Id._d_aaEqual; + if (!verifyHookExist(ee.loc, *sc, hook, "compare AAs")) + return ErrorExp.get(); + + Expression id = new IdentifierExp(ee.loc, Id.empty); + id = new DotIdExp(ee.loc, id, Id.object); + auto tiargs = new Objects(); + id = new DotIdExp(ee.loc, id, hook); + + auto arguments = new Expressions(ee.e1, ee.e2); + auto ce = new CallExp(ee.loc, id, arguments); + if (ee.op == EXP.notEqual) + { + auto ne = new NotExp(ee.loc, ce); + ne.loweredFrom = ee; + return ne.expressionSemantic(sc); + } + ce.loweredFrom = ee; + return ce.expressionSemantic(sc); } override void visit(EqualExp exp) @@ -13312,19 +13795,53 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Type t1 = exp.e1.type.toBasetype(); Type t2 = exp.e2.type.toBasetype(); - // Indicates whether the comparison of the 2 specified array types - // requires an object.__equals() lowering. - static bool needsDirectEq(Type t1, Type t2, Scope* sc) + static bool unifyArrayTypes(Type t1, Type t2, Scope* sc) { Type t1n = t1.nextOf().toBasetype(); Type t2n = t2.nextOf().toBasetype(); + if ((t1n.ty.isSomeChar && t2n.ty.isSomeChar) || (t1n.ty == Tvoid || t2n.ty == Tvoid)) { - return false; + return true; } if (t1n.constOf() != t2n.constOf()) + { + return false; + } + + Type t = t1n; + while (t.toBasetype().nextOf()) + t = t.nextOf().toBasetype(); + if (auto ts = t.isTypeStruct()) + { + // semanticTypeInfo() makes sure hasIdentityEquals has been computed + if (global.params.useTypeInfo && Type.dtypeinfo) + semanticTypeInfo(sc, ts); + + return !ts.sym.hasIdentityEquals; // has custom opEquals + } + + return true; + } + + static bool shouldUseMemcmp(Type t1, Type t2, Scope *sc) + { + Type t1n = t1.nextOf().toBasetype(); + Type t2n = t2.nextOf().toBasetype(); + const t1nsz = t1n.size(); + const t2nsz = t2n.size(); + + if ((t1n.ty == Tvoid || (t1n.isScalar() && !t1n.isFloating())) && + (t2n.ty == Tvoid || (t2n.isScalar() && !t1n.isFloating())) && + t1nsz == t2nsz && t1n.isUnsigned() == t2n.isUnsigned()) + { return true; + } + if (t1n.constOf() != t2n.constOf()) + { + return false; + } Type t = t1n; while (t.toBasetype().nextOf()) @@ -13335,7 +13852,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (global.params.useTypeInfo && Type.dtypeinfo) semanticTypeInfo(sc, ts); - return ts.sym.hasIdentityEquals; // has custom opEquals + auto v = new IsMemcmpableVisitor(); + ts.accept(v); + return v.result; } return false; @@ -13348,9 +13867,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } const isArrayComparison = t1.isStaticOrDynamicArray() && t2.isStaticOrDynamicArray(); - const needsArrayLowering = isArrayComparison && needsDirectEq(t1, t2, sc); - if (!needsArrayLowering) + if (!isArrayComparison || unifyArrayTypes(t1, t2, sc)) { // https://issues.dlang.org/show_bug.cgi?id=23783 if (exp.e1.checkSharedAccess(sc) || exp.e2.checkSharedAccess(sc)) @@ -13380,7 +13898,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } // lower some array comparisons to object.__equals(e1, e2) - if (needsArrayLowering || (t1.ty == Tarray && t2.ty == Tarray)) + if (isArrayComparison && !shouldUseMemcmp(t1, t2, sc)) { //printf("Lowering to __equals %s %s\n", exp.e1.toChars(), exp.e2.toChars()); @@ -13395,13 +13913,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } - if (!verifyHookExist(exp.loc, *sc, Id.__equals, "equal checks on arrays")) + Identifier hook = Id.__equals; + if (!verifyHookExist(exp.loc, *sc, hook, "equal checks on arrays")) return setError(); - Expression __equals = new IdentifierExp(exp.loc, Id.empty); - Identifier id = Identifier.idPool("__equals"); - __equals = new DotIdExp(exp.loc, __equals, Id.object); - __equals = new DotIdExp(exp.loc, __equals, id); + Expression lowering = new IdentifierExp(exp.loc, Id.empty); + lowering = new DotIdExp(exp.loc, lowering, Id.object); + lowering = new DotIdExp(exp.loc, lowering, hook); /* https://issues.dlang.org/show_bug.cgi?id=23674 * @@ -13412,30 +13930,76 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.e1 = exp.e1.optimize(WANTvalue); exp.e2 = exp.e2.optimize(WANTvalue); + auto e1c = exp.e1.copy(); + auto e2c = exp.e2.copy(); + + /* Remove qualifiers from the types of the arguments to reduce the number + * of generated `__equals` instances. + */ + static void unqualifyExp(Expression e) + { + e.type = e.type.unqualify(MODFlags.wild | MODFlags.immutable_ | MODFlags.shared_); + auto eNext = e.type.nextOf(); + if (eNext && !eNext.toBasetype().isTypeStruct()) + e.type = e.type.unqualify(MODFlags.const_); + + if (!e.isArrayLiteralExp()) + return; + + if (auto elems = e.isArrayLiteralExp().elements) + foreach(elem; *elems) + if (elem) + unqualifyExp(elem); + } + unqualifyExp(e1c); + unqualifyExp(e2c); + auto arguments = new Expressions(2); - (*arguments)[0] = exp.e1; - (*arguments)[1] = exp.e2; + (*arguments)[0] = e1c; + (*arguments)[1] = e2c; - __equals = new CallExp(exp.loc, __equals, arguments); + lowering = new CallExp(exp.loc, lowering, arguments); if (exp.op == EXP.notEqual) { - __equals = new NotExp(exp.loc, __equals); + lowering = new NotExp(exp.loc, lowering); } - __equals = __equals.trySemantic(sc); // for better error message - if (!__equals) + lowering = lowering.trySemantic(sc); // for better error message + if (!lowering) { - error(exp.loc, "incompatible types for array comparison: `%s` and `%s`", - exp.e1.type.toChars(), exp.e2.type.toChars()); - __equals = ErrorExp.get(); + if (sc.func) + error(exp.loc, "can't infer return type in function `%s`", sc.func.toChars()); + else + error(exp.loc, "incompatible types for array comparison: `%s` and `%s`", + exp.e1.type.toChars(), exp.e2.type.toChars()); + lowering = ErrorExp.get(); } - - result = __equals; + exp.lowering = lowering; + result = exp; return; } + // When array comparison is not lowered to `__equals`, `memcmp` is used, but + // GC checks occur before the expression is lowered to `memcmp` in e2ir.d. + // Thus, we will consider the literal arrays as on-stack arrays to avoid issues + // during GC checks. + if (isArrayComparison) + { + if (auto ale1 = exp.e1.isArrayLiteralExp()) + { + ale1.onstack = true; + } + + if (auto ale2 = exp.e2.isArrayLiteralExp()) + { + ale2.onstack = true; + } + } + if (exp.e1.type.toBasetype().ty == Taarray) { + result = lowerToAAEqual(exp); semanticTypeInfo(sc, exp.e1.type.toBasetype()); + return; } if (!target.isVectorOpSupported(t1, exp.op, t2)) @@ -13924,7 +14488,8 @@ private bool expressionSemanticDone(Expression e) || e.isTypeExp() // stores its type in the Expression.type field || e.isCompoundLiteralExp() // stores its `(type) {}` in type field, gets rewritten to struct literal || e.isVarExp() // type sometimes gets set already before semantic - || (e.isAssocArrayLiteralExp() && !e.type.vtinfo) // semanticTypeInfo not run during initialization + || (e.isAssocArrayLiteralExp() && // semanticTypeInfo not run during initialization + (!e.type.vtinfo || !e.isAssocArrayLiteralExp().lowering)) ); } @@ -14410,20 +14975,10 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) Expression e = new IntegerExp(exp.loc, actualAlignment, Type.tsize_t); return e; } - else if ((exp.ident == Id.max || exp.ident == Id.min) && - exp.e1.isVarExp() && - exp.e1.isVarExp().var.isBitFieldDeclaration()) - { - // For `x.max` and `x.min` get the max/min of the bitfield, not the max/min of its type - auto bf = exp.e1.isVarExp().var.isBitFieldDeclaration(); - return new IntegerExp(exp.loc, bf.getMinMax(exp.ident), bf.type); - } - else if ((exp.ident == Id.max || exp.ident == Id.min) && - exp.e1.isDotVarExp() && - exp.e1.isDotVarExp().var.isBitFieldDeclaration()) + else if ((exp.ident == Id.max || exp.ident == Id.min) && exp.e1.isBitField()) { // For `x.max` and `x.min` get the max/min of the bitfield, not the max/min of its type - auto bf = exp.e1.isDotVarExp().var.isBitFieldDeclaration(); + auto bf = exp.e1.isBitField(); return new IntegerExp(exp.loc, bf.getMinMax(exp.ident), bf.type); } else @@ -15346,6 +15901,9 @@ Expression resolveLoc(Expression exp, Loc loc, Scope* sc) element = element.resolveLoc(loc, sc); } + if (exp.lowering) + exp.lowering.resolveLoc(loc, sc); + return exp; } @@ -15540,25 +16098,23 @@ Expression addDtorHook(Expression e, Scope* sc) /******************************* * Try to convert an expression to be an lvalue. * - * Give error if we're not an lvalue. + * Give error if we are not an lvalue. * Params: * _this = expression to convert * sc = scope * action = for error messages, what the lvalue is needed for (e.g. take address of for `&x`, modify for `x++`) + * eorig = original un-lowered expression for error messages, in case of recursive calls; null means use `_this` * Returns: converted expression, or `ErrorExp` on error */ -Expression toLvalue(Expression _this, Scope* sc, const(char)* action) -{ - return toLvalueImpl(_this, sc, action, _this); -} - -// e = original un-lowered expression for error messages, in case of recursive calls -private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action, Expression e) +Expression toLvalue(Expression _this, Scope* sc, const(char)* action, Expression eorig = null) { + //printf("toLvalue() %s\n", _this.toChars()); + if (!eorig) + eorig = _this; if (!action) action = "create lvalue of"; - assert(e); + assert(eorig); Expression visit(Expression _this) { // BinaryAssignExp does not have an EXP associated @@ -15567,14 +16123,14 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action if (_this.isBinAssignExp()) return _this; if (!_this.loc.isValid()) - _this.loc = e.loc; + _this.loc = eorig.loc; - if (e.op == EXP.type) - error(_this.loc, "cannot %s type `%s`", action, e.type.toChars()); - else if (e.op == EXP.template_) - error(_this.loc, "cannot %s template `%s`, perhaps instantiate it first", action, e.toErrMsg()); + if (eorig.op == EXP.type) + error(_this.loc, "cannot %s type `%s`", action, eorig.type.toChars()); + else if (eorig.op == EXP.template_) + error(_this.loc, "cannot %s template `%s`, perhaps instantiate it first", action, eorig.toErrMsg()); else - error(_this.loc, "cannot %s expression `%s` because it is not an lvalue", action, e.toErrMsg()); + error(_this.loc, "cannot %s expression `%s` because it is not an lvalue", action, eorig.toErrMsg()); return ErrorExp.get(); } @@ -15582,8 +16138,8 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action Expression visitInteger(IntegerExp _this) { if (!_this.loc.isValid()) - _this.loc = e.loc; - error(e.loc, "cannot %s constant `%s`", action, e.toErrMsg()); + _this.loc = eorig.loc; + error(eorig.loc, "cannot %s constant `%s`", action, eorig.toErrMsg()); return ErrorExp.get(); } @@ -15652,14 +16208,15 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action { auto e1 = _this.e1; auto var = _this.var; - //printf("DotVarExp::toLvalue(%s)\n", toChars()); + //printf("DotVarExp::toLvalue(%s)\n", _this.toChars()); if (sc && sc.inCfile) { /* C11 6.5.2.3-3: A postfix expression followed by the '.' or '->' operator * is an lvalue if the first expression is an lvalue. */ - if (!e1.isLvalue()) - return visit(_this); + e1 = e1.toLvalue(sc, action, eorig); + if (e1.isErrorExp()) + return e1; } if (!_this.isLvalue()) return visit(_this); @@ -15720,7 +16277,7 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action Expression visitVectorArray(VectorArrayExp _this) { - _this.e1 = _this.e1.toLvalueImpl(sc, action, e); + _this.e1 = _this.e1.toLvalue(sc, action, eorig); return _this; } @@ -15745,13 +16302,13 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action Expression visitDelegatePointer(DelegatePtrExp _this) { - _this.e1 = _this.e1.toLvalueImpl(sc, action, e); + _this.e1 = _this.e1.toLvalue(sc, action, eorig); return _this; } Expression visitDelegateFuncptr(DelegateFuncptrExp _this) { - _this.e1 = _this.e1.toLvalueImpl(sc, action, e); + _this.e1 = _this.e1.toLvalue(sc, action, eorig); return _this; } @@ -15778,12 +16335,19 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action Expression visitCond(CondExp _this) { + if (sc && sc.inCfile) + { + // C11 6.5.15: A conditional expression does not yield an lvalue. + return visit(_this); + } // convert (econd ? e1 : e2) to *(econd ? &e1 : &e2) - CondExp e = cast(CondExp)(_this.copy()); - e.e1 = _this.e1.toLvalue(sc, action).addressOf(); - e.e2 = _this.e2.toLvalue(sc, action).addressOf(); - e.type = _this.type.pointerTo(); - return new PtrExp(_this.loc, e, _this.type); + CondExp ecopy = cast(CondExp)(_this.copy()); + ecopy.e1 = _this.e1.toLvalue(sc, action).addressOf(); + checkAddressable(ecopy.e1, sc, "take address of"); + ecopy.e2 = _this.e2.toLvalue(sc, action).addressOf(); + checkAddressable(ecopy.e2, sc, "take address of"); + ecopy.type = _this.type.pointerTo(); + return new PtrExp(_this.loc, ecopy, _this.type); } @@ -15975,17 +16539,14 @@ Modifiable checkModifiable(Expression exp, Scope* sc, ModifyFlags flag = ModifyF * Params: * _this = Expression to convert * sc = scope + * eorig = original / un-lowered expression to print in error messages, if null default to `_this` * Returns: `_this` converted to an lvalue, or an `ErrorExp` */ -Expression modifiableLvalue(Expression _this, Scope* sc) +Expression modifiableLvalue(Expression _this, Scope* sc, Expression eorig = null) { - return modifiableLvalueImpl(_this, sc, _this); -} + if (!eorig) + eorig = _this; -// e = original / un-lowered expression to print in error messages -private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression e) -{ - assert(e); Expression visit(Expression exp) { //printf("Expression::modifiableLvalue() %s, type = %s\n", exp.toChars(), exp.type.toChars()); @@ -16024,7 +16585,7 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression return ErrorExp.get(); } } - return exp.toLvalueImpl(sc, "modify", e); + return exp.toLvalue(sc, "modify", eorig); } Expression visitString(StringExp exp) @@ -16073,7 +16634,7 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression Expression visitComma(CommaExp exp) { - exp.e2 = exp.e2.modifiableLvalueImpl(sc, e); + exp.e2 = exp.e2.modifiableLvalue(sc, eorig); return exp; } @@ -16095,16 +16656,6 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression return visit(exp); } - Expression visitIndex(IndexExp exp) - { - //printf("IndexExp::modifiableLvalue(%s)\n", exp.toChars()); - Expression ex = exp.markSettingAAElem(); - if (ex.op == EXP.error) - return ex; - - return visit(exp); - } - Expression visitCond(CondExp exp) { if (!exp.e1.isLvalue() && !exp.e2.isLvalue()) @@ -16127,7 +16678,6 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression case EXP.comma: return visitComma(_this.isCommaExp()); case EXP.delegatePointer: return visitDelegatePtr(_this.isDelegatePtrExp()); case EXP.delegateFunctionPointer: return visitDelegateFuncptr(_this.isDelegateFuncptrExp()); - case EXP.index: return visitIndex(_this.isIndexExp()); case EXP.question: return visitCond(_this.isCondExp()); } } @@ -16155,9 +16705,12 @@ private bool checkAddressVar(Scope* sc, Expression exp, VarDeclaration v) } if (sc.func && !sc.intypeof && !v.isDataseg()) { + auto msg = (v.storage_class & STC.ref_) ? + "taking the address of local variable `%s`" : + "taking the address of stack-allocated local variable `%s`"; if (sc.useDIP1000 != FeatureState.enabled && !(v.storage_class & STC.temp) && - sc.setUnsafe(false, exp.loc, "taking the address of stack-allocated local variable `%s`", v)) + sc.setUnsafe(false, exp.loc, msg.ptr, v)) { return false; } @@ -16171,11 +16724,13 @@ private bool checkAddressVar(Scope* sc, Expression exp, VarDeclaration v) * Params: * e = expression to check * sc = context + * action = for error messages, what the address is needed for (e.g. ref return, indexing an array) * Returns: * true if the expression is addressable */ -bool checkAddressable(Expression e, Scope* sc) +bool checkAddressable(Expression e, Scope* sc, const(char)* action) { + //printf("checkAddressable() %s\n", e.toChars()); Expression ex = e; while (true) { @@ -16183,11 +16738,11 @@ bool checkAddressable(Expression e, Scope* sc) { case EXP.dotVariable: // https://issues.dlang.org/show_bug.cgi?id=22749 - // Error about taking address of any bit-field, regardless of + // Error about taking address of any bitfield, regardless of // whether SCOPE.Cfile is set. if (auto bf = ex.isDotVarExp().var.isBitFieldDeclaration()) { - error(e.loc, "cannot take address of bit-field `%s`", bf.toChars()); + sc.eSink.error(e.loc, "cannot %s bitfield `%s`", action, ex.toErrMsg()); return false; } goto case EXP.cast_; @@ -16203,7 +16758,7 @@ bool checkAddressable(Expression e, Scope* sc) continue; case EXP.variable: - if (sc.inCfile) + if (sc && sc.inCfile) { // C11 6.5.3.2: A variable that has its address taken cannot be // stored in a register. @@ -16211,10 +16766,7 @@ bool checkAddressable(Expression e, Scope* sc) // or cast to an lvalue pointer cannot be stored in a register. if (ex.isVarExp().var.storage_class & STC.register) { - if (e.isIndexExp()) - error(e.loc, "cannot index through register variable `%s`", ex.toErrMsg()); - else - error(e.loc, "cannot take address of register variable `%s`", ex.toErrMsg()); + sc.eSink.error(e.loc, "cannot %s register variable `%s`", action, ex.toErrMsg()); return false; } } @@ -16275,7 +16827,7 @@ Expression getThisSkipNestedFuncs(Loc loc, Scope* sc, Dsymbol s, AggregateDeclar { n++; e1 = new VarExp(loc, f.vthis); - if (f.hasDualContext()) + if (f.hasDualContext) { // (*__this)[i] if (n > 1) @@ -16382,7 +16934,7 @@ private bool fit(StructDeclaration sd, Loc loc, Scope* sc, Expressions* elements return true; const nfields = sd.nonHiddenFields(); - size_t offset = 0; + ulong bitoffset = 0; for (size_t i = 0; i < elements.length; i++) { Expression e = (*elements)[i]; @@ -16401,7 +16953,13 @@ private bool fit(StructDeclaration sd, Loc loc, Scope* sc, Expressions* elements return false; } VarDeclaration v = sd.fields[i]; - if (v.offset < offset) + // Overlap is checked by comparing bit offsets + ulong vbitoffset = v.offset * 8; + auto vbf = v.isBitFieldDeclaration(); + if (vbf) + vbitoffset += vbf.bitOffset; + + if (vbitoffset < bitoffset) { .error(loc, "overlapping initialization for `%s`", v.toChars()); if (!sd.isUnionDeclaration()) @@ -16416,7 +16974,11 @@ private bool fit(StructDeclaration sd, Loc loc, Scope* sc, Expressions* elements const vsize = v.type.size(); if (vsize == SIZE_INVALID) return false; - offset = cast(uint)(v.offset + vsize); + // Bitsize of types are overridden by any bitfield widths. + if (vbf) + bitoffset = vbitoffset + vbf.fieldWidth; + else + bitoffset = vbitoffset + vsize * 8; Type t = v.type; if (stype) @@ -16615,8 +17177,12 @@ Expression toBoolean(Expression exp, Scope* sc) if (!t.isBoolean()) { if (tb != Type.terror) + { error(exp.loc, "expression `%s` of type `%s` does not have a boolean value", exp.toChars(), t.toChars()); + if (auto ts = tb.isTypeStruct()) + errorSupplemental(ts.sym.loc, "perhaps add Cast Operator Overloading with `bool opCast(T : bool)() => ...`"); + } return ErrorExp.get(); } return e; @@ -17133,7 +17699,7 @@ bool checkDisabled(Declaration d, Loc loc, Scope* sc, bool isAliasedDeclaration * postblit. Print the first field that has * a disabled postblit. */ - if (postblit.isGenerated()) + if (postblit.isGenerated) { auto sd = p.isStructDeclaration(); assert(sd); @@ -17175,7 +17741,7 @@ bool checkDisabled(Declaration d, Loc loc, Scope* sc, bool isAliasedDeclaration if (auto ctor = d.isCtorDeclaration()) { //printf("checkDisabled() %s %s\n", ctor.toPrettyChars(), toChars(ctor.type)); - if (ctor.isCpCtor && ctor.isGenerated()) + if (ctor.isCpCtor && ctor.isGenerated) { .error(loc, "generating an `inout` copy constructor for `struct %s` failed, therefore instances of it are uncopyable", d.parent.toPrettyChars()); return true; @@ -17185,6 +17751,39 @@ bool checkDisabled(Declaration d, Loc loc, Scope* sc, bool isAliasedDeclaration return true; } + +/******************************************* + * If variable has a constant expression initializer, get it. + * Otherwise, return null. + */ +Expression getConstInitializer(VarDeclaration vd, bool needFullType = true) +{ + assert(vd.type && vd._init); + + // Ungag errors when not speculative + const oldgag = global.gag; + if (global.gag) + { + Dsymbol sym = vd.isMember(); + if (sym && !sym.isSpeculative()) + global.gag = 0; + } + + if (vd._scope) + { + vd.inuse++; + vd._init = vd._init.initializerSemantic(vd._scope, vd.type, INITinterpret); + import dmd.semantic2 : lowerStaticAAs; + lowerStaticAAs(vd, vd._scope); + vd._scope = null; + vd.inuse--; + } + + Expression e = vd._init.initializerToExpression(needFullType ? vd.type : null); + global.gag = oldgag; + return e; +} + /******************************************* * Helper function for the expansion of manifest constant. */ @@ -17200,6 +17799,8 @@ private Expression expandInitializer(VarDeclaration vd, Loc loc) } e = e.copy(); + if (auto aae = e.isAssocArrayLiteralExp()) + aae.lowering = null; // need to redo lowering as it contains temporary variables that must be renamed e.loc = loc; // for better error message return e; } @@ -17526,10 +18127,11 @@ void lowerNonArrayAggregate(StaticForeach sfe, Scope* sc) Parameters*[3] pparams = [new Parameters(), new Parameters(), new Parameters()]; foreach (i; 0 .. nvars) { - foreach (params; pparams) + foreach (j, params; pparams) { auto p = sfe.aggrfe ? (*sfe.aggrfe.parameters)[i] : sfe.rangefe.param; - params.push(new Parameter(aloc, p.storageClass, p.type, p.ident, null, null)); + auto storageClass = j == 2 ? p.storageClass : p.storageClass & ~(STC.manifest | STC.alias_); + params.push(new Parameter(aloc, storageClass, p.type, p.ident, null, null)); } } Expression[2] res; @@ -17574,12 +18176,12 @@ void lowerNonArrayAggregate(StaticForeach sfe, Scope* sc) sfe.rangefe.upr = sfe.rangefe.upr.optimize(WANTvalue); sfe.rangefe.upr = sfe.rangefe.upr.ctfeInterpret(); } - auto s1 = new Statements(); + auto stmts = new Statements(); if (tplty) stmts.push(new ExpStatement(sfe.loc, tplty.sym)); stmts.push(new ReturnStatement(aloc, res[0])); - s1.push(sfe.createForeach(aloc, pparams[0], new CompoundStatement(aloc, stmts))); - s1.push(new ExpStatement(aloc, new AssertExp(aloc, IntegerExp.literal!0))); + auto s1 = new Statements(sfe.createForeach(aloc, pparams[0], new CompoundStatement(aloc, stmts)), + new ExpStatement(aloc, new AssertExp(aloc, IntegerExp.literal!0))); Type ety = new TypeTypeof(aloc, sfe.wrapAndCall(aloc, new CompoundStatement(aloc, s1))); auto aty = ety.arrayOf(); auto idres = Identifier.generateId("__res"); @@ -17737,6 +18339,46 @@ int include(Condition c, Scope* sc) return v.result; } +/******************************************* + * Look for constructor declaration. + */ +Dsymbol searchCtor(AggregateDeclaration ad) +{ + auto s = ad.search(Loc.initial, Id.ctor); + if (s) + { + if (!(s.isCtorDeclaration() || + s.isTemplateDeclaration() || + s.isOverloadSet())) + { + error(s.loc, "%s name `__ctor` is not allowed", s.kind); + errorSupplemental(s.loc, "identifiers starting with `__` are reserved for internal use"); + ad.errors = true; + s = null; + } + } + if (s && s.toParent() != ad) + s = null; // search() looks through ancestor classes + if (s) + { + // Finish all constructors semantics to determine this.noDefaultCtor. + static int searchCtor(Dsymbol s, void*) + { + auto f = s.isCtorDeclaration(); + if (f && f.semanticRun == PASS.initial) + f.dsymbolSemantic(null); + return 0; + } + + for (size_t i = 0; i < ad.members.length; i++) + { + auto sm = (*ad.members)[i]; + sm.apply(&searchCtor, null); + } + } + return s; +} + private extern(C++) class IncludeVisitor : Visitor { alias visit = Visitor.visit; @@ -17869,3 +18511,334 @@ private extern(C++) class IncludeVisitor : Visitor { result = (sic.inc == Include.yes); } } + +/*************************************** +* mark ArrayExp on the left side of an assignment recursively as modifiable before +* semantic analysis to defer potential lowerings into the assignment when +* the right side of the expression is analyzed, too, i.e. +* +* a[i][j] = 1 +* +* is parsed to +* +* AssignExp(ArrayExp(ArrayExp(Id('a'), Id('i')), Id('j')), 1) +* +* ArrayExp is converted to IndexExp during semantic analysis, but the lowering +* for IndexExp on associative arrays is different for reading or writing. For +* struct types, it can even depend on the right hand side of an assignment. +* +* Params: +* ae = the ArrayExp to mark +*/ +void markArrayExpModifiable(ArrayExp ae) +{ + ae.modifiable = true; + for (auto ae1 = ae.e1.isArrayExp(); ae1; ae1 = ae1.e1.isArrayExp()) + ae1.modifiable = true; +} + +/*************************************** +* convert an IndexExp on an associative array `aa[key]` +* to `_d_aaGetRvalueX!(K,V)(aa, key)[0]` +* +* Params: +* ie = the IndexExp to lower +* sc = context +* Returns: +* on success, the lowered expression that is still an IndexExp +* with its `loweredFrom` field set to the original expression +* on failure, null is returned +*/ +Expression lowerAAIndexRead(IndexExp ie, Scope* sc) +{ + Expression ce = buildAAIndexRValueX(ie.e1.type, ie.e1, ie.e2, sc); + if (!ce) + return ie; + auto rv = new IndexExp(ie.loc, ce, IntegerExp.literal!0); + rv.loweredFrom = ie; + + return rv.expressionSemantic(sc); +} + +// helper for lowerAAIndexRead and revertIndexAssignToRvalues +// to rewrite `aa[key]` to `_d_aaGetRvalueX!(K,V)(aa, key)[0]` +private Expression buildAAIndexRValueX(Type t, Expression eaa, Expression ekey, Scope* sc) +{ + auto taa = t.toBasetype().isTypeAArray(); + if (!taa) + return null; + + auto loc = eaa.loc; + Identifier hook = Id._d_aaGetRvalueX; + if (!verifyHookExist(loc, *sc, hook, "indexing AA")) + return null; + + Expression func = new IdentifierExp(loc, Id.empty); + func = new DotIdExp(loc, func, Id.object); + auto tiargs = new Objects(taa.index, taa.next); + func = new DotTemplateInstanceExp(loc, func, hook, tiargs); + + Expression e0; + auto arguments = new Expressions(eaa, ekey); + auto call = new CallExp(loc, func, arguments); + e0 = Expression.combine(e0, call); + + if (arrayBoundsCheck(sc.func)) + { + // __aaget = _d_aaGetRvalueX(aa, key), __aaget ? __aaget : onRangeError(__FILE__, __LINE__) + auto ei = new ExpInitializer(loc, e0); + auto vartmp = Identifier.generateId("__aaget"); + auto vardecl = new VarDeclaration(loc, null, vartmp, ei, STC.exptemp); + auto declexp = new DeclarationExp(loc, vardecl); + + //Expression idrange = new IdentifierExp(loc, Identifier.idPool("_d_arraybounds")); + Expression idrange = new IdentifierExp(loc, Id.empty); + idrange = new DotIdExp(loc, idrange, Id.object); + idrange = new DotIdExp(loc, idrange, Identifier.idPool("_d_arraybounds")); + auto locargs = new Expressions(new FileInitExp(loc, EXP.file), new LineInitExp(loc)); + auto ex = new CallExp(loc, idrange, locargs); + + auto idvar1 = new IdentifierExp(loc, vartmp); + auto idvar2 = new IdentifierExp(loc, vartmp); + auto cond = new CondExp(loc, idvar1, idvar2, ex); + auto comma = new CommaExp(loc, declexp, cond); + return comma; + } + return e0; +} + +/*************************************** +* in a multi-dimensional array assignment without the out-most access being to an AA, +* convert AA accesses back to rvalues +* +* Params: +* ie = the IndexExp to lower +* sc = context +* Returns: +* the lowered expression if the assignment was actually on an associative array, +* the original expression otherwise, +* an ErorrExp on failure +*/ +Expression revertIndexAssignToRvalues(IndexExp ie, Scope* sc) +{ + if (auto ie1 = ie.e1.isIndexExp()) + { + // convert inner access first, otherwise we'd have to crawl into the lowered AST + ie.e1 = revertIndexAssignToRvalues(ie1, sc); + } + if (!ie.e1.type.isTypeAArray()) + return ie; + + assert(ie.modifiable); + return lowerAAIndexRead(ie, sc); +} + +// helper for rewriteAAIndexAssign +private Expression implicitConvertToStruct(Expression ev, StructDeclaration sd, Scope* sc) +{ + Type t2 = ev.type.toBasetype(); + Expression ey = null; + if (t2.ty == Tstruct && sd == t2.toDsymbol(sc)) + { + ey = ev; + } + else if (!ev.implicitConvTo(sd.type) && sd.ctor) + { + // Look for implicit constructor call + // Rewrite as S().ctor(e2) + ey = new StructLiteralExp(ev.loc, sd, null); + ey = new DotIdExp(ev.loc, ey, Id.ctor); + ey = new CallExp(ev.loc, ey, ev); + ey = ey.trySemantic(sc); + } + return ey; +} + +// helper for rewriteIndexAssign +private Expression rewriteAAIndexAssign(BinExp exp, Scope* sc, ref Type[2] aliasThisStop) +{ + auto ie = exp.e1.isIndexExp(); + assert(ie); + auto loc = ie.e1.loc; + + Identifier hook = Id._d_aaGetY; + if (!verifyHookExist(loc, *sc, hook, "modifying AA")) + return ErrorExp.get(); + + bool escape = checkNewEscape(*sc, exp.e2, false); + if (escape) + return ErrorExp.get(); + auto gcexp = exp.checkGC(sc); + if (gcexp.op == EXP.error) + return gcexp; + + // build `bool __aafound, auto __aaget = _d_aaGetY(aa, key, found);` + auto idfound = Identifier.generateId("__aafound"); + auto varfound = new VarDeclaration(loc, Type.tbool, idfound, null, STC.temp | STC.ctfe); + auto declfound = new DeclarationExp(loc, varfound); + + // handle side effects before the actual AA insert + Expression e0 = declfound.expressionSemantic(sc); + Expression eaa = ie.e1; + // find the AA of multi dimensional access + for (auto ieaa = ie.e1.isIndexExp(); ieaa && ieaa.e1.type.isTypeAArray(); ieaa = ieaa.e1.isIndexExp()) + eaa = ieaa.e1; + eaa = extractSideEffect(sc, "__aatmp", e0, eaa); + // collect all keys of multi dimensional access + Expressions ekeys; + ekeys.push(ie.e2); + for (auto ieaa = ie.e1.isIndexExp(); ieaa && ieaa.e1.type.isTypeAArray(); ieaa = ieaa.e1.isIndexExp()) + ekeys.push(ieaa.e2); + foreach (ekey; ekeys) + { + Type tidx = ekey.type.toBasetype(); + if (tidx.ty == Tarray && tidx.nextOf().isMutable()) + { + error(loc, "associative arrays can only be assigned values with immutable keys, not `%s`", tidx.toChars()); + return ErrorExp.get(); + } + } + // extract side effects in lexical order + for (size_t i = ekeys.length; i > 0; --i) + ekeys[i-1] = extractSideEffect(sc, "__aakey", e0, ekeys[i-1]); + Expression ev = extractSideEffect(sc, "__aaval", e0, exp.e2); + + // generate series of calls to _d_aaGetY + for (size_t i = ekeys.length; i > 0; --i) + { + auto taa = eaa.type.isTypeAArray(); + assert (taa); // type must not have changed during rewrite + Expression func = new IdentifierExp(loc, Id.empty); + func = new DotIdExp(loc, func, Id.object); + auto tiargs = new Objects(taa.index, taa.next); + func = new DotTemplateInstanceExp(loc, func, hook, tiargs); + + auto arguments = new Expressions(eaa, ekeys[i-1], new IdentifierExp(loc, idfound)); + eaa = new CallExp(loc, func, arguments); + if (i > 1) + { + // extractSideEffect() cannot be used to ref the IndexExp, + // because ConstructExp below will not insert a postblit + auto ie1 = new IndexExp(loc, eaa, IntegerExp.literal!0); + ie1.modifiable = true; + ie1.loweredFrom = eaa; + eaa = ie1; + } + eaa = eaa.expressionSemantic(sc); + if (eaa.op == EXP.error) + return eaa; + } + Expression eg = extractSideEffect(sc, "__aaget", e0, eaa); + auto ie1 = new IndexExp(loc, eg, IntegerExp.literal!0); + ie1.modifiable = true; + ie1.loweredFrom = ie; + auto ex = ie1.expressionSemantic(sc); + ex = ex.optimize(WANTvalue); + if (ex.op == EXP.error) + return ex; + if (!exp.isAssignExp()) + { + // modifying assignments work with zero-initialized inserted value + BinExp bex = cast(BinExp)exp.copy(); + bex.e1 = ex; + bex.e2 = ev; + ex = Expression.combine(e0, bex); + ex.isCommaExp().originalExp = exp; + return ex.expressionSemantic(sc); + } + AssignExp ae = new AssignExp(loc, ex, ev); + auto ts = ie.type.isTypeStruct(); + if (Expression overexp = ts ? ae.opOverloadAssign(sc, aliasThisStop) : null) + { + if (overexp.op == EXP.error) + return overexp; + if (auto ey = implicitConvertToStruct(ev, ts.sym, sc)) + { + // __aafound ? __aaget.opAssign(__aaval) : __aaget.ctor(__aaval) + ey = new ConstructExp(loc, ex, ey); + ey = ey.expressionSemantic(sc); + if (ey.op == EXP.error) + return ey; + ex = overexp; + + // https://issues.dlang.org/show_bug.cgi?id=14144 + // The whole expression should have the common type + // of opAssign() return and assigned AA entry. + // Even if there's no common type, expression should be typed as void. + if (!typeMerge(sc, EXP.question, ex, ey)) + { + ex = new CastExp(ex.loc, ex, Type.tvoid); + ey = new CastExp(ey.loc, ey, Type.tvoid); + } + Expression condfound = new IdentifierExp(loc, idfound); + ex = new CondExp(loc, condfound, ex, ey); + ex = Expression.combine(e0, ex); + ex.isCommaExp().originalExp = exp; + } + else + { + // write back to _aaGetRValueX(aa, key)[0].opAssign(__aaval) + auto call = buildAAIndexRValueX(ie.e1.type, ie.e1, ie.e2, sc); + ie1 = new IndexExp(loc, call, IntegerExp.literal!0); + ie1.loweredFrom = ie; + ex = ie1.expressionSemantic(sc); + ex = ex.optimize(WANTvalue); + ae = new AssignExp(loc, ex, ev); + ex = ae.opOverloadAssign(sc, aliasThisStop); + assert(ex); + } + } + else + { + ex = Expression.combine(e0, ae); + ex.isCommaExp().originalExp = exp; + } + ex = ex.expressionSemantic(sc); + return ex; +} + +/*************************************** +* rewrite multi-dimensional array modifying assignments on associative arrays +* +* Params: +* exp = the assignment expression, AssignExp, ConstructExp, BinAssignExp or PostExp +* sc = context +* aliasThisStop = for recursion check on `alias this` +* Returns: +* the lowered expression if the assignment was actually on an associative array, +* null if no modifying index expression was found on the lhs of the assignemnt +*/ +Expression rewriteIndexAssign(BinExp exp, Scope* sc, Type[2] aliasThisStop) +{ + if (auto ie1 = exp.e1.isIndexExp()) + { + if (ie1.e1.type.isTypeAArray()) + { + if (exp.op == EXP.construct) + exp.e1 = exp.e1.toLvalue(sc, "initialize"); + else + exp.e1 = exp.e1.modifiableLvalue(sc); + if (exp.e1.op == EXP.error) + return exp.e1; + assert(exp.e1 == ie1); + assert(ie1.modifiable); + return rewriteAAIndexAssign(exp, sc, aliasThisStop); + } + exp.e1 = revertIndexAssignToRvalues(ie1, sc); + } + return null; +} + +/**************************************** + * Get bitfield from expression. + */ +BitFieldDeclaration isBitField(Expression e) +{ + if (auto ve = e.isVarExp()) + return ve.var.isBitFieldDeclaration(); + + if (auto dve = e.isDotVarExp()) + return dve.var.isBitFieldDeclaration(); + + return null; +} diff --git a/gcc/d/dmd/file_manager.d b/gcc/d/dmd/file_manager.d index 2ccb1d25497..8116dda79d7 100644 --- a/gcc/d/dmd/file_manager.d +++ b/gcc/d/dmd/file_manager.d @@ -175,121 +175,105 @@ nothrow: whichPathFoundThis = -1; - // see if we should check for the module locally. - bool checkLocal = pathCache.pathExists(filename); - const sdi = FileName.forceExt(filename, hdr_ext); - if (checkLocal && FileName.exists(sdi) == 1) - return sdi; - scope(exit) FileName.free(sdi.ptr); - - const sd = FileName.forceExt(filename, mars_ext); - if (checkLocal && FileName.exists(sd) == 1) - return sd; - scope(exit) FileName.free(sd.ptr); - - if (checkLocal) + // List of extensions to match, in order of precedence. + const(char)[][2] extensions = [ + FileName.forceExt(filename, hdr_ext), + FileName.forceExt(filename, mars_ext), + ]; + const(char)[][3] importCextensions = [ + FileName.forceExt(filename, i_ext), + FileName.forceExt(filename, h_ext), + FileName.forceExt(filename, c_ext), + ]; + scope(exit) { - if (pathCache.isExistingPath(filename)) - { - /* The filename exists but it's a directory. - * Therefore, the result should be: filename/package.d - * iff filename/package.d is a file - */ - const ni = FileName.combine(filename, package_di); - if (FileName.exists(ni) == 1) - return ni; - FileName.free(ni.ptr); - - const n = FileName.combine(filename, package_d); - if (FileName.exists(n) == 1) - return n; - FileName.free(n.ptr); - } + foreach (ext; extensions) + FileName.free(ext.ptr); + foreach (ext; importCextensions) + FileName.free(ext.ptr); } - if (FileName.absolute(filename)) - return null; - if (!pathsInfo.length) - return null; - foreach (pathIndex, entry; pathsInfo) + /* Search for all combinations of files (mod.di, mod.d, mod/package.d, ...) + * within in directory `path`. + */ + const(char)[] lookForSourceFileInPath(const char[] path) { - const p = entry.path.toDString(); - - const(char)[] n = FileName.combine(p, sdi); - - if (!pathCache.pathExists(n)) + // When checking for modules locally, combine won't allocate a new string. + bool checkLocal = path is null; + void freePath(const(char)[] p) { - FileName.free(n.ptr); - continue; // no need to check for anything else. + if (checkLocal) + return; + FileName.free(p.ptr); } - if (FileName.exists(n) == 1) - return n; - FileName.free(n.ptr); + const p = FileName.combine(path, filename); + scope(exit) freePath(p); + if (!pathCache.pathExists(p)) + return null; // no need to check for anything else. - n = FileName.combine(p, sd); - if (FileName.exists(n) == 1) + // Search for any file matching {path}/{file}.{ext} + foreach (ext; extensions) { - whichPathFoundThis = pathIndex; - return n; + const file = FileName.combine(path, ext); + if (FileName.exists(file) == 1) + { + import dmd.root.rmem : xarraydup; + return checkLocal ? file.xarraydup : file; + } + freePath(file); } - FileName.free(n.ptr); - - n = FileName.combine(p, FileName.sansExt(filename)); - scope(exit) FileName.free(n.ptr); - // also cache this if we are looking for package.d[i] + const n = FileName.combine(path, FileName.sansExt(filename)); + scope(exit) freePath(n); if (pathCache.isExistingPath(n)) { - const n2i = FileName.combine(n, package_di); - if (FileName.exists(n2i) == 1) - { - whichPathFoundThis = pathIndex; - return n2i; - } + /* The filename exists but it's a directory. + * Therefore, the result should be: filename/package.d + * iff filename/package.d is a file + */ + const ni = FileName.combine(n, package_di); + if (FileName.exists(ni) == 1) + return ni; + FileName.free(ni.ptr); + + const nd = FileName.combine(n, package_d); + if (FileName.exists(nd) == 1) + return nd; + FileName.free(nd.ptr); + } - FileName.free(n2i.ptr); - const n2 = FileName.combine(n, package_d); - if (FileName.exists(n2) == 1) + /* Search for any file with importC extensions after all attempts + to find a D module/package in the path are exhausted. */ + foreach (ext; importCextensions) + { + const file = FileName.combine(path, ext); + if (FileName.exists(file) == 1) { - whichPathFoundThis = pathIndex; - return n2; + import dmd.root.rmem : xarraydup; + return checkLocal ? file.xarraydup : file; } - FileName.free(n2.ptr); + freePath(file); } + return null; } - /* ImportC: No D modules found, now search along paths[] for .i file, then .c file. - */ - const si = FileName.forceExt(filename, i_ext); - if (FileName.exists(si) == 1) - return si; - scope(exit) FileName.free(si.ptr); - - const sc = FileName.forceExt(filename, c_ext); - if (FileName.exists(sc) == 1) - return sc; - scope(exit) FileName.free(sc.ptr); - foreach (pathIndex, entry; pathsInfo) + // First see if module is found in any search paths. + if (!FileName.absolute(filename)) { - const p = entry.path.toDString(); - - const(char)[] n = FileName.combine(p, si); - if (FileName.exists(n) == 1) - { - whichPathFoundThis = pathIndex; - return n; - } - FileName.free(n.ptr); - - n = FileName.combine(p, sc); - if (FileName.exists(n) == 1) + foreach (pathIndex, entry; pathsInfo) { - whichPathFoundThis = pathIndex; - return n; + if (auto s = lookForSourceFileInPath(entry.path.toDString())) + { + whichPathFoundThis = pathIndex; + return s; + } } - FileName.free(n.ptr); } + // No modules found, check for the module locally. + if (auto s = lookForSourceFileInPath(null)) + return s; + return null; } diff --git a/gcc/d/dmd/func.d b/gcc/d/dmd/func.d index 6a573810a4b..17044233ca8 100644 --- a/gcc/d/dmd/func.d +++ b/gcc/d/dmd/func.d @@ -24,7 +24,6 @@ import dmd.aggregate; import dmd.arraytypes; import dmd.astenums; import dmd.blockexit; -import dmd.gluelayer; import dmd.dcast; import dmd.dclass; import dmd.declaration; @@ -36,7 +35,7 @@ import dmd.dsymbol; import dmd.dtemplate; import dmd.escape; import dmd.expression; -import dmd.funcsem : overloadApply; +import dmd.funcsem : isUnique; import dmd.globals; import dmd.hdrgen; import dmd.id; @@ -240,7 +239,7 @@ extern (C++) class FuncDeclaration : Declaration // Things that should really go into Scope VarDeclaration nrvo_var; /// variable to replace with shidden - Symbol* shidden; /// hidden pointer passed to function + void* shidden; /// hidden pointer passed to function ReturnStatements* returns; @@ -249,7 +248,7 @@ extern (C++) class FuncDeclaration : Declaration version (MARS) { VarDeclarations* alignSectionVars; /// local variables with alignment needs larger than stackAlign - Symbol* salignSection; /// pointer to aligned section, if any + void* salignSection; /// pointer to aligned section, if any } /// set if this is a known, builtin function we can evaluate at compile time @@ -820,45 +819,18 @@ extern (C++) class FuncDeclaration : Declaration bool addPreInvariant() { auto ad = isThis(); - return (ad && global.params.useInvariants == CHECKENABLE.on && (visibility.kind == Visibility.Kind.protected_ || visibility.kind == Visibility.Kind.public_ || visibility.kind == Visibility.Kind.export_) && !this.isNaked()); + return (ad && global.params.useInvariants == CHECKENABLE.on && (visibility.kind == Visibility.Kind.protected_ || visibility.kind == Visibility.Kind.public_ || visibility.kind == Visibility.Kind.export_) && !this.isNaked); } bool addPostInvariant() { auto ad = isThis(); - return (ad && ad.inv && global.params.useInvariants == CHECKENABLE.on && (visibility.kind == Visibility.Kind.protected_ || visibility.kind == Visibility.Kind.public_ || visibility.kind == Visibility.Kind.export_) && !this.isNaked()); + return (ad && ad.inv && global.params.useInvariants == CHECKENABLE.on && (visibility.kind == Visibility.Kind.protected_ || visibility.kind == Visibility.Kind.public_ || visibility.kind == Visibility.Kind.export_) && !this.isNaked); } override const(char)* kind() const { - return this.isGenerated() ? "generated function" : "function"; - } - - /******************************************** - * Returns: - * true if there are no overloads of this function - */ - final bool isUnique() const - { - bool result = false; - overloadApply(cast() this, (Dsymbol s) - { - auto f = s.isFuncDeclaration(); - auto td = s.isTemplateDeclaration(); - if (!f && !td) - return 0; - if (result) - { - result = false; - return 1; // ambiguous, done - } - else - { - result = true; - return 0; - } - }); - return result; + return this.isGenerated ? "generated function" : "function"; } /******************************* @@ -1373,7 +1345,8 @@ extern (C++) final class CtorDeclaration : FuncDeclaration override const(char)* kind() const { - return isCpCtor ? "copy constructor" : "constructor"; + return isCpCtor ? "copy constructor" : + isMoveCtor ? "move constructor" : "constructor"; } override bool isVirtual() const diff --git a/gcc/d/dmd/funcsem.d b/gcc/d/dmd/funcsem.d index 10f6f154b2f..0d147dd0ddc 100644 --- a/gcc/d/dmd/funcsem.d +++ b/gcc/d/dmd/funcsem.d @@ -20,7 +20,6 @@ import dmd.arraytypes; import dmd.astenums; import dmd.attrib; import dmd.blockexit; -import dmd.gluelayer; import dmd.dcast; import dmd.dclass; import dmd.declaration; @@ -103,7 +102,7 @@ public: override void visit(TryFinallyStatement s) { DtorExpStatement des; - if (fd.isNRVO() && s.finalbody && (des = s.finalbody.isDtorExpStatement()) !is null && + if (fd.isNRVO && s.finalbody && (des = s.finalbody.isDtorExpStatement()) !is null && fd.nrvo_var == des.var) { if (!(global.params.useExceptions && ClassDeclaration.throwable)) @@ -138,11 +137,10 @@ public: handler = new CompoundStatement(Loc.initial, handler, ts); } - auto catches = new Catches(); auto ctch = new Catch(Loc.initial, getThrowable(), id, handler); ctch.internalCatch = true; ctch.catchSemantic(sc); // Run semantic to resolve identifier '__o' - catches.push(ctch); + auto catches = new Catches(ctch); Statement s2 = new TryCatchStatement(Loc.initial, s._body, catches); fd.hasNoEH = false; @@ -176,6 +174,33 @@ extern (C++) bool onlyOneMain(FuncDeclaration fd) return true; } +/******************************************** + * Returns: + * true if there are no overloads of this function + */ +bool isUnique(const FuncDeclaration fd) +{ + bool result = false; + overloadApply(cast() fd, (Dsymbol s) + { + auto f = s.isFuncDeclaration(); + auto td = s.isTemplateDeclaration(); + if (!f && !td) + return 0; + if (result) + { + result = false; + return 1; // ambiguous, done + } + else + { + result = true; + return 0; + } + }); + return result; +} + /********************************** * Main semantic routine for functions. */ @@ -224,7 +249,7 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl) AggregateDeclaration ad = funcdecl.isThis(); // Don't nest structs b/c of generated methods which should not access the outer scopes. // https://issues.dlang.org/show_bug.cgi?id=16627 - if (ad && !funcdecl.isGenerated()) + if (ad && !funcdecl.isGenerated) { funcdecl.storage_class |= ad.storage_class & (STC.TYPECTOR | STC.synchronized_); ad.makeNested(); @@ -409,7 +434,7 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl) sc.linkage = funcdecl._linkage; - if (!tf.isNaked() && !(funcdecl.isThis() || funcdecl.isNested())) + if (!tf.isNaked && !(funcdecl.isThis() || funcdecl.isNested())) { import core.bitop : popcnt; auto mods = MODtoChars(tf.mod); @@ -1366,7 +1391,7 @@ bool functionSemantic3(FuncDeclaration fd) return false; } - return !fd.errors && !fd.hasSemantic3Errors(); + return !fd.errors && !fd.hasSemantic3Errors; } // called from semantic3 @@ -1557,7 +1582,7 @@ int findVtblIndex(FuncDeclaration fd, Dsymbol[] vtbl) fdv.toPrettyChars(), fdv.type.toTypeFunction().parameterList.parametersTypeToChars(), fd.toPrettyChars(), fd.type.toTypeFunction().parameterList.parametersTypeToChars(), fd.type.modToChars()); - const char* where = fd.type.isNaked() ? "parameters" : "type"; + const char* where = fd.type.isNaked ? "parameters" : "type"; deprecationSupplemental(fd.loc, "Either remove `override`, or adjust the `const` qualifiers of the " ~ "overriding function %s", where); } @@ -2456,7 +2481,7 @@ int getLevelAndCheck(FuncDeclaration fd, Loc loc, Scope* sc, FuncDeclaration tar * Returns: * true if can */ -bool canInferAttributes(FuncDeclaration fd, Scope* sc) +private bool canInferAttributes(FuncDeclaration fd, Scope* sc) { if (!fd.fbody) return false; @@ -2487,90 +2512,6 @@ bool canInferAttributes(FuncDeclaration fd, Scope* sc) return false; } -/********************************************* - * In the current function 'sc.func', we are calling 'fd'. - * 1. Check to see if the current function can call 'fd' , issue error if not. - * 2. If the current function is not the parent of 'fd' , then add - * the current function to the list of siblings of 'fd' . - * 3. If the current function is a literal, and it's accessing an uplevel scope, - * then mark it as a delegate. - * Returns true if error occurs. - */ -bool checkNestedFuncReference(FuncDeclaration fd, Scope* sc, Loc loc) -{ - //printf("FuncDeclaration::checkNestedFuncReference() %s\n", toPrettyChars()); - if (auto fld = fd.isFuncLiteralDeclaration()) - { - if (fld.tok == TOK.reserved) - { - fld.tok = TOK.function_; - fld.vthis = null; - } - } - if (!fd.parent || fd.parent == sc.parent) - return false; - if (fd.ident == Id.require || fd.ident == Id.ensure) - return false; - if (!fd.isThis() && !fd.isNested()) - return false; - // The current function - FuncDeclaration fdthis = sc.parent.isFuncDeclaration(); - if (!fdthis) - return false; // out of function scope - Dsymbol p = fd.toParentLocal(); - Dsymbol p2 = fd.toParent2(); - // Function literals from fdthis to p must be delegates - ensureStaticLinkTo(fdthis, p); - if (p != p2) - ensureStaticLinkTo(fdthis, p2); - if (!fd.isNested()) - return false; - - // The function that this function is in - bool checkEnclosing(FuncDeclaration fdv) - { - if (!fdv) - return false; - if (fdv == fdthis) - return false; - //printf("this = %s in [%s]\n", this.toChars(), this.loc.toChars()); - //printf("fdv = %s in [%s]\n", fdv .toChars(), fdv .loc.toChars()); - //printf("fdthis = %s in [%s]\n", fdthis.toChars(), fdthis.loc.toChars()); - // Add this function to the list of those which called us - if (fdthis != fd) - { - bool found = false; - for (size_t i = 0; i < fd.siblingCallers.length; ++i) - { - if (fd.siblingCallers[i] == fdthis) - found = true; - } - if (!found) - { - //printf("\tadding sibling %s to %s\n", fdthis.toPrettyChars(), toPrettyChars()); - if (!sc.intypeof && !sc.traitsCompiles) - { - fd.siblingCallers.push(fdthis); - fd.computedEscapingSiblings = false; - } - } - } - const lv = fdthis.getLevelAndCheck(loc, sc, fdv, fd); - if (lv == fd.LevelError) - return true; // error - if (lv == -1) - return false; // downlevel call - if (lv == 0) - return false; // same level call - return false; // Uplevel call - } - if (checkEnclosing(p.isFuncDeclaration())) - return true; - if (checkEnclosing(p == p2 ? null : p2.isFuncDeclaration())) - return true; - return false; -} - /**************************************************** * Check whether result variable can be built. * Returns: @@ -2664,8 +2605,7 @@ Statement mergeFrequire(FuncDeclaration fd, Statement sf, Expressions* params) Statement s2 = new ExpStatement(fd.loc, e); auto c = new Catch(fd.loc, getThrowable(), null, sf); c.internalCatch = true; - auto catches = new Catches(); - catches.push(c); + auto catches = new Catches(c); sf = new TryCatchStatement(fd.loc, s2, catches); } return sf; @@ -2728,8 +2668,7 @@ Statement mergeFrequireInclusivePreview(FuncDeclaration fd, Statement sf, Expres Statement s3 = new CompoundStatement(loc, s2, fail); auto c = new Catch(loc, getThrowable(), id, s3); c.internalCatch = true; - auto catches = new Catches(); - catches.push(c); + auto catches = new Catches(c); sf = new TryCatchStatement(loc, sf, catches); } return sf; @@ -3164,7 +3103,7 @@ extern (D) void checkMain(FuncDeclaration fd) extern (D) bool checkNRVO(FuncDeclaration fd) { //printf("checkNRVO*() %s\n", fd.ident.toChars()); - if (!fd.isNRVO() || fd.returns is null) + if (!fd.isNRVO || fd.returns is null) return false; auto tf = fd.type.toTypeFunction(); @@ -3310,7 +3249,7 @@ extern (D) PURE isPureBypassingInference(FuncDeclaration fd) * Returns: * true if reference to `tb` is isolated from reference to `ta` */ -bool traverseIndirections(Type ta, Type tb) +private bool traverseIndirections(Type ta, Type tb) { //printf("traverseIndirections(%s, %s)\n", ta.toChars(), tb.toChars()); @@ -3401,7 +3340,7 @@ bool traverseIndirections(Type ta, Type tb) * which could have come from the function's parameters, mutable * globals, or uplevel functions. */ -bool isTypeIsolatedIndirect(FuncDeclaration fd, Type t) +private bool isTypeIsolatedIndirect(FuncDeclaration fd, Type t) { //printf("isTypeIsolatedIndirect(t: %s)\n", t.toChars()); assert(t); @@ -3842,3 +3781,34 @@ extern (D) bool checkNestedReference(VarDeclaration vd, Scope* sc, Loc loc) return false; } + +/********************** + * Check to see if array bounds checking code has to be generated + * + * Params: + * fd = function for which code is to be generated + * Returns: + * true if do array bounds checking for the given function + */ +bool arrayBoundsCheck(FuncDeclaration fd) +{ + final switch (global.params.useArrayBounds) + { + case CHECKENABLE.off: + return false; + case CHECKENABLE.on: + return true; + case CHECKENABLE.safeonly: + { + if (fd) + { + Type t = fd.type; + if (t.ty == Tfunction && (cast(TypeFunction)t).trust == TRUST.safe) + return true; + } + return false; + } + case CHECKENABLE._default: + assert(0); + } +} diff --git a/gcc/d/dmd/globals.d b/gcc/d/dmd/globals.d index 624738e4e46..eb8d0553319 100644 --- a/gcc/d/dmd/globals.d +++ b/gcc/d/dmd/globals.d @@ -154,6 +154,7 @@ extern(C++) struct Verbose extern (C++) struct ImportPathInfo { const(char)* path; // char*'s of where to look for import modules + bool isOutOfBinary; // Will any module found from this path be out of binary? } /// Put command line switches in here @@ -189,6 +190,11 @@ extern (C++) struct Param Help help; Verbose v; + // Editions + Edition edition; // edition year + Edition[const(char)*] editionFiles; // Edition corresponding to a filespec + + // Options for `-preview=/-revert=` FeatureState useDIP25 = FeatureState.enabled; // implement https://wiki.dlang.org/DIP25 FeatureState useDIP1000; // implement https://dlang.org/spec/memory-safe-d.html#scope-return-params @@ -284,6 +290,7 @@ enum hdr_ext = "di"; // for D 'header' import files enum json_ext = "json"; // for JSON files enum map_ext = "map"; // for .map files enum c_ext = "c"; // for C source files +enum h_ext = "h"; // for C header source files enum i_ext = "i"; // for preprocessed C source file /** @@ -386,13 +393,10 @@ extern (C++) struct Global errorSinkNull = new ErrorSinkNull; this.fileManager = new FileManager(); + version (MARS) { compileEnv.vendor = "Digital Mars D"; - - // -color=auto is the default value - import dmd.console : detectTerminal, detectColorPreference; - params.v.color = detectTerminal() && detectColorPreference(); } else version (IN_GCC) { @@ -401,12 +405,10 @@ extern (C++) struct Global else version (IN_LLVM) { compileEnv.vendor = "LDC"; - - import dmd.console : detectTerminal; - params.v.color = detectTerminal(); } + else + static assert(0, "unknown vendor"); - params.v.errorPrintMode = ErrorPrintMode.printErrorContext; // Enable error context globally by default compileEnv.versionNumber = parseVersionNumber(versionString()); /* Initialize date, time, and timestamp diff --git a/gcc/d/dmd/globals.h b/gcc/d/dmd/globals.h index 62a575e322e..0427347e487 100644 --- a/gcc/d/dmd/globals.h +++ b/gcc/d/dmd/globals.h @@ -1,4 +1,3 @@ - /* Compiler implementation of the D programming language * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright @@ -159,9 +158,17 @@ struct Verbose struct ImportPathInfo { const char* path; + d_bool isOutOfBinary; - ImportPathInfo() : path(NULL) { } - ImportPathInfo(const char* p) : path(p) { } + ImportPathInfo() : + path(), + isOutOfBinary() + { + } + ImportPathInfo(const char* path, d_bool isOutOfBinary = false) : + path(path), + isOutOfBinary(isOutOfBinary) + {} }; // Put command line switches in here @@ -196,6 +203,9 @@ struct Param Help help; Verbose v; + unsigned short edition; // edition year + void* editionFiles; // Edition corresponding to a filespec + // Options for `-preview=/-revert=` FeatureState useDIP25; // implement https://wiki.dlang.org/DIP25 FeatureState useDIP1000; // implement https://dlang.org/spec/memory-safe-d.html#scope-return-params diff --git a/gcc/d/dmd/gluelayer.d b/gcc/d/dmd/gluelayer.d deleted file mode 100644 index ba7c1e922c8..00000000000 --- a/gcc/d/dmd/gluelayer.d +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Declarations for back-end functions that the front-end invokes. - * - * This 'glues' either the DMC or GCC back-end to the front-end. - * - * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved - * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) - * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/gluelayer.d, _gluelayer.d) - * Documentation: https://dlang.org/phobos/dmd_gluelayer.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/gluelayer.d - */ - -module dmd.gluelayer; - -import dmd.dmodule; -import dmd.dscope; -import dmd.dsymbol; -import dmd.mtype; -import dmd.statement; -import dmd.root.file; - -version (NoBackend) -{ - struct Symbol; - struct code; - struct block; - struct BlockState; - struct elem; - struct TYPE; - alias type = TYPE; - - extern(C++) abstract class ObjcGlue - { - static void initialize() {} - } -} -else version (IN_GCC) -{ - extern (C++) union tree_node; - - alias Symbol = tree_node; - alias code = tree_node; - alias type = tree_node; - - // stubs - extern(C++) abstract class ObjcGlue - { - static void initialize() {} - } -} -else -{ - public import dmd.backend.cc : block, BlockState, Symbol; - public import dmd.backend.type : type; - public import dmd.backend.el : elem; - public import dmd.backend.x86.code_x86 : code; - public import dmd.objc_glue : ObjcGlue; -} diff --git a/gcc/d/dmd/hdrgen.d b/gcc/d/dmd/hdrgen.d index 0f8c8c67b92..73f430e3db8 100644 --- a/gcc/d/dmd/hdrgen.d +++ b/gcc/d/dmd/hdrgen.d @@ -175,7 +175,7 @@ private void truncateForError(ref OutBuffer buf, size_t maxLength) if (buf.length > maxLength) { buf.setsize(maxLength - 3); - buf.writestring("..."); + buf.put("..."); } } @@ -192,7 +192,7 @@ public const(char)* toChars(const Statement s) HdrGenState hgs; OutBuffer buf; toCBuffer(s, buf, hgs); - buf.writeByte(0); + buf.put(cast(ubyte)0); return buf.extractSlice().ptr; } @@ -250,9 +250,9 @@ public const(char)* toChars(const Dsymbol d) if (auto tid = d.isTypeInfoDeclaration()) { OutBuffer buf; - buf.writestring("typeid("); - buf.writestring(tid.tinfo.toChars()); - buf.writeByte(')'); + buf.put("typeid("); + buf.put(tid.tinfo.toChars()); + buf.put(')'); return buf.extractChars(); } @@ -288,25 +288,25 @@ void moduleToBuffer2(Module m, ref OutBuffer buf, ref HdrGenState hgs) { if (m.userAttribDecl) { - buf.writestring("@("); + buf.put("@("); argsToBuffer(m.userAttribDecl.atts, buf, hgs); - buf.writeByte(')'); + buf.put(')'); buf.writenl(); } if (m.md.isdeprecated) { if (m.md.msg) { - buf.writestring("deprecated("); + buf.put("deprecated("); m.md.msg.expressionToBuffer(buf, hgs); - buf.writestring(") "); + buf.put(") "); } else - buf.writestring("deprecated "); + buf.put("deprecated "); } - buf.writestring("module "); - buf.writestring(m.md.toChars()); - buf.writeByte(';'); + buf.put("module "); + buf.put(m.md.toChars()); + buf.put(';'); buf.writenl(); } @@ -326,7 +326,7 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h void visitError(ErrorStatement s) { - buf.writestring("__error__"); + buf.put("__error__"); buf.writenl(); } @@ -341,7 +341,7 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h } if (s.exp) s.exp.expressionToBuffer(buf, hgs); - buf.writeByte(';'); + buf.put(';'); if (!hgs.forStmtInit) buf.writenl(); } @@ -353,9 +353,9 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h void visitMixin(MixinStatement s) { - buf.writestring("mixin("); + buf.put("mixin("); argsToBuffer(s.exps, buf, hgs, null); - buf.writestring(");"); + buf.put(");"); if (!hgs.forStmtInit) buf.writenl(); } @@ -392,14 +392,14 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h anywritten = true; } } - buf.writeByte(';'); + buf.put(';'); if (!hgs.forStmtInit) buf.writenl(); } void visitUnrolledLoop(UnrolledLoopStatement s) { - buf.writestring("/*unrolled*/ {"); + buf.put("/*unrolled*/ {"); buf.writenl(); buf.level++; foreach (sx; *s.statements) @@ -408,19 +408,19 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h sx.statementToBuffer(buf, hgs); } buf.level--; - buf.writeByte('}'); + buf.put('}'); buf.writenl(); } void visitScope(ScopeStatement s) { - buf.writeByte('{'); + buf.put('{'); buf.writenl(); buf.level++; if (s.statement) s.statement.statementToBuffer(buf, hgs); buf.level--; - buf.writeByte('}'); + buf.put('}'); buf.writenl(); } @@ -433,21 +433,21 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h if (!p.type && !stc) stc = STC.auto_; if (stcToBuffer(buf, stc)) - buf.writeByte(' '); + buf.put(' '); if (p.type) typeToBuffer(p.type, p.ident, buf, hgs); else - buf.writestring(p.ident.toString()); - buf.writestring(" = "); + buf.put(p.ident.toString()); + buf.put(" = "); } condition.expressionToBuffer(buf, hgs); } void visitWhile(WhileStatement s) { - buf.writestring("while ("); + buf.put("while ("); printConditionAssignment(s.param, s.condition); - buf.writeByte(')'); + buf.put(')'); buf.writenl(); if (s._body) s._body.statementToBuffer(buf, hgs); @@ -455,19 +455,19 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h void visitDo(DoStatement s) { - buf.writestring("do"); + buf.put("do"); buf.writenl(); if (s._body) s._body.statementToBuffer(buf, hgs); - buf.writestring("while ("); + buf.put("while ("); s.condition.expressionToBuffer(buf, hgs); - buf.writestring(");"); + buf.put(");"); buf.writenl(); } void visitFor(ForStatement s) { - buf.writestring("for ("); + buf.put("for ("); if (s._init) { hgs.forStmtInit++; @@ -475,96 +475,96 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h hgs.forStmtInit--; } else - buf.writeByte(';'); + buf.put(';'); if (s.condition) { - buf.writeByte(' '); + buf.put(' '); s.condition.expressionToBuffer(buf, hgs); } - buf.writeByte(';'); + buf.put(';'); if (s.increment) { - buf.writeByte(' '); + buf.put(' '); s.increment.expressionToBuffer(buf, hgs); } - buf.writeByte(')'); + buf.put(')'); buf.writenl(); - buf.writeByte('{'); + buf.put('{'); buf.writenl(); buf.level++; if (s._body) s._body.statementToBuffer(buf, hgs); buf.level--; - buf.writeByte('}'); + buf.put('}'); buf.writenl(); } void foreachWithoutBody(ForeachStatement s) { - buf.writestring(Token.toString(s.op)); - buf.writestring(" ("); + buf.put(Token.toString(s.op)); + buf.put(" ("); foreach (i, p; *s.parameters) { if (i) - buf.writestring(", "); + buf.put(", "); if (stcToBuffer(buf, p.storageClass)) - buf.writeByte(' '); + buf.put(' '); if (p.type) typeToBuffer(p.type, p.ident, buf, hgs); else - buf.writestring(p.ident.toString()); + buf.put(p.ident.toString()); } - buf.writestring("; "); + buf.put("; "); s.aggr.expressionToBuffer(buf, hgs); - buf.writeByte(')'); + buf.put(')'); buf.writenl(); } void visitForeach(ForeachStatement s) { foreachWithoutBody(s); - buf.writeByte('{'); + buf.put('{'); buf.writenl(); buf.level++; if (s._body) s._body.statementToBuffer(buf, hgs); buf.level--; - buf.writeByte('}'); + buf.put('}'); buf.writenl(); } void foreachRangeWithoutBody(ForeachRangeStatement s) { - buf.writestring(Token.toString(s.op)); - buf.writestring(" ("); + buf.put(Token.toString(s.op)); + buf.put(" ("); if (s.param.type) typeToBuffer(s.param.type, s.param.ident, buf, hgs); else - buf.writestring(s.param.ident.toString()); - buf.writestring("; "); + buf.put(s.param.ident.toString()); + buf.put("; "); s.lwr.expressionToBuffer(buf, hgs); - buf.writestring(" .. "); + buf.put(" .. "); s.upr.expressionToBuffer(buf, hgs); - buf.writeByte(')'); + buf.put(')'); buf.writenl(); } void visitForeachRange(ForeachRangeStatement s) { foreachRangeWithoutBody(s); - buf.writeByte('{'); + buf.put('{'); buf.writenl(); buf.level++; if (s._body) s._body.statementToBuffer(buf, hgs); buf.level--; - buf.writeByte('}'); + buf.put('}'); buf.writenl(); } void visitStaticForeach(StaticForeachStatement s) { - buf.writestring("static "); + buf.put("static "); if (s.sfe.aggrfe) { visitForeach(s.sfe.aggrfe); @@ -583,9 +583,9 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h void visitIf(IfStatement s) { - buf.writestring("if ("); + buf.put("if ("); printConditionAssignment(s.param, s.condition); - buf.writeByte(')'); + buf.put(')'); buf.writenl(); if (s.ifbody.isScopeStatement()) { @@ -599,14 +599,14 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h } if (s.elsebody) { - buf.writestring("else"); + buf.put("else"); if (!s.elsebody.isIfStatement()) { buf.writenl(); } else { - buf.writeByte(' '); + buf.put(' '); } if (s.elsebody.isScopeStatement() || s.elsebody.isIfStatement()) { @@ -625,52 +625,52 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h { s.condition.conditionToBuffer(buf, hgs); buf.writenl(); - buf.writeByte('{'); + buf.put('{'); buf.writenl(); buf.level++; if (s.ifbody) s.ifbody.statementToBuffer(buf, hgs); buf.level--; - buf.writeByte('}'); + buf.put('}'); buf.writenl(); if (s.elsebody) { - buf.writestring("else"); + buf.put("else"); buf.writenl(); - buf.writeByte('{'); + buf.put('{'); buf.level++; buf.writenl(); s.elsebody.statementToBuffer(buf, hgs); buf.level--; - buf.writeByte('}'); + buf.put('}'); } buf.writenl(); } void visitPragma(PragmaStatement s) { - buf.writestring("pragma ("); - buf.writestring(s.ident.toString()); + buf.put("pragma ("); + buf.put(s.ident.toString()); if (s.args && s.args.length) { - buf.writestring(", "); + buf.put(", "); argsToBuffer(s.args, buf, hgs); } - buf.writeByte(')'); + buf.put(')'); if (s._body) { buf.writenl(); - buf.writeByte('{'); + buf.put('{'); buf.writenl(); buf.level++; s._body.statementToBuffer(buf, hgs); buf.level--; - buf.writeByte('}'); + buf.put('}'); buf.writenl(); } else { - buf.writeByte(';'); + buf.put(';'); buf.writenl(); } } @@ -682,20 +682,20 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h void visitSwitch(SwitchStatement s) { - buf.writestring(s.isFinal ? "final switch (" : "switch ("); + buf.put(s.isFinal ? "final switch (" : "switch ("); printConditionAssignment(s.param, s.condition); - buf.writeByte(')'); + buf.put(')'); buf.writenl(); if (s._body) { if (!s._body.isScopeStatement()) { - buf.writeByte('{'); + buf.put('{'); buf.writenl(); buf.level++; s._body.statementToBuffer(buf, hgs); buf.level--; - buf.writeByte('}'); + buf.put('}'); buf.writenl(); } else @@ -707,109 +707,109 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h void visitCase(CaseStatement s) { - buf.writestring("case "); + buf.put("case "); s.exp.expressionToBuffer(buf, hgs); - buf.writeByte(':'); + buf.put(':'); buf.writenl(); s.statement.statementToBuffer(buf, hgs); } void visitCaseRange(CaseRangeStatement s) { - buf.writestring("case "); + buf.put("case "); s.first.expressionToBuffer(buf, hgs); - buf.writestring(": .. case "); + buf.put(": .. case "); s.last.expressionToBuffer(buf, hgs); - buf.writeByte(':'); + buf.put(':'); buf.writenl(); s.statement.statementToBuffer(buf, hgs); } void visitDefault(DefaultStatement s) { - buf.writestring("default:"); + buf.put("default:"); buf.writenl(); s.statement.statementToBuffer(buf, hgs); } void visitGotoDefault(GotoDefaultStatement s) { - buf.writestring("goto default;"); + buf.put("goto default;"); buf.writenl(); } void visitGotoCase(GotoCaseStatement s) { - buf.writestring("goto case"); + buf.put("goto case"); if (s.exp) { - buf.writeByte(' '); + buf.put(' '); s.exp.expressionToBuffer(buf, hgs); } - buf.writeByte(';'); + buf.put(';'); buf.writenl(); } void visitSwitchError(SwitchErrorStatement s) { - buf.writestring("SwitchErrorStatement::toCBuffer()"); + buf.put("SwitchErrorStatement::toCBuffer()"); buf.writenl(); } void visitReturn(ReturnStatement s) { - buf.writestring("return "); + buf.put("return "); if (s.exp) s.exp.expressionToBuffer(buf, hgs); - buf.writeByte(';'); + buf.put(';'); buf.writenl(); } void visitBreak(BreakStatement s) { - buf.writestring("break"); + buf.put("break"); if (s.ident) { - buf.writeByte(' '); - buf.writestring(s.ident.toString()); + buf.put(' '); + buf.put(s.ident.toString()); } - buf.writeByte(';'); + buf.put(';'); buf.writenl(); } void visitContinue(ContinueStatement s) { - buf.writestring("continue"); + buf.put("continue"); if (s.ident) { - buf.writeByte(' '); - buf.writestring(s.ident.toString()); + buf.put(' '); + buf.put(s.ident.toString()); } - buf.writeByte(';'); + buf.put(';'); buf.writenl(); } void visitSynchronized(SynchronizedStatement s) { - buf.writestring("synchronized"); + buf.put("synchronized"); if (s.exp) { - buf.writeByte('('); + buf.put('('); s.exp.expressionToBuffer(buf, hgs); - buf.writeByte(')'); + buf.put(')'); } if (s._body) { - buf.writeByte(' '); + buf.put(' '); s._body.statementToBuffer(buf, hgs); } } void visitWith(WithStatement s) { - buf.writestring("with ("); + buf.put("with ("); s.exp.expressionToBuffer(buf, hgs); - buf.writestring(")"); + buf.put(")"); buf.writenl(); if (s._body) s._body.statementToBuffer(buf, hgs); @@ -817,7 +817,7 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h void visitTryCatch(TryCatchStatement s) { - buf.writestring("try"); + buf.put("try"); buf.writenl(); if (s._body) { @@ -834,37 +834,37 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h } foreach (c; *s.catches) { - buf.writestring("catch"); + buf.put("catch"); if (c.type) { - buf.writeByte('('); + buf.put('('); typeToBuffer(c.type, c.ident, buf, hgs); - buf.writeByte(')'); + buf.put(')'); } buf.writenl(); - buf.writeByte('{'); + buf.put('{'); buf.writenl(); buf.level++; if (c.handler) c.handler.statementToBuffer(buf, hgs); buf.level--; - buf.writeByte('}'); + buf.put('}'); buf.writenl(); } } void visitTryFinally(TryFinallyStatement s) { - buf.writestring("try"); + buf.put("try"); buf.writenl(); - buf.writeByte('{'); + buf.put('{'); buf.writenl(); buf.level++; s._body.statementToBuffer(buf, hgs); buf.level--; - buf.writeByte('}'); + buf.put('}'); buf.writenl(); - buf.writestring("finally"); + buf.put("finally"); buf.writenl(); if (s.finalbody.isScopeStatement()) { @@ -880,17 +880,17 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h void visitScopeGuard(ScopeGuardStatement s) { - buf.writestring(Token.toString(s.tok)); - buf.writeByte(' '); + buf.put(Token.toString(s.tok)); + buf.put(' '); if (s.statement) s.statement.statementToBuffer(buf, hgs); } void visitThrow(ThrowStatement s) { - buf.writestring("throw "); + buf.put("throw "); s.exp.expressionToBuffer(buf, hgs); - buf.writeByte(';'); + buf.put(';'); buf.writenl(); } @@ -904,16 +904,16 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h void visitGoto(GotoStatement s) { - buf.writestring("goto "); - buf.writestring(s.ident.toString()); - buf.writeByte(';'); + buf.put("goto "); + buf.put(s.ident.toString()); + buf.put(';'); buf.writenl(); } void visitLabel(LabelStatement s) { - buf.writestring(s.ident.toString()); - buf.writeByte(':'); + buf.put(s.ident.toString()); + buf.put(':'); buf.writenl(); if (s.statement) s.statement.statementToBuffer(buf, hgs); @@ -921,12 +921,12 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h void visitAsm(AsmStatement s) { - buf.writestring("asm { "); + buf.put("asm { "); Token* t = s.tokens; buf.level++; while (t) { - buf.writestring(t.toString()); + buf.put(t.toString()); if (t.next && t.value != TOK.min && t.value != TOK.comma && t.next.value != TOK.comma && @@ -936,12 +936,12 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h t.next.value != TOK.rightParenthesis && t.value != TOK.dot && t.next.value != TOK.dot) { - buf.writeByte(' '); + buf.put(' '); } t = t.next; } buf.level--; - buf.writestring("; }"); + buf.put("; }"); buf.writenl(); } @@ -976,39 +976,39 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) { void visitDsymbol(Dsymbol s) { - buf.writestring(s.toChars()); + buf.put(s.toChars()); } void visitStaticAssert(StaticAssert s) { - buf.writestring(s.kind()); - buf.writeByte('('); + buf.put(s.kind()); + buf.put('('); s.exp.expressionToBuffer(buf, hgs); if (s.msgs) { foreach (m; (*s.msgs)[]) { - buf.writestring(", "); + buf.put(", "); m.expressionToBuffer(buf, hgs); } } - buf.writestring(");"); + buf.put(");"); buf.writenl(); } void visitDebugSymbol(DebugSymbol s) { - buf.writestring("debug = "); - buf.writestring(s.ident.toString()); - buf.writeByte(';'); + buf.put("debug = "); + buf.put(s.ident.toString()); + buf.put(';'); buf.writenl(); } void visitVersionSymbol(VersionSymbol s) { - buf.writestring("version = "); - buf.writestring(s.ident.toString()); - buf.writeByte(';'); + buf.put("version = "); + buf.put(s.ident.toString()); + buf.put(';'); buf.writenl(); } @@ -1017,10 +1017,10 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) if (em.type) typeToBuffer(em.type, em.ident, buf, hgs); else - buf.writestring(em.ident.toString()); + buf.put(em.ident.toString()); if (em.value) { - buf.writestring(" = "); + buf.put(" = "); em.value.expressionToBuffer(buf, hgs); } } @@ -1030,8 +1030,8 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) if (hgs.hdrgen && imp.id == Id.object) return; // object is imported by default if (imp.isstatic) - buf.writestring("static "); - buf.writestring("import "); + buf.put("static "); + buf.put("import "); if (imp.aliasId) { buf.printf("%s = ", imp.aliasId.toChars()); @@ -1039,32 +1039,32 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) foreach (const pid; imp.packages) { buf.write(pid.toString()); - buf.writeByte('.'); + buf.put('.'); } - buf.writestring(imp.id.toString()); + buf.put(imp.id.toString()); if (imp.names.length) { - buf.writestring(" : "); + buf.put(" : "); foreach (const i, const name; imp.names) { if (i) - buf.writestring(", "); + buf.put(", "); const _alias = imp.aliases[i]; if (_alias) buf.printf("%s = %s", _alias.toChars(), name.toChars()); else - buf.writestring(name.toChars()); + buf.put(name.toChars()); } } - buf.writeByte(';'); + buf.put(';'); buf.writenl(); } void visitAliasThis(AliasThis d) { - buf.writestring("alias "); - buf.writestring(d.ident.toString()); - buf.writestring(" this;\n"); + buf.put("alias "); + buf.put(d.ident.toString()); + buf.put(" this;\n"); } void visitAttribDeclaration(AttribDeclaration d) @@ -1077,32 +1077,32 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) if (!d.decl) { - buf.writeByte(';'); + buf.put(';'); buf.writenl(); return; } if (d.decl.length == 0 || (hgs.hdrgen && d.decl.length == 1 && (*d.decl)[0].isUnitTestDeclaration())) { // hack for https://issues.dlang.org/show_bug.cgi?id=8081 - if (hasSTC) buf.writeByte(' '); - buf.writestring("{}"); + if (hasSTC) buf.put(' '); + buf.put("{}"); } else if (d.decl.length == 1) { - if (hasSTC) buf.writeByte(' '); + if (hasSTC) buf.put(' '); toCBuffer((*d.decl)[0], buf, hgs); return; } else { buf.writenl(); - buf.writeByte('{'); + buf.put('{'); buf.writenl(); buf.level++; foreach (de; *d.decl) toCBuffer(de, buf, hgs); buf.level--; - buf.writeByte('}'); + buf.put('}'); } buf.writenl(); } @@ -1114,17 +1114,17 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) void visitDeprecatedDeclaration(DeprecatedDeclaration d) { - buf.writestring("deprecated("); + buf.put("deprecated("); d.msg.expressionToBuffer(buf, hgs); - buf.writestring(") "); + buf.put(") "); visitAttribDeclaration(d); } void visitLinkDeclaration(LinkDeclaration d) { - buf.writestring("extern ("); - buf.writestring(linkageToString(d.linkage)); - buf.writestring(") "); + buf.put("extern ("); + buf.put(linkageToString(d.linkage)); + buf.put(") "); visitAttribDeclaration(d); } @@ -1142,9 +1142,9 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) case CPPMANGLE.def: break; } - buf.writestring("extern (C++, "); - buf.writestring(s); - buf.writestring(") "); + buf.put("extern (C++, "); + buf.put(s); + buf.put(") "); visitAttribDeclaration(d); } @@ -1153,7 +1153,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) visibilityToBuffer(buf, d.visibility); AttribDeclaration ad = cast(AttribDeclaration)d; if (ad.decl.length <= 1) - buf.writeByte(' '); + buf.put(' '); if (ad.decl.length == 1 && (*ad.decl)[0].isVisibilityDeclaration) visitAttribDeclaration((*ad.decl)[0].isVisibilityDeclaration); else @@ -1167,25 +1167,25 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) foreach (i, exp; (*d.exps)[]) { if (i) - buf.writeByte(' '); - buf.writestring("align ("); + buf.put(' '); + buf.put("align ("); toCBuffer(exp, buf, hgs); - buf.writeByte(')'); + buf.put(')'); } if (d.decl && d.decl.length < 2) - buf.writeByte(' '); + buf.put(' '); } else - buf.writestring("align "); + buf.put("align "); visitAttribDeclaration(d.isAttribDeclaration()); } void visitAnonDeclaration(AnonDeclaration d) { - buf.writestring(d.isunion ? "union" : "struct"); + buf.put(d.isunion ? "union" : "struct"); buf.writenl(); - buf.writestring("{"); + buf.put("{"); buf.writenl(); buf.level++; if (d.decl) @@ -1194,21 +1194,21 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) toCBuffer(de, buf, hgs); } buf.level--; - buf.writestring("}"); + buf.put("}"); buf.writenl(); } void visitPragmaDeclaration(PragmaDeclaration d) { - buf.writestring("pragma ("); - buf.writestring(d.ident.toString()); + buf.put("pragma ("); + buf.put(d.ident.toString()); if (d.args && d.args.length) { - buf.writestring(", "); + buf.put(", "); argsToBuffer(d.args, buf, hgs); } - buf.writeByte(')'); + buf.put(')'); // https://issues.dlang.org/show_bug.cgi?id=14690 // Unconditionally perform a full output dump @@ -1227,7 +1227,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) if (d.decl || d.elsedecl) { buf.writenl(); - buf.writeByte('{'); + buf.put('{'); buf.writenl(); buf.level++; if (d.decl) @@ -1236,23 +1236,23 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) toCBuffer(de, buf, hgs); } buf.level--; - buf.writeByte('}'); + buf.put('}'); if (d.elsedecl) { buf.writenl(); - buf.writestring("else"); + buf.put("else"); buf.writenl(); - buf.writeByte('{'); + buf.put('{'); buf.writenl(); buf.level++; foreach (de; *d.elsedecl) toCBuffer(de, buf, hgs); buf.level--; - buf.writeByte('}'); + buf.put('}'); } } else - buf.writeByte(':'); + buf.put(':'); buf.writenl(); } @@ -1260,22 +1260,22 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) { void foreachWithoutBody(ForeachStatement s) { - buf.writestring(Token.toString(s.op)); - buf.writestring(" ("); + buf.put(Token.toString(s.op)); + buf.put(" ("); foreach (i, p; *s.parameters) { if (i) - buf.writestring(", "); + buf.put(", "); if (stcToBuffer(buf, p.storageClass)) - buf.writeByte(' '); + buf.put(' '); if (p.type) typeToBuffer(p.type, p.ident, buf, hgs); else - buf.writestring(p.ident.toString()); + buf.put(p.ident.toString()); } - buf.writestring("; "); + buf.put("; "); s.aggr.expressionToBuffer(buf, hgs); - buf.writeByte(')'); + buf.put(')'); buf.writenl(); } @@ -1283,21 +1283,21 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) { /* s.op ( param ; lwr .. upr ) */ - buf.writestring(Token.toString(s.op)); - buf.writestring(" ("); + buf.put(Token.toString(s.op)); + buf.put(" ("); if (s.param.type) typeToBuffer(s.param.type, s.param.ident, buf, hgs); else - buf.writestring(s.param.ident.toString()); - buf.writestring("; "); + buf.put(s.param.ident.toString()); + buf.put("; "); s.lwr.expressionToBuffer(buf, hgs); - buf.writestring(" .. "); + buf.put(" .. "); s.upr.expressionToBuffer(buf, hgs); - buf.writeByte(')'); + buf.put(')'); buf.writenl(); } - buf.writestring("static "); + buf.put("static "); if (s.sfe.aggrfe) { foreachWithoutBody(s.sfe.aggrfe); @@ -1307,29 +1307,29 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) assert(s.sfe.rangefe); foreachRangeWithoutBody(s.sfe.rangefe); } - buf.writeByte('{'); + buf.put('{'); buf.writenl(); buf.level++; visitAttribDeclaration(s); buf.level--; - buf.writeByte('}'); + buf.put('}'); buf.writenl(); } void visitMixinDeclaration(MixinDeclaration d) { - buf.writestring("mixin("); + buf.put("mixin("); argsToBuffer(d.exps, buf, hgs, null); - buf.writestring(");"); + buf.put(");"); buf.writenl(); } void visitUserAttributeDeclaration(UserAttributeDeclaration d) { - buf.writestring("@("); + buf.put("@("); argsToBuffer(d.atts, buf, hgs); - buf.writeByte(')'); + buf.put(')'); visitAttribDeclaration(d); } @@ -1337,9 +1337,9 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) { if (!constraint) return; - buf.writestring(" if ("); + buf.put(" if ("); constraint.expressionToBuffer(buf, hgs); - buf.writeByte(')'); + buf.put(')'); } /// Returns: whether `do` is needed to write the function body @@ -1351,13 +1351,13 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) { foreach (frequire; *f.frequires) { - buf.writestring("in"); + buf.put("in"); if (auto es = frequire.isExpStatement()) { assert(es.exp && es.exp.op == EXP.assert_); - buf.writestring(" ("); + buf.put(" ("); (cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs); - buf.writeByte(')'); + buf.put(')'); buf.writenl(); requireDo = false; } @@ -1374,18 +1374,18 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) { foreach (fensure; *f.fensures) { - buf.writestring("out"); + buf.put("out"); if (auto es = fensure.ensure.isExpStatement()) { assert(es.exp && es.exp.op == EXP.assert_); - buf.writestring(" ("); + buf.put(" ("); if (fensure.id) { - buf.writestring(fensure.id.toString()); + buf.put(fensure.id.toString()); } - buf.writestring("; "); + buf.put("; "); (cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs); - buf.writeByte(')'); + buf.put(')'); buf.writenl(); requireDo = false; } @@ -1393,9 +1393,9 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) { if (fensure.id) { - buf.writeByte('('); - buf.writestring(fensure.id.toString()); - buf.writeByte(')'); + buf.put('('); + buf.put(fensure.id.toString()); + buf.put(')'); } buf.writenl(); fensure.ensure.statementToBuffer(buf, hgs); @@ -1415,7 +1415,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) buf.writenl(); contractsToBuffer(f); } - buf.writeByte(';'); + buf.put(';'); buf.writenl(); return; } @@ -1440,15 +1440,15 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) if (requireDo) { - buf.writestring("do"); + buf.put("do"); buf.writenl(); } - buf.writeByte('{'); + buf.put('{'); buf.writenl(); buf.level++; f.fbody.statementToBuffer(buf, hgs); buf.level--; - buf.writeByte('}'); + buf.put('}'); buf.writenl(); hgs.tpltMember = savetlpt; hgs.autoMember = saveauto; @@ -1459,11 +1459,11 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) if (!d || !d.baseclasses.length) return; if (!d.isAnonymous()) - buf.writestring(" : "); + buf.put(" : "); foreach (i, b; *d.baseclasses) { if (i) - buf.writestring(", "); + buf.put(", "); typeToBuffer(b.type, null, buf, hgs); } } @@ -1479,7 +1479,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) { assert(fd.type); if (stcToBuffer(buf, fd.storage_class)) - buf.writeByte(' '); + buf.put(' '); functionToBufferFull(cast(TypeFunction)fd.type, buf, d.ident, hgs, d); visitTemplateConstraint(d.constraint); hgs.tpltMember++; @@ -1489,28 +1489,28 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) } if (AggregateDeclaration ad = onemember.isAggregateDeclaration()) { - buf.writestring(ad.kind()); - buf.writeByte(' '); - buf.writestring(ad.ident.toString()); - buf.writeByte('('); + buf.put(ad.kind()); + buf.put(' '); + buf.put(ad.ident.toString()); + buf.put('('); visitTemplateParameters(hgs.ddoc ? d.origParameters : d.parameters, buf, hgs); - buf.writeByte(')'); + buf.put(')'); visitTemplateConstraint(d.constraint); visitBaseClasses(ad.isClassDeclaration()); hgs.tpltMember++; if (ad.members) { buf.writenl(); - buf.writeByte('{'); + buf.put('{'); buf.writenl(); buf.level++; foreach (s; *ad.members) toCBuffer(s, buf, hgs); buf.level--; - buf.writeByte('}'); + buf.put('}'); } else - buf.writeByte(';'); + buf.put(';'); buf.writenl(); hgs.tpltMember--; return true; @@ -1520,24 +1520,24 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) if (d.constraint) return false; if (stcToBuffer(buf, vd.storage_class)) - buf.writeByte(' '); + buf.put(' '); if (vd.type) typeToBuffer(vd.type, vd.ident, buf, hgs); else - buf.writestring(vd.ident.toString()); - buf.writeByte('('); + buf.put(vd.ident.toString()); + buf.put('('); visitTemplateParameters(hgs.ddoc ? d.origParameters : d.parameters, buf, hgs); - buf.writeByte(')'); + buf.put(')'); if (vd._init) { - buf.writestring(" = "); + buf.put(" = "); ExpInitializer ie = vd._init.isExpInitializer(); if (ie && (ie.exp.op == EXP.construct || ie.exp.op == EXP.blit)) (cast(AssignExp)ie.exp).e2.expressionToBuffer(buf, hgs); else vd._init.initializerToBuffer(buf, hgs); } - buf.writeByte(';'); + buf.put(';'); buf.writenl(); return true; } @@ -1550,31 +1550,31 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) { // Should handle template functions for doc generation if (onemember && onemember.isFuncDeclaration()) - buf.writestring("foo "); + buf.put("foo "); } if ((hgs.hdrgen || hgs.fullDump) && visitEponymousMember(d)) return; if (hgs.ddoc) - buf.writestring(d.kind()); + buf.put(d.kind()); else - buf.writestring("template"); - buf.writeByte(' '); - buf.writestring(d.ident.toString()); - buf.writeByte('('); + buf.put("template"); + buf.put(' '); + buf.put(d.ident.toString()); + buf.put('('); visitTemplateParameters(hgs.ddoc ? d.origParameters : d.parameters, buf, hgs); - buf.writeByte(')'); + buf.put(')'); visitTemplateConstraint(d.constraint); if (hgs.hdrgen || hgs.fullDump) { hgs.tpltMember++; buf.writenl(); - buf.writeByte('{'); + buf.put('{'); buf.writenl(); buf.level++; foreach (s; *d.members) toCBuffer(s, buf, hgs); buf.level--; - buf.writeByte('}'); + buf.put('}'); buf.writenl(); hgs.tpltMember--; } @@ -1582,7 +1582,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) void visitTemplateInstance(TemplateInstance ti) { - buf.writestring(ti.name.toChars()); + buf.put(ti.name.toChars()); tiargsToBuffer(ti, buf, hgs); if (hgs.fullDump) @@ -1594,15 +1594,15 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) void visitTemplateMixin(TemplateMixin tm) { - buf.writestring("mixin "); + buf.put("mixin "); typeToBuffer(tm.tqual, null, buf, hgs); tiargsToBuffer(tm, buf, hgs); if (tm.ident && memcmp(tm.ident.toString().ptr, cast(const(char)*) "__mixin", 7) != 0) { - buf.writeByte(' '); - buf.writestring(tm.ident.toString()); + buf.put(' '); + buf.put(tm.ident.toString()); } - buf.writeByte(';'); + buf.put(';'); buf.writenl(); if (hgs.fullDump) dumpTemplateInstance(tm, buf, hgs); @@ -1613,24 +1613,24 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) auto oldInEnumDecl = hgs.inEnumDecl; scope(exit) hgs.inEnumDecl = oldInEnumDecl; hgs.inEnumDecl = d; - buf.writestring("enum "); + buf.put("enum "); if (d.ident) { - buf.writestring(d.ident.toString()); + buf.put(d.ident.toString()); } if (d.memtype) { - buf.writestring(" : "); + buf.put(" : "); typeToBuffer(d.memtype, null, buf, hgs); } if (!d.members) { - buf.writeByte(';'); + buf.put(';'); buf.writenl(); return; } buf.writenl(); - buf.writeByte('{'); + buf.put('{'); buf.writenl(); buf.level++; foreach (em; *d.members) @@ -1638,11 +1638,11 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) if (!em) continue; toCBuffer(em, buf, hgs); - buf.writeByte(','); + buf.put(','); buf.writenl(); } buf.level--; - buf.writeByte('}'); + buf.put('}'); buf.writenl(); if (!hgs.importcHdr || !d.ident) @@ -1657,48 +1657,48 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) { if (!em) continue; - buf.writestring("alias "); - buf.writestring(em.ident.toString); - buf.writestring(" = "); - buf.writestring(d.ident.toString); - buf.writeByte('.'); - buf.writestring(em.ident.toString); - buf.writeByte(';'); + buf.put("alias "); + buf.put(em.ident.toString); + buf.put(" = "); + buf.put(d.ident.toString); + buf.put('.'); + buf.put(em.ident.toString); + buf.put(';'); buf.writenl(); } } void visitNspace(Nspace d) { - buf.writestring("extern (C++, "); - buf.writestring(d.ident.toString()); - buf.writeByte(')'); + buf.put("extern (C++, "); + buf.put(d.ident.toString()); + buf.put(')'); buf.writenl(); - buf.writeByte('{'); + buf.put('{'); buf.writenl(); buf.level++; foreach (s; *d.members) toCBuffer(s, buf, hgs); buf.level--; - buf.writeByte('}'); + buf.put('}'); buf.writenl(); } void visitStructDeclaration(StructDeclaration d) { //printf("visitStructDeclaration() %s\n", d.ident.toChars()); - buf.writestring(d.kind()); - buf.writeByte(' '); + buf.put(d.kind()); + buf.put(' '); if (!d.isAnonymous()) - buf.writestring(d.toChars()); + buf.put(d.toChars()); if (!d.members) { - buf.writeByte(';'); + buf.put(';'); buf.writenl(); return; } buf.writenl(); - buf.writeByte('{'); + buf.put('{'); buf.writenl(); buf.level++; hgs.insideAggregate++; @@ -1706,7 +1706,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) toCBuffer(s, buf, hgs); hgs.insideAggregate--; buf.level--; - buf.writeByte('}'); + buf.put('}'); buf.writenl(); } @@ -1714,15 +1714,15 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) { if (!d.isAnonymous()) { - buf.writestring(d.kind()); - buf.writeByte(' '); - buf.writestring(d.ident.toString()); + buf.put(d.kind()); + buf.put(' '); + buf.put(d.ident.toString()); } visitBaseClasses(d); if (d.members) { buf.writenl(); - buf.writeByte('{'); + buf.put('{'); buf.writenl(); buf.level++; hgs.insideAggregate++; @@ -1730,10 +1730,10 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) toCBuffer(s, buf, hgs); hgs.insideAggregate--; buf.level--; - buf.writeByte('}'); + buf.put('}'); } else - buf.writeByte(';'); + buf.put(';'); buf.writenl(); } @@ -1743,13 +1743,13 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) return; if (d.hidden) return; - buf.writestring("alias "); + buf.put("alias "); if (d.aliassym) { - buf.writestring(d.ident.toString()); - buf.writestring(" = "); + buf.put(d.ident.toString()); + buf.put(" = "); if (stcToBuffer(buf, d.storage_class)) - buf.writeByte(' '); + buf.put(' '); /* https://issues.dlang.org/show_bug.cgi?id=23223 https://issues.dlang.org/show_bug.cgi?id=23222 @@ -1758,7 +1758,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) */ if (d.aliassym.isModule()) { - buf.writestring(d.aliassym.ident.toString()); + buf.put(d.aliassym.ident.toString()); } else { @@ -1768,34 +1768,34 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) else if (d.type.ty == Tfunction) { if (stcToBuffer(buf, d.storage_class)) - buf.writeByte(' '); + buf.put(' '); typeToBuffer(d.type, d.ident, buf, hgs); } else if (d.ident) { hgs.declstring = (d.ident == Id.string || d.ident == Id.wstring || d.ident == Id.dstring); - buf.writestring(d.ident.toString()); - buf.writestring(" = "); + buf.put(d.ident.toString()); + buf.put(" = "); if (stcToBuffer(buf, d.storage_class)) - buf.writeByte(' '); + buf.put(' '); hgs.inCAlias = hgs.importcHdr; typeToBuffer(d.type, null, buf, hgs); hgs.inCAlias = false; hgs.declstring = false; } - buf.writeByte(';'); + buf.put(';'); buf.writenl(); } void visitAliasAssign(AliasAssign d) { - buf.writestring(d.ident.toString()); - buf.writestring(" = "); + buf.put(d.ident.toString()); + buf.put(" = "); if (d.aliassym) toCBuffer(d.aliassym, buf, hgs); else // d.type typeToBuffer(d.type, null, buf, hgs); - buf.writeByte(';'); + buf.put(';'); buf.writenl(); } @@ -1804,7 +1804,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) if (d.storage_class & STC.local) return; visitVarDecl(d, false, buf, hgs); - buf.writeByte(';'); + buf.put(';'); buf.writenl(); } @@ -1814,8 +1814,8 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) // https://issues.dlang.org/show_bug.cgi?id=24891 // return/scope storage classes are printed as part of function type - if (stcToBuffer(buf, f.storage_class & ~(STC.scope_ | STC.return_ | STC.returnScope))) - buf.writeByte(' '); + if (stcToBuffer(buf, f.storage_class & ~(STC.scope_ | STC.return_ | STC.returnScope | STC.returnRef))) + buf.put(' '); typeToBuffer(f.type, f.ident, buf, hgs); auto tf = f.type.isTypeFunction(); @@ -1840,7 +1840,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) buf.writenl(); contractsToBuffer(f); } - buf.writeByte(';'); + buf.put(';'); buf.writenl(); } else @@ -1854,13 +1854,13 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) { if (f.type.ty == Terror) { - buf.writestring("__error"); + buf.put("__error"); return; } if (f.tok != TOK.reserved && !hgs.errorMsg) { - buf.writestring(f.kind()); - buf.writeByte(' '); + buf.put(f.kind()); + buf.put(' '); } TypeFunction tf = cast(TypeFunction)f.type; @@ -1871,8 +1871,8 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) // https://issues.dlang.org/show_bug.cgi?id=20074 void printAttribute(string str) { - buf.writeByte(' '); - buf.writestring(str); + buf.put(' '); + buf.put(str); } if (!hgs.errorMsg) @@ -1889,7 +1889,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) ReturnStatement rs = s1 ? s1.endsWithReturnStatement() : null; if (rs && rs.exp) { - buf.writestring(" => "); + buf.put(" => "); rs.exp.expressionToBuffer(buf, hgs); } else @@ -1903,29 +1903,29 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) void visitPostBlitDeclaration(PostBlitDeclaration d) { if (stcToBuffer(buf, d.storage_class)) - buf.writeByte(' '); - buf.writestring("this(this)"); + buf.put(' '); + buf.put("this(this)"); bodyToBuffer(d); } void visitDtorDeclaration(DtorDeclaration d) { if (stcToBuffer(buf, d.storage_class)) - buf.writeByte(' '); - buf.writestring("~this()"); + buf.put(' '); + buf.put("~this()"); bodyToBuffer(d); } void visitStaticCtorDeclaration(StaticCtorDeclaration d) { if (stcToBuffer(buf, d.storage_class & ~STC.static_)) - buf.writeByte(' '); + buf.put(' '); if (d.isSharedStaticCtorDeclaration()) - buf.writestring("shared "); - buf.writestring("static this()"); + buf.put("shared "); + buf.put("static this()"); if (hgs.hdrgen && !hgs.tpltMember) { - buf.writeByte(';'); + buf.put(';'); buf.writenl(); } else @@ -1935,13 +1935,13 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) void visitStaticDtorDeclaration(StaticDtorDeclaration d) { if (stcToBuffer(buf, d.storage_class & ~STC.static_)) - buf.writeByte(' '); + buf.put(' '); if (d.isSharedStaticDtorDeclaration()) - buf.writestring("shared "); - buf.writestring("static ~this()"); + buf.put("shared "); + buf.put("static ~this()"); if (hgs.hdrgen && !hgs.tpltMember) { - buf.writeByte(';'); + buf.put(';'); buf.writenl(); } else @@ -1953,14 +1953,14 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) if (hgs.hdrgen) return; if (stcToBuffer(buf, d.storage_class)) - buf.writeByte(' '); - buf.writestring("invariant"); + buf.put(' '); + buf.put("invariant"); auto es = d.fbody.isExpStatement(); if (es && es.exp && es.exp.op == EXP.assert_) { - buf.writestring(" ("); + buf.put(" ("); (cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs); - buf.writestring(");"); + buf.put(");"); buf.writenl(); } else @@ -1974,28 +1974,28 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) if (hgs.hdrgen) return; if (stcToBuffer(buf, d.storage_class)) - buf.writeByte(' '); - buf.writestring("unittest"); + buf.put(' '); + buf.put("unittest"); bodyToBuffer(d); } void visitBitFieldDeclaration(BitFieldDeclaration d) { if (stcToBuffer(buf, d.storage_class)) - buf.writeByte(' '); + buf.put(' '); Identifier id = d.isAnonymous() ? null : d.ident; typeToBuffer(d.type, id, buf, hgs); - buf.writestring(" : "); + buf.put(" : "); d.width.expressionToBuffer(buf, hgs); - buf.writeByte(';'); + buf.put(';'); buf.writenl(); } void visitNewDeclaration(NewDeclaration d) { if (stcToBuffer(buf, d.storage_class & ~STC.static_)) - buf.writeByte(' '); - buf.writestring("new();"); + buf.put(' '); + buf.put("new();"); } void visitModule(Module m) @@ -2062,15 +2062,15 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) public void toCharsMaybeConstraints(const TemplateDeclaration td, ref OutBuffer buf, ref HdrGenState hgs) { - buf.writestring(td.ident == Id.ctor ? "this" : td.ident.toString()); - buf.writeByte('('); + buf.put(td.ident == Id.ctor ? "this" : td.ident.toString()); + buf.put('('); foreach (i, const tp; *td.parameters) { if (i) - buf.writestring(", "); + buf.put(", "); toCBuffer(tp, buf, hgs); } - buf.writeByte(')'); + buf.put(')'); if (hgs.showOneMember && td.onemember) { @@ -2079,10 +2079,10 @@ void toCharsMaybeConstraints(const TemplateDeclaration td, ref OutBuffer buf, re if (TypeFunction tf = cast(TypeFunction)fd.type.isTypeFunction()) { // !! Casted away const - buf.writestring(parametersTypeToChars(tf.parameterList)); + buf.put(parametersTypeToChars(tf.parameterList)); if (tf.mod) { - buf.writeByte(' '); + buf.put(' '); buf.MODtoBuffer(tf.mod); } } @@ -2092,9 +2092,9 @@ void toCharsMaybeConstraints(const TemplateDeclaration td, ref OutBuffer buf, re if (!hgs.skipConstraints && td.constraint) { - buf.writestring(" if ("); + buf.put(" if ("); toCBuffer(td.constraint, buf, hgs); - buf.writeByte(')'); + buf.put(')'); } } @@ -2109,7 +2109,7 @@ private void visitTemplateParameters(TemplateParameters* parameters, ref OutBuff foreach (i, p; *parameters) { if (i) - buf.writestring(", "); + buf.put(", "); toCBuffer(p, buf, hgs); } } @@ -2137,12 +2137,12 @@ private void visitVarDecl(VarDeclaration v, bool anywritten, ref OutBuffer buf, const commentIt = hgs.importcHdr && isSpecialCName(v.ident); if (commentIt) - buf.writestring("/+"); + buf.put("/+"); if (anywritten) { - buf.writestring(", "); - buf.writestring(v.ident.toString()); + buf.put(", "); + buf.put(v.ident.toString()); } else { @@ -2153,26 +2153,26 @@ private void visitVarDecl(VarDeclaration v, bool anywritten, ref OutBuffer buf, if (useTypeof) stc &= ~STC.auto_; if (stcToBuffer(buf, stc)) - buf.writeByte(' '); + buf.put(' '); if (v.type) typeToBuffer(v.type, v.ident, buf, hgs); else if (useTypeof) { - buf.writestring("typeof("); + buf.put("typeof("); vinit(v); - buf.writestring(") "); - buf.writestring(v.ident.toString()); + buf.put(") "); + buf.put(v.ident.toString()); } else - buf.writestring(v.ident.toString()); + buf.put(v.ident.toString()); } if (v._init && !isextern) { - buf.writestring(" = "); + buf.put(" = "); vinit(v); } if (commentIt) - buf.writestring("+/"); + buf.put("+/"); } /************************************* @@ -2204,7 +2204,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt { void visit(Expression e) { - buf.writestring(EXPtoString(e.op)); + buf.put(EXPtoString(e.op)); } void visitInteger(IntegerExp e) @@ -2248,20 +2248,20 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt break; } case Tint8: - buf.writestring("cast(byte)"); + buf.put("cast(byte)"); goto L2; case Tint16: - buf.writestring("cast(short)"); + buf.put("cast(short)"); goto L2; case Tint32: L2: buf.printf("%d", cast(int)v); break; case Tuns8: - buf.writestring("cast(ubyte)"); + buf.put("cast(ubyte)"); goto case Tuns32; case Tuns16: - buf.writestring("cast(ushort)"); + buf.put("cast(ushort)"); goto case Tuns32; case Tuns32: buf.printf("%uu", cast(uint)v); @@ -2272,7 +2272,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt // https://issues.dlang.org/show_bug.cgi?id=23173 // This is a special case because - is not part of the // integer literal and 9223372036854775808L overflows a long - buf.writestring("cast(long)-9223372036854775808"); + buf.put("cast(long)-9223372036854775808"); } else { @@ -2283,20 +2283,20 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt buf.printf("%lluLU", v); break; case Tbool: - buf.writestring(v ? "true" : "false"); + buf.put(v ? "true" : "false"); break; case Tpointer: - buf.writestring("cast("); + buf.put("cast("); HdrGenState hgs2; // should re-examine need for new hgs hgs2.fullQual = (t.ty == Tclass && !t.mod); toCBuffer(t, buf, null, hgs2); - buf.writestring(")cast(size_t)"); + buf.put(")cast(size_t)"); goto case Tuns64; case Tvoid: - buf.writestring("cast(void)0"); + buf.put("cast(void)0"); break; default: @@ -2315,12 +2315,12 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt void visitError(ErrorExp e) { - buf.writestring("__error"); + buf.put("__error"); } void visitVoidInit(VoidInitExp e) { - buf.writestring("void"); + buf.put("void"); } void floatToBuffer(Type type, real_t value) @@ -2338,19 +2338,19 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt /* Print as: * (re+imi) */ - buf.writeByte('('); + buf.put('('); floatToBuffer(e.type, creall(e.value)); - buf.writeByte('+'); + buf.put('+'); floatToBuffer(e.type, cimagl(e.value)); - buf.writestring("i)"); + buf.put("i)"); } void visitIdentifier(IdentifierExp e) { if (hgs.hdrgen || hgs.ddoc) - buf.writestring(e.ident.toHChars2()); + buf.put(e.ident.toHChars2()); else - buf.writestring(e.ident.toString()); + buf.put(e.ident.toString()); } void visitDsymbol(Dsymbol s) @@ -2359,9 +2359,9 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt // This condition is a bit kludge, and can be cleaned up if the // mutual dependency `AST.toChars <> hdrgen.d` gets refactored if (hgs.vcg_ast && s.ident && !s.isTemplateInstance() && !s.isTemplateDeclaration()) - buf.writestring(s.ident.toChars()); + buf.put(s.ident.toChars()); else - buf.writestring(s.toChars()); + buf.put(s.toChars()); } void visitDsymbolExp(DsymbolExp e) @@ -2371,33 +2371,33 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt void visitThis(ThisExp e) { - buf.writestring("this"); + buf.put("this"); } void visitSuper(SuperExp e) { - buf.writestring("super"); + buf.put("super"); } void visitNull(NullExp e) { - buf.writestring("null"); + buf.put("null"); } void visitString(StringExp e) { if (e.hexString || e.sz == 8) { - buf.writeByte('x'); - buf.writeByte('"'); + buf.put('x'); + buf.put('"'); foreach (i; 0 .. e.len) buf.printf("%0*llX", e.sz, e.getIndex(i)); - buf.writeByte('"'); + buf.put('"'); if (e.postfix) - buf.writeByte(e.postfix); + buf.put(e.postfix); return; } - buf.writeByte('"'); + buf.put('"'); const o = buf.length; foreach (i; 0 .. e.len) { @@ -2405,15 +2405,15 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt } if (hgs.ddoc) escapeDdocString(buf, o); - buf.writeByte('"'); + buf.put('"'); if (e.postfix) - buf.writeByte(e.postfix); + buf.put(e.postfix); } void visitInterpolation(InterpExp e) { - buf.writeByte('i'); - buf.writeByte('"'); + buf.put('i'); + buf.put('"'); const o = buf.length; foreach (idx, str; e.interpolatedSet.parts) @@ -2425,54 +2425,59 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt } else { - buf.writeByte('$'); - buf.writeByte('('); + buf.put('$'); + buf.put('('); foreach(ch; str) - buf.writeByte(ch); - buf.writeByte(')'); + buf.put(ch); + buf.put(')'); } } if (hgs.ddoc) escapeDdocString(buf, o); - buf.writeByte('"'); + buf.put('"'); if (e.postfix) - buf.writeByte(e.postfix); + buf.put(e.postfix); } void visitArrayLiteral(ArrayLiteralExp e) { - buf.writeByte('['); + buf.put('['); argsToBuffer(e.elements, buf, hgs, e.basis); - buf.writeByte(']'); + buf.put(']'); } void visitAssocArrayLiteral(AssocArrayLiteralExp e) { - buf.writeByte('['); + if (hgs.vcg_ast && e.lowering) + { + expToBuffer(e.lowering, PREC.assign, buf, hgs); + return; + } + buf.put('['); foreach (i, key; *e.keys) { if (i) - buf.writestring(", "); + buf.put(", "); expToBuffer(key, PREC.assign, buf, hgs); - buf.writeByte(':'); + buf.put(':'); auto value = (*e.values)[i]; expToBuffer(value, PREC.assign, buf, hgs); } - buf.writeByte(']'); + buf.put(']'); } void visitStructLiteral(StructLiteralExp e) { - buf.writestring(e.sd.toChars()); - buf.writeByte('('); + buf.put(e.sd.toChars()); + buf.put('('); // CTFE can generate struct literals that contain an AddrExp pointing // to themselves, need to avoid infinite recursion: // struct S { this(int){ this.s = &this; } S* s; } // const foo = new S(0); if (e.stageflags & StructLiteralExp.StageFlags.toCBuffer) - buf.writestring(""); + buf.put(""); else { const old = e.stageflags; @@ -2480,14 +2485,14 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt argsToBuffer(e.elements, buf, hgs); e.stageflags = old; } - buf.writeByte(')'); + buf.put(')'); } void visitCompoundLiteral(CompoundLiteralExp e) { - buf.writeByte('('); + buf.put('('); typeToBuffer(e.type, null, buf, hgs); - buf.writeByte(')'); + buf.put(')'); e.initializer.initializerToBuffer(buf, hgs); } @@ -2506,44 +2511,49 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt { // fixes bug 6491 if (auto m = e.sds.isModule()) - buf.writestring(m.md.toChars()); + buf.put(m.md.toChars()); else - buf.writestring(e.sds.toChars()); + buf.put(e.sds.toChars()); } else { - buf.writestring(e.sds.kind()); - buf.writeByte(' '); - buf.writestring(e.sds.toChars()); + buf.put(e.sds.kind()); + buf.put(' '); + buf.put(e.sds.toChars()); } } void visitTemplate(TemplateExp e) { - buf.writestring(e.td.toChars()); + buf.put(e.td.toChars()); } void visitNew(NewExp e) { + if (hgs.vcg_ast && e.lowering) + { + expToBuffer(e.lowering, PREC.primary, buf, hgs); + return; + } if (e.thisexp) { expToBuffer(e.thisexp, PREC.primary, buf, hgs); - buf.writeByte('.'); + buf.put('.'); } - buf.writestring("new "); + buf.put("new "); if (e.placement) { - buf.writeByte('('); + buf.put('('); expToBuffer(e.placement, PREC.assign, buf, hgs); - buf.writeByte(')'); - buf.writeByte(' '); + buf.put(')'); + buf.put(' '); } typeToBuffer(e.newtype, null, buf, hgs); if (e.arguments && e.arguments.length) { - buf.writeByte('('); + buf.put('('); argsToBuffer(e.arguments, buf, hgs, null, e.names); - buf.writeByte(')'); + buf.put(')'); } } @@ -2552,22 +2562,22 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt if (e.thisexp) { expToBuffer(e.thisexp, PREC.primary, buf, hgs); - buf.writeByte('.'); + buf.put('.'); } - buf.writestring("new"); + buf.put("new"); if (e.placement) { - buf.writeByte(' '); - buf.writeByte('('); + buf.put(' '); + buf.put('('); expToBuffer(e.placement, PREC.assign, buf, hgs); - buf.writeByte(')'); + buf.put(')'); } - buf.writestring(" class "); + buf.put(" class "); if (e.arguments && e.arguments.length) { - buf.writeByte('('); + buf.put('('); argsToBuffer(e.arguments, buf, hgs); - buf.writeByte(')'); + buf.put(')'); } if (e.cd) e.cd.dsymbolToBuffer(buf, hgs); @@ -2578,7 +2588,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt if (e.offset) buf.printf("(& %s + %llu)", e.var.toChars(), e.offset); else if (e.var.isTypeInfoDeclaration()) - buf.writestring(e.var.toChars()); + buf.put(e.var.toChars()); else buf.printf("& %s", e.var.toChars()); } @@ -2590,31 +2600,31 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt void visitOver(OverExp e) { - buf.writestring(e.vars.ident.toString()); + buf.put(e.vars.ident.toString()); } void visitTuple(TupleExp e) { if (e.e0) { - buf.writeByte('('); + buf.put('('); e.e0.expressionPrettyPrint(buf, hgs); - buf.writestring(", AliasSeq!("); + buf.put(", AliasSeq!("); argsToBuffer(e.exps, buf, hgs); - buf.writestring("))"); + buf.put("))"); } else { - buf.writestring("AliasSeq!("); + buf.put("AliasSeq!("); argsToBuffer(e.exps, buf, hgs); - buf.writeByte(')'); + buf.put(')'); } } void visitFunc(FuncExp e) { e.fd.dsymbolToBuffer(buf, hgs); - //buf.writestring(e.fd.toChars()); + //buf.put(e.fd.toChars()); } void visitDeclaration(DeclarationExp e) @@ -2631,12 +2641,12 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt // - Avoid printing newline. // - Intentionally use the format (Type var;) // which isn't correct as regular D code. - buf.writeByte('('); + buf.put('('); visitVarDecl(var, false, buf, hgs); - buf.writeByte(';'); - buf.writeByte(')'); + buf.put(';'); + buf.put(')'); } else e.declaration.dsymbolToBuffer(buf, hgs); } @@ -2644,62 +2654,62 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt void visitTypeid(TypeidExp e) { - buf.writestring("typeid("); + buf.put("typeid("); objectToBuffer(e.obj, buf, hgs); - buf.writeByte(')'); + buf.put(')'); } void visitTraits(TraitsExp e) { - buf.writestring("__traits("); + buf.put("__traits("); if (e.ident) - buf.writestring(e.ident.toString()); + buf.put(e.ident.toString()); if (e.args) { foreach (arg; *e.args) { - buf.writestring(", "); + buf.put(", "); objectToBuffer(arg, buf, hgs); } } - buf.writeByte(')'); + buf.put(')'); } void visitHalt(HaltExp e) { - buf.writestring("halt"); + buf.put("halt"); } void visitIs(IsExp e) { - buf.writestring("is("); + buf.put("is("); typeToBuffer(e.targ, e.id, buf, hgs); if (e.tok2 != TOK.reserved) { - buf.writeByte(' '); - buf.writestring(Token.toString(e.tok)); - buf.writeByte(' '); - buf.writestring(Token.toString(e.tok2)); + buf.put(' '); + buf.put(Token.toString(e.tok)); + buf.put(' '); + buf.put(Token.toString(e.tok2)); } else if (e.tspec) { if (e.tok == TOK.colon) - buf.writestring(" : "); + buf.put(" : "); else - buf.writestring(" == "); + buf.put(" == "); typeToBuffer(e.tspec, null, buf, hgs); } if (e.parameters && e.parameters.length) { - buf.writestring(", "); + buf.put(", "); visitTemplateParameters(e.parameters, buf, hgs); } - buf.writeByte(')'); + buf.put(')'); } void visitUna(UnaExp e) { - buf.writestring(EXPtoString(e.op)); + buf.put(EXPtoString(e.op)); expToBuffer(e.e1, precedence[e.op], buf, hgs); } @@ -2716,9 +2726,9 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt void visitBin(BinExp e) { expToBuffer(e.e1, precedence[e.op], buf, hgs); - buf.writeByte(' '); - buf.writestring(EXPtoString(e.op)); - buf.writeByte(' '); + buf.put(' '); + buf.put(EXPtoString(e.op)); + buf.put(' '); expToBuffer(e.e2, cast(PREC)(precedence[e.op] + 1), buf, hgs); } @@ -2765,7 +2775,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt if (commaExtract) { - expToBuffer(commaExtract, precedence[exp.op], buf, hgs); + expToBuffer(commaExtract, expPrecedence(hgs, exp), buf, hgs); return; } } @@ -2777,80 +2787,80 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt void visitMixin(MixinExp e) { - buf.writestring("mixin("); + buf.put("mixin("); argsToBuffer(e.exps, buf, hgs, null); - buf.writeByte(')'); + buf.put(')'); } void visitImport(ImportExp e) { - buf.writestring("import("); + buf.put("import("); expToBuffer(e.e1, PREC.assign, buf, hgs); - buf.writeByte(')'); + buf.put(')'); } void visitAssert(AssertExp e) { - buf.writestring("assert("); + buf.put("assert("); expToBuffer(e.e1, PREC.assign, buf, hgs); if (e.msg) { - buf.writestring(", "); + buf.put(", "); expToBuffer(e.msg, PREC.assign, buf, hgs); } - buf.writeByte(')'); + buf.put(')'); } void visitThrow(ThrowExp e) { - buf.writestring("throw "); + buf.put("throw "); expToBuffer(e.e1, PREC.unary, buf, hgs); } void visitDotId(DotIdExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); - buf.writeByte('.'); - buf.writestring(e.ident.toString()); + buf.put('.'); + buf.put(e.ident.toString()); } void visitDotTemplate(DotTemplateExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); - buf.writeByte('.'); - buf.writestring(e.td.toChars()); + buf.put('.'); + buf.put(e.td.toChars()); } void visitDotVar(DotVarExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); - buf.writeByte('.'); + buf.put('.'); visitDsymbol(e.var); } void visitDotTemplateInstance(DotTemplateInstanceExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); - buf.writeByte('.'); + buf.put('.'); e.ti.dsymbolToBuffer(buf, hgs); } void visitDelegate(DelegateExp e) { - buf.writeByte('&'); + buf.put('&'); if (!e.func.isNested() || e.func.needThis()) { expToBuffer(e.e1, PREC.primary, buf, hgs); - buf.writeByte('.'); + buf.put('.'); } - buf.writestring(e.func.toChars()); + buf.put(e.func.toChars()); } void visitDotType(DotTypeExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); - buf.writeByte('.'); - buf.writestring(e.sym.toChars()); + buf.put('.'); + buf.put(e.sym.toChars()); } void visitCall(CallExp e) @@ -2865,163 +2875,199 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt e.e1.expressionPrettyPrint(buf, hgs); } else + { + if (!hgs.vcg_ast && e.loweredFrom) + { + // restore original syntax for expressions lowered to calls + expressionToBuffer(e.loweredFrom, buf, hgs); + return; + } expToBuffer(e.e1, precedence[e.op], buf, hgs); - buf.writeByte('('); + } + buf.put('('); argsToBuffer(e.arguments, buf, hgs, null, e.names); - buf.writeByte(')'); + buf.put(')'); + } + + void visitNot(NotExp e) + { + if (!hgs.vcg_ast && e.loweredFrom) + return expressionToBuffer(e.loweredFrom, buf, hgs); + return visitUna(e); } void visitPtr(PtrExp e) { - buf.writeByte('*'); + buf.put('*'); expToBuffer(e.e1, precedence[e.op], buf, hgs); } void visitDelete(DeleteExp e) { - buf.writestring("delete "); + buf.put("delete "); expToBuffer(e.e1, precedence[e.op], buf, hgs); } void visitCast(CastExp e) { - buf.writestring("cast("); + buf.put("cast("); if (e.to) typeToBuffer(e.to, null, buf, hgs); else { MODtoBuffer(buf, e.mod); } - buf.writeByte(')'); + buf.put(')'); expToBuffer(e.e1, precedence[e.op], buf, hgs); } void visitVector(VectorExp e) { - buf.writestring("cast("); + buf.put("cast("); typeToBuffer(e.to, null, buf, hgs); - buf.writeByte(')'); + buf.put(')'); expToBuffer(e.e1, precedence[e.op], buf, hgs); } void visitVectorArray(VectorArrayExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); - buf.writestring(".array"); + buf.put(".array"); } void visitSlice(SliceExp e) { expToBuffer(e.e1, precedence[e.op], buf, hgs); - buf.writeByte('['); + buf.put('['); if (e.upr || e.lwr) { if (e.lwr) sizeToBuffer(e.lwr, buf, hgs); else - buf.writeByte('0'); - buf.writestring(".."); + buf.put('0'); + buf.put(".."); if (e.upr) sizeToBuffer(e.upr, buf, hgs); else - buf.writeByte('$'); + buf.put('$'); } - buf.writeByte(']'); + buf.put(']'); } void visitArrayLength(ArrayLengthExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); - buf.writestring(".length"); + buf.put(".length"); } void visitInterval(IntervalExp e) { expToBuffer(e.lwr, PREC.assign, buf, hgs); - buf.writestring(".."); + buf.put(".."); expToBuffer(e.upr, PREC.assign, buf, hgs); } void visitDelegatePtr(DelegatePtrExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); - buf.writestring(".ptr"); + buf.put(".ptr"); } void visitDelegateFuncptr(DelegateFuncptrExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); - buf.writestring(".funcptr"); + buf.put(".funcptr"); } void visitArray(ArrayExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); - buf.writeByte('['); + buf.put('['); argsToBuffer(e.arguments, buf, hgs); - buf.writeByte(']'); + buf.put(']'); } void visitDot(DotExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); - buf.writeByte('.'); + buf.put('.'); expToBuffer(e.e2, PREC.primary, buf, hgs); } + void visitCat(CatExp e) + { + if (hgs.vcg_ast && e.lowering) + expressionToBuffer(e.lowering, buf, hgs); + else + visitBin(e); + } + + void visitCatAssign(CatAssignExp e) + { + if (hgs.vcg_ast && e.lowering) + expressionToBuffer(e.lowering, buf, hgs); + else + visitBin(e); + } + void visitIndex(IndexExp e) { + if (!hgs.vcg_ast && e.loweredFrom) + { + expressionToBuffer(e.loweredFrom, buf, hgs); + return; + } expToBuffer(e.e1, PREC.primary, buf, hgs); - buf.writeByte('['); + buf.put('['); sizeToBuffer(e.e2, buf, hgs); - buf.writeByte(']'); + buf.put(']'); } void visitPost(PostExp e) { expToBuffer(e.e1, precedence[e.op], buf, hgs); - buf.writestring(EXPtoString(e.op)); + buf.put(EXPtoString(e.op)); } void visitPre(PreExp e) { - buf.writestring(EXPtoString(e.op)); + buf.put(EXPtoString(e.op)); expToBuffer(e.e1, precedence[e.op], buf, hgs); } void visitRemove(RemoveExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); - buf.writestring(".remove("); + buf.put(".remove("); expToBuffer(e.e2, PREC.assign, buf, hgs); - buf.writeByte(')'); + buf.put(')'); } void visitCond(CondExp e) { expToBuffer(e.econd, PREC.oror, buf, hgs); - buf.writestring(" ? "); + buf.put(" ? "); expToBuffer(e.e1, PREC.expr, buf, hgs); - buf.writestring(" : "); + buf.put(" : "); expToBuffer(e.e2, PREC.cond, buf, hgs); } void visitDefaultInit(DefaultInitExp e) { - buf.writestring(EXPtoString(e.op)); + buf.put(EXPtoString(e.op)); } void visitClassReference(ClassReferenceExp e) { - buf.writestring(e.value.toChars()); + buf.put(e.value.toChars()); } if (e.rvalue) - buf.writestring("__rvalue("); + buf.put("__rvalue("); scope (exit) if (e.rvalue) - buf.writeByte(')'); + buf.put(')'); switch (e.op) { @@ -3077,6 +3123,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt case EXP.delegate_: return visitDelegate(e.isDelegateExp()); case EXP.dotType: return visitDotType(e.isDotTypeExp()); case EXP.call: return visitCall(e.isCallExp()); + case EXP.not: return visitNot(e.isNotExp()); case EXP.star: return visitPtr(e.isPtrExp()); case EXP.delete_: return visitDelete(e.isDeleteExp()); case EXP.cast_: return visitCast(e.isCastExp()); @@ -3090,6 +3137,10 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt case EXP.array: return visitArray(e.isArrayExp()); case EXP.dot: return visitDot(e.isDotExp()); case EXP.index: return visitIndex(e.isIndexExp()); + case EXP.concatenate: return visitCat(e.isCatExp()); + case EXP.concatenateAssign: return visitCatAssign(e.isCatAssignExp()); + case EXP.concatenateElemAssign: return visitCatAssign(e.isCatElemAssignExp()); + case EXP.concatenateDcharAssign: return visitCatAssign(e.isCatDcharAssignExp()); case EXP.minusMinus: case EXP.plusPlus: return visitPost(e.isPostExp()); case EXP.preMinusMinus: @@ -3129,7 +3180,7 @@ void floatToBuffer(Type type, const real_t value, ref OutBuffer buf, const bool if (r != value) // if exact duplication CTFloat.sprint(buffer.ptr, BUFFER_LEN, 'a', value); } - buf.writestring(buffer.ptr); + buf.put(buffer.ptr); if (buffer.ptr[strlen(buffer.ptr) - 1] == '.') buf.remove(buf.length() - 1, 1); @@ -3141,18 +3192,18 @@ void floatToBuffer(Type type, const real_t value, ref OutBuffer buf, const bool case Tfloat32: case Timaginary32: case Tcomplex32: - buf.writeByte('F'); + buf.put('F'); break; case Tfloat80: case Timaginary80: case Tcomplex80: - buf.writeByte('L'); + buf.put('L'); break; default: break; } if (t.isImaginary()) - buf.writeByte('i'); + buf.put('i'); } } @@ -3177,40 +3228,40 @@ public: override void visit(TemplateTypeParameter tp) { - buf.writestring(tp.ident.toString()); + buf.put(tp.ident.toString()); if (tp.specType) { - buf.writestring(" : "); + buf.put(" : "); typeToBuffer(tp.specType, null, *buf, *hgs); } if (tp.defaultType) { - buf.writestring(" = "); + buf.put(" = "); typeToBuffer(tp.defaultType, null, *buf, *hgs); } } override void visit(TemplateThisParameter tp) { - buf.writestring("this "); + buf.put("this "); visit(cast(TemplateTypeParameter)tp); } override void visit(TemplateAliasParameter tp) { - buf.writestring("alias "); + buf.put("alias "); if (tp.specType) typeToBuffer(tp.specType, tp.ident, *buf, *hgs); else - buf.writestring(tp.ident.toString()); + buf.put(tp.ident.toString()); if (tp.specAlias) { - buf.writestring(" : "); + buf.put(" : "); objectToBuffer(tp.specAlias, *buf, *hgs); } if (tp.defaultAlias) { - buf.writestring(" = "); + buf.put(" = "); objectToBuffer(tp.defaultAlias, *buf, *hgs); } } @@ -3220,20 +3271,20 @@ public: typeToBuffer(tp.valType, tp.ident, *buf, *hgs); if (tp.specValue) { - buf.writestring(" : "); + buf.put(" : "); tp.specValue.expressionToBuffer(*buf, *hgs); } if (tp.defaultValue) { - buf.writestring(" = "); + buf.put(" = "); tp.defaultValue.expressionToBuffer(*buf, *hgs); } } override void visit(TemplateTupleParameter tp) { - buf.writestring(tp.ident.toString()); - buf.writestring("..."); + buf.put(tp.ident.toString()); + buf.put("..."); } } @@ -3258,23 +3309,27 @@ public: override void visit(DebugCondition c) { - buf.writestring("debug ("); - buf.writestring(c.ident.toString()); - buf.writeByte(')'); + buf.put("debug"); + if (c.ident) + { + buf.put(" ("); + buf.put(c.ident.toString()); + buf.put(')'); + } } override void visit(VersionCondition c) { - buf.writestring("version ("); - buf.writestring(c.ident.toString()); - buf.writeByte(')'); + buf.put("version ("); + buf.put(c.ident.toString()); + buf.put(')'); } override void visit(StaticIfCondition c) { - buf.writestring("static if ("); + buf.put("static if ("); c.exp.expressionToBuffer(*buf, *hgs); - buf.writeByte(')'); + buf.put(')'); } } @@ -3294,7 +3349,7 @@ void toCBufferInstance(const TemplateInstance ti, ref OutBuffer buf, bool qualif HdrGenState hgs; hgs.fullQual = qualifyTypes; - buf.writestring(ti.name.toChars()); + buf.put(ti.name.toChars()); tiargsToBuffer(cast() ti, buf, hgs); } @@ -3310,19 +3365,19 @@ bool stcToBuffer(ref OutBuffer buf, STC stc) @safe if (stc & STC.scopeinferred) { - //buf.writestring("scope-inferred "); + //buf.put("scope-inferred "); stc &= ~(STC.scope_ | STC.scopeinferred); } if (stc & STC.returninferred) { - //buf.writestring((stc & STC.returnScope) ? "return-scope-inferred " : "return-ref-inferred "); + //buf.put((stc & STC.returnScope) ? "return-scope-inferred " : "return-ref-inferred "); stc &= ~(STC.return_ | STC.returninferred); } // ensure `auto ref` keywords are (almost) adjacent if (stc & STC.auto_) { - buf.writestring("auto "); + buf.put("auto "); stc &= ~STC.auto_; } /* Put scope ref return into a standard order @@ -3344,7 +3399,7 @@ bool stcToBuffer(ref OutBuffer buf, STC stc) @safe case ScopeRef.ReturnRef_Scope: rrs = isout ? "return out scope" : "return ref scope"; goto L1; case ScopeRef.Ref_ReturnScope: rrs = isout ? "out return scope" : "ref return scope"; goto L1; L1: - buf.writestring(rrs); + buf.put(rrs); result = true; stc &= ~(STC.out_ | STC.scope_ | STC.ref_ | STC.return_); break; @@ -3356,9 +3411,9 @@ bool stcToBuffer(ref OutBuffer buf, STC stc) @safe if (!s.length) break; if (result) - buf.writeByte(' '); + buf.put(' '); result = true; - buf.writestring(s); + buf.put(s); } return result; @@ -3433,9 +3488,9 @@ private void linkageToBuffer(ref OutBuffer buf, LINK linkage) @safe const s = linkageToString(linkage); if (s.length) { - buf.writestring("extern ("); - buf.writestring(s); - buf.writeByte(')'); + buf.put("extern ("); + buf.put(s); + buf.put(')'); } } @@ -3463,12 +3518,12 @@ string linkageToString(LINK linkage) pure nothrow @safe void visibilityToBuffer(ref OutBuffer buf, Visibility vis) { - buf.writestring(visibilityToString(vis.kind)); + buf.put(visibilityToString(vis.kind)); if (vis.kind == Visibility.Kind.package_ && vis.pkg) { - buf.writeByte('('); - buf.writestring(vis.pkg.toPrettyChars(true)); - buf.writeByte(')'); + buf.put('('); + buf.put(vis.pkg.toPrettyChars(true)); + buf.put(')'); } } @@ -3528,7 +3583,7 @@ void argExpTypesToCBuffer(ref OutBuffer buf, Expressions* arguments) foreach (i, arg; *arguments) { if (i) - buf.writestring(", "); + buf.put(", "); typeToBuffer(arg.type, null, buf, hgs); } } @@ -3541,7 +3596,7 @@ void arrayObjectsToBuffer(ref OutBuffer buf, Objects* objects) foreach (i, o; *objects) { if (i) - buf.writestring(", "); + buf.put(", "); objectToBuffer(o, buf, hgs); } } @@ -3578,7 +3633,7 @@ const(char)* parameterToChars(Parameter parameter, TypeFunction tf, bool fullQua if (tf.parameterList.varargs == VarArg.typesafe && parameter == tf.parameterList[tf.parameterList.parameters.length - 1]) { - buf.writestring("..."); + buf.put("..."); } return buf.extractChars(); } @@ -3594,11 +3649,11 @@ const(char)* parameterToChars(Parameter parameter, TypeFunction tf, bool fullQua private void parametersToBuffer(ParameterList pl, ref OutBuffer buf, ref HdrGenState hgs) { - buf.writeByte('('); + buf.put('('); foreach (i; 0 .. pl.length) { if (i) - buf.writestring(", "); + buf.put(", "); pl[i].parameterToBuffer(buf, hgs); } final switch (pl.varargs) @@ -3609,17 +3664,17 @@ private void parametersToBuffer(ParameterList pl, ref OutBuffer buf, ref HdrGenS case VarArg.variadic: if (pl.length) - buf.writestring(", "); + buf.put(", "); if (stcToBuffer(buf, pl.stc)) - buf.writeByte(' '); + buf.put(' '); goto case VarArg.typesafe; case VarArg.typesafe: - buf.writestring("..."); + buf.put("..."); break; } - buf.writeByte(')'); + buf.put(')'); } @@ -3634,52 +3689,52 @@ private void parameterToBuffer(Parameter p, ref OutBuffer buf, ref HdrGenState h { if (p.userAttribDecl) { - buf.writeByte('@'); + buf.put('@'); bool isAnonymous = p.userAttribDecl.atts.length > 0 && !(*p.userAttribDecl.atts)[0].isCallExp(); if (isAnonymous) - buf.writeByte('('); + buf.put('('); argsToBuffer(p.userAttribDecl.atts, buf, hgs); if (isAnonymous) - buf.writeByte(')'); - buf.writeByte(' '); + buf.put(')'); + buf.put(' '); } if (p.storageClass & STC.auto_) - buf.writestring("auto "); + buf.put("auto "); STC stc = p.storageClass; if (p.storageClass & STC.in_) { - buf.writestring("in "); + buf.put("in "); if ((p.storageClass & (STC.constscoperef | STC.ref_)) == (STC.constscoperef | STC.ref_)) stc &= ~STC.ref_; } else if (p.storageClass & STC.lazy_) - buf.writestring("lazy "); + buf.put("lazy "); else if (p.storageClass & STC.alias_) - buf.writestring("alias "); + buf.put("alias "); if (p.type && p.type.mod & MODFlags.shared_) stc &= ~STC.shared_; if (stcToBuffer(buf, stc & (STC.const_ | STC.immutable_ | STC.wild | STC.shared_ | STC.return_ | STC.returninferred | STC.scope_ | STC.scopeinferred | STC.out_ | STC.ref_ | STC.returnScope))) - buf.writeByte(' '); + buf.put(' '); const(char)[] s; if (p.storageClass & STC.alias_) { if (p.ident) - buf.writestring(p.ident.toString()); + buf.put(p.ident.toString()); } else if (p.type.isTypeIdentifier() && (s = p.type.isTypeIdentifier().ident.toString()).length > 3 && s[0..3] == "__T") { // print parameter name, instead of undetermined type parameter - buf.writestring(p.ident.toString()); + buf.put(p.ident.toString()); } else { @@ -3688,7 +3743,7 @@ private void parameterToBuffer(Parameter p, ref OutBuffer buf, ref HdrGenState h if (p.defaultArg) { - buf.writestring(" = "); + buf.put(" = "); p.defaultArg.expToBuffer(PREC.assign, buf, hgs); } } @@ -3712,12 +3767,12 @@ private void argsToBuffer(Expressions* expressions, ref OutBuffer buf, ref HdrGe foreach (i, el; *expressions) { if (i) - buf.writestring(", "); + buf.put(", "); if (names && i < names.length && (*names)[i].name) { - buf.writestring((*names)[i].name.toString()); - buf.writestring(": "); + buf.put((*names)[i].name.toString()); + buf.put(": "); } if (!el) el = basis; @@ -3731,9 +3786,9 @@ private void argsToBuffer(Expressions* expressions, ref OutBuffer buf, ref HdrGe // [0..length: basis, 1: e1, 5: e5] if (basis) { - buf.writestring("0.."); + buf.put("0.."); buf.print(expressions.length); - buf.writestring(": "); + buf.put(": "); expToBuffer(basis, PREC.assign, buf, hgs); } foreach (i, el; *expressions) @@ -3742,12 +3797,12 @@ private void argsToBuffer(Expressions* expressions, ref OutBuffer buf, ref HdrGe { if (basis) { - buf.writestring(", "); + buf.put(", "); buf.print(i); - buf.writestring(": "); + buf.put(": "); } else if (i) - buf.writestring(", "); + buf.put(", "); expToBuffer(el, PREC.assign, buf, hgs); } } @@ -3770,7 +3825,7 @@ private void sizeToBuffer(Expression e, ref OutBuffer buf, ref HdrGenState hgs) } if (uval <= 0x7FFF_FFFF_FFFF_FFFFUL) { - buf.writestring("cast(size_t)"); + buf.put("cast(size_t)"); buf.print(uval); return; } @@ -3784,33 +3839,51 @@ private void expressionToBuffer(Expression e, ref OutBuffer buf, ref HdrGenState expressionPrettyPrint(e, buf, hgs); } +// to be called if e could be loweredFrom another expression instead of acessing precedence[e.op] directly +private PREC expPrecedence(ref HdrGenState hgs, Expression e) +{ + if (!hgs.vcg_ast) + { + if (auto ce = e.isCallExp()) + { + if (ce.loweredFrom) + e = ce.loweredFrom; + } + else if (auto ne = e.isNotExp()) + if (ne.loweredFrom) + e = ne.loweredFrom; + } + return precedence[e.op]; +} + /************************************************** * Write expression out to buf, but wrap it * in ( ) if its precedence is less than pr. */ private void expToBuffer(Expression e, PREC pr, ref OutBuffer buf, ref HdrGenState hgs) { + auto prec = expPrecedence(hgs, e); debug { - if (precedence[e.op] == PREC.zero) + if (prec == PREC.zero) printf("precedence not defined for token '%s'\n", EXPtoString(e.op).ptr); } if (e.op == 0xFF) { - buf.writestring(""); + buf.put(""); return; } - assert(precedence[e.op] != PREC.zero); + assert(prec != PREC.zero); assert(pr != PREC.zero); /* Despite precedence, we don't allow a= PREC.or && pr <= PREC.and && precedence[e.op] == PREC.rel)) + if (prec < pr || (pr == PREC.rel && prec == pr) + || (pr >= PREC.or && pr <= PREC.and && prec == PREC.rel)) { - buf.writeByte('('); + buf.put('('); e.expressionToBuffer(buf, hgs); - buf.writeByte(')'); + buf.put(')'); } else { @@ -3833,8 +3906,8 @@ private void typeToBuffer(Type t, const Identifier ident, ref OutBuffer buf, ref visitWithMask(t, modMask, buf, hgs); if (ident) { - buf.writeByte(' '); - buf.writestring(ident.toString()); + buf.put(' '); + buf.put(ident.toString()); } } @@ -3851,32 +3924,32 @@ private void visitWithMask(Type t, ubyte modMask, ref OutBuffer buf, ref HdrGenS if (m & MODFlags.shared_) { MODtoBuffer(buf, MODFlags.shared_); - buf.writeByte('('); + buf.put('('); } if (m & MODFlags.wild) { MODtoBuffer(buf, MODFlags.wild); - buf.writeByte('('); + buf.put('('); } if (m & (MODFlags.const_ | MODFlags.immutable_)) { MODtoBuffer(buf, m & (MODFlags.const_ | MODFlags.immutable_)); - buf.writeByte('('); + buf.put('('); } typeToBufferx(t, buf, hgs); if (m & (MODFlags.const_ | MODFlags.immutable_)) - buf.writeByte(')'); + buf.put(')'); if (m & MODFlags.wild) - buf.writeByte(')'); + buf.put(')'); if (m & MODFlags.shared_) - buf.writeByte(')'); + buf.put(')'); } } private void dumpTemplateInstance(TemplateInstance ti, ref OutBuffer buf, ref HdrGenState hgs) { - buf.writeByte('{'); + buf.put('{'); buf.writenl(); buf.level++; @@ -3892,22 +3965,22 @@ private void dumpTemplateInstance(TemplateInstance ti, ref OutBuffer buf, ref Hd } buf.level--; - buf.writeByte('}'); + buf.put('}'); buf.writenl(); } private void tiargsToBuffer(TemplateInstance ti, ref OutBuffer buf, ref HdrGenState hgs) { - buf.writeByte('!'); + buf.put('!'); if (ti.nest) { - buf.writestring("(...)"); + buf.put("(...)"); return; } if (!ti.tiargs) { - buf.writestring("()"); + buf.put("()"); return; } if (ti.tiargs.length == 1) @@ -3933,16 +4006,16 @@ private void tiargsToBuffer(TemplateInstance ti, ref OutBuffer buf, ref HdrGenSt } } } - buf.writeByte('('); + buf.put('('); ti.nestUp(); foreach (i, arg; *ti.tiargs) { if (i) - buf.writestring(", "); + buf.put(", "); objectToBuffer(arg, buf, hgs); } ti.nestDown(); - buf.writeByte(')'); + buf.put(')'); } /**************************************** @@ -3971,9 +4044,9 @@ private void objectToBuffer(RootObject oarg, ref OutBuffer buf, ref HdrGenState else if (Dsymbol s = isDsymbol(oarg)) { if (s.ident) - buf.writestring(s.ident.toString()); + buf.put(s.ident.toString()); else - buf.writestring(s.toChars()); + buf.put(s.toChars()); } else if (auto v = isTuple(oarg)) { @@ -3981,7 +4054,7 @@ private void objectToBuffer(RootObject oarg, ref OutBuffer buf, ref HdrGenState foreach (i, arg; *args) { if (i) - buf.writestring(", "); + buf.put(", "); objectToBuffer(arg, buf, hgs); } } @@ -3991,7 +4064,7 @@ private void objectToBuffer(RootObject oarg, ref OutBuffer buf, ref HdrGenState } else if (!oarg) { - buf.writestring("NULL"); + buf.put("NULL"); } else { @@ -4015,7 +4088,7 @@ private void visitFuncIdentWithPostfix(TypeFunction t, const char[] ident, ref O if (t.linkage > LINK.d && hgs.ddoc != 1 && !hgs.hdrgen) { linkageToBuffer(buf, t.linkage); - buf.writeByte(' '); + buf.put(' '); } if (t.linkage == LINK.objc && isStatic) buf.write("static "); @@ -4023,25 +4096,25 @@ private void visitFuncIdentWithPostfix(TypeFunction t, const char[] ident, ref O { typeToBuffer(t.next, null, buf, hgs); if (ident) - buf.writeByte(' '); + buf.put(' '); } else if (hgs.ddoc) - buf.writestring("auto "); + buf.put("auto "); if (ident) - buf.writestring(ident); + buf.put(ident); parametersToBuffer(t.parameterList, buf, hgs); /* Use postfix style for attributes */ if (t.mod) { - buf.writeByte(' '); + buf.put(' '); MODtoBuffer(buf, t.mod); } void dg(string str) { - buf.writeByte(' '); - buf.writestring(str); + buf.put(' '); + buf.put(str); } t.attributesApply(&dg); @@ -4063,7 +4136,7 @@ private void visitFuncIdentWithPrefix(TypeFunction t, const Identifier ident, Te if (t.mod && !(hgs.ddoc || hgs.hdrgen)) { MODtoBuffer(buf, t.mod); - buf.writeByte(' '); + buf.put(' '); } void dg(string str) @@ -4073,8 +4146,8 @@ private void visitFuncIdentWithPrefix(TypeFunction t, const Identifier ident, Te // don't write 'ref' for ctors if ((ident == Id.ctor) && str == "ref") return; - buf.writestring(str); - buf.writeByte(' '); + buf.put(str); + buf.put(' '); } } t.attributesApply(&dg); @@ -4082,7 +4155,7 @@ private void visitFuncIdentWithPrefix(TypeFunction t, const Identifier ident, Te if (t.linkage > LINK.d && hgs.ddoc != 1 && !hgs.hdrgen) { linkageToBuffer(buf, t.linkage); - buf.writeByte(' '); + buf.put(' '); } if (ident && ident.toHChars2() != ident.toChars()) { @@ -4092,41 +4165,41 @@ private void visitFuncIdentWithPrefix(TypeFunction t, const Identifier ident, Te { typeToBuffer(t.next, null, buf, hgs); if (ident) - buf.writeByte(' '); + buf.put(' '); } else if (hgs.ddoc) - buf.writestring("auto "); + buf.put("auto "); if (ident) - buf.writestring(ident.toHChars2()); + buf.put(ident.toHChars2()); if (td) { - buf.writeByte('('); + buf.put('('); foreach (i, p; *td.origParameters) { if (i) - buf.writestring(", "); + buf.put(", "); toCBuffer(p, buf, hgs); } - buf.writeByte(')'); + buf.put(')'); } parametersToBuffer(t.parameterList, buf, hgs); // postfix this attributes are more readable if (t.mod && (hgs.ddoc || hgs.hdrgen)) { - buf.writeByte(' '); + buf.put(' '); MODtoBuffer(buf, t.mod); } if (t.isReturnScope && !t.isReturnInferred) { - buf.writestring(" return scope"); + buf.put(" return scope"); } else if (t.isScopeQual && !t.isScopeInferred) { - buf.writestring(" scope"); + buf.put(" scope"); } if (t.isReturn && !t.isReturnScope && !t.isReturnInferred) { - buf.writestring(" return"); + buf.put(" return"); } t.inuse--; } @@ -4136,54 +4209,54 @@ private void initializerToBuffer(Initializer inx, ref OutBuffer buf, ref HdrGenS { void visitError(ErrorInitializer iz) { - buf.writestring("__error__"); + buf.put("__error__"); } void visitVoid(VoidInitializer iz) { - buf.writestring("void"); + buf.put("void"); } void visitDefault(DefaultInitializer iz) { - buf.writestring("{ }"); + buf.put("{ }"); } void visitStruct(StructInitializer si) { //printf("StructInitializer::toCBuffer()\n"); - buf.writeByte('{'); + buf.put('{'); foreach (i, const id; si.field) { if (i) - buf.writestring(", "); + buf.put(", "); if (id) { - buf.writestring(id.toString()); - buf.writeByte(':'); + buf.put(id.toString()); + buf.put(':'); } if (auto iz = si.value[i]) initializerToBuffer(iz, buf, hgs); } - buf.writeByte('}'); + buf.put('}'); } void visitArray(ArrayInitializer ai) { - buf.writeByte('['); + buf.put('['); foreach (i, ex; ai.index) { if (i) - buf.writestring(", "); + buf.put(", "); if (ex) { ex.expressionToBuffer(buf, hgs); - buf.writeByte(':'); + buf.put(':'); } if (auto iz = ai.value[i]) initializerToBuffer(iz, buf, hgs); } - buf.writeByte(']'); + buf.put(']'); } void visitExp(ExpInitializer ei) @@ -4193,32 +4266,32 @@ private void initializerToBuffer(Initializer inx, ref OutBuffer buf, ref HdrGenS void visitC(CInitializer ci) { - buf.writeByte('{'); + buf.put('{'); foreach (i, ref DesigInit di; ci.initializerList) { if (i) - buf.writestring(", "); + buf.put(", "); if (di.designatorList) { foreach (ref Designator d; (*di.designatorList)[]) { if (d.exp) { - buf.writeByte('['); + buf.put('['); toCBuffer(d.exp, buf, hgs); - buf.writeByte(']'); + buf.put(']'); } else { - buf.writeByte('.'); - buf.writestring(d.ident.toString()); + buf.put('.'); + buf.put(d.ident.toString()); } } - buf.writeByte('='); + buf.put('='); } initializerToBuffer(di.initializer, buf, hgs); } - buf.writeByte('}'); + buf.put('}'); } mixin VisitInitializer!void visit; @@ -4236,13 +4309,13 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs) void visitError(TypeError t) { - buf.writestring("_error_"); + buf.put("_error_"); } void visitBasic(TypeBasic t) { //printf("TypeBasic::toCBuffer2(t.mod = %d)\n", t.mod); - buf.writestring(t.dstring); + buf.put(t.dstring); } void visitTraits(TypeTraits t) @@ -4254,17 +4327,17 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs) void visitVector(TypeVector t) { //printf("TypeVector::toCBuffer2(t.mod = %d)\n", t.mod); - buf.writestring("__vector("); + buf.put("__vector("); visitWithMask(t.basetype, t.mod, buf, hgs); - buf.writestring(")"); + buf.put(")"); } void visitSArray(TypeSArray t) { visitWithMask(t.next, t.mod, buf, hgs); - buf.writeByte('['); + buf.put('['); sizeToBuffer(t.dim, buf, hgs); - buf.writeByte(']'); + buf.put(']'); } void visitDArray(TypeDArray t) @@ -4273,25 +4346,25 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs) if (hgs.declstring) goto L1; if (ut.equals(Type.tstring)) - buf.writestring("string"); + buf.put("string"); else if (ut.equals(Type.twstring)) - buf.writestring("wstring"); + buf.put("wstring"); else if (ut.equals(Type.tdstring)) - buf.writestring("dstring"); + buf.put("dstring"); else { L1: visitWithMask(t.next, t.mod, buf, hgs); - buf.writestring("[]"); + buf.put("[]"); } } void visitAArray(TypeAArray t) { visitWithMask(t.next, t.mod, buf, hgs); - buf.writeByte('['); + buf.put('['); visitWithMask(t.index, 0, buf, hgs); - buf.writeByte(']'); + buf.put(']'); } void visitPointer(TypePointer t) @@ -4302,14 +4375,14 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs) else { visitWithMask(t.next, t.mod, buf, hgs); - buf.writeByte('*'); + buf.put('*'); } } void visitReference(TypeReference t) { visitWithMask(t.next, t.mod, buf, hgs); - buf.writeByte('&'); + buf.put('&'); } void visitFunction(TypeFunction t) @@ -4330,23 +4403,23 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs) switch (id.dyncast()) with (DYNCAST) { case dsymbol: - buf.writeByte('.'); + buf.put('.'); TemplateInstance ti = cast(TemplateInstance)id; ti.dsymbolToBuffer(buf, hgs); break; case expression: - buf.writeByte('['); + buf.put('['); (cast(Expression)id).expressionToBuffer(buf, hgs); - buf.writeByte(']'); + buf.put(']'); break; case type: - buf.writeByte('['); + buf.put('['); typeToBufferx(cast(Type)id, buf, hgs); - buf.writeByte(']'); + buf.put(']'); break; default: - buf.writeByte('.'); - buf.writestring(id.toString()); + buf.put('.'); + buf.put(id.toString()); } } } @@ -4354,7 +4427,7 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs) void visitIdentifier(TypeIdentifier t) { //printf("visitTypeIdentifier() %s\n", t.ident.toChars()); - buf.writestring(t.ident.toString()); + buf.put(t.ident.toString()); visitTypeQualifiedHelper(t); } @@ -4366,22 +4439,22 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs) void visitTypeof(TypeTypeof t) { - buf.writestring("typeof("); + buf.put("typeof("); t.exp.expressionToBuffer(buf, hgs); - buf.writeByte(')'); + buf.put(')'); visitTypeQualifiedHelper(t); } void visitReturn(TypeReturn t) { - buf.writestring("typeof(return)"); + buf.put("typeof(return)"); visitTypeQualifiedHelper(t); } void visitEnum(TypeEnum t) { //printf("visitEnum: %s\n", t.sym.toChars()); - buf.writestring(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars()); + buf.put(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars()); } void visitStruct(TypeStruct t) @@ -4393,9 +4466,9 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs) // while printing messages. TemplateInstance ti = t.sym.parent ? t.sym.parent.isTemplateInstance() : null; if (ti && ti.aliasdecl == t.sym) - buf.writestring(hgs.fullQual ? ti.toPrettyChars() : ti.toChars()); + buf.put(hgs.fullQual ? ti.toPrettyChars() : ti.toChars()); else - buf.writestring(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars()); + buf.put(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars()); } void visitClass(TypeClass t) @@ -4405,9 +4478,9 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs) // while printing messages. TemplateInstance ti = t.sym.parent ? t.sym.parent.isTemplateInstance() : null; if (ti && ti.aliasdecl == t.sym) - buf.writestring(hgs.fullQual ? ti.toPrettyChars() : ti.toChars()); + buf.put(hgs.fullQual ? ti.toPrettyChars() : ti.toChars()); else - buf.writestring(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars()); + buf.put(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars()); } void visitTag(TypeTag t) @@ -4417,25 +4490,25 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs) // https://issues.dlang.org/show_bug.cgi?id=24670 // `const` must be parenthesized because it can be a return type if (t.mod & MODFlags.const_) - buf.writestring("const("); + buf.put("const("); // For C to D translation, `struct S` or `enum S` simply becomes `S` - buf.writestring(t.id.toString()); + buf.put(t.id.toString()); if (t.mod & MODFlags.const_) - buf.writestring(")"); + buf.put(")"); return; } // The following produces something like "const enum E : short" if (t.mod & MODFlags.const_) - buf.writestring("const "); - buf.writestring(Token.toString(t.tok)); - buf.writeByte(' '); + buf.put("const "); + buf.put(Token.toString(t.tok)); + buf.put(' '); if (t.id) - buf.writestring(t.id.toString()); + buf.put(t.id.toString()); if (t.tok == TOK.enum_ && t.base && t.base.ty != TY.Tint32) { - buf.writestring(" : "); + buf.put(" : "); visitWithMask(t.base, t.mod, buf, hgs); } } @@ -4448,33 +4521,33 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs) void visitSlice(TypeSlice t) { visitWithMask(t.next, t.mod, buf, hgs); - buf.writeByte('['); + buf.put('['); sizeToBuffer(t.lwr, buf, hgs); - buf.writestring(" .. "); + buf.put(" .. "); sizeToBuffer(t.upr, buf, hgs); - buf.writeByte(']'); + buf.put(']'); } void visitNull(TypeNull t) { - buf.writestring("typeof(null)"); + buf.put("typeof(null)"); } void visitMixin(TypeMixin t) { - buf.writestring("mixin("); + buf.put("mixin("); argsToBuffer(t.exps, buf, hgs, null); - buf.writeByte(')'); + buf.put(')'); } void visitNoreturn(TypeNoreturn t) { - buf.writestring("noreturn"); + buf.put("noreturn"); } if (hgs.importcHdr && !hgs.inCAlias && t.mcache && t.mcache.typedefIdent) { - buf.writestring(t.mcache.typedefIdent.toString()); + buf.put(t.mcache.typedefIdent.toString()); return; } diff --git a/gcc/d/dmd/iasm/gcc.d b/gcc/d/dmd/iasm/gcc.d new file mode 100644 index 00000000000..4be94d76ad4 --- /dev/null +++ b/gcc/d/dmd/iasm/gcc.d @@ -0,0 +1,780 @@ +/** + * Inline assembler for the GCC D compiler. + * + * Copyright (C) 2018-2025 by The D Language Foundation, All Rights Reserved + * Authors: Iain Buclaw + * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasmgcc.d, _iasmgcc.d) + * Documentation: https://dlang.org/phobos/dmd_iasmgcc.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/iasmgcc.d + */ + +module dmd.iasm.gcc; + +import core.stdc.string; + +import dmd.arraytypes; +import dmd.astcodegen; +import dmd.dscope; +import dmd.dsymbol; +import dmd.errors; +import dmd.errorsink; +import dmd.expression; +import dmd.expressionsem; +import dmd.identifier; +import dmd.globals; +import dmd.location; +import dmd.cparse; +import dmd.parse; +import dmd.target; +import dmd.tokens; +import dmd.statement; +import dmd.statementsem; + +/*********************************** + * 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"); + const bool doUnittests = global.params.parsingUnittestsRequired(); + scope p = (sc && sc.inCfile) + ? new CParser!ASTCodegen(sc._module, "", false, global.errorSink, target.c, null, &global.compileEnv) + : new Parser!ASTCodegen(sc._module, "", false, global.errorSink, &global.compileEnv, doUnittests); + + // 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 = p.allocateToken(); + memcpy(*ptoklist, token, Token.sizeof); + ptoklist = &(*ptoklist).next; + *ptoklist = null; + } + // Append closing `;` location. + *ptoklist = p.allocateToken(); + (*ptoklist).value = TOK.semicolon; + (*ptoklist).loc = s.loc; + ptoklist = &(*ptoklist).next; + *ptoklist = null; + + // Adjust starting line number of the parser. + p.token = *toklist; + p.baseLoc.startLine = s.loc.linnum; + p.linnum = s.loc.linnum; + + // Parse the gcc asm statement. + const errors = global.errors; + s = p.parseGccAsm(s); + if (errors != global.errors) + return null; + s.stc = sc.stc; + + // Fold the instruction template string. + s.insn = semanticAsmString(sc, s.insn, "asm instruction template"); + + if (s.labels && s.outputargs) + p.eSink.error(s.loc, "extended asm statements with labels cannot have output constraints"); + + // Analyse all input and output operands. + if (s.args) + { + foreach (i; 0 .. s.args.length) + { + Expression ec = (*s.constraints)[i]; + (*s.constraints)[i] = semanticAsmString(sc, ec, "asm operand"); + + Expression earg = (*s.args)[i]; + earg = earg.expressionSemantic(sc); + // Check argument is a valid lvalue/rvalue. + if (i < s.outputargs) + earg = earg.modifiableLvalue(sc); + else if (earg.checkValue()) + earg = ErrorExp.get(); + (*s.args)[i] = earg; + } + } + + // Analyse all clobbers. + if (s.clobbers) + { + foreach (i; 0 .. s.clobbers.length) + { + Expression ec = (*s.clobbers)[i]; + (*s.clobbers)[i] = semanticAsmString(sc, ec, "asm clobber"); + } + } + + // Analyse all goto labels. + if (s.labels) + { + foreach (i; 0 .. s.labels.length) + { + 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; +} + +/*********************************** + * Run semantic analysis on an CAsmDeclaration. + * Params: + * ad = asm declaration + * sc = the scope where the asm declaration is located + */ +public void gccAsmSemantic(CAsmDeclaration ad, Scope* sc) +{ + import dmd.typesem : pointerTo; + ad.code = semanticString(sc, ad.code, "asm definition"); + ad.code.type = ad.code.type.nextOf().pointerTo(); + + // Asm definition always needs emitting into the root module. + import dmd.dmodule : Module; + if (sc._module && sc._module.isRoot()) + return; + if (Module m = Module.rootModule) + m.members.push(ad); +} + +private: + + +/*********************************** + * Issue error if the current token is not `value`. + * Otherwise, advance to next token. + * Params: + * p = parser state + * value = token value to compare with + * Returns: + * true advanced to next token + * false error was issued + */ +bool requireToken(Parser)(Parser p, TOK value) +{ + if (p.token.value == value) + { + p.nextToken(); + return true; + } + + p.eSink.error(p.token.loc, "found `%s` when expecting `%s`", + p.token.toChars(), Token.toChars(value)); + return false; +} + +/*********************************** + * Run semantic analysis on `exp`, resolving it as a compile-time string. + * Params: + * sc = scope + * exp = Expression which expected as a string + * s = What the string is expected for, used in error diagnostic + * Returns: + * StringExp or ErrorExp + */ +Expression semanticAsmString(Scope* sc, Expression exp, const char *s) +{ + import dmd.dcast : implicitCastTo; + import dmd.dsymbolsem : resolveAliasThis; + import dmd.mtype : isAggregate, Type; + + exp = expressionSemantic(exp, sc); + + // Resolve `alias this` if we were given a struct literal. + if (auto ad = isAggregate(exp.type)) + { + if (ad.aliasthis && ad.type && !ad.type.isTypeError()) + exp = resolveAliasThis(sc, exp); + } + + // Evaluate the expression as a string now or error trying. + if (auto se = semanticString(sc, exp, s)) + exp = implicitCastTo(se, sc, Type.tstring); + + return exp; +} + +/*********************************** + * Parse a D or ImportC assignment expression + */ +Expression parseAssignment(Parser)(Parser p) +{ + if (p.Ccompile) + return (cast(CParser!ASTCodegen)p).cparseAssignExp(); + + return p.parseAssignExp(); +} + +/*********************************** + * Parse a D or ImportC conditional expression + */ +Expression parseConditional(Parser)(Parser p) +{ + if (p.Ccompile) + return (cast(CParser!ASTCodegen)p).cparseCondExp(); + + return p.parseCondExp(); +} + +/*********************************** + * Parse a D or ImportC primary expression + */ +Expression parsePrimary(Parser)(Parser p) +{ + if (p.Ccompile) + return (cast(CParser!ASTCodegen)p).cparsePrimaryExp(); + + return p.parsePrimaryExp(); +} + +/*********************************** + * Parse an expression that evaluates to a string. + * Grammar: + * | AsmStringExpr: + * | StringLiteral + * | ( ConditionalExpression ) + * Params: + * p = parser state + * Returns: + * the parsed string expression + */ +Expression parseAsmString(Parser)(Parser p) +{ + if (p.token.value == TOK.leftParenthesis) + { + // Skip over opening `(` + p.nextToken(); + Expression insn = p.parseConditional(); + if (insn.isErrorExp()) + return insn; + + // Look for closing `)`. + if (!p.requireToken(TOK.rightParenthesis)) + return ErrorExp.get(); + + return insn; + } + else if (p.token.value != TOK.string_) + { + p.eSink.error(p.token.loc, "expected string literal or expression in parentheses"); + return ErrorExp.get(); + } + + return p.parsePrimary(); +} + +/*********************************** + * Parse list of extended asm input or output operands. + * Grammar: + * | Operands: + * | SymbolicName(opt) AsmStringExpr ( AssignExpression ) + * | SymbolicName(opt) AsmStringExpr ( 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; + + if (p.token.value == TOK.colon || + p.token.value == TOK.colonColon || + p.token.value == TOK.semicolon || + p.token.value == TOK.endOfFile) + return numargs; + + while (1) + { + Expression arg; + Identifier name; + + if (p.token.value == TOK.leftBracket) + { + // Skip over opening `[` + p.nextToken(); + if (p.token.value == TOK.identifier) + { + // Store the symbolic name + name = p.token.ident; + p.nextToken(); + } + else + { + p.eSink.error(p.token.loc, "identifier expected after `[`"); + goto Lerror; + } + // Look for closing `]` + if (!p.requireToken(TOK.rightBracket)) + goto Lerror; + } + + // Look for the constraint string. + Expression constraint = p.parseAsmString(); + if (constraint.isErrorExp()) + goto Lerror; + + // Look for the opening `(` + if (!p.requireToken(TOK.leftParenthesis)) + goto Lerror; + + // Parse the assign expression + arg = p.parseAssignment(); + if (arg.isErrorExp()) + goto Lerror; + + // Look for the closing `)` + if (!p.requireToken(TOK.rightParenthesis)) + goto Lerror; + + // Add this operand to the list. + 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 the next token is not a `,`, there are no more operands. + if (p.token.value != TOK.comma) + return numargs; + + // Skip over the `,` token. + p.nextToken(); + } +Lerror: + while (p.token.value != TOK.semicolon && + p.token.value != TOK.endOfFile) + p.nextToken(); + + return 0; +} + +/*********************************** + * Parse list of extended asm clobbers. + * Grammar: + * | Clobbers: + * | AsmStringExpr + * | AsmStringExpr , Clobbers + * Params: + * p = parser state + * Returns: + * array of parsed clobber expressions + */ +Expressions* parseExtAsmClobbers(Parser)(Parser p) +{ + Expressions* clobbers; + + if (p.token.value == TOK.colon || + p.token.value == TOK.colonColon || + p.token.value == TOK.semicolon || + p.token.value == TOK.endOfFile) + return clobbers; + + while (1) + { + // Look for the clobbers string + Expression clobber = p.parseAsmString(); + if (clobber.isErrorExp()) + goto Lerror; + + // Add it to the list. + if (!clobbers) + clobbers = new Expressions(); + clobbers.push(clobber); + + // If the next token is not a `,`, there are no more clobbers. + if (p.token.value != TOK.comma) + return clobbers; + + // Skip over the `,` token. + p.nextToken(); + } +Lerror: + while (p.token.value != TOK.semicolon && + p.token.value != TOK.endOfFile) + p.nextToken(); + + return null; +} + +/*********************************** + * 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) + { + if (p.token.value == TOK.identifier) + { + if (!labels) + labels = new Identifiers(); + labels.push(p.token.ident); + + // If the next token is not a `,`, there are no more labels. + if (p.nextToken() != TOK.comma) + return labels; + + // Skip over the `,` token. + p.nextToken(); + } + else + { + p.eSink.error(p.token.loc, "identifier expected for goto label name, not `%s`", + p.token.toChars()); + goto Lerror; + } + } +Lerror: + while (p.token.value != TOK.semicolon && + p.token.value != TOK.endOfFile) + p.nextToken(); + + return null; +} + +/*********************************** + * Parse a gcc asm statement. + * There are three forms of inline asm statements, basic, extended, and goto. + * Grammar: + * | AsmInstruction: + * | BasicAsmInstruction + * | ExtAsmInstruction + * | GotoAsmInstruction + * | + * | BasicAsmInstruction: + * | AsmStringExpr + * | + * | ExtAsmInstruction: + * | AsmStringExpr : Operands(opt) : Operands(opt) : Clobbers(opt) + * | + * | GotoAsmInstruction: + * | AsmStringExpr : : 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.parseAsmString(); + if (s.insn.isErrorExp()) + return s; + + // No semicolon followed after instruction template, treat as extended asm. + if (p.token.value == TOK.colon || p.token.value == TOK.colonColon) + { + bool inputs; + bool clobbers; + bool labels; + + // Look for outputs. + if (p.token.value == TOK.colon) + { + // Skip over the `:` token. + p.nextToken(); + // Parse the output operands. + s.outputargs = p.parseExtAsmOperands(s); + } + else if (p.token.value == TOK.colonColon) + inputs = true; + + // Look for inputs. + if (inputs || p.token.value == TOK.colon) + { + // Skip over the `:` or `::` token. + p.nextToken(); + // Parse the input operands. + p.parseExtAsmOperands(s); + } + else if (p.token.value == TOK.colonColon) + clobbers = true; + + // Look for clobbers. + if (clobbers || p.token.value == TOK.colon) + { + // Skip over the `:` or `::` token. + p.nextToken(); + // Parse the clobbers. + s.clobbers = p.parseExtAsmClobbers(); + } + else if (p.token.value == TOK.colonColon) + labels = true; + + // Look for labels. + if (labels || p.token.value == TOK.colon) + { + // Skip over the `:` or `::` token. + p.nextToken(); + // Parse the labels. + s.labels = p.parseExtAsmGotoLabels(); + } + } + + if (p.token.value == TOK.endOfFile) + assert(global.errors); + else + p.requireToken(TOK.semicolon); + + return s; +} + +unittest +{ + import dmd.mtype : TypeBasic; + + if (!global.errorSink) + global.errorSink = new ErrorSinkCompiler; + + const errors = global.startGagging(); + scope(exit) global.endGagging(errors); + + // If this check fails, then Type._init() was called before reaching here, + // and the entire chunk of code that follows can be removed. + assert(ASTCodegen.Type.tint32 is null); + // Minimally initialize the cached types in ASTCodegen.Type, as they are + // dependencies for some fail asm tests to succeed. + ASTCodegen.Type.stringtable._init(); + scope(exit) + { + ASTCodegen.Type.deinitialize(); + ASTCodegen.Type.tint32 = null; + ASTCodegen.Type.tchar = null; + } + scope tint32 = new TypeBasic(ASTCodegen.Tint32); + ASTCodegen.Type.tint32 = tint32; + scope tchar = new TypeBasic(ASTCodegen.Tchar); + ASTCodegen.Type.tchar = tchar; + + // Imitates asmSemantic if version = IN_GCC. + static int semanticAsm(Token* tokens, bool importC) + { + const errors = global.errors; + scope gas = new GccAsmStatement(Loc.initial, tokens); + const bool doUnittests = false; + scope p = importC + ? new CParser!ASTCodegen(null, ";", false, global.errorSink, target.c, null, &global.compileEnv) + : new Parser!ASTCodegen(null, ";", false, global.errorSink, &global.compileEnv, doUnittests); + p.token = *tokens; + p.parseGccAsm(gas); + return global.errors - errors; + } + + // Imitates parseStatement for asm statements. + static void parseAsm(string input, bool expectError, bool importC = false) + { + // Generate tokens from input test. + const bool doUnittests = false; + scope p = new Parser!ASTCodegen(null, input, false, global.errorSink, &global.compileEnv, doUnittests); + 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; + if (p.token.value == TOK.colonColon) + { + *ptoklist = p.allocateToken(); + memcpy(*ptoklist, &p.token, Token.sizeof); + (*ptoklist).value = TOK.colon; + ptoklist = &(*ptoklist).next; + + *ptoklist = p.allocateToken(); + memcpy(*ptoklist, &p.token, Token.sizeof); + (*ptoklist).value = TOK.colon; + ptoklist = &(*ptoklist).next; + } + else + { + *ptoklist = p.allocateToken(); + memcpy(*ptoklist, &p.token, Token.sizeof); + ptoklist = &(*ptoklist).next; + } + *ptoklist = null; + p.nextToken(); + } + p.check(TOK.rightCurly); + + auto res = semanticAsm(toklist, importC); + // Checks for both unexpected passes and failures. + assert((res == 0) != expectError, input); + } + + /// 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); + } }, + + // Likewise mixins, permissible so long as the result is a string. + q{ asm { (mixin(`"repne"`, `~ "scasb"`)); + } }, + + // :: token tests + q{ asm { "" : : : "memory"; } }, + q{ asm { "" :: : "memory"; } }, + q{ asm { "" : :: "memory"; } }, + q{ asm { "" ::: "memory"; } }, + q{ asm { "" :::: label; } }, + + // https://github.com/dlang/dmd/issues/21299 + q{ asm { (insn) : (output) (a) : (input) (1) : (clobber); } }, + q{ asm { (['t','e','s','t']) : (['=','r']) (a) : (['r']) (1) : (['m','e','m','o','r','y']); } }, + + // https://github.com/dlang/dmd/issues/21679 + q{ asm { "" : "=r" (s.x); } }, + ]; + + immutable string[] failAsmTests = [ + // Found 'h' when expecting ';' + q{ asm { ""h; + } }, + + // https://issues.dlang.org/show_bug.cgi?id=20592 + q{ asm { "nop" : [name] string (expr); } }, + + // Expression expected, not ';' + q{ asm { ""[; + } }, + + // Expression expected, not ':' + q{ asm { "" + : + : "g" (a ? b : : c); + } }, + + // Found ',' when expecting ':' + q{ asm { "", ""; + } }, + + // Identifier expected, not ';' + q{ asm { "" : ::: ; } }, + q{ asm { "" :: :: ; } }, + q{ asm { "" ::: : ; } }, + q{ asm { "" :::: ; } }, + + // https://issues.dlang.org/show_bug.cgi?id=20593 + q{ asm { "instruction" : : "operand" 123; } }, + + // https://github.com/dlang/dmd/issues/21298 + q{ asm { 1; } }, + q{ asm { int; } }, + q{ asm { : "=r" (i); } }, + q{ asm { (; } }, + q{ asm { (""; } }, + q{ asm { "" ,; } }, + q{ asm { "" d; } }, + q{ asm { "" : (; } }, + q{ asm { "" : (""; } }, + q{ asm { "" : ""; } }, + q{ asm { "" : "" (; } }, + q{ asm { "" : "" (a; } }, + q{ asm { "" : "" (a) ,; } }, + q{ asm { "" : "" (a) d; } }, + q{ asm { "" : "" (a) : (; } }, + q{ asm { "" : "" (a) : (""; } }, + q{ asm { "" : "" (a) : ""; } }, + q{ asm { "" : "" (a) : "" (; } }, + q{ asm { "" : "" (a) : "" (b; } }, + q{ asm { "" : "" (a) : "" (b) ,; } }, + q{ asm { "" : "" (a) : "" (b) d; } }, + q{ asm { "" : "" (a) : "" (b) : (; } }, + q{ asm { "" : "" (a) : "" (b) : (""; } }, + q{ asm { "" : "" (a) : "" (b) : "" ,; } }, + q{ asm { "" : "" (a) : "" (b) : "" d; } }, + q{ asm { "" : "" (a) : "" (b) : "" : (; } }, + q{ asm { "" : "" (a) : "" (b) : "" : c ,; } }, + q{ asm { "" : "" (a) : "" (b) : "" : c d; } }, + q{ asm { "" : "" (a) : "" (b) : "" : c :; } }, + q{ asm { "" : "" (a) : "" (b) : "" : c : (; } }, + q{ asm { "" : "" (a) : "" (b) : "" : c : (""; } }, + q{ asm { "" : "" (a) : "" (b) : "" : c : "" (; } }, + q{ asm { "" : "" (a) : "" (b) : "" : c : "" (d; } }, + q{ asm { "" : "" (a) : "" (b) : "" : c : "" (d); } }, + + // https://github.com/dlang/dmd/issues/21679 + q{ asm { "" : "=r" (s->x); } }, + ]; + + immutable string[] passCAsmTests = [ + // https://github.com/dlang/dmd/issues/21679 + q{ asm { "" : "=r" (s->x); } }, + q{ asm { "" : "=r" (s.x); } } + ]; + + foreach (test; passAsmTests) + parseAsm(test, false); + + foreach (test; failAsmTests) + parseAsm(test, true); + + foreach (test; passCAsmTests) + parseAsm(test, false, /*importC*/true); +} diff --git a/gcc/d/dmd/iasm.d b/gcc/d/dmd/iasm/package.d similarity index 87% rename from gcc/d/dmd/iasm.d rename to gcc/d/dmd/iasm/package.d index 689ef0f9fee..33af0ca11dd 100644 --- a/gcc/d/dmd/iasm.d +++ b/gcc/d/dmd/iasm/package.d @@ -20,6 +20,7 @@ import dmd.dsymbol; import dmd.expression; import dmd.func; import dmd.mtype; +import dmd.target; import dmd.tokens; import dmd.statement; import dmd.statementsem; @@ -29,14 +30,24 @@ version (NoBackend) } else version (IN_GCC) { - import dmd.iasmgcc; + version = Asm_GCC; +} +else version (IN_LLVM) +{ + version = Asm_GCC; } else { - import dmd.iasmdmd; + import dmd.iasm.dmdx86; + import dmd.iasm.dmdaarch64; version = MARS; } +version (Asm_GCC) +{ + import dmd.iasm.gcc; +} + /************************ AsmStatement ***************************************/ Statement asmSemantic(AsmStatement s, Scope* sc) @@ -73,9 +84,11 @@ Statement asmSemantic(AsmStatement s, Scope* sc) } auto ias = new InlineAsmStatement(s.loc, s.tokens); ias.caseSensitive = s.caseSensitive; - return inlineAsmSemantic(ias, sc); + return (target.isAArch64) + ? inlineAsmAArch64Semantic(ias, sc) + : inlineAsmSemantic(ias, sc); // X86_64 } - else version (IN_GCC) + else version (Asm_GCC) { auto eas = new GccAsmStatement(s.loc, s.tokens); return gccAsmSemantic(eas, sc); @@ -94,7 +107,7 @@ void asmSemantic(CAsmDeclaration ad, Scope* sc) version (NoBackend) { } - else version (IN_GCC) + else version (Asm_GCC) { return gccAsmSemantic(ad, sc); } diff --git a/gcc/d/dmd/iasmgcc.d b/gcc/d/dmd/iasmgcc.d deleted file mode 100644 index 4d9a758bb47..00000000000 --- a/gcc/d/dmd/iasmgcc.d +++ /dev/null @@ -1,604 +0,0 @@ -/** - * Inline assembler for the GCC D compiler. - * - * Copyright (C) 2018-2025 by The D Language Foundation, All Rights Reserved - * Authors: Iain Buclaw - * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasmgcc.d, _iasmgcc.d) - * Documentation: https://dlang.org/phobos/dmd_iasmgcc.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/iasmgcc.d - */ - -module dmd.iasmgcc; - -import core.stdc.string; - -import dmd.arraytypes; -import dmd.astcodegen; -import dmd.dscope; -import dmd.dsymbol; -import dmd.errors; -import dmd.errorsink; -import dmd.expression; -import dmd.expressionsem; -import dmd.identifier; -import dmd.globals; -import dmd.location; -import dmd.parse; -import dmd.tokens; -import dmd.statement; -import dmd.statementsem; - -/*********************************** - * 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"); - const bool doUnittests = global.params.parsingUnittestsRequired(); - scope p = new Parser!ASTCodegen(sc._module, ";", false, global.errorSink, &global.compileEnv, doUnittests); - - // Make a safe copy of the token list before parsing. - Token* toklist = null; - Token **ptoklist = &toklist; - - for (Token* token = s.tokens; token; token = token.next) - { - *ptoklist = p.allocateToken(); - memcpy(*ptoklist, token, Token.sizeof); - ptoklist = &(*ptoklist).next; - *ptoklist = null; - } - p.token = *toklist; - p.baseLoc.startLine = s.loc.linnum; - p.linnum = s.loc.linnum; - - // Parse the gcc asm statement. - const errors = global.errors; - s = p.parseGccAsm(s); - if (errors != global.errors) - return null; - s.stc = sc.stc; - - // Fold the instruction template string. - s.insn = semanticString(sc, s.insn, "asm instruction template"); - - if (s.labels && s.outputargs) - p.eSink.error(s.loc, "extended asm statements with labels cannot have output constraints"); - - // Analyse all input and output operands. - if (s.args) - { - foreach (i; 0 .. s.args.length) - { - Expression e = (*s.args)[i]; - e = e.expressionSemantic(sc); - // Check argument is a valid lvalue/rvalue. - if (i < s.outputargs) - e = e.modifiableLvalue(sc); - else if (e.checkValue()) - e = ErrorExp.get(); - (*s.args)[i] = e; - - e = (*s.constraints)[i]; - e = e.expressionSemantic(sc); - assert(e.op == EXP.string_ && (cast(StringExp) e).sz == 1); - (*s.constraints)[i] = e; - } - } - - // Analyse all clobbers. - if (s.clobbers) - { - foreach (i; 0 .. s.clobbers.length) - { - Expression e = (*s.clobbers)[i]; - e = e.expressionSemantic(sc); - assert(e.op == EXP.string_ && (cast(StringExp) e).sz == 1); - (*s.clobbers)[i] = e; - } - } - - // Analyse all goto labels. - if (s.labels) - { - foreach (i; 0 .. s.labels.length) - { - 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; -} - -/*********************************** - * Run semantic analysis on an CAsmDeclaration. - * Params: - * ad = asm declaration - * sc = the scope where the asm declaration is located - */ -public void gccAsmSemantic(CAsmDeclaration ad, Scope* sc) -{ - import dmd.typesem : pointerTo; - ad.code = semanticString(sc, ad.code, "asm definition"); - ad.code.type = ad.code.type.nextOf().pointerTo(); - - // Asm definition always needs emitting into the root module. - import dmd.dmodule : Module; - if (sc._module && sc._module.isRoot()) - return; - if (Module m = Module.rootModule) - m.members.push(ad); -} - -private: - -/*********************************** - * Parse an expression that evaluates to a string. - * Grammar: - * | AsmStringExpr: - * | StringLiteral - * | ( AssignExpression ) - * Params: - * p = parser state - * Returns: - * the parsed string expression - */ -Expression parseAsmString(Parser)(Parser p) -{ - if (p.token.value == TOK.leftParenthesis) - { - p.nextToken(); - Expression insn = p.parseAssignExp(); - p.check(TOK.rightParenthesis); - return insn; - } - else if (p.token.value != TOK.string_) - { - p.eSink.error(p.token.loc, "expected string literal or expression in parentheses"); - return ErrorExp.get(); - } - - return p.parsePrimaryExp(); -} - -/*********************************** - * 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) - { - // Skip over opening `[` - p.nextToken(); - // Store the symbolic name - name = p.token.ident; - p.nextToken(); - } - else - { - p.eSink.error(s.loc, "expected identifier after `[`"); - goto Lerror; - } - // Look for closing `]` - p.check(TOK.rightBracket); - // Look for the string literal and fall through - if (p.token.value == TOK.string_) - goto case; - else - goto default; - - case TOK.string_: - constraint = p.parsePrimaryExp(); - if (p.token.value != TOK.leftParenthesis) - { - arg = p.parseAssignExp(); - p.eSink.error(arg.loc, "`%s` must be surrounded by parentheses", arg.toChars()); - } - else - { - // Look for the opening `(` - p.check(TOK.leftParenthesis); - // Parse the assign expression - arg = p.parseAssignExp(); - // Look for the closing `)` - p.check(TOK.rightParenthesis); - } - - 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.eSink.error(p.token.loc, "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.eSink.error(p.token.loc, "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.eSink.error(p.token.loc, "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: - * | AsmStringExpr - * | - * | ExtAsmInstruction: - * | AsmStringExpr : Operands(opt) : Operands(opt) : Clobbers(opt) - * | - * | GotoAsmInstruction: - * | AsmStringExpr : : 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.parseAsmString(); - if (s.insn.isErrorExp()) - return s; - - if (p.token.value == TOK.semicolon || p.token.value == TOK.endOfFile) - 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 || p.token.value == TOK.endOfFile) - goto Ldone; - } -Ldone: - p.check(TOK.semicolon); - - return s; -} - -unittest -{ - import dmd.mtype : TypeBasic; - - if (!global.errorSink) - global.errorSink = new ErrorSinkCompiler; - - const errors = global.startGagging(); - scope(exit) global.endGagging(errors); - - // If this check fails, then Type._init() was called before reaching here, - // and the entire chunk of code that follows can be removed. - assert(ASTCodegen.Type.tint32 is null); - // Minimally initialize the cached types in ASTCodegen.Type, as they are - // dependencies for some fail asm tests to succeed. - ASTCodegen.Type.stringtable._init(); - scope(exit) - { - ASTCodegen.Type.deinitialize(); - ASTCodegen.Type.tint32 = null; - } - scope tint32 = new TypeBasic(ASTCodegen.Tint32); - ASTCodegen.Type.tint32 = tint32; - - // Imitates asmSemantic if version = IN_GCC. - static int semanticAsm(Token* tokens) - { - const errors = global.errors; - scope gas = new GccAsmStatement(Loc.initial, tokens); - const bool doUnittests = false; - scope p = new Parser!ASTCodegen(null, ";", false, global.errorSink, &global.compileEnv, doUnittests); - p.token = *tokens; - p.parseGccAsm(gas); - return global.errors - errors; - } - - // Imitates parseStatement for asm statements. - static void parseAsm(string input, bool expectError) - { - // Generate tokens from input test. - const bool doUnittests = false; - scope p = new Parser!ASTCodegen(null, input, false, global.errorSink, &global.compileEnv, doUnittests); - 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; - if (p.token.value == TOK.colonColon) - { - *ptoklist = p.allocateToken(); - memcpy(*ptoklist, &p.token, Token.sizeof); - (*ptoklist).value = TOK.colon; - ptoklist = &(*ptoklist).next; - - *ptoklist = p.allocateToken(); - memcpy(*ptoklist, &p.token, Token.sizeof); - (*ptoklist).value = TOK.colon; - ptoklist = &(*ptoklist).next; - } - else - { - *ptoklist = p.allocateToken(); - memcpy(*ptoklist, &p.token, Token.sizeof); - ptoklist = &(*ptoklist).next; - } - *ptoklist = null; - p.nextToken(); - } - p.check(TOK.rightCurly); - - auto res = semanticAsm(toklist); - // Checks for both unexpected passes and failures. - 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); - } }, - - // Likewise mixins, permissible so long as the result is a string. - q{ asm { (mixin(`"repne"`, `~ "scasb"`)); - } }, - - // :: token tests - q{ asm { "" : : : "memory"; } }, - q{ asm { "" :: : "memory"; } }, - q{ asm { "" : :: "memory"; } }, - q{ asm { "" ::: "memory"; } }, - ]; - - immutable string[] failAsmTests = [ - // Found 'h' when expecting ';' - q{ asm { ""h; - } }, - - // https://issues.dlang.org/show_bug.cgi?id=20592 - q{ asm { "nop" : [name] string (expr); } }, - - // Expression expected, not ';' - q{ asm { ""[; - } }, - - // Expression expected, not ':' - q{ asm { "" - : - : "g" (a ? b : : c); - } }, - - // Found ',' when expecting ':' - q{ asm { "", ""; - } }, - - // https://issues.dlang.org/show_bug.cgi?id=20593 - q{ asm { "instruction" : : "operand" 123; } }, - - // https://github.com/dlang/dmd/issues/21298 - q{ asm { 1; } }, - q{ asm { int; } }, - q{ asm { : "=r" (i); } }, - ]; - - foreach (test; passAsmTests) - parseAsm(test, false); - - foreach (test; failAsmTests) - parseAsm(test, true); -} diff --git a/gcc/d/dmd/id.d b/gcc/d/dmd/id.d index 2d10e6f991a..c5988203d1b 100644 --- a/gcc/d/dmd/id.d +++ b/gcc/d/dmd/id.d @@ -206,6 +206,7 @@ immutable Msgtable[] msgtable = { "keys" }, { "values" }, { "rehash" }, + { "dup" }, { "future", "__future" }, { "property" }, @@ -259,11 +260,15 @@ immutable Msgtable[] msgtable = { "FpopBack", "popBack" }, // For internal functions - { "aaLen", "_aaLen" }, - { "aaKeys", "_aaKeys" }, - { "aaValues", "_aaValues" }, - { "aaRehash", "_aaRehash" }, - { "_aaAsStruct" }, + { "_d_aaGetY" }, + { "_d_aaGetRvalueX" }, + { "_d_aaDel" }, + { "_d_aaEqual" }, + { "_d_aaIn" }, + { "_d_aaNew" }, + { "_d_aaLen" }, + { "_d_aaApply" }, + { "_d_aaApply2" }, { "monitorenter", "_d_monitorenter" }, { "monitorexit", "_d_monitorexit" }, { "criticalenter", "_d_criticalenter2" }, @@ -280,15 +285,15 @@ immutable Msgtable[] msgtable = { "_d_newarrayTTrace" }, { "_d_newarraymTX" }, { "_d_newarraymTXTrace" }, + { "_d_arrayliteralTX" }, + { "_d_arrayliteralTXTrace" }, { "_d_assert_fail" }, - { "dup" }, - { "_aaApply" }, - { "_aaApply2" }, { "_d_arrayctor" }, { "_d_arraysetctor" }, { "_d_arraysetassign" }, { "_d_arrayassign_l" }, { "_d_arrayassign_r" }, + { "_d_cast" }, { "imported" }, { "InterpolationHeader" }, @@ -332,6 +337,7 @@ immutable Msgtable[] msgtable = { "_d_arrayappendcTXTrace" }, { "_d_arraycatnTX" }, { "_d_arraycatnTXTrace" }, + { "_d_assocarrayliteralTX" }, // varargs implementation { "stdc" }, diff --git a/gcc/d/dmd/identifier.d b/gcc/d/dmd/identifier.d index c213597eb03..92d3a8c7950 100644 --- a/gcc/d/dmd/identifier.d +++ b/gcc/d/dmd/identifier.d @@ -221,7 +221,7 @@ nothrow: * Identifier (inside Identifier.idPool) with deterministic name based * on the source location. */ - extern (D) static Identifier generateIdWithLoc(string prefix, Loc loc, string parent = "") + extern (D) static Identifier generateIdWithLoc(string prefix, Loc loc, const void* parent = null) { // generate `_L_C` auto sl = SourceLoc(loc); @@ -248,14 +248,15 @@ nothrow: * directly, but that would unnecessary lengthen symbols names. See issue: * https://issues.dlang.org/show_bug.cgi?id=23722 */ - static struct Key { string locKey; string prefix; string parent; } + static struct Key { string locKey; string prefix; const(void)* parent; } __gshared uint[Key] counters; - string locKey = cast(string) (sl.filename ~ idBuf[]); + const locKey = cast(string) (sl.filename ~ idBuf[]); + const key = Key(locKey, prefix, parent); static if (__traits(compiles, counters.update(Key.init, () => 0u, (ref uint a) => 0u))) { // 2.082+ - counters.update(Key(locKey, prefix, parent), + counters.update(key, () => 1u, // insertion (ref uint counter) // update { @@ -267,7 +268,6 @@ nothrow: } else { - const key = Key(locKey, prefix, parent); if (auto pCounter = key in counters) { idBuf.writestring("_"); diff --git a/gcc/d/dmd/import.h b/gcc/d/dmd/import.h index 14bd889bd6a..54efbd24cc0 100644 --- a/gcc/d/dmd/import.h +++ b/gcc/d/dmd/import.h @@ -41,7 +41,6 @@ public: const char *kind() const override; Visibility visible() override; Import *syntaxCopy(Dsymbol *s) override; // copy only syntax trees - Dsymbol *toAlias() override; bool overloadInsert(Dsymbol *s) override; void accept(Visitor *v) override { v->visit(this); } diff --git a/gcc/d/dmd/importc.d b/gcc/d/dmd/importc.d index 059de62b1f9..3f5fe86dc97 100644 --- a/gcc/d/dmd/importc.d +++ b/gcc/d/dmd/importc.d @@ -89,13 +89,13 @@ Expression arrayFuncConv(Expression e, Scope* sc) auto t = e.type.toBasetype(); if (auto ta = t.isTypeDArray()) { - if (!checkAddressable(e, sc)) + if (!checkAddressable(e, sc, "take address of")) return ErrorExp.get(); e = e.castTo(sc, ta.next.pointerTo()); } else if (auto ts = t.isTypeSArray()) { - if (!checkAddressable(e, sc)) + if (!checkAddressable(e, sc, "take address of")) return ErrorExp.get(); e = e.castTo(sc, ts.next.pointerTo()); } @@ -553,10 +553,10 @@ Dsymbol handleSymbolRedeclarations(ref Scope sc, Dsymbol s, Dsymbol s2, ScopeDsy auto vd = s.isVarDeclaration(); // new declaration auto vd2 = s2.isVarDeclaration(); // existing declaration - if (vd && vd.isCmacro()) + if (vd && vd.isCmacro) return s2; - assert(!(vd2 && vd2.isCmacro())); + assert(!(vd2 && vd2.isCmacro)); if (vd && vd2) { diff --git a/gcc/d/dmd/lambdacomp.d b/gcc/d/dmd/lambdacomp.d index 9f9fd77b7e2..2b8a8ed4987 100644 --- a/gcc/d/dmd/lambdacomp.d +++ b/gcc/d/dmd/lambdacomp.d @@ -25,6 +25,7 @@ import dmd.dsymbol; import dmd.dsymbolsem; import dmd.dtemplate; import dmd.expression; +import dmd.expressionsem : getConstInitializer; import dmd.func; import dmd.hdrgen; import dmd.mangle; diff --git a/gcc/d/dmd/lexer.d b/gcc/d/dmd/lexer.d index ed9f7f1ce77..62855fe93f9 100644 --- a/gcc/d/dmd/lexer.d +++ b/gcc/d/dmd/lexer.d @@ -1706,7 +1706,7 @@ class Lexer } break; } - stringbuffer.writeByte(c); + stringbuffer.writeByte(cast(char)c); } } @@ -1750,7 +1750,7 @@ class Lexer if (n & 1) { error("odd number (%d) of hex characters in hex string", n); - stringbuffer.writeByte(v); + stringbuffer.writeByte(cast(char)v); } t.setString(stringbuffer); stringPostfix(t); @@ -1777,7 +1777,7 @@ class Lexer if (n & 1) { v = (v << 4) | c; - stringbuffer.writeByte(v); + stringbuffer.writeByte(cast(char)v); } else v = c; @@ -2195,7 +2195,7 @@ class Lexer } break; } - stringbuffer.writeByte(c); + stringbuffer.writeByte(cast(char)c); } } diff --git a/gcc/d/dmd/mangle/cpp.d b/gcc/d/dmd/mangle/cpp.d index 7e9f0205d1c..8a429037dfc 100644 --- a/gcc/d/dmd/mangle/cpp.d +++ b/gcc/d/dmd/mangle/cpp.d @@ -26,7 +26,7 @@ import dmd.astenums; import dmd.attrib; import dmd.declaration; import dmd.dsymbol; -import dmd.dsymbolsem : isGNUABITag; +import dmd.dsymbolsem : isGNUABITag, toAlias; import dmd.dtemplate; import dmd.errors; import dmd.expression; diff --git a/gcc/d/dmd/mangle/package.d b/gcc/d/dmd/mangle/package.d index cc797614ed0..a6a80b7be7b 100644 --- a/gcc/d/dmd/mangle/package.d +++ b/gcc/d/dmd/mangle/package.d @@ -145,6 +145,7 @@ import dmd.declaration; import dmd.dinterpret; import dmd.dmodule; import dmd.dsymbol; +import dmd.dsymbolsem : toAlias; import dmd.dtemplate; import dmd.errors; import dmd.expression; @@ -318,6 +319,14 @@ void mangleType(Type t, ubyte modMask, ref OutBuffer buf, ref Backref backref) /************************************************************* + * Mangle type of function. writing it to `buf` + * Params: + * t = function type + * ta = consult original function type for attributes + * modMask = type modifiers + * tret = function return type + * buf = sink for mangling characters + * backref = back reference */ void mangleFuncType(TypeFunction t, TypeFunction ta, ubyte modMask, Type tret, ref OutBuffer buf, ref Backref backref) { @@ -401,7 +410,7 @@ void mangleFuncType(TypeFunction t, TypeFunction ta, ubyte modMask, Type tret, r foreach (idx, param; t.parameterList) mangleParameter(param, buf, backref); //if (buf.data[buf.length - 1] == '@') assert(0); - buf.writeByte('Z' - t.parameterList.varargs); // mark end of arg list + buf.writeByte(cast(ubyte)('Z' - t.parameterList.varargs)); // mark end of arg list if (tret !is null) mangleType(tret, 0, buf, backref); t.inuse--; @@ -1205,11 +1214,11 @@ void writeBackRef(ref OutBuffer buf, size_t pos) @safe while (mul >= base) { auto dig = cast(ubyte)(pos / mul); - buf.writeByte('A' + dig); + buf.writeByte(cast(char)('A' + dig)); pos -= dig * mul; mul /= base; } - buf.writeByte('a' + cast(ubyte)pos); + buf.writeByte(cast(char)('a' + pos)); } diff --git a/gcc/d/dmd/module.h b/gcc/d/dmd/module.h index e1780146402..c81b6d7531e 100644 --- a/gcc/d/dmd/module.h +++ b/gcc/d/dmd/module.h @@ -10,6 +10,8 @@ #pragma once +#include + #include "dsymbol.h" struct ModuleDeclaration; @@ -28,12 +30,11 @@ enum PKG PKGpackage // already determined that's an actual package }; -enum class Edition : unsigned char +enum class Edition : uint16_t { - none = 0u, - legacy = 1u, - v2024 = 2u, - latest = 2u, + v2023 = 2023, + v2024, + v2025, }; class Package : public ScopeDsymbol @@ -98,6 +99,8 @@ public: SearchOptFlags searchCacheFlags; // cached flags d_bool insearch; + d_bool isExplicitlyOutOfBinary; // Is this module known to be out of binary, and must be DllImport'd? + // module from command line we're imported from, // i.e. a module that will be taken all the // way to an object file @@ -120,7 +123,6 @@ public: size_t namelen; // length of module name in characters static Module* create(const char *arg, Identifier *ident, int doDocComment, int doHdrGen); - static const char *find(const char *filename); static Module *load(Loc loc, Identifiers *packages, Identifier *ident); const char *kind() const override; @@ -140,19 +142,11 @@ public: bool isCoreModule(Identifier *ident); // Back end - - int doppelganger; // sub-module Symbol *cov; // private uint[] __coverage; DArray covb; // bit array of valid code line numbers - Symbol *sictor; // module order independent constructor - Symbol *sctor; // module constructor - Symbol *sdtor; // module destructor - Symbol *ssharedctor; // module shared constructor - Symbol *sshareddtor; // module shared destructor - Symbol *stest; // module unit test - Symbol *sfilename; // symbol for filename + bool hasCDtor; void *ctfe_cov; // stores coverage information from ctfe diff --git a/gcc/d/dmd/mtype.d b/gcc/d/dmd/mtype.d index c9b57449658..c11ce9d3900 100644 --- a/gcc/d/dmd/mtype.d +++ b/gcc/d/dmd/mtype.d @@ -30,8 +30,6 @@ import dmd.dtemplate; import dmd.enumsem; import dmd.errors; import dmd.expression; -import dmd.dsymbolsem : determineSize; -import dmd.globals; import dmd.hdrgen; import dmd.id; import dmd.identifier; @@ -47,9 +45,8 @@ import dmd.typesem; import dmd.visitor; enum LOGDOTEXP = 0; // log ::dotExp() -enum LOGDEFAULTINIT = 0; // log ::defaultInit() -enum SIZE_INVALID = (~cast(uinteger_t)0); // error return from size() functions +enum SIZE_INVALID = (~cast(ulong)0); // error return from size() functions static if (__VERSION__ < 2095) { @@ -688,19 +685,6 @@ extern (C++) abstract class Type : ASTNode return false; } - /************************** - * When T is mutable, - * Given: - * T a, b; - * Can we bitwise assign: - * a = b; - * ? - */ - bool isAssignable() - { - return true; - } - /************************** * Returns true if T can be converted to boolean value. */ @@ -1323,19 +1307,6 @@ extern (C++) abstract class Type : ASTNode return s; } - /*************************************** - * Use when we prefer the default initializer to be a literal, - * rather than a global immutable variable. - */ - Expression defaultInitLiteral(Loc loc) - { - static if (LOGDEFAULTINIT) - { - printf("Type::defaultInitLiteral() '%s'\n", toChars()); - } - return defaultInit(this, loc); - } - /*************************************** * Return !=0 if the type or any of its subtypes is wild. */ @@ -1399,9 +1370,9 @@ extern (C++) abstract class Type : ASTNode * Return the mask that an integral type will * fit into. */ - extern (D) final uinteger_t sizemask() + extern (D) final ulong sizemask() { - uinteger_t m; + ulong m; switch (toBasetype().ty) { case Tbool: @@ -1572,11 +1543,6 @@ extern (C++) final class TypeError : Type return this; } - override Expression defaultInitLiteral(Loc loc) - { - return ErrorExp.get(); - } - override void accept(Visitor v) { v.visit(this); @@ -2118,17 +2084,6 @@ extern (C++) final class TypeVector : Type return false; } - override Expression defaultInitLiteral(Loc loc) - { - //printf("TypeVector::defaultInitLiteral()\n"); - assert(basetype.ty == Tsarray); - Expression e = basetype.defaultInitLiteral(loc); - auto ve = new VectorExp(loc, e, this); - ve.type = this; - ve.dim = cast(int)(basetype.size(loc) / elementType().size(loc)); - return ve; - } - TypeBasic elementType() { assert(basetype.ty == Tsarray); @@ -2219,25 +2174,6 @@ extern (C++) final class TypeSArray : TypeArray return next.alignment(); } - override Expression defaultInitLiteral(Loc loc) - { - static if (LOGDEFAULTINIT) - { - printf("TypeSArray::defaultInitLiteral() '%s'\n", toChars()); - } - size_t d = cast(size_t)dim.toInteger(); - Expression elementinit; - if (next.ty == Tvoid) - elementinit = tuns8.defaultInitLiteral(loc); - else - elementinit = next.defaultInitLiteral(loc); - auto elements = new Expressions(d); - foreach (ref e; *elements) - e = null; - auto ae = new ArrayLiteralExp(loc, this, elementinit, elements); - return ae; - } - override bool hasUnsafeBitpatterns() { return next.hasUnsafeBitpatterns(); @@ -2603,110 +2539,6 @@ extern (C++) final class TypeFunction : TypeNext return linkage == LINK.d && parameterList.varargs == VarArg.variadic; } - /********************************* - * Append error message to buf. - * Input: - * buf = message sink - * format = printf format - */ - extern(C) static void getMatchError(ref OutBuffer buf, const(char)* format, ...) - { - if (global.gag && !global.params.v.showGaggedErrors) - return; - va_list ap; - va_start(ap, format); - buf.vprintf(format, ap); - va_end(ap); - } - - /******************************** - * Convert an `argumentList`, which may contain named arguments, into - * a list of arguments in the order of the parameter list. - * - * Params: - * argumentList = array of function arguments - * buf = if not null, append error message to it - * Returns: re-ordered argument list, or `null` on error - */ - extern(D) Expressions* resolveNamedArgs(ArgumentList argumentList, OutBuffer* buf) - { - Expression[] args = argumentList.arguments ? (*argumentList.arguments)[] : null; - ArgumentLabel[] names = argumentList.names ? (*argumentList.names)[] : null; - const nParams = parameterList.length(); // cached because O(n) - auto newArgs = new Expressions(nParams); - newArgs.zero(); - size_t ci = 0; - bool hasNamedArgs = false; - const bool isVariadic = parameterList.varargs != VarArg.none; - foreach (i, arg; args) - { - if (!arg) - { - ci++; - continue; - } - auto name = i < names.length ? names[i].name : null; - if (name) - { - hasNamedArgs = true; - const pi = findParameterIndex(name); - if (pi == -1) - { - if (buf) - getMatchError(*buf, "no parameter named `%s`", name.toChars()); - return null; - } - ci = pi; - } - if (ci >= newArgs.length) - { - if (!isVariadic) - { - // Without named args, let the caller diagnose argument overflow - if (hasNamedArgs && buf) - getMatchError(*buf, "argument `%s` goes past end of parameter list", arg.toChars()); - return null; - } - while (ci >= newArgs.length) - newArgs.push(null); - } - - if ((*newArgs)[ci]) - { - if (buf) - getMatchError(*buf, "parameter `%s` assigned twice", parameterList[ci].toChars()); - return null; - } - (*newArgs)[ci++] = arg; - } - foreach (i, arg; (*newArgs)[]) - { - if (arg || parameterList[i].defaultArg) - continue; - - if (isVariadic && i + 1 == newArgs.length) - continue; - - // dtemplate sets `defaultArg=null` to avoid semantic on default arguments, - // don't complain about missing arguments in that case - if (this.incomplete) - continue; - - if (buf) - getMatchError(*buf, "missing argument for parameter #%d: `%s`", - i + 1, parameterToChars(parameterList[i], this, false)); - return null; - } - // strip trailing nulls from default arguments - size_t e = newArgs.length; - while (e > 0 && (*newArgs)[e - 1] is null) - { - --e; - } - newArgs.setDim(e); - return newArgs; - } - /// Returns: `true` the function is `isInOutQual` or `isInOutParam` ,`false` otherwise. bool iswild() const pure nothrow @safe @nogc { @@ -2736,23 +2568,6 @@ extern (C++) final class TypeFunction : TypeNext { v.visit(this); } - - /** - * Look for the index of parameter `ident` in the parameter list - * - * Params: - * ident = identifier of parameter to search for - * Returns: index of parameter with name `ident` or -1 if not found - */ - private extern(D) ptrdiff_t findParameterIndex(Identifier ident) - { - foreach (i, p; this.parameterList) - { - if (p.ident == ident) - return i; - } - return -1; - } } /*********************************************************** @@ -3129,99 +2944,6 @@ extern (C++) final class TypeStruct : Type return sym.alignment; } - /*************************************** - * Use when we prefer the default initializer to be a literal, - * rather than a global immutable variable. - */ - override Expression defaultInitLiteral(Loc loc) - { - static if (LOGDEFAULTINIT) - { - printf("TypeStruct::defaultInitLiteral() '%s'\n", toChars()); - } - sym.size(loc); - if (sym.sizeok != Sizeok.done) - return ErrorExp.get(); - - auto structelems = new Expressions(sym.nonHiddenFields()); - uint offset = 0; - foreach (j; 0 .. structelems.length) - { - VarDeclaration vd = sym.fields[j]; - Expression e; - if (vd.inuse) - { - error(loc, "circular reference to `%s`", vd.toPrettyChars()); - return ErrorExp.get(); - } - if (vd.offset < offset || vd.type.size() == 0) - e = null; - else if (vd._init) - { - if (vd._init.isVoidInitializer()) - e = null; - else - e = vd.getConstInitializer(false); - } - else - e = vd.type.defaultInitLiteral(loc); - if (e && e.op == EXP.error) - return e; - if (e) - offset = vd.offset + cast(uint)vd.type.size(); - (*structelems)[j] = e; - } - auto structinit = new StructLiteralExp(loc, sym, structelems); - - /* Copy from the initializer symbol for larger symbols, - * otherwise the literals expressed as code get excessively large. - */ - if (size(this, loc) > target.ptrsize * 4 && !needsNested()) - structinit.useStaticInit = true; - - structinit.type = this; - return structinit; - } - - override bool isAssignable() - { - bool assignable = true; - uint offset = ~0; // dead-store initialize to prevent spurious warning - - sym.determineSize(sym.loc); - - /* If any of the fields are const or immutable, - * then one cannot assign this struct. - */ - for (size_t i = 0; i < sym.fields.length; i++) - { - VarDeclaration v = sym.fields[i]; - //printf("%s [%d] v = (%s) %s, v.offset = %d, v.parent = %s\n", sym.toChars(), i, v.kind(), v.toChars(), v.offset, v.parent.kind()); - if (i == 0) - { - } - else if (v.offset == offset) - { - /* If any fields of anonymous union are assignable, - * then regard union as assignable. - * This is to support unsafe things like Rebindable templates. - */ - if (assignable) - continue; - } - else - { - if (!assignable) - return false; - } - assignable = v.type.isMutable() && v.type.isAssignable(); - offset = v.offset; - //printf(" -> assignable = %d\n", assignable); - } - - return assignable; - } - override bool isBoolean() { return false; @@ -3383,11 +3105,6 @@ extern (C++) final class TypeEnum : Type return memType().isString(); } - override bool isAssignable() - { - return memType().isAssignable(); - } - override bool needsDestruction() { return memType().needsDestruction(); @@ -3576,16 +3293,14 @@ extern (C++) final class TypeTuple : Type extern (D) this(Type t1) { super(Ttuple); - arguments = new Parameters(); - arguments.push(new Parameter(Loc.initial, STC.none, t1, null, null, null)); + arguments = new Parameters(new Parameter(Loc.initial, STC.none, t1, null, null, null)); } extern (D) this(Type t1, Type t2) { super(Ttuple); - arguments = new Parameters(); - arguments.push(new Parameter(Loc.initial, STC.none, t1, null, null, null)); - arguments.push(new Parameter(Loc.initial, STC.none, t2, null, null, null)); + arguments = new Parameters(new Parameter(Loc.initial, STC.none, t1, null, null, null), + new Parameter(Loc.initial, STC.none, t2, null, null, null)); } static TypeTuple create() @safe @@ -4294,12 +4009,32 @@ void attributesApply(const TypeFunction tf, void delegate(string) dg, TRUSTforma dg("@nogc"); if (tf.isProperty) dg("@property"); + + /* The following is more or less like dmd.hdrgen.stcToBuffer(), in the future + * it should be merged. The idea is consistent ordering + */ + STC stc; if (tf.isRef) - dg("ref"); + stc |= STC.ref_; if (tf.isReturn && !tf.isReturnInferred) - dg("return"); + stc |= STC.return_; if (tf.isScopeQual && !tf.isScopeInferred) - dg("scope"); + stc |= STC.scope_; + if (tf.isReturnScope) + stc |= STC.returnScope; + final switch (buildScopeRef(stc)) + { + case ScopeRef.None: break; + case ScopeRef.Scope: dg("scope"); break; + case ScopeRef.Return: dg("return"); break; + case ScopeRef.ReturnScope: dg("return"); dg("scope"); break; + case ScopeRef.ReturnRef: dg("return"); dg("ref"); break; + case ScopeRef.Ref: dg("ref"); break; + case ScopeRef.RefScope: dg("ref"); dg("scope"); break; + case ScopeRef.ReturnRef_Scope: dg("return"); dg("ref"); dg("scope"); break; + case ScopeRef.Ref_ReturnScope: dg("ref"); dg("return"); dg("scope"); break; + } + if (tf.isLive) dg("@live"); diff --git a/gcc/d/dmd/mtype.h b/gcc/d/dmd/mtype.h index 4d303acf3c3..7f9c026918f 100644 --- a/gcc/d/dmd/mtype.h +++ b/gcc/d/dmd/mtype.h @@ -44,6 +44,7 @@ namespace dmd { Type *typeSemantic(Type *t, Loc loc, Scope *sc); Type *merge(Type *type); + Expression *defaultInitLiteral(Type *t, Loc loc); } enum class TY : uint8_t @@ -238,7 +239,6 @@ public: virtual bool isUnsigned(); virtual bool isScopeClass(); virtual bool isString(); - virtual bool isAssignable(); virtual bool isBoolean(); bool isConst() const { return (mod & MODconst) != 0; } bool isImmutable() const { return (mod & MODimmutable) != 0; } @@ -265,7 +265,6 @@ public: virtual ClassDeclaration *isClassHandle(); virtual structalign_t alignment(); - virtual Expression *defaultInitLiteral(Loc loc); virtual int hasWild() const; virtual bool hasVoidInitPointers(); virtual bool hasUnsafeBitpatterns(); @@ -315,7 +314,6 @@ public: const char *kind() override; TypeError *syntaxCopy() override; - Expression *defaultInitLiteral(Loc loc) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -376,7 +374,6 @@ public: bool isScalar() override; bool isUnsigned() override; bool isBoolean() override; - Expression *defaultInitLiteral(Loc loc) override; TypeBasic *elementType(); void accept(Visitor *v) override { v->visit(this); } @@ -400,7 +397,6 @@ public: unsigned alignsize() override; bool isString() override; structalign_t alignment() override; - Expression *defaultInitLiteral(Loc loc) override; bool hasUnsafeBitpatterns() override; bool hasVoidInitPointers() override; bool hasInvariant() override; @@ -691,8 +687,6 @@ public: unsigned alignsize() override; TypeStruct *syntaxCopy() override; structalign_t alignment() override; - Expression *defaultInitLiteral(Loc loc) override; - bool isAssignable() override; bool isBoolean() override; bool needsDestruction() override; bool needsCopyOrPostblit() override; @@ -723,7 +717,6 @@ public: bool isUnsigned() override; bool isBoolean() override; bool isString() override; - bool isAssignable() override; bool needsDestruction() override; bool needsCopyOrPostblit() override; bool needsNested() override; diff --git a/gcc/d/dmd/mustuse.d b/gcc/d/dmd/mustuse.d index fab9723636c..84a991e6932 100644 --- a/gcc/d/dmd/mustuse.d +++ b/gcc/d/dmd/mustuse.d @@ -14,7 +14,6 @@ import dmd.dscope; import dmd.dsymbol; import dmd.errors; import dmd.expression; -import dmd.globals; import dmd.identifier; import dmd.location; diff --git a/gcc/d/dmd/nogc.d b/gcc/d/dmd/nogc.d index 5e3c164acbb..f528d411bfd 100644 --- a/gcc/d/dmd/nogc.d +++ b/gcc/d/dmd/nogc.d @@ -16,18 +16,22 @@ module dmd.nogc; import core.stdc.stdio; import dmd.aggregate; +import dmd.arraytypes; import dmd.astenums; -import dmd.declaration; import dmd.common.outbuffer; +import dmd.declaration; import dmd.dmodule; import dmd.dscope; -import dmd.dtemplate : isDsymbol; import dmd.dsymbol : PASS; +import dmd.dtemplate : isDsymbol; import dmd.errors; import dmd.escape; import dmd.expression; +import dmd.expressionsem; import dmd.func; import dmd.globals; +import dmd.id; +import dmd.identifier; import dmd.init; import dmd.location; import dmd.mtype; @@ -35,6 +39,7 @@ import dmd.rootobject : RootObject, DYNCAST; import dmd.semantic2; import dmd.semantic3; import dmd.tokens; +import dmd.typesem : unqualify; import dmd.visitor; import dmd.visitor.postorder; @@ -124,7 +129,8 @@ public: override void visit(ArrayLiteralExp e) { - if (e.type.toBasetype().isTypeSArray() || !e.elements || !e.elements.length || e.onstack) + const dim = e.elements ? e.elements.length : 0; + if (e.type.toBasetype().isTypeSArray() || dim == 0 || e.onstack) return; if (setGC(e, "this array literal")) return; @@ -135,6 +141,25 @@ public: return; } + if (!global.params.useGC) + { + if (!checkOnly) + { + version (IN_GCC) + error(e.loc, "this array literal requires the GC and cannot be used with `???`"); + else + error(e.loc, "this array literal requires the GC and cannot be used with `-betterC`"); + } + err = true; + return; + } + + if (!lowerArrayLiteral(e, sc)) + { + err = true; + return; + } + f.printGCUsage(e.loc, "array literal may cause a GC allocation"); } diff --git a/gcc/d/dmd/ob.d b/gcc/d/dmd/ob.d index 099f8115898..7e48b28150f 100644 --- a/gcc/d/dmd/ob.d +++ b/gcc/d/dmd/ob.d @@ -26,13 +26,12 @@ import dmd.astenums; import dmd.declaration; import dmd.dscope; import dmd.dsymbol; +import dmd.dsymbolsem : toAlias; import dmd.dtemplate; import dmd.errors; import dmd.escape; import dmd.expression; - import dmd.func; -import dmd.globals; import dmd.hdrgen; import dmd.identifier; import dmd.init; @@ -79,6 +78,8 @@ void oblive(FuncDeclaration funcdecl) checkObErrors(obstate); } +private: + alias ObNodes = Array!(ObNode*); alias StmtState = dmd.stmtstate.StmtState!ObNode; diff --git a/gcc/d/dmd/objc.d b/gcc/d/dmd/objc.d index 32878b57af9..5b7afd5dd57 100644 --- a/gcc/d/dmd/objc.d +++ b/gcc/d/dmd/objc.d @@ -31,8 +31,6 @@ import dmd.errors; import dmd.expression; import dmd.expressionsem; import dmd.func; -import dmd.globals; -import dmd.gluelayer; import dmd.hdrgen; import dmd.id; import dmd.identifier; @@ -433,7 +431,7 @@ extern(C++) private final class Unsupported : Objc { extern(D) final this() { - ObjcGlue.initialize(); + } override void setObjc(ClassDeclaration cd) @@ -545,7 +543,6 @@ extern(C++) private final class Supported : Objc { VersionCondition.addPredefinedGlobalIdent("D_ObjectiveC"); - ObjcGlue.initialize(); ObjcSelector._init(); } diff --git a/gcc/d/dmd/opover.d b/gcc/d/dmd/opover.d index 7baaeaa7fcf..791762ac18b 100644 --- a/gcc/d/dmd/opover.d +++ b/gcc/d/dmd/opover.d @@ -30,7 +30,6 @@ import dmd.expression; import dmd.expressionsem; import dmd.func; import dmd.funcsem; -import dmd.globals; import dmd.hdrgen; import dmd.id; import dmd.identifier; @@ -129,8 +128,7 @@ Objects* opToArg(Scope* sc, EXP op) { Expression e = new StringExp(Loc.initial, EXPtoString(stripAssignOp(op))); e = e.expressionSemantic(sc); - auto tiargs = new Objects(); - tiargs.push(e); + auto tiargs = new Objects(e); return tiargs; } @@ -340,7 +338,11 @@ Expression opOverloadArray(ArrayExp ae, Scope* sc) // Convert to IndexExp if (ae.arguments.length == 1) - return new IndexExp(ae.loc, ae.e1, (*ae.arguments)[0]).expressionSemantic(sc); + { + auto idxexp = new IndexExp(ae.loc, ae.e1, (*ae.arguments)[0]); + idxexp.modifiable = ae.modifiable; + return idxexp.expressionSemantic(sc); + } } break; } @@ -439,8 +441,7 @@ Expression opOverloadCast(CastExp e, Scope* sc, Type att = null) return build_overload(e.loc, sc, e.e1, null, fd); } } - auto tiargs = new Objects(); - tiargs.push(e.to); + auto tiargs = new Objects(e.to); return dotTemplateCall(e.e1, Id.opCast, tiargs).expressionSemantic(sc); } // Didn't find it. Forward to aliasthis @@ -551,14 +552,15 @@ Expression opOverloadBinary(BinExp e, Scope* sc, Type[2] aliasThisStop) // Try opBinary and opBinaryRight Dsymbol s = search_function(ad1, Id.opBinary); - if (s && !s.isTemplateDeclaration()) + + if (s && !(s.isTemplateDeclaration() || s.isOverloadSet)) { error(e.e1.loc, "`%s.opBinary` isn't a template", e.e1.toChars()); return ErrorExp.get(); } Dsymbol s_r = search_function(ad2, Id.opBinaryRight); - if (s_r && !s_r.isTemplateDeclaration()) + if (s_r && !(s_r.isTemplateDeclaration() || s_r.isOverloadSet())) { error(e.e2.loc, "`%s.opBinaryRight` isn't a template", e.e2.toChars()); return ErrorExp.get(); @@ -906,6 +908,8 @@ Expression opOverloadBinaryAssign(BinAssignExp e, Scope* sc, Type[2] aliasThisSt { if (auto ae = e.e1.isArrayExp()) { + markArrayExpModifiable(ae); + ae.e1 = ae.e1.expressionSemantic(sc); ae.e1 = resolveProperties(sc, ae.e1); Expression ae1old = ae.e1; @@ -993,13 +997,16 @@ Expression opOverloadBinaryAssign(BinAssignExp e, Scope* sc, Type[2] aliasThisSt if (Expression result = e.binSemanticProp(sc)) return result; + if (auto result = rewriteIndexAssign(e, sc, aliasThisStop)) + return result; + // Don't attempt 'alias this' if an error occurred if (e.e1.type.isTypeError() || e.e2.type.isTypeError()) return ErrorExp.get(); AggregateDeclaration ad1 = isAggregate(e.e1.type); Dsymbol s = search_function(ad1, Id.opOpAssign); - if (s && !s.isTemplateDeclaration()) + if (s && !(s.isTemplateDeclaration() || s.isOverloadSet())) { error(e.loc, "`%s.opOpAssign` isn't a template", e.e1.toChars()); return ErrorExp.get(); @@ -1046,7 +1053,7 @@ private Expression pickBestBinaryOverload(Scope* sc, Objects* tiargs, Dsymbol s, if (s) { functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2), null); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) + if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors)) return ErrorExp.get(); } FuncDeclaration lastf = m.lastf; @@ -1054,7 +1061,7 @@ private Expression pickBestBinaryOverload(Scope* sc, Objects* tiargs, Dsymbol s, if (s_r) { functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1), null); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) + if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors)) return ErrorExp.get(); } if (m.count > 1) @@ -1187,6 +1194,8 @@ Dsymbol search_function(ScopeDsymbol ad, Identifier funcid) return fd; if (TemplateDeclaration td = s2.isTemplateDeclaration()) return td; + if (OverloadSet os = s2.isOverloadSet()) + return os; } return null; } diff --git a/gcc/d/dmd/optimize.d b/gcc/d/dmd/optimize.d index b13f8c64584..88a947f6b02 100644 --- a/gcc/d/dmd/optimize.d +++ b/gcc/d/dmd/optimize.d @@ -1138,9 +1138,16 @@ Expression optimize(Expression e, int result, bool keepLvalue = false) void visitEqual(EqualExp e) { - //printf("EqualExp::optimize(result = %x) %s\n", result, e.toChars()); + //printf("EqualExp::optimize(result = %d) %s\n", result, e.toChars()); + if (auto lowering = e.lowering) + { + optimize(lowering, result, keepLvalue); + return; + } + if (binOptimize(e, WANTvalue)) return; + Expression e1 = fromConstInitializer(result, e.e1); Expression e2 = fromConstInitializer(result, e.e2); if (e1.op == EXP.error) diff --git a/gcc/d/dmd/parse.d b/gcc/d/dmd/parse.d index c57f1d3f7f7..ee078f359d7 100644 --- a/gcc/d/dmd/parse.d +++ b/gcc/d/dmd/parse.d @@ -142,6 +142,18 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer md = new AST.ModuleDeclaration(loc, a, id, msg, isdeprecated); + if (token.value == TOK.int32Literal) + { + auto edition = token.intvalue; + if (edition < Edition.min || Edition.max < edition) + { + error("module edition %lld must be in the range %d ... %d", edition, Edition.min, Edition.max); + edition = edition.min; + } + mod.edition = cast(Edition)edition; + nextToken(); + } + if (token.value != TOK.semicolon) error("`;` expected following module declaration instead of `%s`", token.toChars()); nextToken(); @@ -238,7 +250,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (auto id = (*exps)[0].isIdentifierExp()) if (id.ident == Id.__edition_latest_do_not_use) { - mod.edition = Edition.latest; + mod.edition = Edition.max; // latest edition continue; } @@ -1287,6 +1299,38 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer return orig; } + if (0) // replace this with -preview=return in next PR + switch (orig & (STC.out_ | STC.ref_ | STC.scope_ | STC.return_)) + { + case STC.return_ | STC.ref_: + if (!(orig & STC.returnRef)) + error("`return` `ref` attribute pair must be written as '`return ref`'"); + break; + + case STC.return_ | STC.out_: + if (!(orig & STC.returnRef)) + error("`return` `out` attribute pair must be written as '`return out`'"); + break; + + case STC.return_ | STC.scope_: + if (!(orig & STC.returnScope)) + error("`return` `scope` attribute pair must be written as '`return scope`'"); + break; + + case STC.return_ | STC.ref_ | STC.scope_: + if (!(orig & (STC.returnRef | STC.returnScope))) + error("`return` `ref` `scope` attribute triple must be written as '`return ref`' and '`scope`' or '`return scope`' and '`ref`'"); + break; + + case STC.return_ | STC.out_ | STC.scope_: + if (!(orig & (STC.returnRef | STC.returnScope))) + error("`return` `out` `scope` attribute triple must be written as '`return out`' and '`scope`' or '`return scope`' and '`out`'"); + break; + + default: + break; + } + checkConflictSTCGroup(STC.const_ | STC.immutable_ | STC.manifest); checkConflictSTCGroup(STC.gshared | STC.shared_); checkConflictSTCGroup!true(STC.safeGroup); @@ -1398,6 +1442,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer */ private STC parsePostfix(STC storageClass, AST.Expressions** pudas) { + const STC prefix = storageClass; while (1) { STC stc; @@ -1428,10 +1473,23 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer break; case TOK.return_: + { stc = STC.return_; - if (peekNext() == TOK.scope_) - stc |= STC.returnScope; // recognize `return scope` + TOK next = peekNext(); + if (next == TOK.scope_) // recognize the `return scope` pair + { + stc |= STC.scope_ | STC.returnScope; + nextToken(); // consume it + } + else if (next == TOK.ref_) // recognize the `return ref` pair + { + stc |= STC.ref_ | STC.returnRef; + nextToken(); // consume it + } + else if (prefix & STC.ref_) // https://dlang.org/spec/function.html#struct-return-methods + stc |= STC.returnRef; // treat member function `ref int fp() return;` as `int fp() return ref;` break; + } case TOK.scope_: stc = STC.scope_; @@ -1470,6 +1528,14 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer nextToken(); continue; } + + /* This is so we can enforce `return ref`, but not foul things up with an extra `ref` + * which is not accounted for in the semantic routines. The problem comes about because + * there's no way to attach `return ref` to the implicit `this` parameter but not the return value. + */ + if (!(prefix & STC.ref_) && storageClass & STC.ref_ && storageClass & STC.returnRef) + storageClass &= ~STC.ref_; + return storageClass; } storageClass = appendStorageClass(storageClass, stc); @@ -2814,7 +2880,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer STC varargsStc; // Attributes allowed for ... - enum VarArgsStc = STC.const_ | STC.immutable_ | STC.shared_ | STC.scope_ | STC.return_ | STC.returnScope; + enum VarArgsStc = STC.const_ | STC.immutable_ | STC.shared_ | STC.scope_ | STC.return_ | STC.returnScope | STC.returnRef; check(TOK.leftParenthesis); while (1) @@ -2925,10 +2991,26 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer goto L2; case TOK.return_: + { stc = STC.return_; - if (peekNext() == TOK.scope_) - stc |= STC.returnScope; + TOK next = peekNext(); + if (next == TOK.scope_) // recognize the `return scope` pair + { + stc |= STC.scope_ | STC.returnScope; + nextToken(); // consume it + } + else if (next == TOK.ref_) // recognize the `return ref` pair + { + stc |= STC.ref_ | STC.returnRef; + nextToken(); // consume it + } + else if (next == TOK.out_) // recognize the `return out` pair + { + stc |= STC.out_ | STC.returnRef; + nextToken(); // consume it + } goto L2; + } L2: storageClass = appendStorageClass(storageClass, stc); continue; @@ -4707,9 +4789,9 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (width) { if (_init) - error("initializer not allowed for bit-field declaration"); + error("initializer not allowed for bitfield declaration"); if (storage_class) - error("storage class not allowed for bit-field declaration"); + error("storage class not allowed for bitfield declaration"); s = new AST.BitFieldDeclaration(width.loc, t, ident, width); } else @@ -7104,21 +7186,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer error("matching `}` expected, not end of file"); break; - case TOK.colonColon: // treat as two separate : tokens for iasmgcc - *ptoklist = allocateToken(); - **ptoklist = this.token; - (*ptoklist).value = TOK.colon; - ptoklist = &(*ptoklist).next; - - *ptoklist = allocateToken(); - **ptoklist = this.token; - (*ptoklist).value = TOK.colon; - ptoklist = &(*ptoklist).next; - - *ptoklist = null; - nextToken(); - continue; - default: *ptoklist = allocateToken(); **ptoklist = this.token; @@ -9307,7 +9374,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer return e; } - private AST.Expression parseCondExp() + AST.Expression parseCondExp() { auto e = parseOrOrExp(); if (token.value == TOK.question) diff --git a/gcc/d/dmd/root/array.d b/gcc/d/dmd/root/array.d index a80fc804cbd..ee35e805d66 100644 --- a/gcc/d/dmd/root/array.d +++ b/gcc/d/dmd/root/array.d @@ -55,6 +55,18 @@ public: if (data.ptr && data.ptr != &smallarray[0]) mem.xfree(data.ptr); } + + // this is using a template constraint because of ambiguity with this(size_t) when T is + // int, and c++ header generation doesn't accept wrapping this in static if + extern(D) this()(T[] elems ...) pure nothrow if (is(T == struct) || is(T == class)) + { + this(elems.length); + foreach(i; 0 .. elems.length) + { + this[i] = elems[i]; + } + } + ///returns elements comma separated in [] extern(D) const(char)[] toString() const { @@ -1181,3 +1193,19 @@ pure nothrow @nogc @safe unittest b.popFront(); assert(b == expected[]); } + + +/// Test Array array constructor +pure nothrow unittest +{ + //check to make sure that this works with the aliases in arraytypes.d + import dmd.rootobject; + alias Objects = Array!RootObject; + + auto ro1 = new RootObject(); + auto ro2 = new RootObject(); + + auto aoo = new Objects(ro1, ro2); + assert((*aoo)[0] is ro1); + assert((*aoo)[1] is ro2); +} diff --git a/gcc/d/dmd/root/filename.d b/gcc/d/dmd/root/filename.d index 5ad07750b3b..b25392b6386 100644 --- a/gcc/d/dmd/root/filename.d +++ b/gcc/d/dmd/root/filename.d @@ -468,7 +468,20 @@ nothrow: assert(buildPath("a/", "bb", "ccc") == "a/bb/ccc"); } - // Split a path and append the results to `array` + /**** + * Splits a delimiter-separated path string (e.g., from an environment variable like `PATH`) + * into individual path fragments and appends them to the given array. + * + * This is a convenience wrapper around `splitPath` that collects all fragments + * into an `Array!(const(char)*)`. + * + * Params: + * path = A null-terminated string containing path fragments separated by platform-specific delimiters. + * array = The array to which each extracted fragment will be appended. + * + * See_Also: + * `splitPath` + */ extern (C++) static void appendSplitPath(const(char)* path, ref Strings array) { int sink(const(char)* p) nothrow @@ -487,7 +500,7 @@ nothrow: * sink = send the path pieces here, end when sink() returns !=0 * path = the path to split up. */ - static void splitPath(int delegate(const(char)*) nothrow sink, const(char)* path) + static void splitPath(scope int delegate(const(char)*) nothrow sink, const(char)* path) { if (!path) return; diff --git a/gcc/d/dmd/root/hash.d b/gcc/d/dmd/root/hash.d index d327f4b258d..40dc2eb1979 100644 --- a/gcc/d/dmd/root/hash.d +++ b/gcc/d/dmd/root/hash.d @@ -13,7 +13,7 @@ module dmd.root.hash; // MurmurHash2 was written by Austin Appleby, and is placed in the public // domain. The author hereby disclaims copyright to this source code. -// https://sites.google.com/site/murmurhash/ +// https://github.com/aappleby/smhasher/ uint calcHash(scope const(char)[] data) @nogc nothrow pure @safe { return calcHash(cast(const(ubyte)[])data); diff --git a/gcc/d/dmd/safe.d b/gcc/d/dmd/safe.d index 3be9efecc2c..4a3eeb77d03 100644 --- a/gcc/d/dmd/safe.d +++ b/gcc/d/dmd/safe.d @@ -28,7 +28,7 @@ import dmd.errors; import dmd.expression; import dmd.func; import dmd.funcsem : isRootTraitsCompilesScope; -import dmd.globals : FeatureState, global; +import dmd.globals : FeatureState; import dmd.id; import dmd.identifier; import dmd.location; @@ -85,7 +85,6 @@ bool checkUnsafeAccess(Scope* sc, Expression e, bool readonly, bool printmsg) if (ad.sizeok != Sizeok.done) ad.determineSize(ad.loc); - import dmd.globals : FeatureState; const hasPointers = v.type.hasPointers(); if (hasPointers) { diff --git a/gcc/d/dmd/scope.h b/gcc/d/dmd/scope.h index b57d77d9882..0b1d9b66bd0 100644 --- a/gcc/d/dmd/scope.h +++ b/gcc/d/dmd/scope.h @@ -67,10 +67,6 @@ struct Scope final ForeachStatement *fes; // if nested function for ForeachStatement, this is it Scope *callsc; // used for __FUNCTION__, __PRETTY_FUNCTION__ and __MODULE__ Dsymbol *inunion; // !=null if processing members of a union - d_bool nofree; // true if shouldn't free it - d_bool inLoop; // true if inside a loop (where constructor calls aren't allowed) - d_bool inDefaultArg; // true if inside a default argument (where __FILE__, etc are evaluated at the call site) - int intypeof; // in typeof(exp) VarDeclaration *lastVar; // Previous symbol used to prevent goto-skips-init ErrorSink *eSink; // sink for error messages @@ -96,13 +92,12 @@ struct Scope final PragmaDeclaration *inlining; // inlining strategy for functions Visibility visibility; // visibility for class members - int explicitVisibility; // set if in an explicit visibility attribute StorageClass stc; // storage class DeprecatedDeclaration *depdecl; // customized deprecation message - uint16_t flags; + uint16_t flags, flags2; uint16_t previews; // state of preview switches bool ctor() const; diff --git a/gcc/d/dmd/semantic2.d b/gcc/d/dmd/semantic2.d index 26f0fa8a254..6e4c7d10d3c 100644 --- a/gcc/d/dmd/semantic2.d +++ b/gcc/d/dmd/semantic2.d @@ -813,10 +813,11 @@ private void doGNUABITagSemantic(ref Expression e, ref Expression* lastTag) */ void lowerStaticAAs(VarDeclaration vd, Scope* sc) { - if (vd.storage_class & STC.manifest) - return; if (auto ei = vd._init.isExpInitializer()) - lowerStaticAAs(ei.exp, sc); + { + scope v = new StaticAAVisitor(sc, vd.storage_class); + ei.exp.accept(v); + } } /** @@ -828,7 +829,7 @@ void lowerStaticAAs(VarDeclaration vd, Scope* sc) */ void lowerStaticAAs(Expression e, Scope* sc) { - scope v = new StaticAAVisitor(sc); + scope v = new StaticAAVisitor(sc, STC.none); e.accept(v); } @@ -837,33 +838,26 @@ private extern(C++) final class StaticAAVisitor : SemanticTimeTransitiveVisitor { alias visit = SemanticTimeTransitiveVisitor.visit; Scope* sc; + STC storage_class; - this(Scope* sc) scope @safe + this(Scope* sc, STC storage_class) scope @safe { this.sc = sc; + this.storage_class = storage_class; } override void visit(AssocArrayLiteralExp aaExp) { - if (!verifyHookExist(aaExp.loc, *sc, Id._aaAsStruct, "initializing static associative arrays", Id.object)) - return; - - Expression hookFunc = new IdentifierExp(aaExp.loc, Id.empty); - hookFunc = new DotIdExp(aaExp.loc, hookFunc, Id.object); - hookFunc = new DotIdExp(aaExp.loc, hookFunc, Id._aaAsStruct); - auto arguments = new Expressions(); - arguments.push(aaExp); - Expression loweredExp = new CallExp(aaExp.loc, hookFunc, arguments); - - sc = sc.startCTFE(); - loweredExp = loweredExp.expressionSemantic(sc); - loweredExp = resolveProperties(sc, loweredExp); - sc = sc.endCTFE(); - loweredExp = loweredExp.ctfeInterpret(); - - aaExp.lowering = loweredExp; - - semanticTypeInfo(sc, loweredExp.type); + if (!aaExp.lowering) + expressionSemantic(aaExp, sc); + assert(aaExp.lowering); + if (!(storage_class & STC.manifest)) // manifest constants create runtime copies + if (!aaExp.loweringCtfe) + { + aaExp.loweringCtfe = aaExp.lowering.ctfeInterpret(); + aaExp.loweringCtfe.accept(this); // lower AAs in keys and values + } + semanticTypeInfo(sc, aaExp.lowering.type); } // https://issues.dlang.org/show_bug.cgi?id=24602 diff --git a/gcc/d/dmd/semantic3.d b/gcc/d/dmd/semantic3.d index bafb2e7d871..4c4c0ec2b35 100644 --- a/gcc/d/dmd/semantic3.d +++ b/gcc/d/dmd/semantic3.d @@ -1,5 +1,7 @@ /** - * Performs the semantic3 stage, which deals with function bodies. + * Performs the semantic3 stage of semantic analysis, which finalizes + * function bodies and late semantic checks for templates, mixins, + * aggregates, and special members. * * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) @@ -343,7 +345,7 @@ private extern(C++) final class Semantic3Visitor : Visitor sc2.linkage = funcdecl.isCsymbol() ? LINK.c : LINK.d; sc2.stc &= STC.flowThruFunction; sc2.visibility = Visibility(Visibility.Kind.public_); - sc2.explicitVisibility = 0; + sc2.explicitVisibility = false; sc2.aligndecl = null; if (funcdecl.ident != Id.require && funcdecl.ident != Id.ensure) { @@ -402,7 +404,7 @@ private extern(C++) final class Semantic3Visitor : Visitor // functions may be widely used by dmd-compiled projects. // It also gives more time for the implementation of dual-context // functions to be reworked as a frontend-only feature. - if (funcdecl.hasDualContext()) + if (funcdecl.hasDualContext) { .deprecation(funcdecl.loc, "%s `%s` function requires a dual-context, which is deprecated", funcdecl.kind, funcdecl.toPrettyChars); if (auto ti = sc2.parent ? sc2.parent.isInstantiated() : null) @@ -615,7 +617,7 @@ private extern(C++) final class Semantic3Visitor : Visitor if (!funcdecl.fbody) funcdecl.fbody = new CompoundStatement(Loc.initial, new Statements()); - if (funcdecl.isNaked()) + if (funcdecl.isNaked) { fpreinv = null; // can't accommodate with no stack frame fpostinv = null; @@ -925,6 +927,7 @@ private extern(C++) final class Semantic3Visitor : Visitor exp = exp.implicitCastTo(sc2, tret); exp = exp.toLvalue(sc2, "`ref` return"); + checkAddressable(exp, sc2, "`ref` return"); checkReturnEscapeRef(*sc2, exp, false); exp = exp.optimize(WANTvalue, /*keepLvalue*/ true); } @@ -970,7 +973,7 @@ private extern(C++) final class Semantic3Visitor : Visitor /* https://issues.dlang.org/show_bug.cgi?id=10789 * If NRVO is not possible, all returned lvalues should call their postblits. */ - if (!funcdecl.isNRVO()) + if (!funcdecl.isNRVO) exp = doCopyOrMove(sc2, exp, f.next, true, true); if (tret.hasPointers()) @@ -1255,7 +1258,7 @@ private extern(C++) final class Semantic3Visitor : Visitor { // 'this' is the monitor vsync = new VarExp(funcdecl.loc, funcdecl.vthis); - if (funcdecl.hasDualContext()) + if (funcdecl.hasDualContext) { vsync = new PtrExp(funcdecl.loc, vsync); vsync = new IndexExp(funcdecl.loc, vsync, IntegerExp.literal!0); @@ -1298,7 +1301,7 @@ private extern(C++) final class Semantic3Visitor : Visitor } } - if (funcdecl.isNaked() && (funcdecl.fensures || funcdecl.frequires)) + if (funcdecl.isNaked && (funcdecl.fensures || funcdecl.frequires)) .error(funcdecl.loc, "%s `%s` naked assembly functions with contracts are not supported", funcdecl.kind, funcdecl.toPrettyChars); sc2.ctorflow.callSuper = CSX.none; @@ -1516,9 +1519,8 @@ private extern(C++) final class Semantic3Visitor : Visitor auto ts = new ThrowStatement(ctor.loc, new IdentifierExp(ctor.loc, id)); auto handler = new CompoundStatement(ctor.loc, ss, ts); - auto catches = new Catches(); auto ctch = new Catch(ctor.loc, getException(), id, handler); - catches.push(ctch); + auto catches = new Catches(ctch); ctor.fbody = new TryCatchStatement(ctor.loc, ctor.fbody, catches); } @@ -1604,8 +1606,7 @@ private extern(C++) final class Semantic3Visitor : Visitor (ad.type && ad.type.ty != Terror)) { // Evaluate: RTinfo!type - auto tiargs = new Objects(); - tiargs.push(ad.type); + auto tiargs = new Objects(ad.type); auto ti = new TemplateInstance(ad.loc, Type.rtinfo, tiargs); Scope* sc3 = ti.tempdecl._scope.startCTFE(); @@ -1628,8 +1629,83 @@ private extern(C++) final class Semantic3Visitor : Visitor sd.semanticTypeInfoMembers(); ad.semanticRun = PASS.semantic3done; } + + override void visit(TypeInfoAssociativeArrayDeclaration ti) + { + if (ti.semanticRun >= PASS.semantic3) + return; + ti.semanticRun = PASS.semantic3; + + auto t = ti.tinfo.isTypeAArray(); + Loc loc = t.loc; + auto sc2 = sc ? sc : ti._scope; + + auto makeDotExp(Identifier hook) + { + // always called with naked types + auto tiargs = new Objects(t.index, t.next); + + Expression id = new IdentifierExp(loc, Id.empty); + id = new DotIdExp(loc, id, Id.object); + id = new DotIdExp(loc, id, Id.TypeInfo_AssociativeArray); + return new DotTemplateInstanceExp(loc, id, hook, tiargs); + } + + void notTemplateFunction(Loc loc, Identifier id) + { + error(loc, "`%s` isn't a template function", id.toChars()); + } + + // generate ti.entry + auto tempinst = makeDotExp(Id.Entry); + auto e = expressionSemantic(tempinst, sc2); + assert(e.type); + ti.entry = e.type; + if (auto ts = ti.entry.isTypeStruct()) + { + ts.sym.requestTypeInfo = true; + if (auto tmpl = ts.sym.isInstantiated()) + tmpl.minst = sc2._module.importedFrom; // ensure it gets emitted + } + semanticTypeInfo(sc2, ti.entry); // might get deferred + + // generate ti.xtoHash + auto aaGetHash = Identifier.idPool("aaGetHash"); + auto hashinst = makeDotExp(aaGetHash); + e = expressionSemantic(hashinst, sc2); + if (!e.isErrorExp()) + { + if (!e.isVarExp() || !e.type.isTypeFunction()) + notTemplateFunction(e.loc, aaGetHash); + else + { + ti.xtoHash = e.isVarExp().var; + if (auto tmpl = ti.xtoHash.parent.isTemplateInstance()) + tmpl.minst = sc2._module.importedFrom; // ensure it gets emitted + } + } + + // generate ti.xopEqual + auto aaOpEqual = Identifier.idPool("aaOpEqual"); + auto equalinst = makeDotExp(aaOpEqual); + e = expressionSemantic(equalinst, sc2); + if (!e.isErrorExp()) + { + if (!e.isVarExp() || !e.type.isTypeFunction()) + notTemplateFunction(e.loc, aaOpEqual); + else + { + ti.xopEqual = e.isVarExp().var; + if (auto tmpl = ti.xopEqual.parent.isTemplateInstance()) + tmpl.minst = sc2._module.importedFrom; // ensure it gets emitted + } + } + visit(cast(ASTCodegen.TypeInfoDeclaration)ti); + } } +/// Helper for semantic3 analysis of functions. +/// This struct is part of a WIP refactoring to simplify large `visit(FuncDeclaration)` logic. private struct FuncDeclSem3 { // The FuncDeclaration subject to Semantic analysis @@ -1663,6 +1739,37 @@ private struct FuncDeclSem3 } } +/*************************************** + * Search sd for a member function of the form: + * `extern (D) string toString();` + * Params: + * sd = struct declaration to search + * Returns: + * FuncDeclaration of `toString()` if found, `null` if not + */ +FuncDeclaration search_toString(StructDeclaration sd) +{ + Dsymbol s = search_function(sd, Id.tostring); + FuncDeclaration fd = s ? s.isFuncDeclaration() : null; + if (!fd) + return null; + + __gshared TypeFunction tftostring; + if (!tftostring) + { + tftostring = new TypeFunction(ParameterList(), Type.tstring, LINK.d); + tftostring = tftostring.merge().toTypeFunction(); + } + return fd.overloadExactMatch(tftostring); +} + +/** + * Ensures special members of a struct are fully analysed + * before the backend emits TypeInfo. + * + * Handles late semantic analysis for members like `opEquals`, `opCmp`, + * `toString`, `toHash`, postblit, and destructor. + */ void semanticTypeInfoMembers(StructDeclaration sd) { if (sd.xeq && @@ -1715,13 +1822,22 @@ void semanticTypeInfoMembers(StructDeclaration sd) } } -/*********************************************** - * Check that the function contains any closure. - * If it's @nogc, report suitable errors. - * This is mostly consistent with FuncDeclaration::needsClosure(). +/** + * Determine whether the given function will need to allocate a _closure_ and + * verify that such an allocation is allowed under the current compilation + * settings. * - * Returns: - * true if any errors occur. + * Whenever an error is emitted, every nested function that actually closes + * over a variable is listed in a supplemental diagnostic, together with the + * location of the captured variable’s declaration. (This extra walk is + * skipped when the compiler is gagged.) + * + * See_Also: + * $(UL + * $(LI `FuncDeclaration.needsClosure`) + * $(LI `FuncDeclaration.setGC`) + * $(LI `FuncDeclaration.printGCUsage`) + * ) */ extern (D) bool checkClosure(FuncDeclaration fd) { diff --git a/gcc/d/dmd/sideeffect.d b/gcc/d/dmd/sideeffect.d index 5984466d3b9..402a74c0f9b 100644 --- a/gcc/d/dmd/sideeffect.d +++ b/gcc/d/dmd/sideeffect.d @@ -19,7 +19,6 @@ import dmd.expression; import dmd.expressionsem; import dmd.func; import dmd.funcsem; -import dmd.globals; import dmd.hdrgen; import dmd.id; import dmd.identifier; @@ -392,6 +391,7 @@ VarDeclaration copyToTemp(STC stc, const char[] name, Expression e) * e0 = a new side effect part will be appended to it. * e = original expression * alwaysCopy = if true, build new temporary variable even if e is trivial. + * stc = storage class flags to add to new temporary variable * Returns: * When e is trivial and alwaysCopy == false, e itself is returned. * Otherwise, a new VarExp is returned. @@ -399,7 +399,7 @@ VarDeclaration copyToTemp(STC stc, const char[] name, Expression e) * e's lvalue-ness will be handled well by STC.ref_ or STC.rvalue. */ Expression extractSideEffect(Scope* sc, const char[] name, - ref Expression e0, Expression e, bool alwaysCopy = false) + ref Expression e0, Expression e, bool alwaysCopy = false, STC stc = STC.exptemp) { //printf("extractSideEffect(e: %s)\n", e.toChars()); @@ -413,8 +413,8 @@ Expression extractSideEffect(Scope* sc, const char[] name, (sc.ctfe ? !hasSideEffect(e) : isTrivialExp(e))) return e; - auto vd = copyToTemp(STC.none, name, e); - vd.storage_class |= e.isLvalue() ? STC.ref_ : STC.rvalue; + stc |= (e.isLvalue() ? STC.ref_ : STC.rvalue); + auto vd = copyToTemp(stc, name, e); e0 = Expression.combine(e0, new DeclarationExp(vd.loc, vd) .expressionSemantic(sc)); diff --git a/gcc/d/dmd/statement.d b/gcc/d/dmd/statement.d index 2ade0a9a9bb..baf928a13f0 100644 --- a/gcc/d/dmd/statement.d +++ b/gcc/d/dmd/statement.d @@ -439,8 +439,7 @@ extern (C++) final class MixinStatement : Statement extern (D) this(Loc loc, Expression exp) { - Expressions* exps = new Expressions(); - exps.push(exp); + Expressions* exps = new Expressions(exp); this(loc, exps); } @@ -1740,8 +1739,15 @@ extern (C++) final class LabelDsymbol : Dsymbol */ extern (C++) class AsmStatement : Statement { - Token* tokens; - bool caseSensitive; // for register names + Token* tokens; // linked list of tokens for one instruction or pseudo op + static struct BitFields + { + bool caseSensitive; // for register names, and only turned on when doing MASM style inline asm + bool isVolatile; // ImportC asm "volatile" + bool isInline; // ImportC asm "inline" + } + import dmd.common.bitfields; + mixin(generateBitFields!(BitFields, ubyte)); extern (D) this(Loc loc, Token* tokens) @safe { @@ -1772,10 +1778,10 @@ extern (C++) class AsmStatement : Statement extern (C++) final class InlineAsmStatement : AsmStatement { void* asmcode; + ulong regs; // mask of registers modified (must match regm_t in back end) uint asmalign; // alignment of this statement - uint regs; // mask of registers modified (must match regm_t in back end) bool refparam; // true if function parameter is referenced - bool naked; // true if function is to be naked + bool naked; // true if function is to be naked (no prolog/epilog) extern (D) this(Loc loc, Token* tokens) @safe { diff --git a/gcc/d/dmd/statement.h b/gcc/d/dmd/statement.h index 5f962b26d63..c7d941d3433 100644 --- a/gcc/d/dmd/statement.h +++ b/gcc/d/dmd/statement.h @@ -715,7 +715,15 @@ class AsmStatement : public Statement { public: Token *tokens; - d_bool caseSensitive; // for register names +private: + uint8_t bitFields; +public: + bool caseSensitive() const; // for register names + bool caseSensitive(bool v); + bool isVolatile() const; // importC asm volatile + bool isVolatile(bool v); + bool isInline() const; // importC asm inline + bool isInline(bool v); AsmStatement *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } @@ -725,8 +733,8 @@ class InlineAsmStatement final : public AsmStatement { public: void *asmcode; - unsigned asmalign; // alignment of this statement - unsigned regs; // mask of registers modified (must match regm_t in back end) + unsigned long long regs; // mask of registers modified (must match regm_t in back end) + unsigned asmalign; // alignment of this statement d_bool refparam; // true if function parameter is referenced d_bool naked; // true if function is to be naked diff --git a/gcc/d/dmd/statementsem.d b/gcc/d/dmd/statementsem.d index 523ed7b3978..9a9079943b5 100644 --- a/gcc/d/dmd/statementsem.d +++ b/gcc/d/dmd/statementsem.d @@ -365,10 +365,9 @@ Statement statementSemanticVisit(Statement s, Scope* sc) handler = new CompoundStatement(Loc.initial, handler, ts); } - auto catches = new Catches(); auto ctch = new Catch(Loc.initial, getThrowable(), id, handler); ctch.internalCatch = true; - catches.push(ctch); + auto catches = new Catches(ctch); Statement st = new TryCatchStatement(Loc.initial, _body, catches); if (sfinally) @@ -621,8 +620,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) * } finally { v2.~this(); } * } finally { v1.~this(); } */ - auto ainit = new Statements(); - ainit.push(fs._init); + auto ainit = new Statements(fs._init); fs._init = null; ainit.push(fs); Statement s = new CompoundStatement(fs.loc, ainit); @@ -1013,7 +1011,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) Type tindex = (*fs.parameters)[0].type; if (!tindex.isIntegral()) { - error(fs.loc, "foreach: key cannot be of non-integral type `%s`", tindex.toChars()); + error(fs.loc, "foreach: index cannot be of non-integral type `%s`", tindex.toChars()); return retError(); } /* What cases to deprecate implicit conversions for: @@ -1382,8 +1380,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) Expression ve = new VarExp(loc, vd); ve.type = tfront; - auto exps = new Expressions(); - exps.push(ve); + auto exps = new Expressions(ve); int pos = 0; while (exps.length < dim) { @@ -1461,6 +1458,16 @@ Statement statementSemanticVisit(Statement s, Scope* sc) */ //printf("ForeachRangeStatement::semantic() %p\n", fs); + + if (fs.param.storageClass & STC.manifest) + { + error(fs.loc, "cannot declare `enum` loop variables for non-unrolled foreach"); + } + if (fs.param.storageClass & STC.alias_) + { + error(fs.loc, "cannot declare `alias` loop variables for non-unrolled foreach"); + } + auto loc = fs.loc; fs.lwr = fs.lwr.expressionSemantic(sc); fs.lwr = resolveProperties(sc, fs.lwr); @@ -2126,10 +2133,9 @@ Statement statementSemanticVisit(Statement s, Scope* sc) auto arguments = new Expressions(); arguments.push(ss.condition); - auto compileTimeArgs = new Objects(); // The type & label no. - compileTimeArgs.push(new TypeExp(ss.loc, ss.condition.type.nextOf())); + auto compileTimeArgs = new Objects(new TypeExp(ss.loc, ss.condition.type.nextOf())); // The switch labels foreach (caseString; *csCopy) @@ -2709,10 +2715,13 @@ Statement statementSemanticVisit(Statement s, Scope* sc) */ Scope* sc2 = sc.push(); sc2.eSink = global.errorSinkNull; - bool err = checkReturnEscapeRef(*sc2, rs.exp, true); + bool addressable = checkAddressable(rs.exp, sc2, "`ref` return"); + bool escapes = checkReturnEscapeRef(*sc2, rs.exp, true); sc2.pop(); - if (err) + if (!addressable) + turnOffRef(() { checkAddressable(rs.exp, sc, "`ref` return"); }); + else if (escapes) turnOffRef(() { checkReturnEscapeRef(*sc, rs.exp, false); }); else if (!rs.exp.type.constConv(tf.next)) turnOffRef( @@ -3096,11 +3105,9 @@ Statement statementSemanticVisit(Statement s, Scope* sc) auto tmp = copyToTemp(STC.none, "__sync", ss.exp); tmp.dsymbolSemantic(sc); - auto cs = new Statements(); - cs.push(new ExpStatement(ss.loc, tmp)); - - auto args = new Parameters(); - args.push(new Parameter(Loc.initial, STC.none, ClassDeclaration.object.type, null, null, null)); + auto cs = new Statements(new ExpStatement(ss.loc, tmp)); + auto args = new Parameters(new Parameter(Loc.initial, STC.none, 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)); @@ -3131,8 +3138,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) tmp.storage_class |= STC.temp | STC.shared_ | STC.static_; Expression tmpExp = new VarExp(ss.loc, tmp); - auto cs = new Statements(); - cs.push(new ExpStatement(ss.loc, tmp)); + auto cs = new Statements(new ExpStatement(ss.loc, tmp)); /* This is just a dummy variable for "goto skips declaration" error. * Backend optimizer could remove this unused variable. @@ -3141,8 +3147,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) v.dsymbolSemantic(sc); cs.push(new ExpStatement(ss.loc, v)); - auto enterArgs = new Parameters(); - enterArgs.push(new Parameter(Loc.initial, STC.none, t.pointerTo(), null, null, null)); + auto enterArgs = new Parameters(new Parameter(Loc.initial, STC.none, t.pointerTo(), null, null, null)); FuncDeclaration fdenter = FuncDeclaration.genCfunc(enterArgs, Type.tvoid, Id.criticalenter, STC.nothrow_); Expression e = new AddrExp(ss.loc, tmpExp); @@ -3151,8 +3156,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) e.type = Type.tvoid; // do not run semantic on e cs.push(new ExpStatement(ss.loc, e)); - auto exitArgs = new Parameters(); - exitArgs.push(new Parameter(Loc.initial, STC.none, t, null, null, null)); + auto exitArgs = new Parameters(new Parameter(Loc.initial, STC.none, t, null, null, null)); FuncDeclaration fdexit = FuncDeclaration.genCfunc(exitArgs, Type.tvoid, Id.criticalexit, STC.nothrow_); e = new CallExp(ss.loc, fdexit, tmpExp); @@ -3743,7 +3747,7 @@ public bool throwSemantic(Loc loc, ref Expression exp, Scope* sc) exp = exp.checkGC(sc); if (exp.isErrorExp()) return false; - if (!exp.type.isNaked()) + if (!exp.type.isNaked) { // @@@DEPRECATED_2.112@@@ // Deprecated in 2.102, change into an error & return false in 2.112 @@ -3864,10 +3868,8 @@ private extern(D) Expression applyArray(ForeachStatement fs, Expression flde, FuncDeclaration fdapply; TypeDelegate dgty; - auto params = new Parameters(); - params.push(new Parameter(Loc.initial, STC.in_, tn.arrayOf(), null, null, null)); - auto dgparams = new Parameters(); - dgparams.push(new Parameter(Loc.initial, STC.none, Type.tvoidptr, null, null, null)); + auto params = new Parameters(new Parameter(Loc.initial, STC.in_, tn.arrayOf(), null, null, null)); + auto dgparams = new Parameters(new Parameter(Loc.initial, STC.none, Type.tvoidptr, null, null, null)); if (dim == 2) dgparams.push(new Parameter(Loc.initial, STC.none, Type.tvoidptr, null, null, null)); dgty = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d)); @@ -3892,7 +3894,6 @@ private extern(D) Expression applyArray(ForeachStatement fs, Expression flde, private extern(D) Expression applyAssocArray(ForeachStatement fs, Expression flde, Type tab) { auto taa = tab.isTypeAArray(); - Expression ec; const dim = fs.parameters.length; // Check types Parameter p = (*fs.parameters)[0]; @@ -3903,8 +3904,8 @@ private extern(D) Expression applyAssocArray(ForeachStatement fs, Expression fld Type ti = (isRef ? taa.index.addMod(MODFlags.const_) : taa.index); if (isRef ? !ti.constConv(ta) : !ti.implicitConvTo(ta)) { - error(fs.loc, "`foreach`: index must be type `%s`, not `%s`", - ti.toChars(), ta.toChars()); + error(fs.loc, "`foreach`: index parameter `%s%s` must be type `%s`, not `%s`", + isRef ? "ref ".ptr : "".ptr, p.toChars(), ti.toChars(), ta.toChars()); return null; } p = (*fs.parameters)[1]; @@ -3914,55 +3915,28 @@ private extern(D) Expression applyAssocArray(ForeachStatement fs, Expression fld Type taav = taa.nextOf(); if (isRef ? !taav.constConv(ta) : !taav.implicitConvTo(ta)) { - error(fs.loc, "`foreach`: value must be type `%s`, not `%s`", - taav.toChars(), ta.toChars()); + error(fs.loc, "`foreach`: value parameter `%s%s` must be type `%s`, not `%s`", + isRef ? "ref ".ptr : "".ptr, p.toChars(), taav.toChars(), ta.toChars()); return null; } /* Call: - * extern(C) int _aaApply(void*, in size_t, int delegate(void*)) - * _aaApply(aggr, keysize, flde) - * - * extern(C) int _aaApply2(void*, in size_t, int delegate(void*, void*)) - * _aaApply2(aggr, keysize, flde) + * int _d_aaApply(V[K] aa, int delegate(V*)) + * or + * int _d_aaApply2(V[K] aa, int delegate(K*, V*)) */ - __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(Loc.initial, STC.none, Type.tvoid.pointerTo(), null, null, null)); - params.push(new Parameter(Loc.initial, STC.const_, Type.tsize_t, null, null, null)); - auto dgparams = new Parameters(); - dgparams.push(new Parameter(Loc.initial, STC.none, Type.tvoidptr, null, null, null)); - if (dim == 2) - dgparams.push(new Parameter(Loc.initial, STC.none, Type.tvoidptr, null, null, null)); - fldeTy[i] = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d)); - params.push(new Parameter(Loc.initial, STC.none, fldeTy[i], null, null, null)); - fdapply[i] = FuncDeclaration.genCfunc(params, Type.tint32, i ? Id._aaApply2 : Id._aaApply); - } - - auto exps = new Expressions(); - exps.push(fs.aggr); - auto keysize = taa.index.size(); - if (keysize == SIZE_INVALID) - return null; - assert(keysize < keysize.max - target.ptrsize); - keysize = (keysize + (target.ptrsize - 1)) & ~(target.ptrsize - 1); - // paint delegate argument to the type runtime expects - Expression fexp = flde; - if (!fldeTy[i].equals(flde.type)) - { - fexp = new CastExp(fs.loc, flde, flde.type); - fexp.type = fldeTy[i]; - } - exps.push(new IntegerExp(Loc.initial, keysize, Type.tsize_t)); - exps.push(fexp); - ec = new VarExp(Loc.initial, fdapply[i], false); - ec = new CallExp(fs.loc, ec, exps); - ec.type = Type.tint32; // don't run semantic() on ec - return ec; + auto loc = fs.loc; + Identifier hook = dim == 2 ? Id._d_aaApply2 : Id._d_aaApply; + Expression func = new IdentifierExp(loc, Id.empty); + func = new DotIdExp(loc, func, Id.object); + auto tiargs = new Objects(taa.index.substWildTo(MODFlags.const_), + taav.substWildTo(MODFlags.const_), + flde.type.substWildTo(MODFlags.const_)); + func = new DotTemplateInstanceExp(loc, func, hook, tiargs); + + auto arguments = new Expressions(fs.aggr, flde); + auto call = new CallExp(loc, func, arguments); + return call; } private extern(D) Statement loopReturn(Expression e, Statements* cases, Loc loc) @@ -3976,12 +3950,11 @@ private extern(D) Statement loopReturn(Expression e, Statements* cases, Loc loc) // Construct a switch statement around the return value // of the apply function. Statement s; - auto a = new Statements(); // default: break; takes care of cases 0 and 1 s = new BreakStatement(Loc.initial, null); s = new DefaultStatement(Loc.initial, s); - a.push(s); + auto a = new Statements(s); // cases 2... foreach (i, c; *cases) @@ -4007,6 +3980,10 @@ private extern(D) Statement loopReturn(Expression e, Statements* cases, Loc loc) */ private FuncExp foreachBodyToFunction(Scope* sc, ForeachStatement fs, TypeFunction tfld) { + auto tab = fs.aggr.type.toBasetype(); + auto taa = tab.isTypeAArray(); + const isStaticOrDynamicArray = tab.isTypeSArray() || tab.isTypeDArray(); + auto params = new Parameters(); foreach (i, p; *fs.parameters) { @@ -4015,6 +3992,7 @@ private FuncExp foreachBodyToFunction(Scope* sc, ForeachStatement fs, TypeFuncti p.type = p.type.typeSemantic(fs.loc, sc); p.type = p.type.addStorageClass(p.storageClass); + p.type = p.type.substWildTo(MODFlags.const_); if (tfld) { Parameter param = tfld.parameterList[i]; @@ -4044,8 +4022,26 @@ private FuncExp foreachBodyToFunction(Scope* sc, ForeachStatement fs, TypeFuncti LcopyArg: id = Identifier.generateId("__applyArg", cast(int)i); - Initializer ie = new ExpInitializer(fs.loc, new IdentifierExp(fs.loc, id)); - auto v = new VarDeclaration(fs.loc, p.type, p.ident, ie); + // Make sure `p.type` matches the signature expected by the druntime helpers. + const isIndexParam = i == 0 && fs.parameters.length == 2; + Type userType = p.type; + Expression initExp = new IdentifierExp(fs.loc, id); + if (taa) + { + p.type = isIndexParam ? taa.index : taa.nextOf(); + p.type = p.type.substWildTo(MODFlags.const_); + } + else if (isStaticOrDynamicArray && isIndexParam) + { + p.type = Type.tsize_t; + // If the user used an incompatible index type (e.g., `int` instead + // of `size_t` - which is deprecated nowadays), then add a cast. + if (!p.type.implicitConvTo(userType)) + initExp = new CastExp(fs.loc, initExp, userType); + } + + Initializer initializer = new ExpInitializer(fs.loc, initExp); + auto v = new VarDeclaration(fs.loc, userType, p.ident, initializer); v.storage_class |= STC.temp | (stc & STC.scope_); Statement s = new ExpStatement(fs.loc, v); fs._body = new CompoundStatement(fs.loc, s, fs._body); @@ -4104,7 +4100,7 @@ void catchSemantic(Catch c, Scope* sc) // reference .object.Throwable c.type = getThrowable(); } - else if (!c.type.isNaked() && !c.type.isConst()) + else if (!c.type.isNaked && !c.type.isConst()) { // @@@DEPRECATED_2.115@@@ // Deprecated in 2.105, change into an error & uncomment assign in 2.115 @@ -4352,7 +4348,8 @@ public auto makeTupleForeach(Scope* sc, bool isStatic, bool isDecl, ForeachState const bool skipCheck = isStatic && needExpansion; if (!skipCheck && (dim < 1 || dim > 2)) { - error(fs.loc, "only one (value) or two (key,value) arguments allowed for sequence `foreach`"); + error(fs.loc, "only one (element) or two (index, element) arguments allowed for sequence `foreach`, not %llu", + ulong(dim)); return returnEarly(); } @@ -4411,10 +4408,11 @@ public auto makeTupleForeach(Scope* sc, bool isStatic, bool isDecl, ForeachState const bool skip = isStatic && needExpansion; if (!skip && dim == 2) { - // Declare key + // Declare index if (p.isReference() || p.isLazy()) { - error(fs.loc, "no storage class for key `%s`", p.ident.toChars()); + error(fs.loc, "invalid storage class `%s` for index `%s`", + stcToString(p.storageClass).ptr, p.ident.toChars()); return returnEarly(); } @@ -4429,7 +4427,7 @@ public auto makeTupleForeach(Scope* sc, bool isStatic, bool isDecl, ForeachState if (!p.type.isIntegral()) { - error(fs.loc, "foreach: key cannot be of non-integral type `%s`", + error(fs.loc, "foreach: index cannot be of non-integral type `%s`", p.type.toChars()); return returnEarly(); } @@ -4474,7 +4472,8 @@ public auto makeTupleForeach(Scope* sc, bool isStatic, bool isDecl, ForeachState if (storageClass & (STC.out_ | STC.lazy_) || storageClass & STC.ref_ && !te) { - error(fs.loc, "no storage class for value `%s`", ident.toChars()); + error(fs.loc, "invalid storage class `%s` for element `%s`", + stcToString(p.storageClass).ptr, ident.toChars()); return false; } Declaration var; @@ -4572,7 +4571,13 @@ public auto makeTupleForeach(Scope* sc, bool isStatic, bool isDecl, ForeachState var = new AliasDeclaration(loc, ident, t); if (paramtype) { - error(fs.loc, "cannot specify element type for symbol `%s`", fs.toChars()); + error(fs.loc, "cannot specify element type for symbol `%s`", ident.toChars()); + return false; + } + if (storageClass & STC.manifest) + { + error(fs.loc, "invalid storage class `enum` for element `%s`", + ident.toChars()); return false; } } @@ -4689,8 +4694,7 @@ private Statements* flatten(Statement statement, Scope* sc) { static auto errorStatements() { - auto a = new Statements(); - a.push(new ErrorStatement()); + auto a = new Statements(new ErrorStatement()); return a; } @@ -4734,8 +4738,7 @@ private Statements* flatten(Statement statement, Scope* sc) toCBuffer(s, &buf, &hgs); printf("tm ==> s = %s\n", buf.peekChars()); } - auto a = new Statements(); - a.push(s); + auto a = new Statements(s); return a; case STMT.Forwarding: @@ -4787,8 +4790,7 @@ private Statements* flatten(Statement statement, Scope* sc) else s = cs.elsebody; - auto a = new Statements(); - a.push(s); + auto a = new Statements(s); return a; case STMT.StaticForeach: @@ -4802,8 +4804,7 @@ private Statements* flatten(Statement statement, Scope* sc) { return result; } - result = new Statements(); - result.push(s); + result = new Statements(s); return result; } else diff --git a/gcc/d/dmd/target.d b/gcc/d/dmd/target.d index 46926cd1419..5f5d284a64c 100644 --- a/gcc/d/dmd/target.d +++ b/gcc/d/dmd/target.d @@ -335,10 +335,10 @@ struct TargetC BitFieldStyle bitFieldStyle; /// different C compilers do it differently /** - * Indicates whether the specified bit-field contributes to the alignment + * Indicates whether the specified bitfield contributes to the alignment * of the containing aggregate. * E.g., (not all) ARM ABIs do NOT ignore anonymous (incl. 0-length) - * bit-fields. + * bitfields. */ extern (C++) bool contributesToAggregateAlignment(BitFieldDeclaration bfd); } diff --git a/gcc/d/dmd/template.h b/gcc/d/dmd/template.h index bfc41514804..db810511e62 100644 --- a/gcc/d/dmd/template.h +++ b/gcc/d/dmd/template.h @@ -29,6 +29,12 @@ class Expression; class FuncDeclaration; class Parameter; +namespace dmd +{ + bool isDiscardable(TemplateInstance*); + bool needsCodegen(TemplateInstance*); +} + class Tuple final : public RootObject { public: @@ -126,7 +132,6 @@ public: virtual bool declareParameter(Scope *sc) = 0; virtual void print(RootObject *oarg, RootObject *oded) = 0; virtual RootObject *specialization() = 0; - virtual RootObject *defaultArg(Loc instLoc, Scope *sc) = 0; virtual bool hasDefaultArg() = 0; DYNCAST dyncast() const override { return DYNCAST_TEMPLATEPARAMETER; } @@ -148,7 +153,6 @@ public: bool declareParameter(Scope *sc) override final; void print(RootObject *oarg, RootObject *oded) override final; RootObject *specialization() override final; - RootObject *defaultArg(Loc instLoc, Scope *sc) override final; bool hasDefaultArg() override final; void accept(Visitor *v) override { v->visit(this); } }; @@ -179,7 +183,6 @@ public: bool declareParameter(Scope *sc) override; void print(RootObject *oarg, RootObject *oded) override; RootObject *specialization() override; - RootObject *defaultArg(Loc instLoc, Scope *sc) override; bool hasDefaultArg() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -199,7 +202,6 @@ public: bool declareParameter(Scope *sc) override; void print(RootObject *oarg, RootObject *oded) override; RootObject *specialization() override; - RootObject *defaultArg(Loc instLoc, Scope *sc) override; bool hasDefaultArg() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -215,7 +217,6 @@ public: bool declareParameter(Scope *sc) override; void print(RootObject *oarg, RootObject *oded) override; RootObject *specialization() override; - RootObject *defaultArg(Loc instLoc, Scope *sc) override; bool hasDefaultArg() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -267,14 +268,10 @@ public: unsigned char inuse; // for recursive expansion detection TemplateInstance *syntaxCopy(Dsymbol *) override; - Dsymbol *toAlias() override final; // resolve real symbol const char *kind() const override; const char* toPrettyCharsHelper() override final; Identifier *getIdent() override final; - bool isDiscardable(); - bool needsCodegen(); - void accept(Visitor *v) override { v->visit(this); } }; @@ -302,4 +299,5 @@ namespace dmd TemplateParameter *isTemplateParameter(RootObject *o); bool isError(const RootObject *const o); void printTemplateStats(bool listInstances, ErrorSink* eSink); + void printInstantiationTrace(TemplateInstance *ti); } diff --git a/gcc/d/dmd/templatesem.d b/gcc/d/dmd/templatesem.d index a9cf867f114..b392476a44b 100644 --- a/gcc/d/dmd/templatesem.d +++ b/gcc/d/dmd/templatesem.d @@ -57,6 +57,8 @@ import dmd.visitor; alias funcLeastAsSpecialized = dmd.funcsem.leastAsSpecialized; +enum LOG = false; + /************************************ * Perform semantic analysis on template. * Params: @@ -205,6 +207,583 @@ void templateDeclarationSemantic(Scope* sc, TemplateDeclaration tempdecl) tempdecl.semanticRun = PASS.semanticdone; } +/******************************************* + * Match to a particular TemplateParameter. + * Input: + * instLoc location that the template is instantiated. + * tiargs[] actual arguments to template instance + * i i'th argument + * parameters[] template parameters + * dedtypes[] deduced arguments to template instance + * *psparam set to symbol declared and initialized to dedtypes[i] + */ +MATCH matchArg(TemplateParameter tp, Loc instLoc, Scope* sc, Objects* tiargs, size_t i, TemplateParameters* parameters, ref Objects dedtypes, Declaration* psparam) +{ + MATCH matchArgNoMatch() + { + if (psparam) + *psparam = null; + return MATCH.nomatch; + } + + MATCH matchArgParameter() + { + RootObject oarg; + + if (i < tiargs.length) + oarg = (*tiargs)[i]; + else + { + // Get default argument instead + oarg = tp.defaultArg(instLoc, sc); + if (!oarg) + { + assert(i < dedtypes.length); + // It might have already been deduced + oarg = dedtypes[i]; + if (!oarg) + return matchArgNoMatch(); + } + } + return tp.matchArg(sc, oarg, i, parameters, dedtypes, psparam); + } + + MATCH matchArgTuple(TemplateTupleParameter ttp) + { + /* The rest of the actual arguments (tiargs[]) form the match + * for the variadic parameter. + */ + assert(i + 1 == dedtypes.length); // must be the last one + Tuple ovar; + + if (Tuple u = isTuple(dedtypes[i])) + { + // It has already been deduced + ovar = u; + } + else if (i + 1 == tiargs.length && isTuple((*tiargs)[i])) + ovar = isTuple((*tiargs)[i]); + else + { + ovar = new Tuple(); + //printf("ovar = %p\n", ovar); + if (i < tiargs.length) + { + //printf("i = %d, tiargs.length = %d\n", i, tiargs.length); + ovar.objects.setDim(tiargs.length - i); + foreach (j, ref obj; ovar.objects) + obj = (*tiargs)[i + j]; + } + } + return ttp.matchArg(sc, ovar, i, parameters, dedtypes, psparam); + } + + if (auto ttp = tp.isTemplateTupleParameter()) + return matchArgTuple(ttp); + + return matchArgParameter(); +} + +MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, TemplateParameters* parameters, ref Objects dedtypes, Declaration* psparam) +{ + MATCH matchArgNoMatch() + { + //printf("\tm = %d\n", MATCH.nomatch); + if (psparam) + *psparam = null; + return MATCH.nomatch; + } + + MATCH matchArgType(TemplateTypeParameter ttp) + { + //printf("TemplateTypeParameter.matchArg('%s')\n", ttp.ident.toChars()); + MATCH m = MATCH.exact; + Type ta = isType(oarg); + if (!ta) + { + //printf("%s %p %p %p\n", oarg.toChars(), isExpression(oarg), isDsymbol(oarg), isTuple(oarg)); + return matchArgNoMatch(); + } + //printf("ta is %s\n", ta.toChars()); + + if (ttp.specType) + { + if (!ta || ta == TemplateTypeParameter.tdummy) + return matchArgNoMatch(); + + //printf("\tcalling deduceType(): ta is %s, specType is %s\n", ta.toChars(), ttp.specType.toChars()); + MATCH m2 = deduceType(ta, sc, ttp.specType, *parameters, dedtypes); + if (m2 == MATCH.nomatch) + { + //printf("\tfailed deduceType\n"); + return matchArgNoMatch(); + } + + if (m2 < m) + m = m2; + if (dedtypes[i]) + { + Type t = cast(Type)dedtypes[i]; + + if (ttp.dependent && !t.equals(ta)) // https://issues.dlang.org/show_bug.cgi?id=14357 + return matchArgNoMatch(); + + /* This is a self-dependent parameter. For example: + * template X(T : T*) {} + * template X(T : S!T, alias S) {} + */ + //printf("t = %s ta = %s\n", t.toChars(), ta.toChars()); + ta = t; + } + } + else + { + if (dedtypes[i]) + { + // Must match already deduced type + Type t = cast(Type)dedtypes[i]; + + if (!t.equals(ta)) + { + //printf("t = %s ta = %s\n", t.toChars(), ta.toChars()); + return matchArgNoMatch(); + } + } + else + { + // So that matches with specializations are better + m = MATCH.convert; + } + } + dedtypes[i] = ta; + + if (psparam) + *psparam = new AliasDeclaration(ttp.loc, ttp.ident, ta); + //printf("\tm = %d\n", m); + return ttp.dependent ? MATCH.exact : m; + } + + MATCH matchArgValue(TemplateValueParameter tvp) + { + //printf("TemplateValueParameter.matchArg('%s')\n", tvp.ident.toChars()); + MATCH m = MATCH.exact; + + Expression ei = isExpression(oarg); + Type vt; + + if (!ei && oarg) + { + Dsymbol si = isDsymbol(oarg); + FuncDeclaration f = si ? si.isFuncDeclaration() : null; + if (!f || !f.fbody || f.needThis()) + return matchArgNoMatch(); + + ei = new VarExp(tvp.loc, f); + ei = ei.expressionSemantic(sc); + + /* If a function is really property-like, and then + * it's CTFEable, ei will be a literal expression. + */ + const olderrors = global.startGagging(); + ei = resolveProperties(sc, ei); + ei = ei.ctfeInterpret(); + if (global.endGagging(olderrors) || ei.op == EXP.error) + return matchArgNoMatch(); + + /* https://issues.dlang.org/show_bug.cgi?id=14520 + * A property-like function can match to both + * TemplateAlias and ValueParameter. But for template overloads, + * it should always prefer alias parameter to be consistent + * template match result. + * + * template X(alias f) { enum X = 1; } + * template X(int val) { enum X = 2; } + * int f1() { return 0; } // CTFEable + * int f2(); // body-less function is not CTFEable + * enum x1 = X!f1; // should be 1 + * enum x2 = X!f2; // should be 1 + * + * e.g. The x1 value must be same even if the f1 definition will be moved + * into di while stripping body code. + */ + m = MATCH.convert; + } + + if (ei && ei.op == EXP.variable) + { + // Resolve const variables that we had skipped earlier + ei = ei.ctfeInterpret(); + } + + //printf("\tvalType: %s, ty = %d\n", tvp.valType.toChars(), tvp.valType.ty); + vt = tvp.valType.typeSemantic(tvp.loc, sc); + //printf("ei: %s, ei.type: %s\n", ei.toChars(), ei.type.toChars()); + //printf("vt = %s\n", vt.toChars()); + + if (ei.type) + { + MATCH m2 = ei.implicitConvTo(vt); + //printf("m: %d\n", m); + if (m2 < m) + m = m2; + if (m == MATCH.nomatch) + return matchArgNoMatch(); + ei = ei.implicitCastTo(sc, vt); + ei = ei.ctfeInterpret(); + } + + if (tvp.specValue) + { + if (ei is null || (cast(void*)ei.type in TemplateValueParameter.edummies && + TemplateValueParameter.edummies[cast(void*)ei.type] == ei)) + return matchArgNoMatch(); + + Expression e = tvp.specValue; + + sc = sc.startCTFE(); + e = e.expressionSemantic(sc); + e = resolveProperties(sc, e); + sc = sc.endCTFE(); + e = e.implicitCastTo(sc, vt); + e = e.ctfeInterpret(); + + ei = ei.syntaxCopy(); + sc = sc.startCTFE(); + ei = ei.expressionSemantic(sc); + sc = sc.endCTFE(); + ei = ei.implicitCastTo(sc, vt); + ei = ei.ctfeInterpret(); + //printf("\tei: %s, %s\n", ei.toChars(), ei.type.toChars()); + //printf("\te : %s, %s\n", e.toChars(), e.type.toChars()); + if (!ei.equals(e)) + return matchArgNoMatch(); + } + else + { + if (dedtypes[i]) + { + // Must match already deduced value + Expression e = cast(Expression)dedtypes[i]; + if (!ei || !ei.equals(e)) + return matchArgNoMatch(); + } + } + dedtypes[i] = ei; + + if (psparam) + { + Initializer _init = new ExpInitializer(tvp.loc, ei); + Declaration sparam = new VarDeclaration(tvp.loc, vt, tvp.ident, _init); + sparam.storage_class = STC.manifest; + *psparam = sparam; + } + return tvp.dependent ? MATCH.exact : m; + } + + MATCH matchArgAlias(TemplateAliasParameter tap) + { + //printf("TemplateAliasParameter.matchArg('%s')\n", tap.ident.toChars()); + MATCH m = MATCH.exact; + Type ta = isType(oarg); + RootObject sa = ta && !ta.deco ? null : getDsymbol(oarg); + Expression ea = isExpression(oarg); + if (ea) + { + if (auto te = ea.isThisExp()) + sa = te.var; + else if (auto se = ea.isSuperExp()) + sa = se.var; + else if (auto se = ea.isScopeExp()) + sa = se.sds; + } + if (sa) + { + if ((cast(Dsymbol)sa).isAggregateDeclaration()) + m = MATCH.convert; + + /* specType means the alias must be a declaration with a type + * that matches specType. + */ + if (tap.specType) + { + tap.specType = typeSemantic(tap.specType, tap.loc, sc); + Declaration d = (cast(Dsymbol)sa).isDeclaration(); + if (!d) + return matchArgNoMatch(); + if (!d.type.equals(tap.specType)) + return matchArgNoMatch(); + } + } + else + { + sa = oarg; + if (ea) + { + if (tap.specType) + { + if (!ea.type.equals(tap.specType)) + return matchArgNoMatch(); + } + } + else if (ta && ta.ty == Tinstance && !tap.specAlias) + { + /* Specialized parameter should be preferred + * match to the template type parameter. + * template X(alias a) {} // a == this + * template X(alias a : B!A, alias B, A...) {} // B!A => ta + */ + } + else if (sa && sa == TemplateTypeParameter.tdummy) + { + /* https://issues.dlang.org/show_bug.cgi?id=2025 + * Aggregate Types should preferentially + * match to the template type parameter. + * template X(alias a) {} // a == this + * template X(T) {} // T => sa + */ + } + else if (ta && ta.ty != Tident) + { + /* Match any type that's not a TypeIdentifier to alias parameters, + * but prefer type parameter. + * template X(alias a) { } // a == ta + * + * TypeIdentifiers are excluded because they might be not yet resolved aliases. + */ + m = MATCH.convert; + } + else + return matchArgNoMatch(); + } + + if (tap.specAlias) + { + if (sa == TemplateAliasParameter.sdummy) + return matchArgNoMatch(); + // check specialization if template arg is a symbol + Dsymbol sx = isDsymbol(sa); + if (sa != tap.specAlias && sx) + { + Type talias = isType(tap.specAlias); + if (!talias) + return matchArgNoMatch(); + + TemplateInstance ti = sx.isTemplateInstance(); + if (!ti && sx.parent) + { + ti = sx.parent.isTemplateInstance(); + if (ti && ti.name != sx.ident) + return matchArgNoMatch(); + } + if (!ti) + return matchArgNoMatch(); + + Type t = new TypeInstance(Loc.initial, ti); + MATCH m2 = deduceType(t, sc, talias, *parameters, dedtypes); + if (m2 == MATCH.nomatch) + return matchArgNoMatch(); + } + // check specialization if template arg is a type + else if (ta) + { + if (Type tspec = isType(tap.specAlias)) + { + MATCH m2 = ta.implicitConvTo(tspec); + if (m2 == MATCH.nomatch) + return matchArgNoMatch(); + } + else + { + error(tap.loc, "template parameter specialization for a type must be a type and not `%s`", + tap.specAlias.toChars()); + return matchArgNoMatch(); + } + } + } + else if (dedtypes[i]) + { + // Must match already deduced symbol + RootObject si = dedtypes[i]; + if (!sa || si != sa) + return matchArgNoMatch(); + } + dedtypes[i] = sa; + + if (psparam) + { + if (Dsymbol s = isDsymbol(sa)) + { + *psparam = new AliasDeclaration(tap.loc, tap.ident, s); + } + else if (Type t = isType(sa)) + { + *psparam = new AliasDeclaration(tap.loc, tap.ident, t); + } + else + { + assert(ea); + + // Declare manifest constant + Initializer _init = new ExpInitializer(tap.loc, ea); + auto v = new VarDeclaration(tap.loc, null, tap.ident, _init); + v.storage_class = STC.manifest; + v.dsymbolSemantic(sc); + *psparam = v; + } + } + return tap.dependent ? MATCH.exact : m; + } + + MATCH matchArgTuple(TemplateTupleParameter ttp) + { + //printf("TemplateTupleParameter.matchArg('%s')\n", ttp.ident.toChars()); + Tuple ovar = isTuple(oarg); + if (!ovar) + return MATCH.nomatch; + if (dedtypes[i]) + { + Tuple tup = isTuple(dedtypes[i]); + if (!tup) + return MATCH.nomatch; + if (!match(tup, ovar)) + return MATCH.nomatch; + } + dedtypes[i] = ovar; + + if (psparam) + *psparam = new TupleDeclaration(ttp.loc, ttp.ident, &ovar.objects); + return ttp.dependent ? MATCH.exact : MATCH.convert; + } + + if (auto ttp = tp.isTemplateTypeParameter()) + return matchArgType(ttp); + if (auto tvp = tp.isTemplateValueParameter()) + return matchArgValue(tvp); + if (auto tap = tp.isTemplateAliasParameter()) + return matchArgAlias(tap); + if (auto ttp = tp.isTemplateTupleParameter()) + return matchArgTuple(ttp); + assert(0); +} + +/********************************************** + * Confirm s is a valid template, then store it. + * Input: + * sc + * s candidate symbol of template. It may be: + * TemplateDeclaration + * FuncDeclaration with findTemplateDeclRoot() != NULL + * OverloadSet which contains candidates + * Returns: + * true if updating succeeds. + */ +bool updateTempDecl(TemplateInstance ti, Scope* sc, Dsymbol s) +{ + if (!s) + return ti.tempdecl !is null; + + Identifier id = ti.name; + s = s.toAlias(); + + /* If an OverloadSet, look for a unique member that is a template declaration + */ + if (OverloadSet os = s.isOverloadSet()) + { + s = null; + foreach (s2; os.a) + { + if (FuncDeclaration f = s2.isFuncDeclaration()) + s2 = f.findTemplateDeclRoot(); + else + s2 = s2.isTemplateDeclaration(); + if (s2) + { + if (s) + { + ti.tempdecl = os; + return true; + } + s = s2; + } + } + if (!s) + { + .error(ti.loc, "%s `%s` template `%s` is not defined", ti.kind, ti.toPrettyChars, id.toChars()); + return false; + } + } + + if (OverDeclaration od = s.isOverDeclaration()) + { + ti.tempdecl = od; // TODO: more strict check + return true; + } + + /* It should be a TemplateDeclaration, not some other symbol + */ + if (FuncDeclaration f = s.isFuncDeclaration()) + ti.tempdecl = f.findTemplateDeclRoot(); + else + ti.tempdecl = s.isTemplateDeclaration(); + + // We're done + if (ti.tempdecl) + return true; + + // Error already issued, just return `false` + if (!s.parent && global.errors) + return false; + + if (!s.parent && s.getType()) + { + Dsymbol s2 = s.getType().toDsymbol(sc); + if (!s2) + { + .error(ti.loc, "`%s` is not a valid template instance, because `%s` is not a template declaration but a type (`%s == %s`)", ti.toChars(), id.toChars(), id.toChars(), s.getType.kind()); + return false; + } + // because s can be the alias created for a TemplateParameter + const AliasDeclaration ad = s.isAliasDeclaration(); + version (none) + { + if (ad && ad.isAliasedTemplateParameter()) + printf("`%s` is an alias created from a template parameter\n", s.toChars()); + } + if (!ad || !ad.isAliasedTemplateParameter()) + s = s2; + } + + TemplateInstance ti2 = s.parent ? s.parent.isTemplateInstance() : null; + + /* This avoids the VarDeclaration.toAlias() which runs semantic() too soon + */ + static bool matchId(TemplateInstance _ti, Identifier id) + { + if (_ti.aliasdecl && _ti.aliasdecl.isVarDeclaration()) + return _ti.aliasdecl.isVarDeclaration().ident == id; + return _ti.toAlias().ident == id; + } + + if (ti2 && (ti2.name == s.ident || matchId(ti2, s.ident)) && ti2.tempdecl) + { + /* This is so that one can refer to the enclosing + * template, even if it has the same name as a member + * of the template, if it has a !(arguments) + */ + TemplateDeclaration td = ti2.tempdecl.isTemplateDeclaration(); + assert(td); + if (td.overroot) // if not start of overloaded list of TemplateDeclaration's + td = td.overroot; // then get the start + ti.tempdecl = td; + return true; + } + else + { + .error(ti.loc, "%s `%s` `%s` is not a template declaration, it is a %s", + ti.kind, ti.toPrettyChars, id.toChars(), s.kind()); + return false; + } +} /*************************************** * Given that ti is an instance of this TemplateDeclaration, @@ -402,30 +981,256 @@ MATCH matchWithInstance(Scope* sc, TemplateDeclaration td, TemplateInstance ti, return m; } -/**************************** - * Check to see if constraint is satisfied. +/** + Returns: true if the instances' innards are discardable. + + The idea of this function is to see if the template instantiation + can be 100% replaced with its eponymous member. All other members + can be discarded, even in the compiler to free memory (for example, + the template could be expanded in a region allocator, deemed trivial, + the end result copied back out independently and the entire region freed), + and can be elided entirely from the binary. + + The current implementation affects code that generally looks like: + + --- + template foo(args...) { + some_basic_type_or_string helper() { .... } + enum foo = helper(); + } + --- + + since it was the easiest starting point of implementation but it can and + should be expanded more later. +*/ +bool isDiscardable(TemplateInstance ti) +{ + if (ti.aliasdecl is null) + return false; + + auto v = ti.aliasdecl.isVarDeclaration(); + if (v is null) + return false; + + if (!(v.storage_class & STC.manifest)) + return false; + + // Currently only doing basic types here because it is the easiest proof-of-concept + // implementation with minimal risk of side effects, but it could likely be + // expanded to any type that already exists outside this particular instance. + if (!(v.type.equals(Type.tstring) || (v.type.isTypeBasic() !is null))) + return false; + + // Static ctors and dtors, even in an eponymous enum template, are still run, + // so if any of them are in here, we'd better not assume it is trivial lest + // we break useful code + foreach(member; *ti.members) + { + if(member.hasStaticCtorOrDtor()) + return false; + if(member.isStaticDtorDeclaration()) + return false; + if(member.isStaticCtorDeclaration()) + return false; + } + + // but if it passes through this gauntlet... it should be fine. D code will + // see only the eponymous member, outside stuff can never access it, even through + // reflection; the outside world ought to be none the wiser. Even dmd should be + // able to simply free the memory of everything except the final result. + + return true; +} + + +/*********************************************** + * Returns true if this is not instantiated in non-root module, and + * is a part of non-speculative instantiatiation. + * + * Note: minst does not stabilize until semantic analysis is completed, + * so don't call this function during semantic analysis to return precise result. */ -bool evaluateConstraint(TemplateDeclaration td, TemplateInstance ti, Scope* sc, Scope* paramscope, Objects* dedargs, FuncDeclaration fd) +bool needsCodegen(TemplateInstance ti) { - /* Detect recursive attempts to instantiate this template declaration, - * https://issues.dlang.org/show_bug.cgi?id=4072 - * void foo(T)(T x) if (is(typeof(foo(x)))) { } - * static assert(!is(typeof(foo(7)))); - * Recursive attempts are regarded as a constraint failure. - */ - /* There's a chicken-and-egg problem here. We don't know yet if this template - * instantiation will be a local one (enclosing is set), and we won't know until - * after selecting the correct template. Thus, function we're nesting inside - * is not on the sc scope chain, and this can cause errors in FuncDeclaration.getLevel(). - * Workaround the problem by setting a flag to relax the checking on frame errors. - */ + //printf("needsCodegen() %s\n", toChars()); + + // minst is finalized after the 1st invocation. + // tnext is only needed for the 1st invocation and + // cleared for further invocations. + TemplateInstance tnext = ti.tnext; + TemplateInstance tinst = ti.tinst; + ti.tnext = null; + + // Don't do codegen if the instance has errors, + // is a dummy instance (see evaluateConstraint), + // or is determined to be discardable. + if (ti.errors || ti.inst is null || ti.inst.isDiscardable()) + { + ti.minst = null; // mark as speculative + return false; + } - for (TemplatePrevious* p = td.previous; p; p = p.prev) + // This should only be called on the primary instantiation. + assert(ti is ti.inst); + + /* Hack for suppressing linking errors against template instances + * of `_d_arrayliteralTX` (e.g. `_d_arrayliteralTX!(int[])`). + * + * This happens, for example, when a lib module is compiled with `preview=dip1000` and + * the array literal is placed on stack instead of using the lowering. In this case, + * if the root module is compiled without `preview=dip1000`, the compiler will consider + * the template already instantiated within the lib module, and thus skip the codegen for it + * in the root module object file, thinking that the linker will find the instance in the lib module. + * + * To bypass this edge case, we always do codegen for `_d_arrayliteralTX` template instances, + * even if an instance already exists in non-root module. + */ + if (ti.inst && ti.inst.name == Id._d_arrayliteralTX) { - if (!arrayObjectMatch(*p.dedargs, *dedargs)) - continue; - //printf("recursive, no match p.sc=%p %p %s\n", p.sc, this, this.toChars()); - /* It must be a subscope of p.sc, other scope chains are not recursive + return true; + } + + if (global.params.allInst) + { + // Do codegen if there is an instantiation from a root module, to maximize link-ability. + static ThreeState needsCodegenAllInst(TemplateInstance tithis, TemplateInstance tinst) + { + // Do codegen if `this` is instantiated from a root module. + if (tithis.minst && tithis.minst.isRoot()) + return ThreeState.yes; + + // Do codegen if the ancestor needs it. + if (tinst && tinst.inst && tinst.inst.needsCodegen()) + { + tithis.minst = tinst.inst.minst; // cache result + assert(tithis.minst); + assert(tithis.minst.isRoot()); + return ThreeState.yes; + } + return ThreeState.none; + } + + if (const needsCodegen = needsCodegenAllInst(ti, tinst)) + return needsCodegen == ThreeState.yes ? true : false; + + // Do codegen if a sibling needs it. + for (; tnext; tnext = tnext.tnext) + { + const needsCodegen = needsCodegenAllInst(tnext, tnext.tinst); + if (needsCodegen == ThreeState.yes) + { + ti.minst = tnext.minst; // cache result + assert(ti.minst); + assert(ti.minst.isRoot()); + return true; + } + else if (!ti.minst && tnext.minst) + { + ti.minst = tnext.minst; // cache result from non-speculative sibling + // continue searching + } + else if (needsCodegen != ThreeState.none) + break; + } + + // Elide codegen because there's no instantiation from any root modules. + return false; + } + + // Prefer instantiations from non-root modules, to minimize object code size. + + /* If a TemplateInstance is ever instantiated from a non-root module, + * we do not have to generate code for it, + * because it will be generated when the non-root module is compiled. + * + * But, if the non-root 'minst' imports any root modules, it might still need codegen. + * + * The problem is if A imports B, and B imports A, and both A + * and B instantiate the same template, does the compilation of A + * or the compilation of B do the actual instantiation? + * + * See https://issues.dlang.org/show_bug.cgi?id=2500. + * + * => Elide codegen if there is at least one instantiation from a non-root module + * which doesn't import any root modules. + */ + static ThreeState needsCodegenRootOnly(TemplateInstance tithis, TemplateInstance tinst) + { + // If the ancestor isn't speculative, + // 1. do codegen if the ancestor needs it + // 2. elide codegen if the ancestor doesn't need it (non-root instantiation of ancestor incl. subtree) + if (tinst && tinst.inst) + { + tinst = tinst.inst; + const needsCodegen = tinst.needsCodegen(); // sets tinst.minst + if (tinst.minst) // not speculative + { + tithis.minst = tinst.minst; // cache result + return needsCodegen ? ThreeState.yes : ThreeState.no; + } + } + + // Elide codegen if `this` doesn't need it. + if (tithis.minst && !tithis.minst.isRoot() && !tithis.minst.rootImports()) + return ThreeState.no; + + return ThreeState.none; + } + + if (const needsCodegen = needsCodegenRootOnly(ti, tinst)) + return needsCodegen == ThreeState.yes ? true : false; + + // Elide codegen if a (non-speculative) sibling doesn't need it. + for (; tnext; tnext = tnext.tnext) + { + const needsCodegen = needsCodegenRootOnly(tnext, tnext.tinst); // sets tnext.minst + if (tnext.minst) // not speculative + { + if (needsCodegen == ThreeState.no) + { + ti.minst = tnext.minst; // cache result + assert(!ti.minst.isRoot() && !ti.minst.rootImports()); + return false; + } + else if (!ti.minst) + { + ti.minst = tnext.minst; // cache result from non-speculative sibling + // continue searching + } + else if (needsCodegen != ThreeState.none) + break; + } + } + + // Unless `this` is still speculative (=> all further siblings speculative too), + // do codegen because we found no guaranteed-codegen'd non-root instantiation. + return ti.minst !is null; +} + +/**************************** + * Check to see if constraint is satisfied. + */ +bool evaluateConstraint(TemplateDeclaration td, TemplateInstance ti, Scope* sc, Scope* paramscope, Objects* dedargs, FuncDeclaration fd) +{ + /* Detect recursive attempts to instantiate this template declaration, + * https://issues.dlang.org/show_bug.cgi?id=4072 + * void foo(T)(T x) if (is(typeof(foo(x)))) { } + * static assert(!is(typeof(foo(7)))); + * Recursive attempts are regarded as a constraint failure. + */ + /* There's a chicken-and-egg problem here. We don't know yet if this template + * instantiation will be a local one (enclosing is set), and we won't know until + * after selecting the correct template. Thus, function we're nesting inside + * is not on the sc scope chain, and this can cause errors in FuncDeclaration.getLevel(). + * Workaround the problem by setting a flag to relax the checking on frame errors. + */ + + for (TemplatePrevious* p = td.previous; p; p = p.prev) + { + if (!arrayObjectMatch(*p.dedargs, *dedargs)) + continue; + //printf("recursive, no match p.sc=%p %p %s\n", p.sc, this, this.toChars()); + /* It must be a subscope of p.sc, other scope chains are not recursive * instantiations. * the chain of enclosing scopes is broken by paramscope (its enclosing * scope is _scope, but paramscope.callsc is the instantiating scope). So @@ -528,6 +1333,317 @@ bool evaluateConstraint(TemplateDeclaration td, TemplateInstance ti, Scope* sc, return result; } +/**************************** + * Destructively get the error message from the last constraint evaluation + * Params: + * td = TemplateDeclaration + * tip = tip to show after printing all overloads + */ +const(char)* getConstraintEvalError(TemplateDeclaration td, ref const(char)* tip) +{ + import dmd.staticcond; + + // there will be a full tree view in verbose mode, and more compact list in the usual + const full = global.params.v.verbose; + uint count; + const msg = visualizeStaticCondition(td.constraint, td.lastConstraint, td.lastConstraintNegs[], full, count); + scope (exit) + { + td.lastConstraint = null; + td.lastConstraintTiargs = null; + td.lastConstraintNegs.setDim(0); + } + if (!msg) + return null; + + OutBuffer buf; + + assert(td.parameters && td.lastConstraintTiargs); + if (td.parameters.length > 0) + { + formatParamsWithTiargs(*td.parameters, *td.lastConstraintTiargs, td.isVariadic() !is null, buf); + buf.writenl(); + } + if (!full) + { + // choosing singular/plural + const s = (count == 1) ? + " must satisfy the following constraint:" : + " must satisfy one of the following constraints:"; + buf.writestring(s); + buf.writenl(); + // the constraints + buf.writeByte('`'); + buf.writestring(msg); + buf.writeByte('`'); + } + else + { + buf.writestring(" whose parameters have the following constraints:"); + buf.writenl(); + const sep = " `~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`"; + buf.writestring(sep); + buf.writenl(); + // the constraints + buf.writeByte('`'); + buf.writestring(msg); + buf.writeByte('`'); + buf.writestring(sep); + tip = "not satisfied constraints are marked with `>`"; + } + return buf.extractChars(); +} + +/********************************** + * Find the TemplateDeclaration that matches this TemplateInstance best. + * + * Params: + * ti = TemplateInstance + * sc = the scope this TemplateInstance resides in + * argumentList = function arguments in case of a template function + * + * Returns: + * `true` if a match was found, `false` otherwise + */ +bool findBestMatch(TemplateInstance ti, Scope* sc, ArgumentList argumentList) +{ + if (ti.havetempdecl) + { + TemplateDeclaration tempdecl = ti.tempdecl.isTemplateDeclaration(); + assert(tempdecl); + assert(tempdecl._scope); + // Deduce tdtypes + ti.tdtypes.setDim(tempdecl.parameters.length); + if (!matchWithInstance(sc, tempdecl, ti, ti.tdtypes, argumentList, 2)) + { + .error(ti.loc, "%s `%s` incompatible arguments for template instantiation", + ti.kind, ti.toPrettyChars); + return false; + } + // TODO: Normalizing tiargs for https://issues.dlang.org/show_bug.cgi?id=7469 is necessary? + return true; + } + + static if (LOG) + { + printf("TemplateInstance.findBestMatch()\n"); + } + + const errs = global.errors; + TemplateDeclaration td_last = null; + Objects dedtypes; + + /* Since there can be multiple TemplateDeclaration's with the same + * name, look for the best match. + */ + auto tovers = ti.tempdecl.isOverloadSet(); + foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1) + { + TemplateDeclaration td_best; + TemplateDeclaration td_ambig; + MATCH m_best = MATCH.nomatch; + + Dsymbol dstart = tovers ? tovers.a[oi] : ti.tempdecl; + overloadApply(dstart, (Dsymbol s) + { + auto td = s.isTemplateDeclaration(); + if (!td) + return 0; + if (td == td_best) // skip duplicates + return 0; + + //printf("td = %s\n", td.toPrettyChars()); + // If more arguments than parameters, + // then this is no match. + if (td.parameters.length < ti.tiargs.length) + { + if (!td.isVariadic()) + return 0; + } + + dedtypes.setDim(td.parameters.length); + dedtypes.zero(); + assert(td.semanticRun != PASS.initial); + + MATCH m = matchWithInstance(sc, td, ti, dedtypes, argumentList, 0); + //printf("matchWithInstance = %d\n", m); + if (m == MATCH.nomatch) // no match at all + return 0; + if (m < m_best) goto Ltd_best; + if (m > m_best) goto Ltd; + + // Disambiguate by picking the most specialized TemplateDeclaration + { + MATCH c1 = leastAsSpecialized(sc, td, td_best, argumentList); + MATCH c2 = leastAsSpecialized(sc, td_best, td, argumentList); + //printf("c1 = %d, c2 = %d\n", c1, c2); + if (c1 > c2) goto Ltd; + if (c1 < c2) goto Ltd_best; + } + + td_ambig = td; + return 0; + + Ltd_best: + // td_best is the best match so far + td_ambig = null; + return 0; + + Ltd: + // td is the new best match + td_ambig = null; + td_best = td; + m_best = m; + ti.tdtypes.setDim(dedtypes.length); + memcpy(ti.tdtypes.tdata(), dedtypes.tdata(), ti.tdtypes.length * (void*).sizeof); + return 0; + }); + + if (td_ambig) + { + .error(ti.loc, "%s `%s.%s` matches more than one template declaration:", + td_best.kind(), td_best.parent.toPrettyChars(), td_best.ident.toChars()); + .errorSupplemental(td_best.loc, "`%s`\nand:", td_best.toChars()); + .errorSupplemental(td_ambig.loc, "`%s`", td_ambig.toChars()); + return false; + } + if (td_best) + { + if (!td_last) + td_last = td_best; + else if (td_last != td_best) + { + ScopeDsymbol.multiplyDefined(ti.loc, td_last, td_best); + return false; + } + } + } + + if (td_last) + { + /* https://issues.dlang.org/show_bug.cgi?id=7469 + * Normalize tiargs by using corresponding deduced + * template value parameters and tuples for the correct mangling. + * + * By doing this before hasNestedArgs, CTFEable local variable will be + * accepted as a value parameter. For example: + * + * void foo() { + * struct S(int n) {} // non-global template + * const int num = 1; // CTFEable local variable + * S!num s; // S!1 is instantiated, not S!num + * } + */ + size_t dim = td_last.parameters.length - (td_last.isVariadic() ? 1 : 0); + for (size_t i = 0; i < dim; i++) + { + if (ti.tiargs.length <= i) + ti.tiargs.push(ti.tdtypes[i]); + assert(i < ti.tiargs.length); + + auto tvp = (*td_last.parameters)[i].isTemplateValueParameter(); + if (!tvp) + continue; + assert(ti.tdtypes[i]); + // tdtypes[i] is already normalized to the required type in matchArg + + (*ti.tiargs)[i] = ti.tdtypes[i]; + } + if (td_last.isVariadic() && ti.tiargs.length == dim && ti.tdtypes[dim]) + { + Tuple va = isTuple(ti.tdtypes[dim]); + assert(va); + ti.tiargs.pushSlice(va.objects[]); + } + } + else if (ti.errors && ti.inst) + { + // instantiation was failed with error reporting + assert(global.errors); + return false; + } + else + { + auto tdecl = ti.tempdecl.isTemplateDeclaration(); + + if (errs != global.errors) + errorSupplemental(ti.loc, "while looking for match for `%s`", ti.toChars()); + else if (tdecl && !tdecl.overnext) + { + // Only one template, so we can give better error message + const(char)* msg = "does not match template declaration"; + const(char)* tip; + OutBuffer buf; + HdrGenState hgs; + hgs.skipConstraints = true; + toCharsMaybeConstraints(tdecl, buf, hgs); + const tmsg = buf.peekChars(); + const cmsg = tdecl.getConstraintEvalError(tip); + if (cmsg) + { + .error(ti.loc, "%s `%s` %s `%s`\n%s", ti.kind, ti.toPrettyChars, msg, tmsg, cmsg); + if (tip) + .tip(tip); + } + else + { + .error(ti.loc, "%s `%s` %s `%s`", ti.kind, ti.toPrettyChars, msg, tmsg); + + if (tdecl.parameters.length == ti.tiargs.length) + { + // https://issues.dlang.org/show_bug.cgi?id=7352 + // print additional information, e.g. `foo` is not a type + foreach (i, param; *tdecl.parameters) + { + MATCH match = param.matchArg(ti.loc, sc, ti.tiargs, i, tdecl.parameters, dedtypes, null); + auto arg = (*ti.tiargs)[i]; + auto sym = arg.isDsymbol; + auto exp = arg.isExpression; + + if (exp) + exp = exp.optimize(WANTvalue); + + if (match == MATCH.nomatch && + ((sym && sym.isFuncDeclaration) || + (exp && exp.isVarExp))) + { + if (param.isTemplateTypeParameter) + errorSupplemental(ti.loc, "`%s` is not a type", arg.toChars); + else if (auto tvp = param.isTemplateValueParameter) + errorSupplemental(ti.loc, "`%s` is not of a value of type `%s`", + arg.toChars, tvp.valType.toChars); + + } + } + } + } + } + else + { + .error(ti.loc, "%s `%s` does not match any template declaration", ti.kind(), ti.toPrettyChars()); + bool found; + overloadApply(ti.tempdecl, (s){ + if (!found) + errorSupplemental(ti.loc, "Candidates are:"); + found = true; + errorSupplemental(s.loc, "%s", s.toChars()); + return 0; + }); + } + return false; + } + + /* The best match is td_last + */ + ti.tempdecl = td_last; + + static if (LOG) + { + printf("\tIt's a match with template declaration '%s'\n", tempdecl.toChars()); + } + return (errs == global.errors); +} + /******************************************* * Append to buf a textual representation of template parameters with their arguments. * Params: @@ -536,7 +1652,7 @@ bool evaluateConstraint(TemplateDeclaration td, TemplateInstance ti, Scope* sc, * variadic = if it's a variadic argument list * buf = where the text output goes */ -void formatParamsWithTiargs(ref TemplateParameters parameters, ref Objects tiargs, bool variadic, ref OutBuffer buf) +private void formatParamsWithTiargs(ref TemplateParameters parameters, ref Objects tiargs, bool variadic, ref OutBuffer buf) { buf.writestring(" with `"); @@ -677,6 +1793,79 @@ L1: return MATCH.nomatch; } +RootObject defaultArg(TemplateParameter tp, Loc instLoc, Scope* sc) +{ + if (tp.isTemplateTupleParameter()) + return null; + if (auto tpp = tp.isTemplateTypeParameter()) + { + Type t = tpp.defaultType; + if (t) + { + t = t.syntaxCopy(); + t = t.typeSemantic(tpp.loc, sc); // use the parameter loc + } + return t; + } + if (auto tap = tp.isTemplateAliasParameter()) + { + RootObject da = tap.defaultAlias; + if (auto ta = isType(tap.defaultAlias)) + { + switch (ta.ty) + { + // If the default arg is a template, instantiate for each type + case Tinstance: + // same if the default arg is a mixin, traits, typeof + // since the content might rely on a previous parameter + // (https://issues.dlang.org/show_bug.cgi?id=23686) + case Tmixin, Ttypeof, Ttraits : + da = ta.syntaxCopy(); + break; + default: + } + } + + RootObject o = aliasParameterSemantic(tap.loc, sc, da, null); // use the parameter loc + return o; + } + if (auto tvp = tp.isTemplateValueParameter()) + { + Expression e = tvp.defaultValue; + if (!e) + return null; + + e = e.syntaxCopy(); + Scope* sc2 = sc.push(); + sc2.inDefaultArg = true; + e = e.expressionSemantic(sc2); + sc2.pop(); + if (e is null) + return null; + if (auto te = e.isTemplateExp()) + { + assert(sc && sc.tinst); + if (te.td == sc.tinst.tempdecl) + { + // defaultValue is a reference to its template declaration + // i.e: `template T(int arg = T)` + // Raise error now before calling resolveProperties otherwise we'll + // start looping on the expansion of the template instance. + auto td = sc.tinst.tempdecl; + .error(td.loc, "%s `%s` recursive template expansion", td.kind, td.toPrettyChars); + return ErrorExp.get(); + } + } + if ((e = resolveProperties(sc, e)) is null) + return null; + e = e.resolveLoc(instLoc, sc); // use the instantiated loc + e = e.optimize(WANTvalue); + + return e; + } + assert(0); +} + /************************************************* * Match function arguments against a specific template function. * @@ -1873,43 +3062,322 @@ extern (D) RootObject declareParameter(TemplateDeclaration td, Scope* sc, Templa return o; } -/************************************************* - * Given function arguments, figure out which template function - * to expand, and return matching result. - * Params: - * m = matching result - * dstart = the root of overloaded function templates - * loc = instantiation location - * sc = instantiation scope - * tiargs = initial list of template arguments - * tthis = if !NULL, the 'this' pointer argument - * argumentList= arguments to function - * errorHelper = delegate to send error message to if not null +/********************************** + * Run semantic of tiargs as arguments of template. + * Input: + * loc + * sc + * tiargs array of template arguments + * flags 1: replace const variables with their initializers + * 2: don't devolve Parameter to Type + * atd tuple being optimized. If found, it's not expanded here + * but in AliasAssign semantic. + * Returns: + * false if one or more arguments have errors. */ -void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, Objects* tiargs, - Type tthis, ArgumentList argumentList, void delegate(const(char)*) scope errorHelper = null) +bool TemplateInstance_semanticTiargs(Loc loc, Scope* sc, Objects* tiargs, int flags, TupleDeclaration atd = null) { - version (none) + // Run semantic on each argument, place results in tiargs[] + //printf("+TemplateInstance.semanticTiargs()\n"); + if (!tiargs) + return true; + bool err = false; + + // The arguments are not treated as part of a default argument, + // because they are evaluated at compile time. + const inCondition = sc.condition; + sc = sc.push(); + sc.inDefaultArg = false; + + // https://issues.dlang.org/show_bug.cgi?id=24699 + sc.condition = inCondition; + + for (size_t j = 0; j < tiargs.length; j++) { - printf("functionResolve() dstart: %s (", dstart.toChars()); - for (size_t i = 0; i < (tiargs ? (*tiargs).length : 0); i++) - { - if (i) printf(", "); - RootObject arg = (*tiargs)[i]; - printf("%s", arg.toChars()); - } - printf(")("); - for (size_t i = 0; i < (argumentList.arguments ? (*argumentList.arguments).length : 0); i++) - { - if (i) printf(", "); - Expression arg = (*argumentList.arguments)[i]; - printf("%s %s", arg.type.toChars(), arg.toChars()); - //printf("\tty = %d\n", arg.type.ty); - } - printf(")\n"); - //printf("stc = %llx\n", dstart._scope.stc); - //printf("match:t/f = %d/%d\n", ta_last, m.last); - } + RootObject o = (*tiargs)[j]; + Type ta = isType(o); + Expression ea = isExpression(o); + Dsymbol sa = isDsymbol(o); + + //printf("1: (*tiargs)[%d] = %p, s=%p, v=%p, ea=%p, ta=%p\n", j, o, isDsymbol(o), isTuple(o), ea, ta); + if (ta) + { + //printf("type %s\n", ta.toChars()); + + // It might really be an Expression or an Alias + ta.resolve(loc, sc, ea, ta, sa, (flags & 1) != 0); + if (ea) + goto Lexpr; + if (sa) + goto Ldsym; + if (ta is null) + { + assert(global.errors); + ta = Type.terror; + } + + Ltype: + if (TypeTuple tt = ta.isTypeTuple()) + { + // Expand tuple + size_t dim = tt.arguments.length; + tiargs.remove(j); + if (dim) + { + tiargs.reserve(dim); + foreach (i, arg; *tt.arguments) + { + if (flags & 2 && (arg.storageClass & STC.parameter)) + tiargs.insert(j + i, arg); + else + tiargs.insert(j + i, arg.type); + } + } + j--; + continue; + } + if (ta.ty == Terror) + { + err = true; + continue; + } + (*tiargs)[j] = ta.merge2(); + } + else if (ea) + { + Lexpr: + //printf("+[%d] ea = %s %s\n", j, EXPtoString(ea.op).ptr, ea.toChars()); + if (flags & 1) // only used by __traits + { + ea = ea.expressionSemantic(sc); + + // must not interpret the args, excepting template parameters + if (!ea.isVarExp() || (ea.isVarExp().var.storage_class & STC.templateparameter)) + { + ea = ea.optimize(WANTvalue); + } + } + else + { + sc = sc.startCTFE(); + ea = ea.expressionSemantic(sc); + sc = sc.endCTFE(); + + if (auto varExp = ea.isVarExp()) + { + /* If the parameter is a function that is not called + * explicitly, i.e. `foo!func` as opposed to `foo!func()`, + * then it is a dsymbol, not the return value of `func()` + */ + Declaration vd = varExp.var; + if (auto fd = vd.isFuncDeclaration()) + { + sa = fd; + goto Ldsym; + } + /* Otherwise skip substituting a const var with + * its initializer. The problem is the initializer won't + * match with an 'alias' parameter. Instead, do the + * const substitution in TemplateValueParameter.matchArg(). + */ + } + else if (definitelyValueParameter(ea)) + { + if (ea.checkValue()) // check void expression + ea = ErrorExp.get(); + const olderrs = global.errors; + ea = ea.ctfeInterpret(); + if (global.errors != olderrs) + ea = ErrorExp.get(); + } + } + //printf("-[%d] ea = %s %s\n", j, EXPtoString(ea.op).ptr, ea.toChars()); + if (TupleExp te = ea.isTupleExp()) + { + // Expand tuple + size_t dim = te.exps.length; + tiargs.remove(j); + if (dim) + { + tiargs.reserve(dim); + foreach (i, exp; *te.exps) + tiargs.insert(j + i, exp); + } + j--; + continue; + } + if (ea.op == EXP.error) + { + err = true; + continue; + } + (*tiargs)[j] = ea; + + if (ea.op == EXP.type) + { + ta = ea.type; + goto Ltype; + } + if (ea.op == EXP.scope_) + { + sa = ea.isScopeExp().sds; + goto Ldsym; + } + if (FuncExp fe = ea.isFuncExp()) + { + /* A function literal, that is passed to template and + * already semanticed as function pointer, never requires + * outer frame. So convert it to global function is valid. + */ + if (fe.fd.tok == TOK.reserved && fe.type.ty == Tpointer) + { + // change to non-nested + fe.fd.tok = TOK.function_; + fe.fd.vthis = null; + } + else if (fe.td) + { + /* If template argument is a template lambda, + * get template declaration itself. */ + //sa = fe.td; + //goto Ldsym; + } + } + if (ea.op == EXP.dotVariable && !(flags & 1)) + { + // translate expression to dsymbol. + sa = ea.isDotVarExp().var; + goto Ldsym; + } + if (auto te = ea.isTemplateExp()) + { + sa = te.td; + goto Ldsym; + } + if (ea.op == EXP.dotTemplateDeclaration && !(flags & 1)) + { + // translate expression to dsymbol. + sa = ea.isDotTemplateExp().td; + goto Ldsym; + } + if (auto de = ea.isDotExp()) + { + if (auto se = de.e2.isScopeExp()) + { + sa = se.sds; + goto Ldsym; + } + } + } + else if (sa) + { + Ldsym: + //printf("dsym %s %s\n", sa.kind(), sa.toChars()); + if (sa.errors) + { + err = true; + continue; + } + + TupleDeclaration d = sa.toAlias().isTupleDeclaration(); + if (d) + { + if (d is atd) + { + (*tiargs)[j] = d; + continue; + } + // Expand tuple + tiargs.remove(j); + tiargs.insert(j, d.objects); + j--; + continue; + } + if (FuncAliasDeclaration fa = sa.isFuncAliasDeclaration()) + { + FuncDeclaration f = fa.toAliasFunc(); + if (!fa.hasOverloads && f.isUnique()) + { + // Strip FuncAlias only when the aliased function + // does not have any overloads. + sa = f; + } + } + (*tiargs)[j] = sa; + + TemplateDeclaration td = sa.isTemplateDeclaration(); + if (td && td.semanticRun == PASS.initial && td.literal) + { + td.dsymbolSemantic(sc); + } + FuncDeclaration fd = sa.isFuncDeclaration(); + if (fd) + functionSemantic(fd); + } + else if (isParameter(o)) + { + } + else + { + assert(0); + } + //printf("1: (*tiargs)[%d] = %p\n", j, (*tiargs)[j]); + } + sc.pop(); + version (none) + { + printf("-TemplateInstance.semanticTiargs()\n"); + for (size_t j = 0; j < tiargs.length; j++) + { + RootObject o = (*tiargs)[j]; + Type ta = isType(o); + Expression ea = isExpression(o); + Dsymbol sa = isDsymbol(o); + Tuple va = isTuple(o); + printf("\ttiargs[%d] = ta %p, ea %p, sa %p, va %p\n", j, ta, ea, sa, va); + } + } + return !err; +} + + +/************************************************* + * Given function arguments, figure out which template function + * to expand, and return matching result. + * Params: + * m = matching result + * dstart = the root of overloaded function templates + * loc = instantiation location + * sc = instantiation scope + * tiargs = initial list of template arguments + * tthis = if !NULL, the 'this' pointer argument + * argumentList= arguments to function + * errorHelper = delegate to send error message to if not null + */ +void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, Objects* tiargs, + Type tthis, ArgumentList argumentList, void delegate(const(char)*) scope errorHelper = null) +{ + version (none) + { + printf("functionResolve() dstart: %s (", dstart.toChars()); + for (size_t i = 0; i < (tiargs ? (*tiargs).length : 0); i++) + { + if (i) printf(", "); + RootObject arg = (*tiargs)[i]; + printf("%s", arg.toChars()); + } + printf(")("); + for (size_t i = 0; i < (argumentList.arguments ? (*argumentList.arguments).length : 0); i++) + { + if (i) printf(", "); + Expression arg = (*argumentList.arguments)[i]; + printf("%s %s", arg.type.toChars(), arg.toChars()); + //printf("\tty = %d\n", arg.type.ty); + } + printf(")\n"); + //printf("stc = %llx\n", dstart._scope.stc); + //printf("match:t/f = %d/%d\n", ta_last, m.last); + } // results int property = 0; // 0: uninitialized @@ -2501,3 +3969,1865 @@ private extern(C++) class DummyArgVisitor : Visitor result = null; } } + +/** + * Returns the common type of the 2 types. + */ +private Type rawTypeMerge(Type t1, Type t2) +{ + if (t1.equals(t2)) + return t1; + if (t1.equivalent(t2)) + return t1.castMod(MODmerge(t1.mod, t2.mod)); + + auto t1b = t1.toBasetype(); + auto t2b = t2.toBasetype(); + if (t1b.equals(t2b)) + return t1b; + if (t1b.equivalent(t2b)) + return t1b.castMod(MODmerge(t1b.mod, t2b.mod)); + + auto ty = implicitConvCommonTy(t1b.ty, t2b.ty); + if (ty != Terror) + return Type.basic[ty]; + + return null; +} + +private auto X(T, U)(T m, U n) +{ + return (m << 4) | n; +} + +private ubyte deduceWildHelper(Type t, Type* at, Type tparam) +{ + if ((tparam.mod & MODFlags.wild) == 0) + return 0; + + *at = null; + + switch (X(tparam.mod, t.mod)) + { + case X(MODFlags.wild, 0): + case X(MODFlags.wild, MODFlags.const_): + case X(MODFlags.wild, MODFlags.shared_): + case X(MODFlags.wild, MODFlags.shared_ | MODFlags.const_): + case X(MODFlags.wild, MODFlags.immutable_): + case X(MODFlags.wildconst, 0): + case X(MODFlags.wildconst, MODFlags.const_): + case X(MODFlags.wildconst, MODFlags.shared_): + case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_): + case X(MODFlags.wildconst, MODFlags.immutable_): + case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_): + case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.const_): + case X(MODFlags.shared_ | MODFlags.wild, MODFlags.immutable_): + case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_): + case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_): + case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.immutable_): + { + ubyte wm = (t.mod & ~MODFlags.shared_); + if (wm == 0) + wm = MODFlags.mutable; + ubyte m = (t.mod & (MODFlags.const_ | MODFlags.immutable_)) | (tparam.mod & t.mod & MODFlags.shared_); + *at = t.unqualify(m); + return wm; + } + case X(MODFlags.wild, MODFlags.wild): + case X(MODFlags.wild, MODFlags.wildconst): + case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wild): + case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst): + case X(MODFlags.wildconst, MODFlags.wild): + case X(MODFlags.wildconst, MODFlags.wildconst): + case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild): + case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst): + case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wild): + case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst): + case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild): + case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst): + { + *at = t.unqualify(tparam.mod & t.mod); + return MODFlags.wild; + } + default: + return 0; + } +} + +private MATCH deduceTypeHelper(Type t, out Type at, Type tparam) +{ + // 9*9 == 81 cases + switch (X(tparam.mod, t.mod)) + { + case X(0, 0): + case X(0, MODFlags.const_): + case X(0, MODFlags.wild): + case X(0, MODFlags.wildconst): + case X(0, MODFlags.shared_): + case X(0, MODFlags.shared_ | MODFlags.const_): + case X(0, MODFlags.shared_ | MODFlags.wild): + case X(0, MODFlags.shared_ | MODFlags.wildconst): + case X(0, MODFlags.immutable_): + // foo(U) T => T + // foo(U) const(T) => const(T) + // foo(U) inout(T) => inout(T) + // foo(U) inout(const(T)) => inout(const(T)) + // foo(U) shared(T) => shared(T) + // foo(U) shared(const(T)) => shared(const(T)) + // foo(U) shared(inout(T)) => shared(inout(T)) + // foo(U) shared(inout(const(T))) => shared(inout(const(T))) + // foo(U) immutable(T) => immutable(T) + { + at = t; + return MATCH.exact; + } + case X(MODFlags.const_, MODFlags.const_): + case X(MODFlags.wild, MODFlags.wild): + case X(MODFlags.wildconst, MODFlags.wildconst): + case X(MODFlags.shared_, MODFlags.shared_): + case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_ | MODFlags.const_): + case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wild): + case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst): + case X(MODFlags.immutable_, MODFlags.immutable_): + // foo(const(U)) const(T) => T + // foo(inout(U)) inout(T) => T + // foo(inout(const(U))) inout(const(T)) => T + // foo(shared(U)) shared(T) => T + // foo(shared(const(U))) shared(const(T)) => T + // foo(shared(inout(U))) shared(inout(T)) => T + // foo(shared(inout(const(U)))) shared(inout(const(T))) => T + // foo(immutable(U)) immutable(T) => T + { + at = t.mutableOf().unSharedOf(); + return MATCH.exact; + } + case X(MODFlags.const_, MODFlags.shared_ | MODFlags.const_): + case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wild): + case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst): + // foo(const(U)) shared(const(T)) => shared(T) + // foo(inout(U)) shared(inout(T)) => shared(T) + // foo(inout(const(U))) shared(inout(const(T))) => shared(T) + { + at = t.mutableOf(); + return MATCH.exact; + } + case X(MODFlags.const_, 0): + case X(MODFlags.const_, MODFlags.wild): + case X(MODFlags.const_, MODFlags.wildconst): + case X(MODFlags.const_, MODFlags.shared_ | MODFlags.wild): + case X(MODFlags.const_, MODFlags.shared_ | MODFlags.wildconst): + case X(MODFlags.const_, MODFlags.immutable_): + case X(MODFlags.shared_ | MODFlags.const_, MODFlags.immutable_): + // foo(const(U)) T => T + // foo(const(U)) inout(T) => T + // foo(const(U)) inout(const(T)) => T + // foo(const(U)) shared(inout(T)) => shared(T) + // foo(const(U)) shared(inout(const(T))) => shared(T) + // foo(const(U)) immutable(T) => T + // foo(shared(const(U))) immutable(T) => T + { + at = t.mutableOf(); + return MATCH.constant; + } + case X(MODFlags.const_, MODFlags.shared_): + // foo(const(U)) shared(T) => shared(T) + { + at = t; + return MATCH.constant; + } + case X(MODFlags.shared_, MODFlags.shared_ | MODFlags.const_): + case X(MODFlags.shared_, MODFlags.shared_ | MODFlags.wild): + case X(MODFlags.shared_, MODFlags.shared_ | MODFlags.wildconst): + // foo(shared(U)) shared(const(T)) => const(T) + // foo(shared(U)) shared(inout(T)) => inout(T) + // foo(shared(U)) shared(inout(const(T))) => inout(const(T)) + { + at = t.unSharedOf(); + return MATCH.exact; + } + case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_): + // foo(shared(const(U))) shared(T) => T + { + at = t.unSharedOf(); + return MATCH.constant; + } + case X(MODFlags.wildconst, MODFlags.immutable_): + case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_ | MODFlags.wildconst): + case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.immutable_): + case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild): + // foo(inout(const(U))) immutable(T) => T + // foo(shared(const(U))) shared(inout(const(T))) => T + // foo(shared(inout(const(U)))) immutable(T) => T + // foo(shared(inout(const(U)))) shared(inout(T)) => T + { + at = t.unSharedOf().mutableOf(); + return MATCH.constant; + } + case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_ | MODFlags.wild): + // foo(shared(const(U))) shared(inout(T)) => T + { + at = t.unSharedOf().mutableOf(); + return MATCH.constant; + } + case X(MODFlags.wild, 0): + case X(MODFlags.wild, MODFlags.const_): + case X(MODFlags.wild, MODFlags.wildconst): + case X(MODFlags.wild, MODFlags.immutable_): + case X(MODFlags.wild, MODFlags.shared_): + case X(MODFlags.wild, MODFlags.shared_ | MODFlags.const_): + case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst): + case X(MODFlags.wildconst, 0): + case X(MODFlags.wildconst, MODFlags.const_): + case X(MODFlags.wildconst, MODFlags.wild): + case X(MODFlags.wildconst, MODFlags.shared_): + case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_): + case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild): + case X(MODFlags.shared_, 0): + case X(MODFlags.shared_, MODFlags.const_): + case X(MODFlags.shared_, MODFlags.wild): + case X(MODFlags.shared_, MODFlags.wildconst): + case X(MODFlags.shared_, MODFlags.immutable_): + case X(MODFlags.shared_ | MODFlags.const_, 0): + case X(MODFlags.shared_ | MODFlags.const_, MODFlags.const_): + case X(MODFlags.shared_ | MODFlags.const_, MODFlags.wild): + case X(MODFlags.shared_ | MODFlags.const_, MODFlags.wildconst): + case X(MODFlags.shared_ | MODFlags.wild, 0): + case X(MODFlags.shared_ | MODFlags.wild, MODFlags.const_): + case X(MODFlags.shared_ | MODFlags.wild, MODFlags.wild): + case X(MODFlags.shared_ | MODFlags.wild, MODFlags.wildconst): + case X(MODFlags.shared_ | MODFlags.wild, MODFlags.immutable_): + case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_): + case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.const_): + case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst): + case X(MODFlags.shared_ | MODFlags.wildconst, 0): + case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.const_): + case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.wild): + case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.wildconst): + case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_): + case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_): + case X(MODFlags.immutable_, 0): + case X(MODFlags.immutable_, MODFlags.const_): + case X(MODFlags.immutable_, MODFlags.wild): + case X(MODFlags.immutable_, MODFlags.wildconst): + case X(MODFlags.immutable_, MODFlags.shared_): + case X(MODFlags.immutable_, MODFlags.shared_ | MODFlags.const_): + case X(MODFlags.immutable_, MODFlags.shared_ | MODFlags.wild): + case X(MODFlags.immutable_, MODFlags.shared_ | MODFlags.wildconst): + // foo(inout(U)) T => nomatch + // foo(inout(U)) const(T) => nomatch + // foo(inout(U)) inout(const(T)) => nomatch + // foo(inout(U)) immutable(T) => nomatch + // foo(inout(U)) shared(T) => nomatch + // foo(inout(U)) shared(const(T)) => nomatch + // foo(inout(U)) shared(inout(const(T))) => nomatch + // foo(inout(const(U))) T => nomatch + // foo(inout(const(U))) const(T) => nomatch + // foo(inout(const(U))) inout(T) => nomatch + // foo(inout(const(U))) shared(T) => nomatch + // foo(inout(const(U))) shared(const(T)) => nomatch + // foo(inout(const(U))) shared(inout(T)) => nomatch + // foo(shared(U)) T => nomatch + // foo(shared(U)) const(T) => nomatch + // foo(shared(U)) inout(T) => nomatch + // foo(shared(U)) inout(const(T)) => nomatch + // foo(shared(U)) immutable(T) => nomatch + // foo(shared(const(U))) T => nomatch + // foo(shared(const(U))) const(T) => nomatch + // foo(shared(const(U))) inout(T) => nomatch + // foo(shared(const(U))) inout(const(T)) => nomatch + // foo(shared(inout(U))) T => nomatch + // foo(shared(inout(U))) const(T) => nomatch + // foo(shared(inout(U))) inout(T) => nomatch + // foo(shared(inout(U))) inout(const(T)) => nomatch + // foo(shared(inout(U))) immutable(T) => nomatch + // foo(shared(inout(U))) shared(T) => nomatch + // foo(shared(inout(U))) shared(const(T)) => nomatch + // foo(shared(inout(U))) shared(inout(const(T))) => nomatch + // foo(shared(inout(const(U)))) T => nomatch + // foo(shared(inout(const(U)))) const(T) => nomatch + // foo(shared(inout(const(U)))) inout(T) => nomatch + // foo(shared(inout(const(U)))) inout(const(T)) => nomatch + // foo(shared(inout(const(U)))) shared(T) => nomatch + // foo(shared(inout(const(U)))) shared(const(T)) => nomatch + // foo(immutable(U)) T => nomatch + // foo(immutable(U)) const(T) => nomatch + // foo(immutable(U)) inout(T) => nomatch + // foo(immutable(U)) inout(const(T)) => nomatch + // foo(immutable(U)) shared(T) => nomatch + // foo(immutable(U)) shared(const(T)) => nomatch + // foo(immutable(U)) shared(inout(T)) => nomatch + // foo(immutable(U)) shared(inout(const(T))) => nomatch + return MATCH.nomatch; + + default: + assert(0); + } +} + +__gshared Expression emptyArrayElement = null; + +/* + * Returns `true` if `t` is a reference type, or an array of reference types. + */ +private bool isTopRef(Type t) +{ + auto tb = t.baseElemOf(); + return tb.ty == Tclass || + tb.ty == Taarray || + tb.ty == Tstruct && tb.hasPointers(); +} + +/* + * Returns a valid `Loc` for semantic routines. + * Some paths require a location derived from the first + * template parameter when available. + */ +private Loc semanticLoc(scope ref TemplateParameters parameters) +{ + Loc loc; + if (parameters.length) + loc = parameters[0].loc; + return loc; +} + +private MATCH deduceAliasThis(Type t, Scope* sc, Type tparam, + ref TemplateParameters parameters, ref Objects dedtypes, uint* wm) +{ + if (auto tc = t.isTypeClass()) + { + if (tc.sym.aliasthis && !(tc.att & AliasThisRec.tracingDT)) + { + if (auto ato = t.aliasthisOf()) + { + tc.att = cast(AliasThisRec)(tc.att | AliasThisRec.tracingDT); + auto m = deduceType(ato, sc, tparam, parameters, dedtypes, wm); + tc.att = cast(AliasThisRec)(tc.att & ~AliasThisRec.tracingDT); + return m; + } + } + } + else if (auto ts = t.isTypeStruct()) + { + if (ts.sym.aliasthis && !(ts.att & AliasThisRec.tracingDT)) + { + if (auto ato = t.aliasthisOf()) + { + ts.att = cast(AliasThisRec)(ts.att | AliasThisRec.tracingDT); + auto m = deduceType(ato, sc, tparam, parameters, dedtypes, wm); + ts.att = cast(AliasThisRec)(ts.att & ~AliasThisRec.tracingDT); + return m; + } + } + } + return MATCH.nomatch; +} + +private MATCH deduceParentInstance(Scope* sc, Dsymbol sym, TypeInstance tpi, + ref TemplateParameters parameters, ref Objects dedtypes, uint* wm) +{ + if (tpi.idents.length) + { + RootObject id = tpi.idents[tpi.idents.length - 1]; + if (id.dyncast() == DYNCAST.identifier && sym.ident.equals(cast(Identifier)id)) + { + Type tparent = sym.parent.getType(); + if (tparent) + { + tpi.idents.length--; + auto m = deduceType(tparent, sc, tpi, parameters, dedtypes, wm); + tpi.idents.length++; + return m; + } + } + } + return MATCH.nomatch; +} + +private MATCH matchAll(TypeDeduced td, Type tt) +{ + MATCH match = MATCH.exact; + foreach (j, e; td.argexps) + { + assert(e); + if (e == emptyArrayElement) + continue; + + Type t = tt.addMod(td.tparams[j].mod).substWildTo(MODFlags.const_); + + MATCH m = e.implicitConvTo(t); + if (match > m) + match = m; + if (match == MATCH.nomatch) + break; + } + return match; +} +/**** + * Given an identifier, figure out which TemplateParameter it is. + * Return IDX_NOTFOUND if not found. + */ +private size_t templateIdentifierLookup(Identifier id, TemplateParameters* parameters) +{ + for (size_t i = 0; i < parameters.length; i++) + { + TemplateParameter tp = (*parameters)[i]; + if (tp.ident.equals(id)) + return i; + } + return IDX_NOTFOUND; +} + +private size_t templateParameterLookup(Type tparam, TemplateParameters* parameters) +{ + if (TypeIdentifier tident = tparam.isTypeIdentifier()) + { + //printf("\ttident = '%s'\n", tident.toChars()); + return templateIdentifierLookup(tident.ident, parameters); + } + return IDX_NOTFOUND; +} +/* These form the heart of template argument deduction. + * Given 'this' being the type argument to the template instance, + * it is matched against the template declaration parameter specialization + * 'tparam' to determine the type to be used for the parameter. + * Example: + * template Foo(T:T*) // template declaration + * Foo!(int*) // template instantiation + * Input: + * this = int* + * tparam = T* + * parameters = [ T:T* ] // Array of TemplateParameter's + * Output: + * dedtypes = [ int ] // Array of Expression/Type's + */ +MATCH deduceType(scope RootObject o, scope Scope* sc, scope Type tparam, + scope ref TemplateParameters parameters, scope ref Objects dedtypes, + scope uint* wm = null, size_t inferStart = 0, bool ignoreAliasThis = false) +{ + extern (C++) final class DeduceType : Visitor + { + alias visit = Visitor.visit; + public: + MATCH result; + + extern (D) this() @safe + { + result = MATCH.nomatch; + } + + override void visit(Type t) + { + if (!tparam) + goto Lnomatch; + + if (t == tparam) + goto Lexact; + + if (tparam.ty == Tident) + { + // Determine which parameter tparam is + size_t i = templateParameterLookup(tparam, ¶meters); + if (i == IDX_NOTFOUND) + { + if (!sc) + goto Lnomatch; + + /* Need a loc to go with the semantic routine. */ + Loc loc = semanticLoc(parameters); + + /* BUG: what if tparam is a template instance, that + * has as an argument another Tident? + */ + tparam = tparam.typeSemantic(loc, sc); + assert(tparam.ty != Tident); + result = deduceType(t, sc, tparam, parameters, dedtypes, wm); + return; + } + + TemplateParameter tp = parameters[i]; + + TypeIdentifier tident = tparam.isTypeIdentifier(); + if (tident.idents.length > 0) + { + //printf("matching %s to %s\n", tparam.toChars(), t.toChars()); + Dsymbol s = t.toDsymbol(sc); + for (size_t j = tident.idents.length; j-- > 0;) + { + RootObject id = tident.idents[j]; + if (id.dyncast() == DYNCAST.identifier) + { + if (!s || !s.parent) + goto Lnomatch; + Dsymbol s2 = s.parent.search(Loc.initial, cast(Identifier)id); + if (!s2) + goto Lnomatch; + s2 = s2.toAlias(); + //printf("[%d] s = %s %s, s2 = %s %s\n", j, s.kind(), s.toChars(), s2.kind(), s2.toChars()); + if (s != s2) + { + if (Type tx = s2.getType()) + { + if (s != tx.toDsymbol(sc)) + goto Lnomatch; + } + else + goto Lnomatch; + } + s = s.parent; + } + else + goto Lnomatch; + } + //printf("[e] s = %s\n", s?s.toChars():"(null)"); + if (tp.isTemplateTypeParameter()) + { + Type tt = s.getType(); + if (!tt) + goto Lnomatch; + Type at = cast(Type)dedtypes[i]; + if (at && at.ty == Tnone) + at = (cast(TypeDeduced)at).tded; + if (!at || tt.equals(at)) + { + dedtypes[i] = tt; + goto Lexact; + } + } + if (tp.isTemplateAliasParameter()) + { + Dsymbol s2 = cast(Dsymbol)dedtypes[i]; + if (!s2 || s == s2) + { + dedtypes[i] = s; + goto Lexact; + } + } + goto Lnomatch; + } + + // Found the corresponding parameter tp + /+ + https://issues.dlang.org/show_bug.cgi?id=23578 + To pattern match: + static if (is(S!int == S!av, alias av)) + + We eventually need to deduce `int` (Tint32 [0]) and `av` (Tident). + Previously this would not get pattern matched at all, but now we check if the + template parameter `av` came from. + + This note has been left to serve as a hint for further explorers into + how IsExp matching works. + +/ + if (auto ta = tp.isTemplateAliasParameter()) + { + dedtypes[i] = t; + goto Lexact; + } + // (23578) - ensure previous behaviour for non-alias template params + if (!tp.isTemplateTypeParameter()) + { + goto Lnomatch; + } + + Type at = cast(Type)dedtypes[i]; + Type tt; + if (ubyte wx = wm ? deduceWildHelper(t, &tt, tparam) : 0) + { + // type vs (none) + if (!at) + { + dedtypes[i] = tt; + *wm |= wx; + result = MATCH.constant; + return; + } + + // type vs expressions + if (at.ty == Tnone) + { + auto xt = cast(TypeDeduced)at; + result = xt.matchAll(tt); + if (result > MATCH.nomatch) + { + dedtypes[i] = tt; + if (result > MATCH.constant) + result = MATCH.constant; // limit level for inout matches + } + return; + } + + // type vs type + if (tt.equals(at)) + { + dedtypes[i] = tt; // Prefer current type match + goto Lconst; + } + if (tt.implicitConvTo(at.constOf())) + { + dedtypes[i] = at.constOf().mutableOf(); + *wm |= MODFlags.const_; + goto Lconst; + } + if (at.implicitConvTo(tt.constOf())) + { + dedtypes[i] = tt.constOf().mutableOf(); + *wm |= MODFlags.const_; + goto Lconst; + } + goto Lnomatch; + } + else if (MATCH m = deduceTypeHelper(t, tt, tparam)) + { + // type vs (none) + if (!at) + { + dedtypes[i] = tt; + result = m; + return; + } + + // type vs expressions + if (at.ty == Tnone) + { + auto xt = cast(TypeDeduced)at; + result = xt.matchAll(tt); + if (result > MATCH.nomatch) + { + dedtypes[i] = tt; + } + return; + } + + // type vs type + if (tt.equals(at)) + { + goto Lexact; + } + if (tt.ty == Tclass && at.ty == Tclass) + { + result = tt.implicitConvTo(at); + return; + } + if (tt.ty == Tsarray && at.ty == Tarray && tt.nextOf().implicitConvTo(at.nextOf()) >= MATCH.constant) + { + goto Lexact; + } + } + goto Lnomatch; + } + + if (tparam.ty == Ttypeof) + { + /* Need a loc to go with the semantic routine. */ + Loc loc = semanticLoc(parameters); + + tparam = tparam.typeSemantic(loc, sc); + } + if (t.ty != tparam.ty) + { + if (Dsymbol sym = t.toDsymbol(sc)) + { + if (sym.isforwardRef() && !tparam.deco) + goto Lnomatch; + } + + MATCH m = t.implicitConvTo(tparam); + if (m == MATCH.nomatch && !ignoreAliasThis) + { + m = deduceAliasThis(t, sc, tparam, parameters, dedtypes, wm); + } + result = m; + return; + } + + if (t.nextOf()) + { + if (tparam.deco && !tparam.hasWild()) + { + result = t.implicitConvTo(tparam); + return; + } + + Type tpn = tparam.nextOf(); + if (wm && t.ty == Taarray && tparam.isWild()) + { + // https://issues.dlang.org/show_bug.cgi?id=12403 + // In IFTI, stop inout matching on transitive part of AA types. + tpn = tpn.substWildTo(MODFlags.mutable); + } + + result = deduceType(t.nextOf(), sc, tpn, parameters, dedtypes, wm); + return; + } + + Lexact: + result = MATCH.exact; + return; + + Lnomatch: + result = MATCH.nomatch; + return; + + Lconst: + result = MATCH.constant; + } + + override void visit(TypeVector t) + { + if (auto tp = tparam.isTypeVector()) + { + result = deduceType(t.basetype, sc, tp.basetype, parameters, dedtypes, wm); + return; + } + visit(cast(Type)t); + } + + override void visit(TypeDArray t) + { + visit(cast(Type)t); + } + + override void visit(TypeSArray t) + { + // Extra check that array dimensions must match + if (!tparam) + { + visit(cast(Type)t); + return; + } + + if (tparam.ty == Tarray) + { + MATCH m = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm); + result = (m >= MATCH.constant) ? MATCH.convert : MATCH.nomatch; + return; + } + + TemplateParameter tp = null; + Expression edim = null; + size_t i; + if (auto tsa = tparam.isTypeSArray()) + { + if (tsa.dim.isVarExp() && tsa.dim.isVarExp().var.storage_class & STC.templateparameter) + { + Identifier id = tsa.dim.isVarExp().var.ident; + i = templateIdentifierLookup(id, ¶meters); + assert(i != IDX_NOTFOUND); + tp = parameters[i]; + } + else + edim = tsa.dim; + } + else if (auto taa = tparam.isTypeAArray()) + { + i = templateParameterLookup(taa.index, ¶meters); + if (i != IDX_NOTFOUND) + tp = parameters[i]; + else + { + Loc loc; + // The "type" (it hasn't been resolved yet) of the function parameter + // does not have a location but the parameter it is related to does, + // so we use that for the resolution (better error message). + if (inferStart < parameters.length) + { + TemplateParameter loctp = parameters[inferStart]; + loc = loctp.loc; + } + + Expression e; + Type tx; + Dsymbol s; + taa.index.resolve(loc, sc, e, tx, s); + edim = s ? getValue(s) : getValue(e); + } + } + if ((tp && tp.matchArg(sc, t.dim, i, ¶meters, dedtypes, null)) || + (edim && edim.isIntegerExp() && edim.toInteger() == t.dim.toInteger()) + ) + { + result = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm); + return; + } + + visit(cast(Type)t); + } + + override void visit(TypeAArray t) + { + // Extra check that index type must match + if (tparam && tparam.ty == Taarray) + { + TypeAArray tp = tparam.isTypeAArray(); + if (!deduceType(t.index, sc, tp.index, parameters, dedtypes)) + { + result = MATCH.nomatch; + return; + } + } + visit(cast(Type)t); + } + + override void visit(TypeFunction t) + { + // Extra check that function characteristics must match + if (!tparam) + return visit(cast(Type)t); + + auto tp = tparam.isTypeFunction(); + if (!tp) + { + visit(cast(Type)t); + return; + } + + if (t.parameterList.varargs != tp.parameterList.varargs || t.linkage != tp.linkage) + { + result = MATCH.nomatch; + return; + } + + foreach (fparam; *tp.parameterList.parameters) + { + // https://issues.dlang.org/show_bug.cgi?id=2579 + // Apply function parameter storage classes to parameter types + fparam.type = fparam.type.addStorageClass(fparam.storageClass); + fparam.storageClass &= ~STC.TYPECTOR; + + // https://issues.dlang.org/show_bug.cgi?id=15243 + // Resolve parameter type if it's not related with template parameters + if (!reliesOnTemplateParameters(fparam.type, parameters[inferStart .. parameters.length])) + { + auto tx = fparam.type.typeSemantic(Loc.initial, sc); + if (tx.ty == Terror) + { + result = MATCH.nomatch; + return; + } + fparam.type = tx; + } + } + + const size_t nfargs = t.parameterList.length; + size_t nfparams = tp.parameterList.length; + + if (!deduceFunctionTuple(t, tp, parameters, dedtypes, nfargs, nfparams)) + { + result = MATCH.nomatch; + return; + } + + L2: + assert(nfparams <= tp.parameterList.length); + foreach (i, ap; tp.parameterList) + { + if (i == nfparams) + break; + + Parameter a = t.parameterList[i]; + + if (!a.isCovariant(t.isRef, ap) || + !deduceType(a.type, sc, ap.type, parameters, dedtypes)) + { + result = MATCH.nomatch; + return; + } + } + + visit(cast(Type)t); + } + + override void visit(TypeIdentifier t) + { + // Extra check + if (tparam && tparam.ty == Tident) + { + TypeIdentifier tp = tparam.isTypeIdentifier(); + for (size_t i = 0; i < t.idents.length; i++) + { + RootObject id1 = t.idents[i]; + RootObject id2 = tp.idents[i]; + if (!id1.equals(id2)) + { + result = MATCH.nomatch; + return; + } + } + } + visit(cast(Type)t); + } + + override void visit(TypeInstance t) + { + // Extra check + if (!tparam || tparam.ty != Tinstance || !t.tempinst.tempdecl) + { + visit(cast(Type)t); + return; + } + + TemplateDeclaration tempdecl = t.tempinst.tempdecl.isTemplateDeclaration(); + assert(tempdecl); + + TypeInstance tp = tparam.isTypeInstance(); + + //printf("tempinst.tempdecl = %p\n", tempdecl); + //printf("tp.tempinst.tempdecl = %p\n", tp.tempinst.tempdecl); + if (!tp.tempinst.tempdecl) + { + //printf("tp.tempinst.name = '%s'\n", tp.tempinst.name.toChars()); + + /* Handle case of: + * template Foo(T : sa!(T), alias sa) + */ + size_t i = templateIdentifierLookup(tp.tempinst.name, ¶meters); + if (i == IDX_NOTFOUND) + { + /* Didn't find it as a parameter identifier. Try looking + * it up and seeing if is an alias. + * https://issues.dlang.org/show_bug.cgi?id=1454 + */ + auto tid = new TypeIdentifier(tp.loc, tp.tempinst.name); + Type tx; + Expression e; + Dsymbol s; + tid.resolve(tp.loc, sc, e, tx, s); + if (tx) + { + s = tx.toDsymbol(sc); + if (TemplateInstance ti = s ? s.parent.isTemplateInstance() : null) + { + // https://issues.dlang.org/show_bug.cgi?id=14290 + // Try to match with ti.tempecl, + // only when ti is an enclosing instance. + Dsymbol p = sc.parent; + while (p && p != ti) + p = p.parent; + if (p) + s = ti.tempdecl; + } + } + if (s) + { + s = s.toAlias(); + TemplateDeclaration td = s.isTemplateDeclaration(); + if (td) + { + if (td.overroot) + td = td.overroot; + for (; td; td = td.overnext) + { + if (td == tempdecl) + goto L2; + } + } + } + goto Lnomatch; + } + + TemplateParameter tpx = parameters[i]; + if (!tpx.matchArg(sc, tempdecl, i, ¶meters, dedtypes, null)) + goto Lnomatch; + } + else if (tempdecl != tp.tempinst.tempdecl) + goto Lnomatch; + + L2: + if (!resolveTemplateInstantiation(sc, ¶meters, t.tempinst.tiargs, &t.tempinst.tdtypes, tempdecl, tp, &dedtypes)) + goto Lnomatch; + + visit(cast(Type)t); + return; + + Lnomatch: + //printf("no match\n"); + result = MATCH.nomatch; + } + + override void visit(TypeStruct t) + { + /* If this struct is a template struct, and we're matching + * it against a template instance, convert the struct type + * to a template instance, too, and try again. + */ + TemplateInstance ti = t.sym.parent.isTemplateInstance(); + + if (tparam && tparam.ty == Tinstance) + { + if (ti && ti.toAlias() == t.sym) + { + auto tx = new TypeInstance(Loc.initial, ti); + auto m = deduceType(tx, sc, tparam, parameters, dedtypes, wm); + // if we have a no match we still need to check alias this + if (m != MATCH.nomatch) + { + result = m; + return; + } + } + + TypeInstance tpi = tparam.isTypeInstance(); + auto m = deduceParentInstance(sc, t.sym, tpi, parameters, dedtypes, wm); + if (m != MATCH.nomatch) + { + result = m; + return; + } + } + + // Extra check + if (tparam && tparam.ty == Tstruct) + { + TypeStruct tp = tparam.isTypeStruct(); + + //printf("\t%d\n", cast(MATCH) t.implicitConvTo(tp)); + if (wm && t.deduceWild(tparam, false)) + { + result = MATCH.constant; + return; + } + result = t.implicitConvTo(tp); + return; + } + visit(cast(Type)t); + } + + override void visit(TypeEnum t) + { + // Extra check + if (tparam && tparam.ty == Tenum) + { + TypeEnum tp = tparam.isTypeEnum(); + if (t.sym == tp.sym) + visit(cast(Type)t); + else + result = MATCH.nomatch; + return; + } + Type tb = t.toBasetype(); + if (tb.ty == tparam.ty || tb.ty == Tsarray && tparam.ty == Taarray) + { + result = deduceType(tb, sc, tparam, parameters, dedtypes, wm); + if (result == MATCH.exact) + result = MATCH.convert; + return; + } + visit(cast(Type)t); + } + + override void visit(TypeClass t) + { + //printf("TypeClass.deduceType(this = %s)\n", t.toChars()); + + /* If this class is a template class, and we're matching + * it against a template instance, convert the class type + * to a template instance, too, and try again. + */ + TemplateInstance ti = t.sym.parent.isTemplateInstance(); + + if (tparam && tparam.ty == Tinstance) + { + if (ti && ti.toAlias() == t.sym) + { + auto tx = new TypeInstance(Loc.initial, ti); + MATCH m = deduceType(tx, sc, tparam, parameters, dedtypes, wm); + // Even if the match fails, there is still a chance it could match + // a base class. + if (m != MATCH.nomatch) + { + result = m; + return; + } + } + + TypeInstance tpi = tparam.isTypeInstance(); + auto m = deduceParentInstance(sc, t.sym, tpi, parameters, dedtypes, wm); + if (m != MATCH.nomatch) + { + result = m; + return; + } + + // If it matches exactly or via implicit conversion, we're done + visit(cast(Type)t); + if (result != MATCH.nomatch) + return; + + /* There is still a chance to match via implicit conversion to + * a base class or interface. Because there could be more than one such + * match, we need to check them all. + */ + + int numBaseClassMatches = 0; // Have we found an interface match? + + // Our best guess at dedtypes + auto best = new Objects(dedtypes.length); + + ClassDeclaration s = t.sym; + while (s && s.baseclasses.length > 0) + { + // Test the base class + deduceBaseClassParameters(*(*s.baseclasses)[0], sc, tparam, parameters, dedtypes, *best, numBaseClassMatches); + + // Test the interfaces inherited by the base class + foreach (b; s.interfaces) + { + deduceBaseClassParameters(*b, sc, tparam, parameters, dedtypes, *best, numBaseClassMatches); + } + s = (*s.baseclasses)[0].sym; + } + + if (numBaseClassMatches == 0) + { + result = MATCH.nomatch; + return; + } + + // If we got at least one match, copy the known types into dedtypes + memcpy(dedtypes.tdata(), best.tdata(), best.length * (void*).sizeof); + result = MATCH.convert; + return; + } + + // Extra check + if (tparam && tparam.ty == Tclass) + { + TypeClass tp = tparam.isTypeClass(); + + //printf("\t%d\n", cast(MATCH) t.implicitConvTo(tp)); + if (wm && t.deduceWild(tparam, false)) + { + result = MATCH.constant; + return; + } + result = t.implicitConvTo(tp); + return; + } + visit(cast(Type)t); + } + + override void visit(Expression e) + { + //printf("Expression.deduceType(e = %s)\n", e.toChars()); + size_t i = templateParameterLookup(tparam, ¶meters); + if (i == IDX_NOTFOUND || tparam.isTypeIdentifier().idents.length > 0) + { + if (e == emptyArrayElement && tparam.ty == Tarray) + { + Type tn = (cast(TypeNext)tparam).next; + result = deduceType(emptyArrayElement, sc, tn, parameters, dedtypes, wm); + return; + } + e.type.accept(this); + return; + } + + TemplateTypeParameter tp = parameters[i].isTemplateTypeParameter(); + if (!tp) + return; // nomatch + + if (e == emptyArrayElement) + { + if (dedtypes[i]) + { + result = MATCH.exact; + return; + } + if (tp.defaultType) + { + tp.defaultType.accept(this); + return; + } + } + + Type at = cast(Type)dedtypes[i]; + Type tt; + if (ubyte wx = deduceWildHelper(e.type, &tt, tparam)) + { + *wm |= wx; + result = MATCH.constant; + } + else if (MATCH m = deduceTypeHelper(e.type, tt, tparam)) + { + result = m; + } + else if (!isTopRef(e.type)) + { + /* https://issues.dlang.org/show_bug.cgi?id=15653 + * In IFTI, recognize top-qualifier conversions + * through the value copy, e.g. + * int --> immutable(int) + * immutable(string[]) --> immutable(string)[] + */ + tt = e.type.mutableOf(); + result = MATCH.convert; + } + else + return; // nomatch + + // expression vs (none) + if (!at) + { + dedtypes[i] = new TypeDeduced(tt, e, tparam); + return; + } + + TypeDeduced xt = null; + if (at.ty == Tnone) + { + xt = cast(TypeDeduced)at; + at = xt.tded; + } + + // From previous matched expressions to current deduced type + MATCH match1 = xt ? xt.matchAll(tt) : MATCH.nomatch; + + // From current expressions to previous deduced type + Type pt = at.addMod(tparam.mod); + if (*wm) + pt = pt.substWildTo(*wm); + MATCH match2 = e.implicitConvTo(pt); + + if (match1 > MATCH.nomatch && match2 > MATCH.nomatch) + { + if (at.implicitConvTo(tt) == MATCH.nomatch) + match1 = MATCH.nomatch; // Prefer at + else if (tt.implicitConvTo(at) == MATCH.nomatch) + match2 = MATCH.nomatch; // Prefer tt + else if (tt.isTypeBasic() && tt.ty == at.ty && tt.mod != at.mod) + { + if (!tt.isMutable() && !at.isMutable()) + tt = tt.mutableOf().addMod(MODmerge(tt.mod, at.mod)); + else if (tt.isMutable()) + { + if (at.mod == 0) // Prefer unshared + match1 = MATCH.nomatch; + else + match2 = MATCH.nomatch; + } + else if (at.isMutable()) + { + if (tt.mod == 0) // Prefer unshared + match2 = MATCH.nomatch; + else + match1 = MATCH.nomatch; + } + //printf("tt = %s, at = %s\n", tt.toChars(), at.toChars()); + } + else + { + match1 = MATCH.nomatch; + match2 = MATCH.nomatch; + } + } + if (match1 > MATCH.nomatch) + { + // Prefer current match: tt + if (xt) + xt.update(tt, e, tparam); + else + dedtypes[i] = tt; + result = match1; + return; + } + if (match2 > MATCH.nomatch) + { + // Prefer previous match: (*dedtypes)[i] + if (xt) + xt.update(e, tparam); + result = match2; + return; + } + + /* Deduce common type + */ + if (Type t = rawTypeMerge(at, tt)) + { + if (xt) + xt.update(t, e, tparam); + else + dedtypes[i] = t; + + pt = tt.addMod(tparam.mod); + if (*wm) + pt = pt.substWildTo(*wm); + result = e.implicitConvTo(pt); + return; + } + + result = MATCH.nomatch; + } + + private MATCH deduceEmptyArrayElement() + { + if (!emptyArrayElement) + { + emptyArrayElement = new IdentifierExp(Loc.initial, Id.p); // dummy + emptyArrayElement.type = Type.tvoid; + } + assert(tparam.ty == Tarray); + + Type tn = (cast(TypeNext)tparam).next; + return deduceType(emptyArrayElement, sc, tn, parameters, dedtypes, wm); + } + + override void visit(NullExp e) + { + if (tparam.ty == Tarray && e.type.ty == Tnull) + { + // tparam:T[] <- e:null (void[]) + result = deduceEmptyArrayElement(); + return; + } + visit(cast(Expression)e); + } + + override void visit(StringExp e) + { + Type taai; + if (e.type.ty == Tarray && (tparam.ty == Tsarray || tparam.ty == Taarray && (taai = (cast(TypeAArray)tparam).index).ty == Tident && (cast(TypeIdentifier)taai).idents.length == 0)) + { + // Consider compile-time known boundaries + e.type.nextOf().sarrayOf(e.len).accept(this); + return; + } + visit(cast(Expression)e); + } + + override void visit(ArrayLiteralExp e) + { + // https://issues.dlang.org/show_bug.cgi?id=20092 + if (e.elements && e.elements.length && e.type.toBasetype().nextOf().ty == Tvoid) + { + result = deduceEmptyArrayElement(); + return; + } + if ((!e.elements || !e.elements.length) && e.type.toBasetype().nextOf().ty == Tvoid && tparam.ty == Tarray) + { + // tparam:T[] <- e:[] (void[]) + result = deduceEmptyArrayElement(); + return; + } + + if (tparam.ty == Tarray && e.elements && e.elements.length) + { + Type tn = (cast(TypeDArray)tparam).next; + result = MATCH.exact; + if (e.basis) + { + MATCH m = deduceType(e.basis, sc, tn, parameters, dedtypes, wm); + if (m < result) + result = m; + } + foreach (el; *e.elements) + { + if (result == MATCH.nomatch) + break; + if (!el) + continue; + MATCH m = deduceType(el, sc, tn, parameters, dedtypes, wm); + if (m < result) + result = m; + } + return; + } + + Type taai; + if (e.type.ty == Tarray && (tparam.ty == Tsarray || tparam.ty == Taarray && (taai = (cast(TypeAArray)tparam).index).ty == Tident && (cast(TypeIdentifier)taai).idents.length == 0)) + { + // Consider compile-time known boundaries + e.type.nextOf().sarrayOf(e.elements.length).accept(this); + return; + } + visit(cast(Expression)e); + } + + override void visit(AssocArrayLiteralExp e) + { + if (tparam.ty == Taarray && e.keys && e.keys.length) + { + TypeAArray taa = cast(TypeAArray)tparam; + result = MATCH.exact; + foreach (i, key; *e.keys) + { + MATCH m1 = deduceType(key, sc, taa.index, parameters, dedtypes, wm); + if (m1 < result) + result = m1; + if (result == MATCH.nomatch) + break; + MATCH m2 = deduceType((*e.values)[i], sc, taa.next, parameters, dedtypes, wm); + if (m2 < result) + result = m2; + if (result == MATCH.nomatch) + break; + } + return; + } + visit(cast(Expression)e); + } + + override void visit(FuncExp e) + { + //printf("e.type = %s, tparam = %s\n", e.type.toChars(), tparam.toChars()); + if (e.td) + { + Type to = tparam; + if (!to.nextOf()) + return; + auto tof = to.nextOf().isTypeFunction(); + if (!tof) + return; + + // Parameter types inference from 'tof' + assert(e.td._scope); + TypeFunction tf = e.fd.type.isTypeFunction(); + //printf("\ttof = %s\n", tof.toChars()); + //printf("\ttf = %s\n", tf.toChars()); + const dim = tf.parameterList.length; + + if (tof.parameterList.length != dim || tof.parameterList.varargs != tf.parameterList.varargs) + return; + + auto tiargs = new Objects(); + tiargs.reserve(e.td.parameters.length); + + foreach (tp; *e.td.parameters) + { + size_t u = 0; + foreach (i, p; tf.parameterList) + { + if (p.type.ty == Tident && (cast(TypeIdentifier)p.type).ident == tp.ident) + break; + ++u; + } + assert(u < dim); + Parameter pto = tof.parameterList[u]; + if (!pto) + break; + Type t = pto.type.syntaxCopy(); // https://issues.dlang.org/show_bug.cgi?id=11774 + if (reliesOnTemplateParameters(t, parameters[inferStart .. parameters.length])) + return; + t = t.typeSemantic(e.loc, sc); + if (t.ty == Terror) + return; + tiargs.push(t); + } + + // Set target of return type inference + if (!tf.next && tof.next) + e.fd.treq = tparam; + + auto ti = new TemplateInstance(e.loc, e.td, tiargs); + Expression ex = (new ScopeExp(e.loc, ti)).expressionSemantic(e.td._scope); + + // Reset inference target for the later re-semantic + e.fd.treq = null; + + if (ex.op == EXP.error) + return; + if (ex.op != EXP.function_) + return; + visit(ex.type); + return; + } + + Type t = e.type; + + if (t.ty == Tdelegate && tparam.ty == Tpointer) + return; + + // Allow conversion from implicit function pointer to delegate + if (e.tok == TOK.reserved && t.ty == Tpointer && tparam.ty == Tdelegate) + { + TypeFunction tf = t.nextOf().isTypeFunction(); + t = (new TypeDelegate(tf)).merge(); + } + //printf("tparam = %s <= e.type = %s, t = %s\n", tparam.toChars(), e.type.toChars(), t.toChars()); + visit(t); + } + + override void visit(SliceExp e) + { + Type taai; + if (e.type.ty == Tarray && (tparam.ty == Tsarray || tparam.ty == Taarray && (taai = (cast(TypeAArray)tparam).index).ty == Tident && (cast(TypeIdentifier)taai).idents.length == 0)) + { + // Consider compile-time known boundaries + if (Type tsa = toStaticArrayType(e)) + { + tsa.accept(this); + if (result > MATCH.convert) + result = MATCH.convert; // match with implicit conversion at most + return; + } + } + visit(cast(Expression)e); + } + + override void visit(CommaExp e) + { + e.e2.accept(this); + } + } + + scope DeduceType v = new DeduceType(); + if (Type t = isType(o)) + t.accept(v); + else if (Expression e = isExpression(o)) + { + assert(wm); + e.accept(v); + } + else + assert(0); + return v.result; +} + + +/* Helper for TypeClass.deduceType(). + * Classes can match with implicit conversion to a base class or interface. + * This is complicated, because there may be more than one base class which + * matches. In such cases, one or more parameters remain ambiguous. + * For example, + * + * interface I(X, Y) {} + * class C : I(uint, double), I(char, double) {} + * C x; + * foo(T, U)( I!(T, U) x) + * + * deduces that U is double, but T remains ambiguous (could be char or uint). + * + * Given a baseclass b, and initial deduced types 'dedtypes', this function + * tries to match tparam with b, and also tries all base interfaces of b. + * If a match occurs, numBaseClassMatches is incremented, and the new deduced + * types are ANDed with the current 'best' estimate for dedtypes. + */ +private void deduceBaseClassParameters(ref BaseClass b, Scope* sc, Type tparam, ref TemplateParameters parameters, ref Objects dedtypes, ref Objects best, ref int numBaseClassMatches) +{ + if (TemplateInstance parti = b.sym ? b.sym.parent.isTemplateInstance() : null) + { + // Make a temporary copy of dedtypes so we don't destroy it + auto tmpdedtypes = new Objects(dedtypes.length); + memcpy(tmpdedtypes.tdata(), dedtypes.tdata(), dedtypes.length * (void*).sizeof); + + auto t = new TypeInstance(Loc.initial, parti); + MATCH m = deduceType(t, sc, tparam, parameters, *tmpdedtypes); + if (m > MATCH.nomatch) + { + // If this is the first ever match, it becomes our best estimate + if (numBaseClassMatches == 0) + memcpy(best.tdata(), tmpdedtypes.tdata(), tmpdedtypes.length * (void*).sizeof); + else + for (size_t k = 0; k < tmpdedtypes.length; ++k) + { + // If we've found more than one possible type for a parameter, + // mark it as unknown. + if ((*tmpdedtypes)[k] != best[k]) + best[k] = dedtypes[k]; + } + ++numBaseClassMatches; + } + } + + // Now recursively test the inherited interfaces + foreach (ref bi; b.baseInterfaces) + { + deduceBaseClassParameters(bi, sc, tparam, parameters, dedtypes, best, numBaseClassMatches); + } +} + +/* + * Handle tuple matching for function parameters. + * If the last parameter of `tp` is a template tuple parameter, + * collect the corresponding argument types from `t`. + * Params: + * t = actual function type + * tp = template function type + * parameters = template parameters + * dedtypes = deduced types array + * nfargs = number of arguments in `t` + * nfparams = number of parameters in `tp` (updated on success) + * Returns: `true` on success, `false` on mismatch. + */ +private bool deduceFunctionTuple(TypeFunction t, TypeFunction tp, + ref TemplateParameters parameters, ref Objects dedtypes, + size_t nfargs, ref size_t nfparams) +{ + if (nfparams > 0 && nfargs >= nfparams - 1) + { + Parameter fparam = tp.parameterList[nfparams - 1]; + assert(fparam && fparam.type); + if (fparam.type.ty == Tident) + { + TypeIdentifier tid = fparam.type.isTypeIdentifier(); + if (tid.idents.length == 0) + { + size_t tupi = 0; + for (; tupi < parameters.length; ++tupi) + { + TemplateParameter tx = parameters[tupi]; + TemplateTupleParameter tup = tx.isTemplateTupleParameter(); + if (tup && tup.ident.equals(tid.ident)) + break; + } + if (tupi == parameters.length) + return nfargs == nfparams; + + size_t tuple_dim = nfargs - (nfparams - 1); + + RootObject o = dedtypes[tupi]; + if (o) + { + Tuple tup = isTuple(o); + if (!tup || tup.objects.length != tuple_dim) + return false; + for (size_t i = 0; i < tuple_dim; ++i) + { + if (!t.parameterList[nfparams - 1 + i].type.equals(tup.objects[i])) + return false; + } + } + else + { + auto tup = new Tuple(tuple_dim); + for (size_t i = 0; i < tuple_dim; ++i) + tup.objects[i] = t.parameterList[nfparams - 1 + i].type; + dedtypes[tupi] = tup; + } + --nfparams; // ignore tuple parameter for further deduction + return true; + } + } + } + return nfargs == nfparams; +} + +/******************** + * Match template `parameters` to the target template instance. + * Example: + * struct Temp(U, int Z) {} + * void foo(T)(Temp!(T, 3)); + * foo(Temp!(int, 3)()); + * Input: + * sc = context + * parameters = template params of foo -> [T] + * tiargs = .tiargs -> [int, 3] + * tdtypes = .tdtypes -> [int, 3] + * tempdecl = -> [T, Z] + * tp = + * Output: + * dedtypes = deduced params of `foo(Temp!(int, 3)())` -> [int] + */ +private bool resolveTemplateInstantiation(Scope* sc, TemplateParameters* parameters, Objects* tiargs, Objects* tdtypes, TemplateDeclaration tempdecl, TypeInstance tp, Objects* dedtypes) +{ + for (size_t i = 0; 1; i++) + { + //printf("\ttest: tempinst.tiargs[%zu]\n", i); + RootObject o1 = null; + if (i < tiargs.length) + o1 = (*tiargs)[i]; + else if (i < tdtypes.length && i < tp.tempinst.tiargs.length) + { + // Pick up default arg + o1 = (*tdtypes)[i]; + } + else if (i >= tp.tempinst.tiargs.length) + break; + //printf("\ttest: o1 = %s\n", o1.toChars()); + if (i >= tp.tempinst.tiargs.length) + { + size_t dim = tempdecl.parameters.length - (tempdecl.isVariadic() ? 1 : 0); + while (i < dim && ((*tempdecl.parameters)[i].dependent || (*tempdecl.parameters)[i].hasDefaultArg())) + { + i++; + } + if (i >= dim) + break; // match if all remained parameters are dependent + return false; + } + + RootObject o2 = (*tp.tempinst.tiargs)[i]; + Type t2 = isType(o2); + //printf("\ttest: o2 = %s\n", o2.toChars()); + size_t j = (t2 && t2.ty == Tident && i == tp.tempinst.tiargs.length - 1) + ? templateParameterLookup(t2, parameters) : IDX_NOTFOUND; + if (j != IDX_NOTFOUND && j == parameters.length - 1 && + (*parameters)[j].isTemplateTupleParameter()) + { + /* Given: + * struct A(B...) {} + * alias A!(int, float) X; + * static if (is(X Y == A!(Z), Z...)) {} + * deduce that Z is a tuple(int, float) + */ + + /* Create tuple from remaining args + */ + size_t vtdim = (tempdecl.isVariadic() ? tiargs.length : tdtypes.length) - i; + auto vt = new Tuple(vtdim); + for (size_t k = 0; k < vtdim; k++) + { + RootObject o; + if (k < tiargs.length) + o = (*tiargs)[i + k]; + else // Pick up default arg + o = (*tdtypes)[i + k]; + vt.objects[k] = o; + } + + Tuple v = cast(Tuple)(*dedtypes)[j]; + if (v) + { + if (!match(v, vt)) + return false; + } + else + (*dedtypes)[j] = vt; + break; + } + else if (!o1) + break; + + Type t1 = isType(o1); + Dsymbol s1 = isDsymbol(o1); + Dsymbol s2 = isDsymbol(o2); + Expression e1 = s1 ? getValue(s1) : getValue(isExpression(o1)); + Expression e2 = isExpression(o2); + version (none) + { + Tuple v1 = isTuple(o1); + Tuple v2 = isTuple(o2); + if (t1) + printf("t1 = %s\n", t1.toChars()); + if (t2) + printf("t2 = %s\n", t2.toChars()); + if (e1) + printf("e1 = %s\n", e1.toChars()); + if (e2) + printf("e2 = %s\n", e2.toChars()); + if (s1) + printf("s1 = %s\n", s1.toChars()); + if (s2) + printf("s2 = %s\n", s2.toChars()); + if (v1) + printf("v1 = %s\n", v1.toChars()); + if (v2) + printf("v2 = %s\n", v2.toChars()); + } + + if (t1 && t2) + { + if (!deduceType(t1, sc, t2, *parameters, *dedtypes)) + return false; + } + else if (e1 && e2) + { + Le: + e1 = e1.ctfeInterpret(); + + /* If it is one of the template parameters for this template, + * we should not attempt to interpret it. It already has a value. + */ + if (e2.op == EXP.variable && (e2.isVarExp().var.storage_class & STC.templateparameter)) + { + /* + * (T:Number!(e2), int e2) + */ + j = templateIdentifierLookup(e2.isVarExp().var.ident, parameters); + if (j != IDX_NOTFOUND) + goto L1; + // The template parameter was not from this template + // (it may be from a parent template, for example) + } + + e2 = e2.expressionSemantic(sc); // https://issues.dlang.org/show_bug.cgi?id=13417 + e2 = e2.ctfeInterpret(); + + //printf("e1 = %s, type = %s %d\n", e1.toChars(), e1.type.toChars(), e1.type.ty); + //printf("e2 = %s, type = %s %d\n", e2.toChars(), e2.type.toChars(), e2.type.ty); + if (!e1.equals(e2)) + { + if (!e2.implicitConvTo(e1.type)) + return false; + + e2 = e2.implicitCastTo(sc, e1.type); + e2 = e2.ctfeInterpret(); + if (!e1.equals(e2)) + return false; + } + } + else if (e1 && t2 && t2.ty == Tident) + { + j = templateParameterLookup(t2, parameters); + L1: + if (j == IDX_NOTFOUND) + { + t2.resolve((cast(TypeIdentifier)t2).loc, sc, e2, t2, s2); + if (e2) + goto Le; + return false; + } + if (!(*parameters)[j].matchArg(sc, e1, j, parameters, *dedtypes, null)) + return false; + } + else if (s1 && s2) + { + Ls: + if (!s1.equals(s2)) + return false; + } + else if (s1 && t2 && t2.ty == Tident) + { + j = templateParameterLookup(t2, parameters); + if (j == IDX_NOTFOUND) + { + t2.resolve((cast(TypeIdentifier)t2).loc, sc, e2, t2, s2); + if (s2) + goto Ls; + return false; + } + if (!(*parameters)[j].matchArg(sc, s1, j, parameters, *dedtypes, null)) + return false; + } + else + return false; + } + return true; +} + +private Expression getValue(ref Dsymbol s) +{ + if (s) + { + if (VarDeclaration v = s.isVarDeclaration()) + { + if (v.storage_class & STC.manifest) + return v.getConstInitializer(); + } + } + return null; +} + +/*********************** + * Try to get value from manifest constant + */ +private Expression getValue(Expression e) +{ + if (!e) + return null; + if (auto ve = e.isVarExp()) + { + if (auto v = ve.var.isVarDeclaration()) + { + if (v.storage_class & STC.manifest) + { + e = v.getConstInitializer(); + } + } + } + return e; +} + +Expression getExpression(RootObject o) +{ + auto s = isDsymbol(o); + return s ? .getValue(s) : .getValue(isExpression(o)); +} diff --git a/gcc/d/dmd/tokens.d b/gcc/d/dmd/tokens.d index a10620772ac..d3044367660 100644 --- a/gcc/d/dmd/tokens.d +++ b/gcc/d/dmd/tokens.d @@ -1180,7 +1180,7 @@ void writeCharLiteral(ref OutBuffer buf, dchar c) if (c <= 0xFF) { if (isprint(c)) - buf.writeByte(c); + buf.writeByte(cast(char)c); else buf.printf("\\x%02x", c); } diff --git a/gcc/d/dmd/traits.d b/gcc/d/dmd/traits.d index 8cce1732e87..40195aa2fa1 100644 --- a/gcc/d/dmd/traits.d +++ b/gcc/d/dmd/traits.d @@ -51,6 +51,7 @@ import dmd.root.array; import dmd.root.speller; import dmd.root.stringtable; import dmd.target; +import dmd.templatesem : TemplateInstance_semanticTiargs; import dmd.tokens; import dmd.typesem; import dmd.visitor; @@ -340,7 +341,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) sc.stc |= STC.deprecated_; Scope* sc2 = sc.startCTFE(); scope(exit) { sc2.endCTFE(); } - if (!TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1)) + if (!TemplateInstance_semanticTiargs(e.loc, sc2, e.args, 1)) { sc.stc = save; return ErrorExp.get(); @@ -735,7 +736,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) * 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, 2)) + if (!TemplateInstance_semanticTiargs(e.loc, sc, e.args, 2)) return ErrorExp.get(); if (dim != 1) return dimError(1); @@ -774,7 +775,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) sc2.copyFlagsFrom(sc); sc2.noAccessCheck = true; sc2.ignoresymbolvisibility = true; - bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1); + bool ok = TemplateInstance_semanticTiargs(e.loc, sc2, e.args, 1); sc2.pop(); if (!ok) return ErrorExp.get(); @@ -849,7 +850,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) sc2.copyFlagsFrom(sc); sc2.noAccessCheck = true; sc2.ignoresymbolvisibility = true; - bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1); + bool ok = TemplateInstance_semanticTiargs(e.loc, sc2, e.args, 1); sc2.pop(); if (!ok) return ErrorExp.get(); @@ -1304,7 +1305,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) * 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)) + if (!TemplateInstance_semanticTiargs(e.loc, sc, e.args, 3)) return ErrorExp.get(); if (dim != 1) @@ -1882,9 +1883,9 @@ Expression semanticTraits(TraitsExp e, Scope* sc) ob1.push((*e.args)[0]); Objects ob2; ob2.push((*e.args)[1]); - if (!TemplateInstance.semanticTiargs(e.loc, sc, &ob1, 0)) + if (!TemplateInstance_semanticTiargs(e.loc, sc, &ob1, 0)) return ErrorExp.get(); - if (!TemplateInstance.semanticTiargs(e.loc, sc, &ob2, 0)) + if (!TemplateInstance_semanticTiargs(e.loc, sc, &ob2, 0)) return ErrorExp.get(); if (ob1.length != ob2.length) return False(); diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d index ffbdfed6eb3..caddc5e0c89 100644 --- a/gcc/d/dmd/typesem.d +++ b/gcc/d/dmd/typesem.d @@ -467,8 +467,7 @@ bool isCopyable(Type t) assert(ctor); scope el = new IdentifierExp(Loc.initial, Id.p); // dummy lvalue el.type = cast() ts; - Expressions* args = new Expressions(); - args.push(el); + Expressions* args = new Expressions(el); FuncDeclaration f = resolveFuncCall(Loc.initial, null, ctor, null, cast()ts, ArgumentList(args), FuncResolveFlag.quiet); if (!f || f.storage_class & STC.disable) return false; @@ -477,6 +476,60 @@ bool isCopyable(Type t) return true; } +/************************** + * When T is mutable, + * Given: + * T a, b; + * Can we bitwise assign: + * a = b; + * ? + */ +bool isAssignable(Type t) +{ + if (auto te = t.isTypeEnum()) + t = te.memType(); + TypeStruct ts = t.isTypeStruct(); + if (!ts) + return true; + + bool assignable = true; + uint offset = ~0; // dead-store initialize to prevent spurious warning + + auto sym = ts.sym; + sym.determineSize(sym.loc); + + /* If any of the fields are const or immutable, + * then one cannot assign this struct. + */ + for (size_t i = 0; i < sym.fields.length; i++) + { + VarDeclaration v = sym.fields[i]; + //printf("%s [%d] v = (%s) %s, v.offset = %d, v.parent = %s\n", sym.toChars(), i, v.kind(), v.toChars(), v.offset, v.parent.kind()); + if (i == 0) + { + } + else if (v.offset == offset) + { + /* If any fields of anonymous union are assignable, + * then regard union as assignable. + * This is to support unsafe things like Rebindable templates. + */ + if (assignable) + continue; + } + else + { + if (!assignable) + return false; + } + assignable = v.type.isMutable() && v.type.isAssignable(); + offset = v.offset; + //printf(" -> assignable = %d\n", assignable); + } + + return assignable; +} + /************************************ * Determine mutability of indirections in (ref) t. * @@ -688,6 +741,129 @@ extern (D) bool checkComplexTransition(Type type, Loc loc, Scope* sc) return false; } +/** + * Look for the index of parameter `ident` in the parameter list + * + * Params: + * tf = function type + * ident = identifier of parameter to search for + * Returns: index of parameter with name `ident` or -1 if not found + */ +private extern(D) ptrdiff_t findParameterIndex(TypeFunction tf, Identifier ident) +{ + foreach (i, p; tf.parameterList) + { + if (p.ident == ident) + return i; + } + return -1; +} + +/********************************* + * Append error message to buf. + * Input: + * buf = message sink + * format = printf format + */ +private extern(C) void getMatchError(ref OutBuffer buf, const(char)* format, ...) +{ + if (global.gag && !global.params.v.showGaggedErrors) + return; + va_list ap; + va_start(ap, format); + buf.vprintf(format, ap); + va_end(ap); +} + +/******************************** + * Convert an `argumentList`, which may contain named arguments, into + * a list of arguments in the order of the parameter list. + * + * Params: + * tf = function type + * argumentList = array of function arguments + * buf = if not null, append error message to it + * Returns: re-ordered argument list, or `null` on error + */ +extern(D) Expressions* resolveNamedArgs(TypeFunction tf, ArgumentList argumentList, OutBuffer* buf) +{ + Expression[] args = argumentList.arguments ? (*argumentList.arguments)[] : null; + ArgumentLabel[] names = argumentList.names ? (*argumentList.names)[] : null; + const nParams = tf.parameterList.length(); // cached because O(n) + auto newArgs = new Expressions(nParams); + newArgs.zero(); + size_t ci = 0; + bool hasNamedArgs = false; + const bool isVariadic = tf.parameterList.varargs != VarArg.none; + foreach (i, arg; args) + { + if (!arg) + { + ci++; + continue; + } + auto name = i < names.length ? names[i].name : null; + if (name) + { + hasNamedArgs = true; + const pi = tf.findParameterIndex(name); + if (pi == -1) + { + if (buf) + getMatchError(*buf, "no parameter named `%s`", name.toChars()); + return null; + } + ci = pi; + } + if (ci >= newArgs.length) + { + if (!isVariadic) + { + // Without named args, let the caller diagnose argument overflow + if (hasNamedArgs && buf) + getMatchError(*buf, "argument `%s` goes past end of parameter list", arg.toChars()); + return null; + } + while (ci >= newArgs.length) + newArgs.push(null); + } + + if ((*newArgs)[ci]) + { + if (buf) + getMatchError(*buf, "parameter `%s` assigned twice", tf.parameterList[ci].toChars()); + return null; + } + (*newArgs)[ci++] = arg; + } + foreach (i, arg; (*newArgs)[]) + { + if (arg || tf.parameterList[i].defaultArg) + continue; + + if (isVariadic && i + 1 == newArgs.length) + continue; + + // dtemplate sets `defaultArg=null` to avoid semantic on default arguments, + // don't complain about missing arguments in that case + if (tf.incomplete) + continue; + + if (buf) + getMatchError(*buf, "missing argument for parameter #%d: `%s`", + i + 1, parameterToChars(tf.parameterList[i], tf, false)); + return null; + } + // strip trailing nulls from default arguments + size_t e = newArgs.length; + while (e > 0 && (*newArgs)[e - 1] is null) + { + --e; + } + newArgs.setDim(e); + return newArgs; +} + /******************************** * 'args' are being matched to function type 'tf' * Determine match level. @@ -868,8 +1044,8 @@ extern (D) MATCH callMatch(FuncDeclaration fd, TypeFunction tf, Type tthis, Argu if (errorHelper) { if (u >= args.length) - TypeFunction.getMatchError(buf, "missing argument for parameter #%d: `%s`", - u + 1, parameterToChars(p, tf, false)); + getMatchError(buf, "missing argument for parameter #%d: `%s`", + u + 1, parameterToChars(p, tf, false)); // If an error happened previously, `pMessage` was already filled else if (buf.length == 0) buf.writestring(tf.getParamError(args[u], p)); @@ -886,7 +1062,7 @@ extern (D) MATCH callMatch(FuncDeclaration fd, TypeFunction tf, Type tthis, Argu { // all parameters had a match, but there are surplus args OutBuffer buf2; - TypeFunction.getMatchError(buf2, "expected %d argument(s), not %d", nparams, args.length); + getMatchError(buf2, "expected %d argument(s), not %d", nparams, args.length); errorHelper(buf2.extractChars()); return MATCH.nomatch; } @@ -944,7 +1120,7 @@ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct, if (!f) return nocpctor(); - if (f.isDisabled() && !f.isGenerated()) + if (f.isDisabled() && !f.isGenerated) { /* https://issues.dlang.org/show_bug.cgi?id=24301 * Copy constructor is explicitly disabled @@ -967,7 +1143,7 @@ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct, bsafe ? "@safe ".ptr : nullptr, bnogc ? "nogc" .ptr : nullptr); } - else if (f.isGenerated() && f.isDisabled()) + else if (f.isGenerated && f.isDisabled()) { /* https://issues.dlang.org/show_bug.cgi?id=23097 * Compiler generated copy constructor failed. @@ -1180,6 +1356,12 @@ private extern(D) MATCH argumentMatchParameter (FuncDeclaration fd, TypeFunction return MATCH.nomatch; } + if (arg.isBitField()) + { + if (pMessage) *pMessage = tf.getParamError(arg, p); + return MATCH.nomatch; + } + return m; } @@ -1193,13 +1375,21 @@ private const(char)* getParamError(TypeFunction tf, Expression arg, Parameter pa // when comparing the type with strcmp, we need to drop the qualifier bool qual = !arg.type.mutableOf().equals(par.type.mutableOf()) && strcmp(arg.type.mutableOf().toChars(), par.type.mutableOf().toChars()) == 0; - auto at = qual ? arg.type.toPrettyChars(true) : arg.type.toChars(); OutBuffer buf; - // only mention rvalue if it's relevant - const rv = !arg.isLvalue() && par.isReference(); - buf.printf("cannot pass %sargument `%s` of type `%s` to parameter `%s`", - rv ? "rvalue ".ptr : "".ptr, arg.toErrMsg(), at, - parameterToChars(par, tf, qual)); + // only mention rvalue or bitfield if it's relevant + if (arg.isBitField()) + { + buf.printf("cannot pass bitfield argument `%s` to parameter `%s`", + arg.toErrMsg(), parameterToChars(par, tf, qual)); + } + else + { + auto at = qual ? arg.type.toPrettyChars(true) : arg.type.toChars(); + const rv = !arg.isLvalue() && par.isReference() && !(par.storageClass & STC.constscoperef); + buf.printf("cannot pass %sargument `%s` of type `%s` to parameter `%s`", + rv ? "rvalue ".ptr : "".ptr, arg.toErrMsg(), at, + parameterToChars(par, tf, qual)); + } return buf.extractChars(); } @@ -1232,7 +1422,7 @@ private extern(D) MATCH matchTypeSafeVarArgs(TypeFunction tf, Parameter p, if (pMessage) { OutBuffer buf; - TypeFunction.getMatchError(buf, "expected %llu variadic argument(s), not %zu", + getMatchError(buf, "expected %llu variadic argument(s), not %zu", sz, trailingArgs.length); *pMessage = buf.extractChars(); } @@ -1290,6 +1480,35 @@ private extern(D) MATCH matchTypeSafeVarArgs(TypeFunction tf, Parameter p, } } +/// Compute cached type properties for `TypeStruct` +void determineTypeProperties(StructDeclaration sd) +{ + import dmd.dsymbolsem : hasPointers; + if (sd.computedTypeProperties) + return; + foreach (vd; sd.fields) + { + if (vd.storage_class & STC.ref_ || vd.hasPointers()) + { + sd.hasPointerField = true; + sd.hasUnsafeBitpatterns = true; + } + + if (vd._init && vd._init.isVoidInitializer() && vd.hasPointers()) + sd.hasVoidInitPointers = true; + + if (vd.storage_class & STC.system || vd.type.hasUnsafeBitpatterns()) + sd.hasUnsafeBitpatterns = true; + + if (!vd._init && vd.type.hasVoidInitPointers()) + sd.hasVoidInitPointers = true; + + if (vd.type.hasInvariant()) + sd.hasFieldWithInvariant = true; + } + sd.computedTypeProperties = true; +} + /*************************************** * Return !=0 if type has pointers that need to * be scanned by the GC during a collection cycle. @@ -2027,7 +2246,7 @@ Type typeSemantic(Type type, Loc loc, Scope* sc) // duplicate a part of StructDeclaration::semanticTypeInfoMembers //printf("AA = %s, key: xeq = %p, xerreq = %p xhash = %p\n", toChars(), sd.xeq, sd.xerreq, sd.xhash); - if (sd.xeq && sd.xeq.isGenerated() && sd.xeq._scope && sd.xeq.semanticRun < PASS.semantic3done) + if (sd.xeq && sd.xeq.isGenerated && sd.xeq._scope && sd.xeq.semanticRun < PASS.semantic3done) { uint errors = global.startGagging(); sd.xeq.semantic3(sd.xeq._scope); @@ -2658,7 +2877,7 @@ Type typeSemantic(Type type, Loc loc, Scope* sc) if (farg && (eparam.storageClass & STC.ref_)) { - if (!farg.isLvalue()) + if (!farg.isLvalue() || farg.isBitField()) eparam.storageClass &= ~STC.ref_; // value parameter eparam.storageClass &= ~STC.auto_; // https://issues.dlang.org/show_bug.cgi?id=14656 eparam.storageClass |= STC.autoref; @@ -3315,6 +3534,11 @@ Type merge(Type type) return type; goto default; + case Tfunction: + if (!type.nextOf()) // don't merge if return type is unknown + return type; + goto default; + default: if (type.nextOf() && !type.nextOf().deco) return type; @@ -3377,6 +3601,95 @@ Type merge2(Type type) return t; } +private enum LOGDEFAULTINIT = 0; +/*************************************** + * Use when we prefer the default initializer to be a literal, + * rather than a global immutable variable. + */ +Expression defaultInitLiteral(Type t, Loc loc) +{ + + if (t.isTypeError()) + return ErrorExp.get(); + if (auto ts = t.isTypeStruct()) + { + static if (LOGDEFAULTINIT) + { + printf("TypeStruct::defaultInitLiteral() '%s'\n", toChars()); + } + ts.sym.size(loc); + if (ts.sym.sizeok != Sizeok.done) + return ErrorExp.get(); + + auto structelems = new Expressions(ts.sym.nonHiddenFields()); + uint offset = 0; + foreach (j; 0 .. structelems.length) + { + VarDeclaration vd = ts.sym.fields[j]; + Expression e; + if (vd.inuse) + { + error(loc, "circular reference to `%s`", vd.toPrettyChars()); + return ErrorExp.get(); + } + if (vd.offset < offset || vd.type.size() == 0) + e = null; + else if (vd._init) + { + if (vd._init.isVoidInitializer()) + e = null; + else + e = vd.getConstInitializer(false); + } + else + e = vd.type.defaultInitLiteral(loc); + if (e && e.op == EXP.error) + return e; + if (e) + offset = vd.offset + cast(uint)vd.type.size(); + (*structelems)[j] = e; + } + auto structinit = new StructLiteralExp(loc, ts.sym, structelems); + + /* Copy from the initializer symbol for larger symbols, + * otherwise the literals expressed as code get excessively large. + */ + if (size(ts, loc) > target.ptrsize * 4 && !ts.needsNested()) + structinit.useStaticInit = true; + + structinit.type = ts; + return structinit; + } + if (auto tv = t.isTypeVector()) + { + //printf("TypeVector::defaultInitLiteral()\n"); + assert(tv.basetype.ty == Tsarray); + Expression e = tv.basetype.defaultInitLiteral(loc); + auto ve = new VectorExp(loc, e, tv); + ve.type = tv; + ve.dim = cast(int)(tv.basetype.size(loc) / tv.elementType().size(loc)); + return ve; + } + if (auto tsa = t.isTypeSArray()) + { + static if (LOGDEFAULTINIT) + { + printf("TypeSArray::defaultInitLiteral() '%s'\n", toChars()); + } + size_t d = cast(size_t)tsa.dim.toInteger(); + Expression elementinit; + if (tsa.next.ty == Tvoid) + elementinit = Type.tuns8.defaultInitLiteral(loc); + else + elementinit = tsa.next.defaultInitLiteral(loc); + auto elements = new Expressions(d); + foreach (ref e; *elements) + e = null; + auto ae = new ArrayLiteralExp(loc, tsa, elementinit, elements); + return ae; + } + return defaultInit(t, loc); +} /*************************************** * Calculate built-in properties which just the type is necessary. * @@ -3516,8 +3829,7 @@ Expression getProperty(Type t, Scope* scope_, Loc loc, Identifier ident, int fla { e = mt.defaultInitLiteral(loc); auto se = new StringExp(e.loc, ident.toString()); - auto tiargs = new Objects(); - tiargs.push(se); + auto tiargs = new Objects(se); auto dti = new DotTemplateInstanceExp(e.loc, e, Id.opDispatch, tiargs); dti.ti.tempdecl = td; dti.dotTemplateSemanticProp(scope_, DotExpFlag.none); @@ -4888,20 +5200,15 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag } if (ident == Id.length) { - __gshared FuncDeclaration fd_aaLen = null; - if (fd_aaLen is null) - { - auto fparams = new Parameters(); - fparams.push(new Parameter(Loc.initial, STC.const_ | STC.scope_, mt, null, null, null)); - fd_aaLen = FuncDeclaration.genCfunc(fparams, Type.tsize_t, Id.aaLen); - TypeFunction tf = fd_aaLen.type.toTypeFunction(); - tf.purity = PURE.const_; - tf.isNothrow = true; - tf.isNogc = false; - } - Expression ev = new VarExp(e.loc, fd_aaLen, false); - e = new CallExp(e.loc, ev, e); - e.type = fd_aaLen.type.toTypeFunction().next; + auto loc = e.loc; + Expression hookFunc = new IdentifierExp(loc, Id.empty); + hookFunc = new DotIdExp(loc, hookFunc, Id.object); + auto keytype = mt.index.substWildTo(MODFlags.const_); + auto valtype = mt.nextOf().substWildTo(MODFlags.const_); + auto tiargs = new Objects(keytype, valtype); + hookFunc = new DotTemplateInstanceExp(loc, hookFunc, Id._d_aaLen, tiargs); + Expression e = new CallExp(loc, hookFunc, e); + e = e.expressionSemantic(sc); return e; } else @@ -5013,17 +5320,31 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag /* Rewrite e.ident as: * e.opDispatch!("ident") */ - TemplateDeclaration td = fd.isTemplateDeclaration(); - if (!td) - { - .error(fd.loc, "%s `%s` must be a template `opDispatch(string s)`, not a %s", fd.kind, fd.toPrettyChars, fd.kind()); - return returnExp(ErrorExp.get()); - } + auto se = new StringExp(e.loc, ident.toString()); - auto tiargs = new Objects(); - tiargs.push(se); + auto tiargs = new Objects(se); auto dti = new DotTemplateInstanceExp(e.loc, e, Id.opDispatch, tiargs); - dti.ti.tempdecl = td; + + if (OverloadSet os = fd.isOverloadSet()) + { + if (!findTempDecl(dti, sc)) + { + .error(fd.loc, "Couldn't find template declaration for opDispatch"); + return returnExp(ErrorExp.get()); + } + } + else + { + TemplateDeclaration td = fd.isTemplateDeclaration(); + if (!td) + { + .error(fd.loc, "%s `%s` must be a template `opDispatch(string s)`, not a %s", + fd.kind, fd.toPrettyChars, fd.kind()); + return returnExp(ErrorExp.get()); + } + dti.ti.tempdecl = td; + } + /* opDispatch, which doesn't need IFTI, may occur instantiate error. * e.g. * template opDispatch(name) if (isValid!name) { ... } @@ -5743,7 +6064,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag Expression e1; Type t; /* returns: true to continue, false to return */ - if (f.hasDualContext()) + if (f.hasDualContext) { if (f.followInstantiationContext(ad)) { @@ -6345,7 +6666,7 @@ Type getComplexLibraryType(Loc loc, Scope* sc, TY ty) return *pt; *pt = Type.terror; - Module mConfig = Module.loadCoreStdcConfig(); + Module mConfig = loadCoreStdcConfig(); if (!mConfig) { error(loc, "`core.stdc.config` is required for complex numbers"); @@ -6466,9 +6787,9 @@ Covariant covariant(Type src, Type t, STC* pstc = null, bool cppCovariant = fals */ if (t1.linkage == LINK.cpp && cppCovariant) { - notcovariant |= tp1.isNaked() != tp2.isNaked(); + notcovariant |= tp1.isNaked != tp2.isNaked; if (auto tpn1 = tp1.nextOf()) - notcovariant |= tpn1.isNaked() != tp2.nextOf().isNaked(); + notcovariant |= tpn1.isNaked != tp2.nextOf().isNaked; } } } @@ -6574,7 +6895,7 @@ Lcovariant: /* https://issues.dlang.org/show_bug.cgi?id=23135 * extern(C++) mutable member functions are not covariant with const. */ - if (t1.linkage == LINK.cpp && cppCovariant && t1.isNaked() != t2.isNaked()) + if (t1.linkage == LINK.cpp && cppCovariant && t1.isNaked != t2.isNaked) goto Lnotcovariant; /* Can convert mutable to const @@ -6663,7 +6984,7 @@ STC parameterStorageClass(TypeFunction tf, Type tthis, Parameter p, VarDeclarati /* If haven't inferred the return type yet, can't infer storage classes */ - if (!tf.nextOf() || !tf.isNothrow()) + if (!tf.nextOf() || !tf.isNothrow) return stc; tf.purityLevel(); @@ -7346,7 +7667,7 @@ Type substWildTo(Type type, uint mod) t = t.addMod(MODFlags.shared_); //printf("-Type.substWildTo t = %s\n", t.toChars()); - return t; + return t.merge(); } if (!tf.iswild && !(tf.mod & MODFlags.wild)) diff --git a/gcc/d/dmd/typinf.d b/gcc/d/dmd/typinf.d index 1c7ed320a96..503d00bfd34 100644 --- a/gcc/d/dmd/typinf.d +++ b/gcc/d/dmd/typinf.d @@ -22,6 +22,7 @@ import dmd.expression; import dmd.globals; import dmd.location; import dmd.mtype; +import dmd.templatesem; import dmd.typesem; import core.stdc.stdio; @@ -165,27 +166,9 @@ TypeInfoDeclaration getTypeInfoAssocArrayDeclaration(TypeAArray t, Scope* sc) auto ti = TypeInfoAssociativeArrayDeclaration.create(t); t.vtinfo = ti; // assign it early to avoid recursion in expressionSemantic - Loc loc = t.loc; - auto tiargs = new Objects(); - tiargs.push(t.index); // always called with naked types - tiargs.push(t.next); - - Expression id = new IdentifierExp(loc, Id.empty); - id = new DotIdExp(loc, id, Id.object); - id = new DotIdExp(loc, id, Id.TypeInfo_AssociativeArray); - auto tempinst = new DotTemplateInstanceExp(loc, id, Id.Entry, tiargs); - auto e = expressionSemantic(tempinst, sc); - assert(e.type); - ti.entry = e.type; - if (auto ts = ti.entry.isTypeStruct()) - { - ts.sym.requestTypeInfo = true; - if (auto tmpl = ts.sym.isInstantiated()) - tmpl.minst = sc._module.importedFrom; // ensure it get's emitted - } - getTypeInfoType(loc, ti.entry, sc); - assert(ti.entry.vtinfo); - + ti._scope = sc; + sc.setNoFree(); + Module.addDeferredSemantic3(ti); return ti; } diff --git a/gcc/d/dmd/visitor/foreachvar.d b/gcc/d/dmd/visitor/foreachvar.d index 80611d6f26a..73d7c3ef9d6 100644 --- a/gcc/d/dmd/visitor/foreachvar.d +++ b/gcc/d/dmd/visitor/foreachvar.d @@ -45,7 +45,7 @@ import dmd.visitor.postorder; * e = expression tree to visit * dgVar = call when a variable is declared */ -void foreachVar(Expression e, void delegate(VarDeclaration) dgVar) +void foreachVar(Expression e, scope void delegate(VarDeclaration) dgVar) { if (!e) return; @@ -110,8 +110,8 @@ void foreachVar(Expression e, void delegate(VarDeclaration) dgVar) * dgVar = delegate to pass found VarDeclarations to */ void foreachExpAndVar(Statement s, - void delegate(Expression) dgExp, - void delegate(VarDeclaration) dgVar) + scope void delegate(Expression) dgExp, + scope void delegate(VarDeclaration) dgVar) { void visit(Statement s) { diff --git a/gcc/d/dmd/visitor/postorder.d b/gcc/d/dmd/visitor/postorder.d index 22549da45d5..f7c1f8dccf0 100644 --- a/gcc/d/dmd/visitor/postorder.d +++ b/gcc/d/dmd/visitor/postorder.d @@ -106,6 +106,42 @@ public: doCond(e.e1) || doCond(e.e2) || applyTo(e); } + override void visit(CatExp e) + { + if (auto lowering = e.lowering) + { + doCond(lowering) || applyTo(e); + } + else + { + visit(cast(BinExp)e); + } + } + + override void visit(CatAssignExp e) + { + if (auto lowering = e.lowering) + { + doCond(lowering) || applyTo(e); + } + else + { + visit(cast(BinExp)e); + } + } + + override void visit(EqualExp e) + { + if (auto lowering = e.lowering) + { + doCond(lowering) || applyTo(e); + } + else + { + visit(cast(BinExp)e); + } + } + override void visit(AssertExp e) { //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); diff --git a/gcc/d/expr.cc b/gcc/d/expr.cc index 21a41fe2c4c..c9ee5a6696f 100644 --- a/gcc/d/expr.cc +++ b/gcc/d/expr.cc @@ -381,101 +381,87 @@ public: /* For static and dynamic arrays, equality is defined as the lengths of the arrays matching, and all the elements are equal. */ Type *t1elem = tb1->nextOf ()->toBasetype (); - Type *t2elem = tb1->nextOf ()->toBasetype (); - /* Check if comparisons of arrays can be optimized using memcmp. + /* Use lowering if it has already been handled by the front-end. */ + if (e->lowering != NULL) + { + this->result_ = build_expr (e->lowering); + return; + } + + /* For all other arrays, comparisons can be optimized using memcmp. This will inline EQ expressions as: e1.length == e2.length && memcmp(e1.ptr, e2.ptr, size) == 0; Or when generating a NE expression: e1.length != e2.length || memcmp(e1.ptr, e2.ptr, size) != 0; */ - if ((t1elem->isIntegral () || t1elem->ty == TY::Tvoid - || (t1elem->ty == TY::Tstruct - && !t1elem->isTypeStruct ()->sym->xeq)) - && t1elem->ty == t2elem->ty) - { - tree t1 = d_array_convert (e->e1); - tree t2 = d_array_convert (e->e2); - tree result; - - /* Make temporaries to prevent multiple evaluations. */ - tree t1saved = d_save_expr (t1); - tree t2saved = d_save_expr (t2); - - /* Length of arrays, for comparisons done before calling memcmp. */ - tree t1len = d_array_length (t1saved); - tree t2len = d_array_length (t2saved); - - /* Reference to array data. */ - tree t1ptr = d_array_ptr (t1saved); - tree t2ptr = d_array_ptr (t2saved); + tree t1 = d_array_convert (e->e1); + tree t2 = d_array_convert (e->e2); + tree result; - /* Compare arrays using memcmp if possible, otherwise for structs, - each field is compared inline. */ - if (t1elem->ty != TY::Tstruct - || identity_compare_p (t1elem->isTypeStruct ()->sym)) - { - tree size = - size_mult_expr (t1len, size_int (dmd::size (t1elem))); + /* Make temporaries to prevent multiple evaluations. */ + tree t1saved = d_save_expr (t1); + tree t2saved = d_save_expr (t2); - result = build_memcmp_call (t1ptr, t2ptr, size); - result = build_boolop (code, result, integer_zero_node); - } - else - { - StructDeclaration *sd = t1elem->isTypeStruct ()->sym; + /* Length of arrays, for comparisons done before calling memcmp. */ + tree t1len = d_array_length (t1saved); + tree t2len = d_array_length (t2saved); - result = build_array_struct_comparison (code, sd, t1len, - t1ptr, t2ptr); - } + /* Reference to array data. */ + tree t1ptr = d_array_ptr (t1saved); + tree t2ptr = d_array_ptr (t2saved); - /* Check array length first before passing to memcmp. - For equality expressions, this becomes: - (e1.length == 0 || memcmp); - Otherwise for inequality: - (e1.length != 0 && memcmp); */ - tree tsizecmp = build_boolop (code, t1len, size_zero_node); - if (e->op == EXP::equal) - result = build_boolop (TRUTH_ORIF_EXPR, tsizecmp, result); - else - result = build_boolop (TRUTH_ANDIF_EXPR, tsizecmp, result); + /* Compare arrays using memcmp if possible, otherwise for structs, + each field is compared inline. */ + if (t1elem->ty != TY::Tstruct + || identity_compare_p (t1elem->isTypeStruct ()->sym)) + { + tree size = + size_mult_expr (t1len, size_int (dmd::size (t1elem))); - /* Finally, check if lengths of both arrays match if dynamic. - The frontend should have already guaranteed that static arrays - have same size. */ - if (tb1->ty == TY::Tsarray && tb2->ty == TY::Tsarray) - gcc_assert (dmd::size (tb1) == dmd::size (tb2)); - else - { - tree tlencmp = build_boolop (code, t1len, t2len); - if (e->op == EXP::equal) - result = build_boolop (TRUTH_ANDIF_EXPR, tlencmp, result); - else - result = build_boolop (TRUTH_ORIF_EXPR, tlencmp, result); - } + result = build_memcmp_call (t1ptr, t2ptr, size); + result = build_boolop (code, result, integer_zero_node); + } + else + { + StructDeclaration *sd = t1elem->isTypeStruct ()->sym; - /* Ensure left-to-right order of evaluation. */ - if (TREE_SIDE_EFFECTS (t2)) - result = compound_expr (t2saved, result); + result = build_array_struct_comparison (code, sd, t1len, + t1ptr, t2ptr); + } - if (TREE_SIDE_EFFECTS (t1)) - result = compound_expr (t1saved, result); + /* Check array length first before passing to memcmp. + For equality expressions, this becomes: + (e1.length == 0 || memcmp); + Otherwise for inequality: + (e1.length != 0 && memcmp); */ + tree tsizecmp = build_boolop (code, t1len, size_zero_node); + if (e->op == EXP::equal) + result = build_boolop (TRUTH_ORIF_EXPR, tsizecmp, result); + else + result = build_boolop (TRUTH_ANDIF_EXPR, tsizecmp, result); - this->result_ = result; - } + /* Finally, check if lengths of both arrays match if dynamic. + The frontend should have already guaranteed that static arrays + have same size. */ + if (tb1->ty == TY::Tsarray && tb2->ty == TY::Tsarray) + gcc_assert (dmd::size (tb1) == dmd::size (tb2)); else { - /* Use _adEq2() to compare each element. */ - Type *t1array = dmd::arrayOf (t1elem); - tree result = build_libcall (LIBCALL_ADEQ2, e->type, 3, - d_array_convert (e->e1), - d_array_convert (e->e2), - build_typeinfo (e, t1array)); + tree tlencmp = build_boolop (code, t1len, t2len); + if (e->op == EXP::equal) + result = build_boolop (TRUTH_ANDIF_EXPR, tlencmp, result); + else + result = build_boolop (TRUTH_ORIF_EXPR, tlencmp, result); + } - if (e->op == EXP::notEqual) - result = build1 (TRUTH_NOT_EXPR, build_ctype (e->type), result); + /* Ensure left-to-right order of evaluation. */ + if (TREE_SIDE_EFFECTS (t2)) + result = compound_expr (t2saved, result); - this->result_ = result; - } + if (TREE_SIDE_EFFECTS (t1)) + result = compound_expr (t1saved, result); + + this->result_ = result; } else if (TypeStruct *ts = tb1->isTypeStruct ()) { @@ -490,16 +476,9 @@ public: } else if (tb1->ty == TY::Taarray && tb2->ty == TY::Taarray) { - /* Use _aaEqual() for associative arrays. */ - tree result = build_libcall (LIBCALL_AAEQUAL, e->type, 3, - build_typeinfo (e, tb1), - build_expr (e->e1), - build_expr (e->e2)); - - if (e->op == EXP::notEqual) - result = build1 (TRUTH_NOT_EXPR, build_ctype (e->type), result); - - this->result_ = result; + /* Call to `_d_aaEqual' for associative arrays has already been handled + by the front-end. */ + gcc_unreachable (); } else { @@ -517,17 +496,10 @@ public: exists in an associative array. The result is a pointer to the element, or null if false. */ - void visit (InExp *e) final override + void visit (InExp *) final override { - Type *tb2 = e->e2->type->toBasetype (); - Type *tkey = tb2->isTypeAArray ()->index->toBasetype (); - tree key = convert_expr (build_expr (e->e1), e->e1->type, tkey); - - /* Build a call to _aaInX(). */ - this->result_ = build_libcall (LIBCALL_AAINX, e->type, 3, - build_expr (e->e2), - build_typeinfo (e, tkey), - build_address (key)); + /* Call to `_d_aaIn' has already been handled by the front-end. */ + gcc_unreachable (); } /* Build a relational expression. The result type is bool. */ @@ -1152,42 +1124,8 @@ public: if (tb1->ty == TY::Taarray) { - /* Get the key for the associative array. */ - Type *tkey = tb1->isTypeAArray ()->index->toBasetype (); - tree key = convert_expr (build_expr (e->e2), e->e2->type, tkey); - libcall_fn libcall; - tree tinfo, ptr; - - if (e->modifiable) - { - libcall = LIBCALL_AAGETY; - ptr = build_address (build_expr (e->e1)); - tinfo = build_typeinfo (e, dmd::mutableOf (dmd::unSharedOf (tb1))); - } - else - { - libcall = LIBCALL_AAGETRVALUEX; - ptr = build_expr (e->e1); - tinfo = build_typeinfo (e, tkey); - } - - /* Index the associative array. */ - tree result = build_libcall (libcall, dmd::pointerTo (e->type), 4, - ptr, tinfo, - size_int (dmd::size (tb1->nextOf ())), - build_address (key)); - - if (!e->indexIsInBounds && array_bounds_check ()) - { - tree tassert = build_array_bounds_call (e->loc); - - result = d_save_expr (result); - result = build_condition (TREE_TYPE (result), - d_truthvalue_conversion (result), - result, tassert); - } - - this->result_ = indirect_ref (build_ctype (e->type), result); + /* Associative arrays have already been handled by the front-end. */ + gcc_unreachable (); } else { @@ -1369,10 +1307,14 @@ public: { Type *ebtype = e->e1->type->toBasetype (); Type *tbtype = e->to->toBasetype (); - tree result = build_expr (e->e1, this->constp_, this->literalp_); - /* Just evaluate e1 if it has any side effects. */ - if (tbtype->ty == TY::Tvoid) + /* Use lowering if it has already been handled by the front-end. */ + Expression *cast = (e->lowering != NULL) ? e->lowering : e->e1; + tree result = build_expr (cast, this->constp_, this->literalp_); + + /* When expression has been rewritten or is a cast to `void', just evaluate + the result if it has any side effects. */ + if (e->lowering != NULL || tbtype->ty == TY::Tvoid) this->result_ = build_nop (build_ctype (tbtype), result); else this->result_ = convert_for_rvalue (result, ebtype, tbtype); @@ -1415,14 +1357,8 @@ public: /* Check that the array is actually an associative array. */ if (e->e1->type->toBasetype ()->ty == TY::Taarray) { - Type *tb = e->e1->type->toBasetype (); - Type *tkey = tb->isTypeAArray ()->index->toBasetype (); - tree index = convert_expr (build_expr (e->e2), e->e2->type, tkey); - - this->result_ = build_libcall (LIBCALL_AADELX, Type::tbool, 3, - build_expr (e->e1), - build_typeinfo (e, tkey), - build_address (index)); + /* Call to `_d_aaDel' has already been handled by the front-end. */ + gcc_unreachable (); } else { @@ -1550,8 +1486,12 @@ public: Taking the address of a struct literal is otherwise illegal. */ if (e->e1->op == EXP::structLiteral) { - StructLiteralExp *sle = e->e1->isStructLiteralExp ()->origin; - gcc_assert (sle != NULL); + StructLiteralExp *sle = e->e1->isStructLiteralExp (); + if (!this->constp_) + { + gcc_assert (sle->origin != NULL); + sle = sle->origin; + } /* Build the reference symbol, the decl is built first as the initializer may have recursive references. */ @@ -2429,17 +2369,10 @@ public: } else if (tb->ty == TY::Taarray) { - /* Allocating memory for a new associative array. */ - tree arg = build_typeinfo (e, e->newtype); - tree mem = build_libcall (LIBCALL_AANEW, Type::tvoidptr, 1, arg); - - /* Return an associative array pointed to by MEM. */ - tree aatype = build_ctype (tb); - vec *ce = NULL; - CONSTRUCTOR_APPEND_ELT (ce, TYPE_FIELDS (aatype), mem); - - result = build_nop (build_ctype (e->type), - build_padded_constructor (aatype, ce)); + /* Allocating memory for a new associative array has already been + handled by the front-end. */ + gcc_assert (e->lowering); + result = build_expr (e->lowering); } else gcc_unreachable (); @@ -2684,10 +2617,9 @@ public: else { /* Allocate space on the memory managed heap. */ - tree mem = build_libcall (LIBCALL_ARRAYLITERALTX, - dmd::pointerTo (etype), 2, - build_typeinfo (e, dmd::arrayOf (etype)), - size_int (e->elements->length)); + gcc_assert (e->lowering); + tree mem = build_nop (build_pointer_type (satype), + build_expr (e->lowering)); mem = d_save_expr (mem); /* Now copy the constructor into memory. */ @@ -2715,55 +2647,24 @@ public: void visit (AssocArrayLiteralExp *e) final override { - if (this->constp_ && e->lowering != NULL) + if (this->constp_ && e->loweringCtfe != NULL) { /* When an associative array literal gets lowered, it's converted into a struct literal suitable for static initialization. */ - this->result_ = build_expr (e->lowering, this->constp_, true); - return ; + this->result_ = build_expr (e->loweringCtfe, this->constp_, true); + return; } - /* Want the mutable type for typeinfo reference. */ - Type *tb = dmd::mutableOf (e->type->toBasetype ()); - /* Handle empty assoc array literals. */ - TypeAArray *ta = tb->isTypeAArray (); if (e->keys->length == 0) { - this->result_ = build_padded_constructor (build_ctype (ta), NULL); + this->result_ = build_padded_constructor (build_ctype (e->type), NULL); return; } - /* Build an expression that assigns all expressions in KEYS - to a constructor. */ - Type *tkarray = dmd::sarrayOf (ta->index, e->keys->length); - tree akeys = build_array_from_exprs (tkarray, e->keys, this->constp_); - tree init = stabilize_expr (&akeys); - - /* Do the same with all expressions in VALUES. */ - Type *tvarray = dmd::sarrayOf (ta->next, e->values->length); - tree avals = build_array_from_exprs (tvarray, e->values, this->constp_); - init = compound_expr (init, stabilize_expr (&avals)); - /* Generate: _d_assocarrayliteralTX (ti, keys, vals); */ - tree keys = d_array_value (build_ctype (dmd::arrayOf (ta->index)), - size_int (e->keys->length), - build_address (akeys)); - tree vals = d_array_value (build_ctype (dmd::arrayOf (ta->next)), - size_int (e->values->length), - build_address (avals)); - - tree mem = build_libcall (LIBCALL_ASSOCARRAYLITERALTX, Type::tvoidptr, 3, - build_typeinfo (e, ta), keys, vals); - - /* Return an associative array pointed to by MEM. */ - tree aatype = build_ctype (ta); - vec *ce = NULL; - CONSTRUCTOR_APPEND_ELT (ce, TYPE_FIELDS (aatype), mem); - - tree result = build_nop (build_ctype (e->type), - build_padded_constructor (aatype, ce)); - this->result_ = compound_expr (init, result); + gcc_assert (e->lowering); + this->result_ = build_expr (e->lowering); } /* Build a struct literal. */ diff --git a/gcc/d/imports.cc b/gcc/d/imports.cc index e41e4c18c47..e2e2ba49b7d 100644 --- a/gcc/d/imports.cc +++ b/gcc/d/imports.cc @@ -144,7 +144,7 @@ public: /* Alias symbols aren't imported, but their targets are. */ void visit (AliasDeclaration *d) final override { - Dsymbol *dsym = d->toAlias (); + Dsymbol *dsym = dmd::toAlias (d); if (dsym == d) { diff --git a/gcc/d/runtime.def b/gcc/d/runtime.def index 8db5672c9c3..9651c07dc8b 100644 --- a/gcc/d/runtime.def +++ b/gcc/d/runtime.def @@ -64,21 +64,6 @@ DEF_D_RUNTIME (CALLFINALIZER, "_d_callfinalizer", RT(VOID), P1(VOIDPTR), 0) DEF_D_RUNTIME (CALLINTERFACEFINALIZER, "_d_callinterfacefinalizer", RT(VOID), P1(VOIDPTR), 0) -/* Used for casting to a class or interface. */ -DEF_D_RUNTIME (DYNAMIC_CAST, "_d_dynamic_cast", RT(OBJECT), - P2(OBJECT, CLASSINFO), 0) -DEF_D_RUNTIME (INTERFACE_CAST, "_d_interface_cast", RT(OBJECT), - P2(OBJECT, CLASSINFO), 0) - -/* Used for allocating an array literal on the GC heap. */ -DEF_D_RUNTIME (ARRAYLITERALTX, "_d_arrayliteralTX", RT(VOIDPTR), - P2(CONST_TYPEINFO, SIZE_T), 0) - -/* Used for value equality (x == y) and comparisons (x < y) of non-trivial - arrays. Such as an array of structs or classes. */ -DEF_D_RUNTIME (ADEQ2, "_adEq2", RT(INT), - P3(ARRAY_VOID, ARRAY_VOID, CONST_TYPEINFO), 0) - /* Used for (array.length = n) expressions. The `i' variant is for when the initializer is nonzero. */ DEF_D_RUNTIME (ARRAYSETLENGTHT, "_d_arraysetlengthT", RT(ARRAY_VOID), @@ -106,31 +91,6 @@ DEF_D_RUNTIME (ARRAYAPPENDCD, "_d_arrayappendcd", RT(ARRAY_VOID), DEF_D_RUNTIME (ARRAYAPPENDWD, "_d_arrayappendwd", RT(ARRAY_VOID), P2(ARRAYPTR_BYTE, DCHAR), 0) -/* Used for allocating a new associative array. */ -DEF_D_RUNTIME (ASSOCARRAYLITERALTX, "_d_assocarrayliteralTX", RT(VOIDPTR), - P3(CONST_TYPEINFO, ARRAY_VOID, ARRAY_VOID), 0) -DEF_D_RUNTIME (AANEW, "_aaNew", RT(VOIDPTR), P1(CONST_TYPEINFO), 0) - -/* Used for value equality of two associative arrays. */ -DEF_D_RUNTIME (AAEQUAL, "_aaEqual", RT(INT), - P3(CONST_TYPEINFO, ASSOCARRAY, ASSOCARRAY), 0) - -/* Used to determine is a key exists in an associative array. */ -DEF_D_RUNTIME (AAINX, "_aaInX", RT(VOIDPTR), - P3(ASSOCARRAY, CONST_TYPEINFO, VOIDPTR), 0) - -/* Used to retrieve a value from an associative array index by a key. The - `Rvalue' variant returns null if the key is not found, where as aaGetY - will create new key entry for assignment. */ -DEF_D_RUNTIME (AAGETY, "_aaGetY", RT(VOIDPTR), - P4(POINTER_ASSOCARRAY, CONST_TYPEINFO, SIZE_T, VOIDPTR), 0) -DEF_D_RUNTIME (AAGETRVALUEX, "_aaGetRvalueX", RT(VOIDPTR), - P4(ASSOCARRAY, CONST_TYPEINFO, SIZE_T, VOIDPTR), 0) - -/* Used when calling delete on a key entry in an associative array. */ -DEF_D_RUNTIME (AADELX, "_aaDelX", RT(BOOL), - P3(ASSOCARRAY, CONST_TYPEINFO, VOIDPTR), 0) - /* Used for throw() expressions. */ DEF_D_RUNTIME (THROW, "_d_throw", RT(VOID), P1(OBJECT), ECF_NORETURN) DEF_D_RUNTIME (BEGIN_CATCH, "__gdc_begin_catch", RT(VOIDPTR), P1(VOIDPTR), 0) diff --git a/gcc/d/typeinfo.cc b/gcc/d/typeinfo.cc index 160d37846f2..b6792dfe5ca 100644 --- a/gcc/d/typeinfo.cc +++ b/gcc/d/typeinfo.cc @@ -730,7 +730,9 @@ public: void *__monitor; TypeInfo value; TypeInfo key; - TypeInfo entry; */ + TypeInfo entry; + bool function(in void*, in void*) xopEquals; + hash_t function(in void*) xtoHash; */ void visit (TypeInfoAssociativeArrayDeclaration *d) final override { @@ -750,6 +752,16 @@ public: this->layout_field (build_typeinfo (d->loc, d->entry)); else this->layout_field (null_pointer_node); + + /* bool function(in void*, in void*) xopEquals; */ + tree xeq = (d->xopEqual) ? build_address (get_symbol_decl (d->xopEqual)) + : null_pointer_node; + this->layout_field (xeq); + + /* hash_t function (in void*) xtoHash; */ + tree xhash = (d->xtoHash) ? build_address (get_symbol_decl (d->xtoHash)) + : null_pointer_node; + this->layout_field (xhash); } /* Layout of TypeInfo_Vector is: @@ -1442,6 +1454,15 @@ check_typeinfo_type (const Loc &loc, Scope *sc, Expression *expr) error_at (make_location_t (loc), "% cannot be used with %<-fno-rtti%>"); + if (expr != NULL || !warned) + { + /* Print the location of where the error came from. */ + if (sc && sc->tinst) + dmd::printInstantiationTrace (sc->tinst); + + global.errors++; + } + warned = 1; } } @@ -1653,6 +1674,7 @@ create_typeinfo (Type *type, Scope *sc) { ident = Identifier::idPool ("TypeInfo_AssociativeArray"); make_internal_typeinfo (tk, ident, ptr_type_node, ptr_type_node, + ptr_type_node, ptr_type_node, ptr_type_node, NULL); } t->vtinfo = sc && have_typeinfo_p (Type::typeinfoassociativearray) diff --git a/gcc/d/types.cc b/gcc/d/types.cc index 30d69629095..cab9b6c800a 100644 --- a/gcc/d/types.cc +++ b/gcc/d/types.cc @@ -1259,7 +1259,7 @@ public: /* For structs with a user defined postblit, copy constructor, or a destructor, also set TREE_ADDRESSABLE on the type and all variants. This will make the struct be passed around by reference. */ - if (!t->sym->isPOD ()) + if (!dmd::isPOD (t->sym)) { for (tree tv = t->ctype; tv != NULL_TREE; tv = TYPE_NEXT_VARIANT (tv)) { diff --git a/gcc/testsuite/gdc.dg/asm1.d b/gcc/testsuite/gdc.dg/asm1.d index e2f36b01c94..95cacbeaa85 100644 --- a/gcc/testsuite/gdc.dg/asm1.d +++ b/gcc/testsuite/gdc.dg/asm1.d @@ -5,7 +5,7 @@ void parse1() { asm { - ""h; // { dg-error "found 'h' when expecting ':'" } + ""h; // { dg-error "found 'h' when expecting ';'" } } } @@ -16,13 +16,13 @@ void parse2() "" : : "g" (1 ? 2 : 3); "" : : "g" (1 ? 2 : :) 3; // { dg-error "expression expected, not ':'" "" { target *-*-* } .-1 } - // { dg-error "expected constant string constraint for operand" "" { target *-*-* } .-2 } + // { dg-error "found '3' when expecting ';'" "" { target *-*-* } .-2 } } } void parse3() { - asm { "" [; } // { dg-error "found '\\\[' when expecting ':'" } + asm { "" [; } // { dg-error "found '\\\[' when expecting ';'" } } void parse4() @@ -30,7 +30,7 @@ void parse4() int expr; asm { - "%name" : [name] string (expr); // { dg-error "expected constant string constraint for operand, not 'string'" } + "%name" : [name] string (expr); // { dg-error "expected string literal or expression in parentheses" } } } diff --git a/gcc/testsuite/gdc.dg/asm5.d b/gcc/testsuite/gdc.dg/asm5.d index b525a2131ce..1b44c976de3 100644 --- a/gcc/testsuite/gdc.dg/asm5.d +++ b/gcc/testsuite/gdc.dg/asm5.d @@ -7,6 +7,6 @@ void test(int a) { asm { - "cpuid" : : "a" a; // { dg-error "'a' must be surrounded by parentheses" } + "cpuid" : : "a" a; // { dg-error "found 'a' when expecting '\\\('" } } } diff --git a/gcc/testsuite/gdc.dg/pr100967.d b/gcc/testsuite/gdc.dg/pr100967.d index 3861a37899c..473ce752a8b 100644 --- a/gcc/testsuite/gdc.dg/pr100967.d +++ b/gcc/testsuite/gdc.dg/pr100967.d @@ -3,9 +3,13 @@ module object; +class Object {} +class TypeInfo {} +class TypeInfo_AssociativeArray : TypeInfo {} // { dg-note "defined here" } + extern(C) int main() { - int[int] aa; // { dg-error ".object.TypeInfo. could not be found" } - aa[0] = 1; // { dg-error ".object.TypeInfo. could not be found, but is implicitly used" } + int[int] aa; // { dg-error "no property" } + aa[0] = 1; // { dg-error "'object._d_aaGetY' not found" } return 0; } diff --git a/gcc/testsuite/gdc.dg/rtti1.d b/gcc/testsuite/gdc.dg/rtti1.d index 29b8265c821..32391066b0b 100644 --- a/gcc/testsuite/gdc.dg/rtti1.d +++ b/gcc/testsuite/gdc.dg/rtti1.d @@ -2,12 +2,27 @@ // { dg-options "-fno-rtti" } // { dg-shouldfail "expressions depend on TypeInfo" } -int* testInExp(int key, int[int] aa) +module object; + +class Object {} +class TypeInfo {} + +struct AA(K, V) { - return key in aa; // { dg-error "requires .object.TypeInfo. and cannot be used with .-fno-rtti." } + this(int sz) nothrow + { + keyTI = typeid(K); // { dg-error "'object.TypeInfo' cannot be used with '-fno-rtti'" } + } + TypeInfo keyTI; } -bool testAAEqual(int[string] aa1, int[string] aa2) +auto _d_aaIn(T : V[K], K, V, K2)(inout T a, auto ref scope K2 key) +{ + auto aa = *(cast(inout(AA!(K, V))*)&a); // { dg-note "instantiated from here" } + return null; +} + +int* testInExp(int key, int[int] aa) { - return aa1 == aa2; // { dg-error "requires .object.TypeInfo. and cannot be used with .-fno-rtti." } + return key in aa; // { dg-note "instantiated from here" } } diff --git a/gcc/testsuite/gdc.dg/rtti2.d b/gcc/testsuite/gdc.dg/rtti2.d new file mode 100644 index 00000000000..56cb04e89a1 --- /dev/null +++ b/gcc/testsuite/gdc.dg/rtti2.d @@ -0,0 +1,29 @@ +// { dg-do compile } +// { dg-options "-fno-rtti" } +// { dg-shouldfail "expressions depend on TypeInfo" } + +module object; + +class Object {} +class TypeInfo {} +class TypeInfo_Array : TypeInfo {} + +struct AA(K, V) +{ + this(int sz) nothrow + { + keyTI = typeid(K); // { dg-error "'object.TypeInfo' cannot be used with '-fno-rtti'" } + } + TypeInfo keyTI; +} + +bool _d_aaEqual(K, V)(scope const V[K] a1, scope const V[K] a2) +{ + auto aa1 = *(cast(const(AA!(K, V))*)&a1); // { dg-note "instantiated from here" } + return false; +} + +bool testAAEqual(int[immutable(char)[]] aa1, int[immutable(char)[]] aa2) +{ + return aa1 == aa2; // { dg-note "instantiated from here" } +} diff --git a/gcc/testsuite/gdc.test/compilable/b1215.d b/gcc/testsuite/gdc.test/compilable/b1215.d index 682881969ed..6f331dda9c9 100644 --- a/gcc/testsuite/gdc.test/compilable/b1215.d +++ b/gcc/testsuite/gdc.test/compilable/b1215.d @@ -134,7 +134,7 @@ struct Bar14986 Bar14986 test14986() { - Foo14986[] types; + Foo14986[] types = [Foo14986(1)]; auto a1 = new void[types[0].tsize]; // TypeIdentifier::toExpression auto a2 = new void[Id14986!types[0].tsize]; // TypeInstance::toExpression diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/header1.d b/gcc/testsuite/gdc.test/compilable/extra-files/header1.d index ee698f16dcf..e2408c971c0 100644 --- a/gcc/testsuite/gdc.test/compilable/extra-files/header1.d +++ b/gcc/testsuite/gdc.test/compilable/extra-files/header1.d @@ -566,7 +566,7 @@ struct SafeS { this(int[1] x) scope {} this(int[2] x) return scope {} - this(int[3] x) scope return {} + this(int[3] x) return ref scope {} this(int[4] x) return {} @safe: @@ -627,3 +627,5 @@ interface I12344 { int i12344(int x) in(x > 0) out(result) {assert(result > 0);}; } + +debug enum issue21406 = 1; diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/test21331.d b/gcc/testsuite/gdc.test/compilable/extra-files/test21331.d new file mode 100644 index 00000000000..59f30d1f1c3 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/extra-files/test21331.d @@ -0,0 +1,17 @@ +struct S(alias fun) { + int foo() { + int r = fun(0); + static foreach (i; 0..2) + r += (x => 2)(0); + return r; + } +} + +int bar() { + int r; + static foreach (i; 0..2) + r += S!(x => 1)().foo(); + return r; +} + +// nm test21331.o | grep __lambda_L5_C19 diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/test21359a.d b/gcc/testsuite/gdc.test/compilable/extra-files/test21359a.d new file mode 100644 index 00000000000..b52d9db5cda --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/extra-files/test21359a.d @@ -0,0 +1,3 @@ +void registerAll() { + import test21359b; +} diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/test21359b.d b/gcc/testsuite/gdc.test/compilable/extra-files/test21359b.d new file mode 100644 index 00000000000..f9aca7fdb63 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/extra-files/test21359b.d @@ -0,0 +1,11 @@ +struct SumType(Types...) +{ + static foreach (T; Types) + T values_; +} + +SumType!(string[int]) convertObserver; + +void hackAroundLinkerError() { + auto t = typeid(const(immutable(char)[][])); +} diff --git a/gcc/testsuite/gdc.test/compilable/imports/h20184.h b/gcc/testsuite/gdc.test/compilable/imports/h20184.h new file mode 100644 index 00000000000..5342a1d345b --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/imports/h20184.h @@ -0,0 +1 @@ +int f20184(void); diff --git a/gcc/testsuite/gdc.test/compilable/interpret4.d b/gcc/testsuite/gdc.test/compilable/interpret4.d index 019ff226ea8..98ebcdff745 100644 --- a/gcc/testsuite/gdc.test/compilable/interpret4.d +++ b/gcc/testsuite/gdc.test/compilable/interpret4.d @@ -29,3 +29,20 @@ static if (__traits(compiles, int4)) enum int4 F = D * E; static assert(F.array == [1, 4, 9, 16]); } + +// https://github.com/dlang/dmd/issues/20114 + +int* find(int[] arr, int needle) +{ + foreach(ref a; arr) + if(a == needle) + return &a; + return null; +} + +enum int[int] aa = [0: 0]; +enum int[] da = [0, 1, 2]; +static assert(0 in aa); +static assert(&da[1]); +static assert(find(da, 1)); +static assert(!find(da, 3)); diff --git a/gcc/testsuite/gdc.test/compilable/mixintype.d b/gcc/testsuite/gdc.test/compilable/mixintype.d index a53805b4066..119c366ed39 100644 --- a/gcc/testsuite/gdc.test/compilable/mixintype.d +++ b/gcc/testsuite/gdc.test/compilable/mixintype.d @@ -4,9 +4,9 @@ alias Lint = mixin("Int"); int test1(mixin("int")* p) { - mixin("int")[] a; - mixin("int[]") b; - mixin("int[] c;"); + mixin("int")[] a = [1]; + mixin("int[]") b = [1]; + mixin("int[] c = [1];"); mixin("*p = c[0];"); *p = mixin("c[0]"); return *p + a[0] + b[0] + c[0]; diff --git a/gcc/testsuite/gdc.test/compilable/parens_inc.d b/gcc/testsuite/gdc.test/compilable/parens_inc.d index b9d11eb9b8e..73921e96cb2 100644 --- a/gcc/testsuite/gdc.test/compilable/parens_inc.d +++ b/gcc/testsuite/gdc.test/compilable/parens_inc.d @@ -6,7 +6,8 @@ void main(){ *(x)++=0; (*(x)--)=0; (*x++)=0; // ok - int*[] z; + int a = 1, b = 2; + int*[] z = [&a, &b]; *(z[0])++=0; //ok (y[0])--; *x++=0; @@ -16,7 +17,7 @@ void f() { int b; (b)++; - int[] a; + int[] a = [1]; b = (a)[0]++; //ok (a[0])--; b = (int).init; //ok diff --git a/gcc/testsuite/gdc.test/compilable/shared.d b/gcc/testsuite/gdc.test/compilable/shared.d index 647910ecf16..a9392d7bcf9 100644 --- a/gcc/testsuite/gdc.test/compilable/shared.d +++ b/gcc/testsuite/gdc.test/compilable/shared.d @@ -5,7 +5,7 @@ pure nothrow @nogc ref @safe shared(C1)(return ref shared(C1) c) pure nothrow @nogc ref @safe shared(int)(return ref shared(C3) c) --- */ -ref shared(int) f(return shared ref int y) +ref shared(int) f(return ref shared int y) { return y; } @@ -97,7 +97,7 @@ shared(C2)* test_dotvarexp_4(return ref shared C3 c) return &c.c2; } -ref shared(int) test_dotvarexp_5(return shared ref C3 c) +ref shared(int) test_dotvarexp_5(return ref shared C3 c) { return c.c1.c1.value; } diff --git a/gcc/testsuite/gdc.test/compilable/test14838.d b/gcc/testsuite/gdc.test/compilable/test14838.d index 766406898e6..aa6249af55b 100644 --- a/gcc/testsuite/gdc.test/compilable/test14838.d +++ b/gcc/testsuite/gdc.test/compilable/test14838.d @@ -5,7 +5,7 @@ class C { A!int[1] array; } void test14838() pure nothrow @nogc @safe { - C c; + scope C c = new C; c.__xdtor(); // C.~this() will also be inferred to // pure nothrow @nogc @safe diff --git a/gcc/testsuite/gdc.test/compilable/test17146.d b/gcc/testsuite/gdc.test/compilable/test17146.d index 098bfe4c5d0..3299f1521c7 100644 --- a/gcc/testsuite/gdc.test/compilable/test17146.d +++ b/gcc/testsuite/gdc.test/compilable/test17146.d @@ -6,7 +6,7 @@ struct S { int[] a; int b; } void foo() { - S[] s; + S[] s = [S([],0)]; if (s[$-1] == S.init) {} } diff --git a/gcc/testsuite/gdc.test/compilable/test20100.d b/gcc/testsuite/gdc.test/compilable/test20100.d index bc8ad2c40e6..0e455ec6177 100644 --- a/gcc/testsuite/gdc.test/compilable/test20100.d +++ b/gcc/testsuite/gdc.test/compilable/test20100.d @@ -36,9 +36,11 @@ void main() { void test20989() @safe { uint[] arr = [1, 2, 3]; + uint[] arr2 = null; assert(arr.ptr); - assert(!arr.ptr); + assert(!arr2.ptr); assert(arr.ptr is arr.ptr); + assert(arr2.ptr is arr2.ptr); } // https://issues.dlang.org/show_bug.cgi?id=21765 diff --git a/gcc/testsuite/gdc.test/compilable/test20184.d b/gcc/testsuite/gdc.test/compilable/test20184.d new file mode 100644 index 00000000000..10390659d80 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test20184.d @@ -0,0 +1,5 @@ +// EXTRA_FILES: imports/h20184.h +import imports.h20184; + +alias fun_signature = extern (C) int(); +static assert(is(typeof(f20184) : fun_signature)); diff --git a/gcc/testsuite/gdc.test/compilable/test20365.d b/gcc/testsuite/gdc.test/compilable/test20365.d new file mode 100644 index 00000000000..045e9916a49 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test20365.d @@ -0,0 +1,20 @@ +// REQUIRED_ARGS: -preview=bitfields + +struct A { int x : 16, y : 16; } + +void autoref_assign(A a) +{ + auto ref int x = a.x; +} + +void f()(auto ref int); + +void autoref_param(A a) +{ + f(a.y); +} + +auto ref int autoref_return(ref A a) +{ + return a.y; +} diff --git a/gcc/testsuite/gdc.test/compilable/test21331.sh b/gcc/testsuite/gdc.test/compilable/test21331.sh new file mode 100755 index 00000000000..855e563134f --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test21331.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +objfile="${OUTPUT_BASE}${OBJ}" +$DMD -c -m${MODEL} -of${objfile} ${EXTRA_FILES}/${TEST_NAME}.d + +nm ${objfile} | grep -E '16__lambda_L13_C17.+15__lambda_L5_C19' +nm ${objfile} | grep -E '16__lambda_L13_C17.+17__lambda_L5_C19_1' +nm ${objfile} | grep -E '18__lambda_L13_C17_1.+15__lambda_L5_C19' +nm ${objfile} | grep -E '18__lambda_L13_C17_1.+17__lambda_L5_C19_1' + +rm_retry ${objfile} diff --git a/gcc/testsuite/gdc.test/compilable/test21359.sh b/gcc/testsuite/gdc.test/compilable/test21359.sh new file mode 100755 index 00000000000..ed4719c1fe8 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test21359.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +objfile="${OUTPUT_BASE}${OBJ}" +$DMD -c -m${MODEL} -allinst -of${objfile} ${EXTRA_FILES}/${TEST_NAME}a.d -I${EXTRA_FILES} + +# This test is brittle and might break in the future. +# For now, ensure a specific AA TypeInfo isn't defined multiple times, as happened with DMD v2.111.0. +mangled_sym='_D14TypeInfo_HiAya6__initZ' +num_typeinfo_syms=$(nm --defined-only ${objfile} | grep -F ${mangled_sym} | wc -l) +if [[ "${num_typeinfo_syms}" -ne 1 ]]; then + echo "Expected 1 '${mangled_sym}' symbol definition but got: ${num_typeinfo_syms}" + exit 1 +fi + +rm_retry ${objfile} diff --git a/gcc/testsuite/gdc.test/compilable/test21416.d b/gcc/testsuite/gdc.test/compilable/test21416.d new file mode 100644 index 00000000000..e24f7292945 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test21416.d @@ -0,0 +1,17 @@ +// https://github.com/dlang/dmd/issues/21416 + +struct S +{ + @disable this(ref S); +} + +ref id(ref S s) => s; + +S* p; +void foo(S s) { } + +void main() +{ + S s; + foo(__rvalue(id(s))); +} diff --git a/gcc/testsuite/gdc.test/compilable/test21476.d b/gcc/testsuite/gdc.test/compilable/test21476.d new file mode 100644 index 00000000000..a2e307eebdd --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test21476.d @@ -0,0 +1,13 @@ +// https://github.com/dlang/dmd/issues/21476 + +struct S21476 +{ + string field; + this(ref return scope S21476); + this(return scope S21476); +} + +void test21476() +{ + auto o = S21476("aoe"); +} diff --git a/gcc/testsuite/gdc.test/compilable/test23169.d b/gcc/testsuite/gdc.test/compilable/test23169.d index 6237661a923..f8223549ea9 100644 --- a/gcc/testsuite/gdc.test/compilable/test23169.d +++ b/gcc/testsuite/gdc.test/compilable/test23169.d @@ -5,7 +5,7 @@ struct Ptr { int* impl; void* fun0() return scope {return impl;} - void* fun1() scope return {return impl;} + void* fun1() scope return ref {return impl;} void* fun2() return {return &this;} } diff --git a/gcc/testsuite/gdc.test/compilable/test3004.d b/gcc/testsuite/gdc.test/compilable/test3004.d index 664452ef4e6..aefc76cddb5 100644 --- a/gcc/testsuite/gdc.test/compilable/test3004.d +++ b/gcc/testsuite/gdc.test/compilable/test3004.d @@ -1,7 +1,7 @@ // https://issues.dlang.org/show_bug.cgi?id=3004 /* REQUIRED_ARGS: -ignore -v -TRANSFORM_OUTPUT: remove_lines("^(predefs|binary|version|config|DFLAG|parse|import|\(imported|semantic|entry|library|function object|function core|\s*$)") +TRANSFORM_OUTPUT: remove_lines("^(predefs|binary|version|config|DFLAG|parse|inline|.*_d_newarrayU|import|\(imported|semantic|entry|library|function object|function core|\s*$)") TEST_OUTPUT: --- pragma GNU_attribute (__error) diff --git a/gcc/testsuite/gdc.test/compilable/testgotoskips.d b/gcc/testsuite/gdc.test/compilable/testgotoskips.d index 659b5f776a5..190feb4767c 100644 --- a/gcc/testsuite/gdc.test/compilable/testgotoskips.d +++ b/gcc/testsuite/gdc.test/compilable/testgotoskips.d @@ -15,3 +15,13 @@ class A { return; } } + +// https://github.com/dlang/dmd/issues/18018 +int main () +{ + string[string] aa; + goto A; // line 4 + aa["X"] = "Y"; // line 5 +A: + return 0; +} diff --git a/gcc/testsuite/gdc.test/compilable/testsctreturn.d b/gcc/testsuite/gdc.test/compilable/testsctreturn.d index 0d0af2e61f0..45b42c9e895 100644 --- a/gcc/testsuite/gdc.test/compilable/testsctreturn.d +++ b/gcc/testsuite/gdc.test/compilable/testsctreturn.d @@ -27,7 +27,7 @@ struct S int i; int* ptr; - int* wannabeReturnRef() scope return + int* wannabeReturnRef() return ref scope { return &i; } diff --git a/gcc/testsuite/gdc.test/compilable/traits_getFunctionAttributes.d b/gcc/testsuite/gdc.test/compilable/traits_getFunctionAttributes.d index f4defb48095..7c1fdcb16cc 100644 --- a/gcc/testsuite/gdc.test/compilable/traits_getFunctionAttributes.d +++ b/gcc/testsuite/gdc.test/compilable/traits_getFunctionAttributes.d @@ -44,8 +44,8 @@ void test_getFunctionAttributes() static assert(__traits(getFunctionAttributes, S.sharedF) == tuple!("shared", "@system")); static assert(__traits(getFunctionAttributes, typeof(S.sharedF)) == tuple!("shared", "@system")); - static assert(__traits(getFunctionAttributes, S.refF) == tuple!("ref", "return", "@system")); - static assert(__traits(getFunctionAttributes, typeof(S.refF)) == tuple!("ref", "return", "@system")); + static assert(__traits(getFunctionAttributes, S.refF) == tuple!("return", "ref", "@system")); + static assert(__traits(getFunctionAttributes, typeof(S.refF)) == tuple!("return", "ref", "@system")); static assert(__traits(getFunctionAttributes, S.propertyF) == tuple!("@property", "@system")); static assert(__traits(getFunctionAttributes, typeof(&S.propertyF)) == tuple!("@property", "@system")); diff --git a/gcc/testsuite/gdc.test/compilable/vcg-ast.d b/gcc/testsuite/gdc.test/compilable/vcg-ast.d index bcb7256723c..6a905557400 100644 --- a/gcc/testsuite/gdc.test/compilable/vcg-ast.d +++ b/gcc/testsuite/gdc.test/compilable/vcg-ast.d @@ -4,6 +4,8 @@ PERMUTE_ARGS: OUTPUT_FILES: compilable/vcg-ast.d.cg EXTRA_FILES: imports/vcg_ast_import.d TEST_OUTPUT_FILE: extra-files/vcg-ast.d.cg +// size_t currently must be ulong in this test, not uint +DISABLED: freebsd32 openbsd32 linux32 osx32 win32 */ module vcg; diff --git a/gcc/testsuite/gdc.test/compilable/warn3882.d b/gcc/testsuite/gdc.test/compilable/warn3882.d index 0474315be57..592ee315ce3 100644 --- a/gcc/testsuite/gdc.test/compilable/warn3882.d +++ b/gcc/testsuite/gdc.test/compilable/warn3882.d @@ -76,3 +76,41 @@ void test13899() { } } + +import core.checkedint; + +// check inlining of checkedint with -wi +T testCheckedSigned(T)(T x, T y) +{ + bool overflow; + T z = adds(x, y, overflow); + z = subs(z, x, overflow); + z = muls(z, x, overflow); + z = negs(z, overflow); + return z; +} + +T testCheckedUnsigned(T)(T x, T y) +{ + bool overflow; + T z = addu(x, y, overflow); + z = subu(z, x, overflow); + z = mulu(z, x, overflow); + return z; +} + +void testCkeckedInt() +{ + assert(testCheckedSigned!int(3,4) == -12); + assert(testCheckedSigned!long(3,4) == -12); + static if (is(cent)) + assert(testCheckedSigned!cent(3,4) == -12); + + bool overflow; + assert(mulu(cast(long)3, cast(uint)4, overflow) == 12); + + assert(testCheckedUnsigned!uint(3,4) == 12); + assert(testCheckedUnsigned!ulong(3,4) == 12); + static if (is(ucent)) + assert(testCheckedUnsigned!ucent(3,4) == 12); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/aa_assign.d b/gcc/testsuite/gdc.test/fail_compilation/aa_assign.d new file mode 100644 index 00000000000..3c6c5d2b1d4 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/aa_assign.d @@ -0,0 +1,13 @@ +/* TEST_OUTPUT: +--- +fail_compilation\aa_assign.d(11): Error: associative arrays can only be assigned values with immutable keys, not `char[]` +--- +*/ + +void test_mutable_key() +{ + int[char[]] aa; + char[] str = "123".dup; + aa[str] = 3; + assert(str in aa); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/aaerrors.d b/gcc/testsuite/gdc.test/fail_compilation/aaerrors.d new file mode 100644 index 00000000000..571f4722543 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/aaerrors.d @@ -0,0 +1,43 @@ +/* TEST_OUTPUT: +TRANSFORM_OUTPUT: remove_lines(called from here) +--- +fail_compilation\aaerrors.d-mixin-29(29): Error: `assert(aai[1] == 0)` failed +fail_compilation\aaerrors.d-mixin-30(30): Error: `assert((aai[1] = 1) == 0)` failed +fail_compilation\aaerrors.d-mixin-31(31): Error: `assert(*(1 in aai) == 3)` failed +fail_compilation\aaerrors.d-mixin-32(32): Error: `assert(aai.remove(2))` failed +fail_compilation\aaerrors.d-mixin-33(33): Error: `assert(aai != [1:2])` failed +fail_compilation\aaerrors.d-mixin-34(34): Error: `assert(aai == [1:3])` failed +fail_compilation\aaerrors.d-mixin-41(41): Error: `assert(aas[1].x == 0)` failed +fail_compilation\aaerrors.d-mixin-42(42): Error: `assert((aas[1] = 1).x == 0)` failed +fail_compilation\aaerrors.d-mixin-43(43): Error: `assert((*(1 in aas)).x == 0)` failed +--- +*/ + + +struct S +{ + int x; + this(int _x){ x = _x; } + ref S opAssign(int _x){ x = _x; return this; } +} + +string gentest_ii(string expr) +{ + return "() { int[int] aai = [ 1 : 2 ]; assert(" ~ expr ~ ");\n return true; }()\n"; +} + +const ii1 = mixin(gentest_ii("aai[1] == 0")); +const ii2 = mixin(gentest_ii("(aai[1] = 1) == 0")); +const ii3 = mixin(gentest_ii("*(1 in aai) == 3")); +const ii4 = mixin(gentest_ii("aai.remove(2)")); +const ii5 = mixin(gentest_ii("aai != [1:2]")); +const ii6 = mixin(gentest_ii("aai == [1:3]")); + +string gentest_is(string expr) +{ + return "() { S[int] aas = [ 1 : S(2) ]; assert(" ~ expr ~ ");\n return true; }()\n"; +} + +const is1 = mixin(gentest_is("aas[1].x == 0")); +const is2 = mixin(gentest_is("(aas[1] = 1).x == 0")); +const is3 = mixin(gentest_is("(1 in aas).x == 0")); diff --git a/gcc/testsuite/gdc.test/fail_compilation/biterrors.d b/gcc/testsuite/gdc.test/fail_compilation/biterrors.d index a8f0faa0739..9515039a721 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/biterrors.d +++ b/gcc/testsuite/gdc.test/fail_compilation/biterrors.d @@ -1,8 +1,8 @@ /* REQUIRED_ARGS: -preview=bitfields * TEST_OUTPUT: --- -fail_compilation/biterrors.d(103): Error: initializer not allowed for bit-field declaration -fail_compilation/biterrors.d(104): Error: storage class not allowed for bit-field declaration +fail_compilation/biterrors.d(103): Error: initializer not allowed for bitfield declaration +fail_compilation/biterrors.d(104): Error: storage class not allowed for bitfield declaration --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/biterrors2.d b/gcc/testsuite/gdc.test/fail_compilation/biterrors2.d index 77671eb853f..85554931d13 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/biterrors2.d +++ b/gcc/testsuite/gdc.test/fail_compilation/biterrors2.d @@ -1,9 +1,9 @@ /* REQUIRED_ARGS: -preview=bitfields * TEST_OUTPUT: --- -fail_compilation/biterrors2.d(100): Error: variable `biterrors2.a` - bit-field must be member of struct, union, or class -fail_compilation/biterrors2.d(104): Error: bit-field `b` has zero width -fail_compilation/biterrors2.d(105): Error: bit-field type `float` is not an integer type +fail_compilation/biterrors2.d(100): Error: variable `biterrors2.a` - bitfield must be member of struct, union, or class +fail_compilation/biterrors2.d(104): Error: bitfield `b` has zero width +fail_compilation/biterrors2.d(105): Error: bitfield type `float` is not an integer type --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/biterrors3.d b/gcc/testsuite/gdc.test/fail_compilation/biterrors3.d index 09d7be66d64..e956fc2f6eb 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/biterrors3.d +++ b/gcc/testsuite/gdc.test/fail_compilation/biterrors3.d @@ -1,7 +1,7 @@ /* REQUIRED_ARGS: -preview=bitfields * TEST_OUTPUT: --- -fail_compilation/biterrors3.d(103): Error: storage class not allowed for bit-field declaration +fail_compilation/biterrors3.d(103): Error: storage class not allowed for bitfield declaration fail_compilation/biterrors3.d(106): Error: expected `,` or `=` after identifier, not `:` fail_compilation/biterrors3.d(106): Error: found `:` when expecting `,` fail_compilation/biterrors3.d(106): Error: found `3` when expecting `identifier` diff --git a/gcc/testsuite/gdc.test/fail_compilation/biterrors4.d b/gcc/testsuite/gdc.test/fail_compilation/biterrors4.d index 0f2ca2d0d3f..53b5cdc232f 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/biterrors4.d +++ b/gcc/testsuite/gdc.test/fail_compilation/biterrors4.d @@ -1,7 +1,7 @@ /* REQUIRED_ARGS: -preview=bitfields * TEST_OUTPUT: --- -fail_compilation/biterrors4.d(109): Error: cannot take address of bit-field `a` +fail_compilation/biterrors4.d(109): Error: cannot take address of bitfield `s.a` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/biterrors5.d b/gcc/testsuite/gdc.test/fail_compilation/biterrors5.d index 2665d69cc98..e17516b8f89 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/biterrors5.d +++ b/gcc/testsuite/gdc.test/fail_compilation/biterrors5.d @@ -1,8 +1,10 @@ /* REQUIRED_ARGS: -preview=bitfields * TEST_OUTPUT: --- -fail_compilation/biterrors5.d(23): Error: bitfield symbol expected not struct `biterrors5.S` -fail_compilation/biterrors5.d(24): Error: bitfield symbol expected not variable `biterrors5.test0.i` +fail_compilation/biterrors5.d(25): Error: bitfield symbol expected not struct `biterrors5.S` +fail_compilation/biterrors5.d(26): Error: bitfield symbol expected not variable `biterrors5.test0.i` +fail_compilation/biterrors5.d(35): Error: cannot take address of bitfield `a.x` +fail_compilation/biterrors5.d(35): Error: cannot take address of bitfield `a.y` --- */ @@ -23,3 +25,12 @@ void test0() i = __traits(getBitfieldOffset, S); i = __traits(getBitfieldOffset, i); } + +/****************************************/ + +struct B { int x: 3; int y: 5; } + +void test1(int val, int choice, B a) +{ + (choice ? a.x : a.y) = val; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag13320.d b/gcc/testsuite/gdc.test/fail_compilation/diag13320.d index 40b99ab39d0..3608d9f4991 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag13320.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag13320.d @@ -1,15 +1,23 @@ /* TEST_OUTPUT: --- -fail_compilation/diag13320.d(14): Error: operator `++` not supported for `f` of type `Foo` -fail_compilation/diag13320.d(9): perhaps implement `auto opUnary(string op : "++")() {}` or `auto opOpAssign(string op : "+")(int) {}` +fail_compilation/diag13320.d(20): Error: operator `++` not supported for `f` of type `Foo` +fail_compilation/diag13320.d(14): perhaps implement `auto opUnary(string op : "++")() {}` or `auto opOpAssign(string op : "+")(int) {}` +fail_compilation/diag13320.d(21): Error: expression `f` of type `Foo` does not have a boolean value +fail_compilation/diag13320.d(14): perhaps add Cast Operator Overloading with `bool opCast(T : bool)() => ...` +fail_compilation/diag13320.d(22): Error: expression `Foo()` of type `E` does not have a boolean value +fail_compilation/diag13320.d(14): perhaps add Cast Operator Overloading with `bool opCast(T : bool)() => ...` --- */ + struct Foo {} +enum E : Foo { a = Foo.init } void main() { Foo f; ++f; + if (f) {} + assert(E.a); } diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag16976.d b/gcc/testsuite/gdc.test/fail_compilation/diag16976.d index 1dbacfd3e54..92032bcac01 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag16976.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag16976.d @@ -1,37 +1,37 @@ /* TEST_OUTPUT: --- -fail_compilation/diag16976.d(44): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(45): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(46): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(47): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(48): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(49): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(50): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(51): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(52): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(53): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(54): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(55): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(56): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(57): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(58): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(59): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(65): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(66): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(67): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(68): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(69): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(70): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(71): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(72): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(73): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(74): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(75): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(76): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(77): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(78): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(79): Error: foreach: key cannot be of non-integral type `float` -fail_compilation/diag16976.d(80): Error: foreach: key cannot be of non-integral type `float` +fail_compilation/diag16976.d(44): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(45): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(46): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(47): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(48): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(49): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(50): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(51): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(52): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(53): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(54): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(55): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(56): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(57): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(58): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(59): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(65): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(66): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(67): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(68): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(69): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(70): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(71): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(72): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(73): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(74): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(75): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(76): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(77): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(78): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(79): Error: foreach: index cannot be of non-integral type `float` +fail_compilation/diag16976.d(80): Error: foreach: index cannot be of non-integral type `float` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag7420.d b/gcc/testsuite/gdc.test/fail_compilation/diag7420.d index 3267e669243..a19796d5d9b 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag7420.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag7420.d @@ -2,17 +2,16 @@ /* TEST_OUTPUT: --- -fail_compilation/diag7420.d(21): Error: static variable `x` cannot be read at compile time -fail_compilation/diag7420.d(21): while evaluating: `static assert(x < 4)` +fail_compilation/diag7420.d(20): Error: static variable `x` cannot be read at compile time +fail_compilation/diag7420.d(20): while evaluating: `static assert(x < 4)` +fail_compilation/diag7420.d(21): Error: static variable `y` cannot be read at compile time +fail_compilation/diag7420.d(21): while evaluating: `static assert(y == "abc")` fail_compilation/diag7420.d(22): Error: static variable `y` cannot be read at compile time -fail_compilation/diag7420.d(22): called from here: `__equals(y, "abc")` -fail_compilation/diag7420.d(22): while evaluating: `static assert(y == "abc")` +fail_compilation/diag7420.d(22): while evaluating: `static assert(cast(ubyte[])y != null)` fail_compilation/diag7420.d(23): Error: static variable `y` cannot be read at compile time -fail_compilation/diag7420.d(23): while evaluating: `static assert(cast(ubyte[])y != null)` +fail_compilation/diag7420.d(23): while evaluating: `static assert(cast(int)y[0] == 1)` fail_compilation/diag7420.d(24): Error: static variable `y` cannot be read at compile time -fail_compilation/diag7420.d(24): while evaluating: `static assert(cast(int)y[0] == 1)` -fail_compilation/diag7420.d(25): Error: static variable `y` cannot be read at compile time -fail_compilation/diag7420.d(25): while evaluating: `static assert(y[0..1].length == 1u)` +fail_compilation/diag7420.d(24): while evaluating: `static assert(y[0..1].length == 1u)` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/discard_value.d b/gcc/testsuite/gdc.test/fail_compilation/discard_value.d index 7fe30a68706..848fb19536c 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/discard_value.d +++ b/gcc/testsuite/gdc.test/fail_compilation/discard_value.d @@ -5,7 +5,7 @@ fail_compilation/discard_value.d(24): Error: the result of the equality expressi fail_compilation/discard_value.d(25): Error: the result of the equality expression `null !is null` is discarded fail_compilation/discard_value.d(26): Error: the result of the equality expression `v == 0` is discarded fail_compilation/discard_value.d(27): Error: the result of the equality expression `v == 0` is discarded -fail_compilation/discard_value.d(28): Error: `!__equals("", "")` has no effect +fail_compilation/discard_value.d(28): Error: the result of the equality expression `"" != ""` is discarded fail_compilation/discard_value.d(29): Error: the result of the equality expression `"" == ""` is discarded fail_compilation/discard_value.d(30): Error: the result of the equality expression `fun().i == 4` is discarded fail_compilation/discard_value.d(30): note that `fun().i` may have a side effect diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13756.d b/gcc/testsuite/gdc.test/fail_compilation/fail13756.d index cdf0e854d54..85d31db0cf1 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail13756.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail13756.d @@ -1,7 +1,10 @@ /* TEST_OUTPUT: --- -fail_compilation/fail13756.d(11): Error: `foreach`: index must be type `const(int)`, not `int` +fail_compilation/fail13756.d(14): Error: `foreach`: index parameter `ref k` must be type `const(int)`, not `int` +fail_compilation/fail13756.d(17): Error: cannot implicitly convert expression `__applyArg0` of type `int` to `string` +fail_compilation/fail13756.d(19): Error: cannot implicitly convert expression `__applyArg1` of type `int` to `char` +fail_compilation/fail13756.d(20): Error: `foreach`: value parameter `ref val` must be type `int`, not `dchar` --- */ @@ -11,4 +14,8 @@ void maiin() foreach (ref int k, v; aa) { } + foreach (string key, val; aa) {} + + foreach (key, char val; aa) {} + foreach (key, ref dchar val; aa) {} } diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20365.d b/gcc/testsuite/gdc.test/fail_compilation/fail20365.d new file mode 100644 index 00000000000..a7ce47624a7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail20365.d @@ -0,0 +1,29 @@ +/* REQUIRED_ARGS: -preview=bitfields +TEST_OUTPUT: +--- +fail_compilation/fail20365.d(16): Error: bitfield `a.x` cannot be assigned to `ref x` +fail_compilation/fail20365.d(23): Error: function `f` is not callable using argument types `(int)` +fail_compilation/fail20365.d(23): cannot pass bitfield argument `a.y` to parameter `ref int` +fail_compilation/fail20365.d(19): `fail20365.f(ref int)` declared here +fail_compilation/fail20365.d(28): Error: cannot `ref` return bitfield `a.y` +--- +*/ + +struct A { int x : 16, y : 16; } + +void ref_assign(A a) +{ + ref int x = a.x; +} + +void f(ref int); + +void ref_param(A a) +{ + f(a.y); +} + +ref int ref_return(ref A a) +{ + return a.y; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail21414.d b/gcc/testsuite/gdc.test/fail_compilation/fail21414.d new file mode 100644 index 00000000000..d07b2e2cbd3 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail21414.d @@ -0,0 +1,47 @@ +// https://github.com/dlang/dmd/issues/21414 + +/* REQUIRED_ARGS: -o- +TEST_OUTPUT: +--- +fail_compilation/fail21414.d(42): Error: moving variable `__rvalue(s)` with `__rvalue` is not allowed in a `@safe` function +fail_compilation/fail21414.d(42): Error: moving variable `__rvalue(s)` with `__rvalue` is not allowed in a `@safe` function +fail_compilation/fail21414.d(44): Error: calling `__rvalue`-annotated function `unsafeMove` is not allowed in a `@safe` function +fail_compilation/fail21414.d(44): Error: calling `__rvalue`-annotated function `unsafeMove` is not allowed in a `@safe` function +fail_compilation/fail21414.d(46): Error: moving result of `ref` function `id` with `__rvalue` is not allowed in a `@safe` function +fail_compilation/fail21414.d(46): Error: moving result of `ref` function `id` with `__rvalue` is not allowed in a `@safe` function +--- +*/ + +@safe: + +struct S +{ + int x; + this(int x) + { + this.x = x; + } + ~this() { } + this(S s) { } +} + +void foo(S s, immutable(S) t) +{ + assert(t.x == 2); + s.x = 3; + assert(t.x == 2); +} + +ref unsafeMove(T)(ref T arg) __rvalue => arg; + +ref id(T)(return ref T arg) => arg; + +void main() +{ + auto s = S(2); + foo(__rvalue(s), __rvalue(s)); + auto t = S(2); + foo(unsafeMove(t), unsafeMove(t)); + auto u = S(2); + foo(__rvalue(id(u)), __rvalue(id(u))); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail21660.d b/gcc/testsuite/gdc.test/fail_compilation/fail21660.d new file mode 100644 index 00000000000..50dfa8ef836 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail21660.d @@ -0,0 +1,57 @@ +// REQUIRED_ARGS: -preview=bitfields +/* +TEST_OUTPUT: +--- +fail_compilation/fail21660.d(29): Error: overlapping initialization for field `b` and `c` +fail_compilation/fail21660.d(29): `struct` initializers that contain anonymous unions must initialize only the first member of a `union`. All subsequent non-overlapping fields are default initialized +fail_compilation/fail21660.d(30): Error: overlapping initialization for field `b` and `c` +fail_compilation/fail21660.d(30): `struct` initializers that contain anonymous unions must initialize only the first member of a `union`. All subsequent non-overlapping fields are default initialized +fail_compilation/fail21660.d(52): Error: overlapping initialization for field `a` and `b` +fail_compilation/fail21660.d(52): Error: overlapping initialization for field `a` and `d` +fail_compilation/fail21660.d(52): Error: overlapping initialization for field `c` and `d` +fail_compilation/fail21660.d(54): Error: overlapping initialization for field `a` and `d` +--- +*/ + +struct S +{ + uint a : 1; + union { + uint b : 2; + struct { + uint c : 3; + } + } +} + +void testS() +{ + S s = S(1, 2, 3); // b + c overlap + S t = S(a:1, b:2, c:3); // b + c overlap + S u = S(a:1, c:3); // ok +} + +union U +{ + union { + uint a : 5; + uint b : 4; + } + struct { + uint : 5; + uint c : 11; + } + struct { + uint : 4; + uint d : 12; + } +} + +void testU() +{ + U s = U(1, 2, 3, 4); // a + b, a + d, c + d overlap + U t = U(a:1, c:3); // ok + U u = U(a:1, d:2); // a + d overlap + U v = U(b:1, c:3); // ok + U w = U(b:1, d:2); // ok +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail21693.d b/gcc/testsuite/gdc.test/fail_compilation/fail21693.d new file mode 100644 index 00000000000..77cf20c3a5b --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail21693.d @@ -0,0 +1,17 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail21693.d(15): Error: function `fail` is not callable using argument types `(uint)` +fail_compilation/fail21693.d(15): cannot pass rvalue argument `__rvalue(s.x)` of type `uint` to parameter `ref uint` +fail_compilation/fail21693.d(11): `fail21693.fail(ref uint)` declared here +fail_compilation/fail21693.d(16): Error: rvalue `__rvalue(s.x)` cannot be assigned to `ref x` +--- +*/ +struct S { uint x; } +void fail(ref uint); + +void test21693(S s) +{ + fail(__rvalue(s.x)); + ref x = __rvalue(s.x); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail22749.d b/gcc/testsuite/gdc.test/fail_compilation/fail22749.d index 56ccac64fae..8057e1ccb15 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail22749.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail22749.d @@ -1,7 +1,7 @@ // EXTRA_FILES: imports/imp22749.c /* TEST_OUTPUT: --- -fail_compilation/fail22749.d(12): Error: cannot take address of bit-field `field` +fail_compilation/fail22749.d(12): Error: cannot take address of bitfield `s.field` --- */ import imports.imp22749; diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail6497.d b/gcc/testsuite/gdc.test/fail_compilation/fail6497.d index 00c66a6574f..0fd1ff2f4ee 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail6497.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail6497.d @@ -1,8 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/fail6497.d(12): Error: taking the address of stack-allocated local variable `n` is not allowed in a `@safe` function -fail_compilation/fail6497.d(12): Error: taking the address of stack-allocated local variable `n` is not allowed in a `@safe` function +fail_compilation/fail6497.d(13): Error: taking the address of stack-allocated local variable `n` is not allowed in a `@safe` function +fail_compilation/fail6497.d(13): Error: taking the address of stack-allocated local variable `n` is not allowed in a `@safe` function +fail_compilation/fail6497.d(19): Error: taking the address of local variable `i` is not allowed in a `@safe` function --- */ @@ -11,3 +12,9 @@ void main() @safe int n; auto b = &(0 ? n : n); } + +void f() @safe +{ + ref i = *new int; + auto b = &i; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail6795.d b/gcc/testsuite/gdc.test/fail_compilation/fail6795.d index dbdd5d5666b..e566aff570a 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail6795.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail6795.d @@ -7,10 +7,10 @@ fail_compilation/fail6795.d(20): Error: cannot modify expression `[0:0][0]` beca fail_compilation/fail6795.d(22): Error: cannot modify expression `[0][0]` because it is not an lvalue fail_compilation/fail6795.d(23): Error: cannot modify expression `[0:0][0]` because it is not an lvalue fail_compilation/fail6795.d(25): Error: cannot take address of expression `[0][0]` because it is not an lvalue -fail_compilation/fail6795.d(26): Error: cannot take address of expression `[0:0][0]` because it is not an lvalue fail_compilation/fail6795.d(30): Error: cannot modify expression `Some["zz"]` because it is not an lvalue --- */ + void test_wrong_line_num() { enum int[1] sa = [0]; @@ -23,7 +23,7 @@ void test_wrong_line_num() aa[0] /= 3; auto ps = &sa[0]; - auto pa = &aa[0]; + auto pa = &aa[0]; // ok with AA lowering, just as `pa = 0 in aa` // https://issues.dlang.org/show_bug.cgi?id=24845 enum Maps : int[string] { Some = ["aa" : 12], Other = ["bb" : 24] } diff --git a/gcc/testsuite/gdc.test/fail_compilation/fix22108.d b/gcc/testsuite/gdc.test/fail_compilation/fix22108.d index 8fa75cbf6f1..175e5825920 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fix22108.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fix22108.d @@ -7,7 +7,7 @@ fail_compilation/fix22108.d(12): Error: returning scope variable `p` is not allo // https://issues.dlang.org/show_bug.cgi?id=22108 -@safe ref int test(ref scope return int* p) +@safe ref int test(return ref scope int* p) { return *p; } diff --git a/gcc/testsuite/gdc.test/fail_compilation/foreach_seq.d b/gcc/testsuite/gdc.test/fail_compilation/foreach_seq.d new file mode 100644 index 00000000000..9291e209215 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/foreach_seq.d @@ -0,0 +1,38 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/foreach_seq.d(25): Error: only one (element) or two (index, element) arguments allowed for sequence `foreach`, not 3 +fail_compilation/foreach_seq.d(26): Error: invalid storage class `ref` for index `i` +fail_compilation/foreach_seq.d(27): Error: foreach: index cannot be of non-integral type `void` +fail_compilation/foreach_seq.d(28): Error: index type `bool` cannot cover index range 0..3 +fail_compilation/foreach_seq.d(29): Error: `foreach` loop variable cannot be both `enum` and `alias` +fail_compilation/foreach_seq.d(30): Error: constant value `1` cannot be `ref` +fail_compilation/foreach_seq.d(31): Error: invalid storage class `enum` for index `i` +fail_compilation/foreach_seq.d(33): Error: invalid storage class `ref` for element `e` +fail_compilation/foreach_seq.d(34): Error: symbol `object` cannot be `ref` +fail_compilation/foreach_seq.d(35): Error: cannot specify element type for symbol `e` +fail_compilation/foreach_seq.d(36): Error: cannot specify element type for symbol `object` +fail_compilation/foreach_seq.d(37): Error: invalid storage class `enum` for element `e` +--- +*/ + +// test semantic errors on foreach parameters +void main() +{ + alias AliasSeq(A...) = A; + alias seq = AliasSeq!(1, 2, 3); + + foreach (a, b, c; seq) {} + foreach (ref i, e; seq) {} + foreach (void i, e; seq) {} + foreach (bool i, e; seq) {} + foreach (enum alias e; seq) {} + foreach (ref enum e; seq) {} + foreach (ref enum i, e; seq) {} + + foreach (ref e; AliasSeq!int) {} + foreach (ref e; AliasSeq!object) {} + foreach (int e; AliasSeq!int) {} + foreach (int e; AliasSeq!object) {} + foreach (enum e; AliasSeq!int) {} +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10938.d b/gcc/testsuite/gdc.test/fail_compilation/ice10938.d index 16eaa99b3d8..c1e6cffc9e8 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice10938.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice10938.d @@ -2,7 +2,7 @@ TEST_OUTPUT: --- fail_compilation/ice10938.d(14): Error: no property `opts` for `this` of type `ice10938.C` -fail_compilation/ice10938.d(19): Error: forward reference to inferred return type of function call `this.opDispatch()` +fail_compilation/ice10938.d(19): Error: can't infer return type in function `opDispatch` fail_compilation/ice10938.d(14): Error: template instance `ice10938.C.opDispatch!"opts"` error instantiating --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13644.d b/gcc/testsuite/gdc.test/fail_compilation/ice13644.d index aa44709124a..4d1673fba9f 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice13644.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice13644.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/ice13644.d(22): Error: foreach: key cannot be of non-integral type `string` +fail_compilation/ice13644.d(22): Error: foreach: index cannot be of non-integral type `string` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/issue21630.d b/gcc/testsuite/gdc.test/fail_compilation/issue21630.d new file mode 100644 index 00000000000..832ce3b757f --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/issue21630.d @@ -0,0 +1,18 @@ +/+ +TEST_OUTPUT: +--- +fail_compilation/issue21630.d(14): Error: cannot declare `enum` loop variables for non-unrolled foreach +fail_compilation/issue21630.d(15): Error: cannot declare `alias` loop variables for non-unrolled foreach +fail_compilation/issue21630.d(16): Error: cannot declare `enum` loop variables for non-unrolled foreach +fail_compilation/issue21630.d(17): Error: cannot declare `alias` loop variables for non-unrolled foreach +--- ++/ + +void main() +{ + enum a = [1, 2, 3]; + foreach(enum i; a) { } // error + foreach(alias i; a) { } // error + foreach(enum i; 0 .. 3) { } // error + foreach(alias i; 0 .. 3) { } // error +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/previewin3.d b/gcc/testsuite/gdc.test/fail_compilation/previewin3.d new file mode 100644 index 00000000000..cd1ac733bbb --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/previewin3.d @@ -0,0 +1,19 @@ +/* +REQUIRED_ARGS: -preview=in -preview=dip1000 +TEST_OUTPUT: +---- +fail_compilation/previewin3.d(2): Error: function `foo` is not callable using argument types `(int)` +fail_compilation/previewin3.d(2): cannot pass argument `42` of type `int` to parameter `in WithDtor` +fail_compilation/previewin3.d(8): `previewin3.foo(in WithDtor)` declared here +---- + */ + +#line 1 +void rvalueErrorMsg () { + foo(42); +} + +// Add a dtor to ensure things are passed by ref +struct WithDtor { ~this() @safe pure nothrow @nogc {} } + +void foo(in WithDtor) {} diff --git a/gcc/testsuite/gdc.test/fail_compilation/pull12941.d b/gcc/testsuite/gdc.test/fail_compilation/pull12941.d index 4d14993ef5a..6b3ac69a236 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/pull12941.d +++ b/gcc/testsuite/gdc.test/fail_compilation/pull12941.d @@ -18,11 +18,11 @@ fail_compilation/pull12941.d(105): `pull12941.abc(return ref int* p)` dec #line 100 -int* foo(ref scope return int* p); -int* foo(out scope return int* p); +int* foo(return ref scope int* p); +int* foo(return out scope int* p); -int* bar(scope return int* p); -int* abc(ref return int* p); +int* bar(return scope int* p); +int* abc(return ref int* p); void test() { diff --git a/gcc/testsuite/gdc.test/fail_compilation/retscope2.d b/gcc/testsuite/gdc.test/fail_compilation/retscope2.d index f663e4f4fb0..18c7fe64cc9 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/retscope2.d +++ b/gcc/testsuite/gdc.test/fail_compilation/retscope2.d @@ -130,7 +130,7 @@ fail_compilation/retscope2.d(721): Error: returning `s.get1()` escapes a referen struct S700 { - @safe S700* get1() scope return + @safe S700* get1() return ref scope { return &this; } diff --git a/gcc/testsuite/gdc.test/fail_compilation/shared.d b/gcc/testsuite/gdc.test/fail_compilation/shared.d index 7ab1d7cd173..075d96370a9 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/shared.d +++ b/gcc/testsuite/gdc.test/fail_compilation/shared.d @@ -153,13 +153,13 @@ shared(int)* test_dotvarexp_3(return shared C1* c) } // First level DotVarExp dereferencing -ref shared(int) test_dotvarexp_4(return shared ref C2 c) +ref shared(int) test_dotvarexp_4(return ref shared C2 c) { return c.c1.value; } // Second level DotVarExp dereferencing -ref shared(int) test_dotvarexp_5(return shared ref C3 c) +ref shared(int) test_dotvarexp_5(return ref shared C3 c) { return c.c1.c1.value; } @@ -201,7 +201,7 @@ ref shared(K1) test_newexp_1() // Inference tests // Fails because no `ref` -auto test_inference_1(return shared ref C3 c) +auto test_inference_1(return ref shared C3 c) { return c; } @@ -221,7 +221,7 @@ auto ref test_inference_3() } // Fails because `const` conversion -auto ref Object test_inference_4(const return shared ref Object a) +auto ref Object test_inference_4(const return ref shared Object a) { return a; } diff --git a/gcc/testsuite/gdc.test/fail_compilation/test15191.d b/gcc/testsuite/gdc.test/fail_compilation/test15191.d index dcd6f4dd232..a696945099d 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test15191.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test15191.d @@ -25,8 +25,8 @@ int* bar(return ref int s) return &foo(s); } -ref int identity(ref return int x) {return x;} -ref int* identityPtr(ref return int* x) {return x;} +ref int identity(return ref int x) {return x;} +ref int* identityPtr(return ref int* x) {return x;} int* addrOfRefEscape() { diff --git a/gcc/testsuite/gdc.test/fail_compilation/test15704.d b/gcc/testsuite/gdc.test/fail_compilation/test15704.d index ce9e5bb1885..70e50251a9d 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test15704.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test15704.d @@ -1,9 +1,11 @@ /* * TEST_OUTPUT: --- -fail_compilation/test15704.d(17): Error: copying `void[]` to `void[]` is not allowed in a `@safe` function -fail_compilation/test15704.d(18): Error: copying `const(void)[]` to `void[]` is not allowed in a `@safe` function -fail_compilation/test15704.d(19): Deprecation: copying `int[]` to `void[]` will become `@system` in a future release +fail_compilation/test15704.d(19): Error: copying `void[]` to `void[]` is not allowed in a `@safe` function +fail_compilation/test15704.d(20): Error: copying `const(void)[]` to `void[]` is not allowed in a `@safe` function +fail_compilation/test15704.d(21): Deprecation: copying `int[]` to `void[]` will become `@system` in a future release +fail_compilation/test15704.d(22): Error: cannot implicitly convert expression `cast(byte)0` of type `byte` to `void[]` +fail_compilation/test15704.d(22): Error: cannot copy `byte` to `void[]` --- */ @@ -17,4 +19,5 @@ void main() @safe { arr1[] = arr2[]; // overwrites pointers with arbitrary ints arr1[] = new const(void)[3]; arr1[] = [5]; + arr1[] = byte(); // filling not allowed } diff --git a/gcc/testsuite/gdc.test/fail_compilation/test18282.d b/gcc/testsuite/gdc.test/fail_compilation/test18282.d index b5db07fd938..f06b1bfc601 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test18282.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test18282.d @@ -15,7 +15,7 @@ fail_compilation/test18282.d(53): Error: escaping a reference to local variable string* f() @safe { - scope string*[] ls; + scope string*[] ls = [null]; return ls[0]; } diff --git a/gcc/testsuite/gdc.test/fail_compilation/test20881.d b/gcc/testsuite/gdc.test/fail_compilation/test20881.d index add4d738ce0..0f3ac298537 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test20881.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test20881.d @@ -17,7 +17,7 @@ struct S auto borrowA() return /*scope inferred*/ { return ptr; } int* borrowB() return { return ptr; } - int* borrowC() scope return { return ptr; } + int* borrowC() scope return ref { return ptr; } } void main() diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21408.d b/gcc/testsuite/gdc.test/fail_compilation/test21408.d new file mode 100644 index 00000000000..43fd2bf74d0 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test21408.d @@ -0,0 +1,23 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/test21408.d(12): Error: can't infer return type in function `opEquals` +fail_compilation/test21408.d(19): Error: can't infer return type in function `opEquals` +--- +*/ + +struct A { + A[] as; + auto opEquals(A x) { + return as == x.as; + } +} + +struct B { + B[] as; + auto opEquals(B x) { + return x == this; + } +} + +void main() {} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21429.d b/gcc/testsuite/gdc.test/fail_compilation/test21429.d new file mode 100644 index 00000000000..00ce8507503 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test21429.d @@ -0,0 +1,21 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/test21429.d(20): Error: no property `z` for `s` of type `test21429.S` +fail_compilation/test21429.d(13): struct `S` defined here +--- +*/ +//make sure this fails properly after fixing bug 21429.d +template mixinOpDispatch(string id){ + string opDispatch(string s)() if(s == id){ return id; } +} + +struct S { + mixin mixinOpDispatch!"x"; + mixin mixinOpDispatch!"y"; +} + +void main(){ + S s; + auto fail = s.z; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21477.d b/gcc/testsuite/gdc.test/fail_compilation/test21477.d index c9c7c7fcd50..b523cccb679 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test21477.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test21477.d @@ -1,7 +1,7 @@ /* REQUIRED_ARGS: -betterC TEST_OUTPUT: --- -fail_compilation/test21477.d(103): Error: expression `[1]` uses the GC and cannot be used with switch `-betterC` +fail_compilation/test21477.d(103): Error: this array literal requires the GC and cannot be used with `-betterC` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21546.d b/gcc/testsuite/gdc.test/fail_compilation/test21546.d index 22565e4a8a2..7a831ebaaeb 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test21546.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test21546.d @@ -3,10 +3,10 @@ fail_compilation/test21546.d(113): Error: cannot implicitly convert expression `pc` of type `const(int)* delegate() return` to `int* delegate() return` fail_compilation/test21546.d(114): Error: cannot implicitly convert expression `pc` of type `const(int)* delegate() return` to `immutable(int)* delegate() return` fail_compilation/test21546.d(115): Error: cannot implicitly convert expression `pi` of type `immutable(int)* delegate() return` to `int* delegate() return` -fail_compilation/test21546.d(213): Error: cannot implicitly convert expression `dc` of type `const(int) delegate() ref return` to `int delegate() ref return` -fail_compilation/test21546.d(214): Error: cannot implicitly convert expression `dc` of type `const(int) delegate() ref return` to `immutable(int) delegate() ref return` -fail_compilation/test21546.d(215): Error: cannot implicitly convert expression `di` of type `immutable(int) delegate() ref return` to `int delegate() ref return` -fail_compilation/test21546.d(305): Error: cannot implicitly convert expression `[dgi]` of type `immutable(int) delegate() ref return[]` to `int delegate() ref return[]` +fail_compilation/test21546.d(213): Error: cannot implicitly convert expression `dc` of type `const(int) delegate() return ref` to `int delegate() return ref` +fail_compilation/test21546.d(214): Error: cannot implicitly convert expression `dc` of type `const(int) delegate() return ref` to `immutable(int) delegate() return ref` +fail_compilation/test21546.d(215): Error: cannot implicitly convert expression `di` of type `immutable(int) delegate() return ref` to `int delegate() return ref` +fail_compilation/test21546.d(305): Error: cannot implicitly convert expression `[dgi]` of type `immutable(int) delegate() return ref[]` to `int delegate() return ref[]` --- */ // https://issues.dlang.org/show_bug.cgi?id=21546 diff --git a/gcc/testsuite/gdc.test/fail_compilation/test23710.d b/gcc/testsuite/gdc.test/fail_compilation/test23710.d index e834b78f7d5..e7b6ea7d701 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test23710.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test23710.d @@ -1,7 +1,7 @@ /* REQUIRED_ARGS: -betterC TEST_OUTPUT: --- -fail_compilation/test23710.d(111): Error: array concatenation of expression `foo ~ [1, 2, 3]` requires the GC which is not available with -betterC +fail_compilation/test23710.d(112): Error: array concatenation of expression `foo ~ cast(int[])a` requires the GC which is not available with -betterC --- */ // https://issues.dlang.org/show_bug.cgi?id=23710 @@ -12,13 +12,14 @@ int test(int i) { int j; int[] foo; + int[3] a = [1,2,3]; if (0) { for (;;) { import core.stdc.stdio; printf("start body\n"); - foo = foo ~ [1,2,3]; + foo = foo ~ a; L1: printf("foo.length = %zu\n", foo.length); j += foo.length; diff --git a/gcc/testsuite/gdc.test/fail_compilation/varargsstc.d b/gcc/testsuite/gdc.test/fail_compilation/varargsstc.d index 2345a66764e..5f34c0c10ce 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/varargsstc.d +++ b/gcc/testsuite/gdc.test/fail_compilation/varargsstc.d @@ -7,6 +7,6 @@ fail_compilation/varargsstc.d(103): Error: variadic parameter cannot have attrib #line 100 -int printf(const(char)*, const scope shared return ...); -int printf(const(char)*, ref out scope immutable shared return ...); -int printf(const(char)*, ref scope immutable shared return ...); +int printf(const(char)*, const return scope shared ...); +int printf(const(char)*, return ref out scope immutable shared ...); +int printf(const(char)*, return ref scope immutable shared ...); diff --git a/gcc/testsuite/gdc.test/fail_compilation/verifyhookexist.d b/gcc/testsuite/gdc.test/fail_compilation/verifyhookexist.d index d7b8f6646c3..a05641ebd51 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/verifyhookexist.d +++ b/gcc/testsuite/gdc.test/fail_compilation/verifyhookexist.d @@ -8,12 +8,11 @@ EXTRA_SOURCES: extra-files/minimal/object.d /* TEST_OUTPUT: --- -fail_compilation/verifyhookexist.d(22): Error: `object.__ArrayCast` not found. The current runtime does not support casting array of structs, or the runtime is corrupt. -fail_compilation/verifyhookexist.d(28): Error: `object.__equals` not found. The current runtime does not support equal checks on arrays, or the runtime is corrupt. -fail_compilation/verifyhookexist.d(29): Error: `object.__cmp` not found. The current runtime does not support comparing arrays, or the runtime is corrupt. -fail_compilation/verifyhookexist.d(33): Error: `object._d_assert_fail` not found. The current runtime does not support generating assert messages, or the runtime is corrupt. -fail_compilation/verifyhookexist.d(36): Error: `object.__switch` not found. The current runtime does not support switch cases on strings, or the runtime is corrupt. -fail_compilation/verifyhookexist.d(41): Error: `object.__switch_error` not found. The current runtime does not support generating assert messages, or the runtime is corrupt. +fail_compilation/verifyhookexist.d(21): Error: `object.__ArrayCast` not found. The current runtime does not support casting array of structs, or the runtime is corrupt. +fail_compilation/verifyhookexist.d(28): Error: `object.__cmp` not found. The current runtime does not support comparing arrays, or the runtime is corrupt. +fail_compilation/verifyhookexist.d(32): Error: `object._d_assert_fail` not found. The current runtime does not support generating assert messages, or the runtime is corrupt. +fail_compilation/verifyhookexist.d(35): Error: `object.__switch` not found. The current runtime does not support switch cases on strings, or the runtime is corrupt. +fail_compilation/verifyhookexist.d(40): Error: `object.__switch_error` not found. The current runtime does not support generating assert messages, or the runtime is corrupt. --- */ diff --git a/gcc/testsuite/gdc.test/runnable/bit.d b/gcc/testsuite/gdc.test/runnable/bit.d index 289e1bcfbd6..73fe52eb4c2 100644 --- a/gcc/testsuite/gdc.test/runnable/bit.d +++ b/gcc/testsuite/gdc.test/runnable/bit.d @@ -95,6 +95,21 @@ void test3() assert(__traits(getMember, u, "b") == 2); } +/********************************************/ +// https://github.com/dlang/dmd/issues/18247 + +struct S4 +{ + ulong a:64; +} + +void test4() +{ + S4 s; + s.a = 1; + assert(s.a == 1); +} + /********************************************/ int main() @@ -102,5 +117,6 @@ int main() test1(); test2(); test3(); + test4(); return 0; } diff --git a/gcc/testsuite/gdc.test/runnable/bug19652.d b/gcc/testsuite/gdc.test/runnable/bug19652.d index e922b5926a4..d605294ae3f 100644 --- a/gcc/testsuite/gdc.test/runnable/bug19652.d +++ b/gcc/testsuite/gdc.test/runnable/bug19652.d @@ -17,6 +17,6 @@ auto otherTest(inout ref Base block) @nogc { assert(0); } auto otherTest(inout ref A block) @nogc {} void main() { - B* thingie; + B* thingie = new B; otherTest(*thingie); } diff --git a/gcc/testsuite/gdc.test/runnable/constfold.d b/gcc/testsuite/gdc.test/runnable/constfold.d index 7857eaf544a..4aa7a968ae0 100644 --- a/gcc/testsuite/gdc.test/runnable/constfold.d +++ b/gcc/testsuite/gdc.test/runnable/constfold.d @@ -418,7 +418,7 @@ class C8939regression string[2] str; refValue(str[n1]); - int[] da; + int[] da = [1]; refValue(da[n2]); int n; int* p = &n; diff --git a/gcc/testsuite/gdc.test/runnable/foreach5.d b/gcc/testsuite/gdc.test/runnable/foreach5.d index 59b88ec88cb..b0befcceb62 100644 --- a/gcc/testsuite/gdc.test/runnable/foreach5.d +++ b/gcc/testsuite/gdc.test/runnable/foreach5.d @@ -1,5 +1,6 @@ /* EXTRA_FILES: imports/test15777a.d imports/test15777b.d +REQUIRED_ARGS: -verrors=simple TEST_OUTPUT: --- int @@ -10,6 +11,7 @@ int foobar7406(T) int test7406() +runnable/foreach5.d(1200): Deprecation: foreach: loop index implicitly converted from `size_t` to `short` --- */ @@ -1046,6 +1048,21 @@ void test13756() { static assert(is(typeof(k) == const int)); } + + // https://github.com/dlang/dmd/issues/21456 + foreach (long k, long v; aa) + { + assert(k == 1 && v == 20); + } + static struct S + { + int x, y, z; + this(int a) { z = a; } + } + foreach (long k, S s; aa) + { + assert(k == 1 && s.z == 20); + } } /***************************************/ @@ -1175,6 +1192,22 @@ void test17041() assert(foo17041(cast(int[2]) [10, 20]) == [31, 41]); // test rvalue } +/***************************************/ +// https://github.com/ldc-developers/ldc/issues/326 + +void testLDC326() +{ + foreach (short i, dchar c; "ab") + { + if (c == 'a') + assert(i == 0); + else if (c == 'b') + assert(i == 1); + else + assert(0); + } +} + /***************************************/ int main() @@ -1207,6 +1240,7 @@ int main() test13756(); test14653(); test17041(); + testLDC326(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/mars1.d b/gcc/testsuite/gdc.test/runnable/mars1.d index 1c2ae55b79c..04731ac9085 100644 --- a/gcc/testsuite/gdc.test/runnable/mars1.d +++ b/gcc/testsuite/gdc.test/runnable/mars1.d @@ -2549,6 +2549,21 @@ void test9() //////////////////////////////////////////////////////////////////////// +void test10() +{ + double a,b,c; + double* pa,pb; + + a = 5; + b = a++; + assert(a == 6); + pa = &a; + pb = &b; + *pb = (*pa)++; +} + +//////////////////////////////////////////////////////////////////////// + int main() { // All the various integer divide tests @@ -2651,6 +2666,7 @@ int main() test20574(); test8(); test9(); + test10(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/placenew.d b/gcc/testsuite/gdc.test/runnable/placenew.d index 834a1f76bcb..6c8bc565352 100644 --- a/gcc/testsuite/gdc.test/runnable/placenew.d +++ b/gcc/testsuite/gdc.test/runnable/placenew.d @@ -132,6 +132,31 @@ void test8() assert(s.m.a == 3); } +/*************************************************/ +// https://github.com/dlang/dmd/issues/21549 + +struct Test9 +{ + size_t a, b = 3; + this(int x) + { + a = x; + } +} + +void new9(ref Test9 p) +{ + new(p) Test9(1); +} + +void test9() +{ + Test9 t9 = void; + new9(t9); + assert(t9.a == 1); + // assert(t9.b == 3); // Test9.init not copied yet +} + /*************************************************/ int main() @@ -144,6 +169,7 @@ int main() test6(); test7(); test8(); + test9(); return 0; } diff --git a/gcc/testsuite/gdc.test/runnable/sdtor.d b/gcc/testsuite/gdc.test/runnable/sdtor.d index 507c1143fec..e5727006d3b 100644 --- a/gcc/testsuite/gdc.test/runnable/sdtor.d +++ b/gcc/testsuite/gdc.test/runnable/sdtor.d @@ -4848,6 +4848,22 @@ void test24010() assert(x24010 == 71); } +/**********************************/ +extern (C) struct S21303 +{ + static bool dtorCalled = false; + + ~this() { dtorCalled = true; } +} + +void test21303() +{ + { + S21303 s; + } + assert(S21303.dtorCalled); +} + /**********************************/ int main() @@ -4990,6 +5006,7 @@ int main() test68(); testPR12012(); test24010(); + test21303(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/staticaa.d b/gcc/testsuite/gdc.test/runnable/staticaa.d index 6b8fb458075..9311a279643 100644 --- a/gcc/testsuite/gdc.test/runnable/staticaa.d +++ b/gcc/testsuite/gdc.test/runnable/staticaa.d @@ -206,6 +206,18 @@ void testClassLiteral() ///////////////////////////////////////////// +// https://github.com/dlang/dmd/issues/21690 +void testMultiDim() +{ + // int[int][int] aa1 = [ 1: [2: 3] ]; // Error: invalid value `[2:3]` in initializer (see #17804) + int[int][int] aa2 = ([ 1: [2: 3] ]); // workaround + auto aa3 = [ 1: [2: 3] ]; // works, too + static auto aa4 = [ 1: [2: 3] ]; // Error: internal compiler error: failed to detect static initialization of associative array + assert(aa2 == aa3); + assert(aa3 == aa4); +} + +///////////////////////////////////////////// void main() { @@ -219,4 +231,5 @@ void main() testEnumInit(); testStaticArray(); testClassLiteral(); + testMultiDim(); } diff --git a/gcc/testsuite/gdc.test/runnable/test17487.d b/gcc/testsuite/gdc.test/runnable/test17487.d new file mode 100644 index 00000000000..e0428e246b6 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/test17487.d @@ -0,0 +1,179 @@ +// https://github.com/dlang/dmd/issues/17487 +import core.stdc.stdio; + +// version = PRINT; + +void testKey() +{ + version(PRINT) printf("--- testKey\n"); + auto count = new Count; + + string[S] aa; + aa[S(count, 42)] = "foo"; + + version(PRINT) printf("--- after init\n"); + version(PRINT) count.print(); + assert(count.refCount == 1); + + version(PRINT) printf("---\n"); + assert(S(count, 42) in aa); + + version(PRINT) printf("--- after in\n"); + version(PRINT) count.print(); + assert(count.refCount == 1); + + version(PRINT) printf("---\n"); + assert(S(new Count, 22) !in aa); + version(PRINT) printf("--- after !in\n"); + version(PRINT) count.print(); + assert(count.refCount == 1); + + version(PRINT) printf("---\n"); + aa[S(count, 42)] = "foo2"; + + version(PRINT) printf("--- after assign value\n"); + version(PRINT) count.print(); + assert(count.refCount == 1); + assert(count.copyConstructed == 1); // copy ctor used when adding key, not during lookup +} + +void testValue() +{ + version(PRINT) printf("--- testValue\n"); + auto count = new Count; + + S[int] aa; + aa[42] = S(count, 42); + + version(PRINT) printf("--- after init\n"); + version(PRINT) count.print(); + assert(count.refCount == 1); + assert(count.constructed == 1); + + version(PRINT) printf("---\n"); + aa[22] = S(count, 22); + + version(PRINT) printf("--- after create by value\n"); + version(PRINT) count.print(); + assert(count.refCount == 2); + assert(count.constructed == 2); + + version(PRINT) printf("---\n"); + aa[2] = aa[22]; + + version(PRINT) printf("--- after create by copy ctor\n"); + version(PRINT) count.print(); + assert(count.refCount == 3); + assert(count.copyConstructed == 1); + + version(PRINT) printf("---\n"); + aa[22] = aa[42]; + + version(PRINT) printf("--- after assign by copy\n"); + version(PRINT) count.print(); + assert(count.refCount == 3); + assert(count.assignedFrom == 1); + assert(count.assignedTo == 1); + + version(PRINT) printf("---\n"); +} + +void main() +{ + testKey(); + testValue(); + version(PRINT) printf("--- cleanup\n"); +} + +struct Count +{ + int constructed; + int copyConstructed; + int assignedFrom; + int assignedTo; + int destroyed; + int refCount; + + void print() + { + printf("Constructed: %d times\n", constructed); + printf("Copy Constructed: %d times\n", copyConstructed); + printf("Assigned from: %d times\n", assignedFrom); + printf("Assigned To: %d times\n", assignedTo); + printf("Destroyed: %d times\n", destroyed); + printf("refCount: %d\n", refCount); + } +} + +struct S +{ + Count* count; + int i; + bool destroyed; + + this(Count* count, int i) + { + this.count = count; + this.i = i; + ++count.constructed; + ++count.refCount; + version(PRINT) printf("Constructing: %d, count(%p)\n", i, count); + } + + this(ref S rhs) + { + this.count = rhs.count; + this.i = rhs.i; + + ++count.copyConstructed; + ++count.refCount; + version(PRINT) printf("Copy Constructing: %d, count(%p)\n", i, count); + } + + ~this() + { + version(PRINT) printf("Destroying: %d, count(%p)\n", i, count); + + if(count) + { + ++count.destroyed; + --count.refCount; + } + + destroyed = true; + } + + void opAssign()(auto ref S rhs) + { + version(PRINT) printf("Assigning %d, count(%p) to %d, count(%p)\n", + rhs.i, rhs.count, this.i, this.count); + + if(this.count) + { + ++this.count.assignedTo; + --this.count.refCount; + } + + if(rhs.count) + { + ++rhs.count.assignedFrom; + ++rhs.count.refCount; + } + + this.count = rhs.count; + this.i = rhs.i; + } + + invariant + { + assert(!destroyed); + } + + bool opEquals()(const auto ref S rhs) const { + return this.i == rhs.i; + } + + size_t toHash() const { + return 0; + } +} diff --git a/gcc/testsuite/gdc.test/runnable/test20473.d b/gcc/testsuite/gdc.test/runnable/test20473.d new file mode 100644 index 00000000000..faba7953361 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/test20473.d @@ -0,0 +1,110 @@ +// https://github.com/dlang/dmd/issues/20473 +// REQUIRED_ARGS: -preview=bitfields + +struct S1 +{ + char[] slice; + bool flag:1; +} + +void test1() +{ + auto a = S1(['1','2'], false); + auto b = S1(['1','2'], true); + auto c = S1(['1','2'], true); + assert(a != b); + assert(b == c); + assert(typeid(S1).getHash(&a) != typeid(S1).getHash(&b)); + assert(typeid(S1).getHash(&b) == typeid(S1).getHash(&c)); +} + +struct S2 +{ + bool flag:1; + Object o; +} + +void test2() +{ + auto o = new Object; + auto a = S2(false, o); + auto b = S2(true, o); + auto c = S2(true, o); + assert(a != b); + assert(b == c); + assert(typeid(S2).getHash(&a) != typeid(S2).getHash(&b)); + assert(typeid(S2).getHash(&b) == typeid(S2).getHash(&c)); +} + +struct Wrapper3 +{ + void[] wrapped; +} +struct S3 +{ + bool flag:1; + Wrapper3 wrapper; +} + +void test3() +{ + auto a = S3(false, Wrapper3([1,2,3])); + auto b = S3(true, Wrapper3([1,2,3])); + auto c = S3(true, Wrapper3([1,2,3])); + assert(a != b); + assert(b == c); + assert(typeid(S3).getHash(&a) != typeid(S3).getHash(&b)); + assert(typeid(S3).getHash(&b) == typeid(S3).getHash(&c)); +} + +struct Wrapper4 +{ + Object wrapped; +} +struct S4 +{ + bool flag:1; + Wrapper4 wrapper; +} + +void test4() +{ + auto o = new Object; + auto a = S4(false, Wrapper4(o)); + auto b = S4(true, Wrapper4(o)); + auto c = S4(true, Wrapper4(o)); + assert(a != b); + assert(b == c); + assert(typeid(S4).getHash(&a) != typeid(S4).getHash(&b)); + assert(typeid(S4).getHash(&b) == typeid(S4).getHash(&c)); +} + +enum Wrapper5 : string +{ + empty = "" +} +struct S5 +{ + bool flag:1; + Wrapper5 wrapper; +} + +void test5() +{ + auto a = S5(false, Wrapper5.empty); + auto b = S5(true, Wrapper5.empty); + auto c = S5(true, Wrapper5.empty); + assert(a != b); + assert(b == c); + assert(typeid(S5).getHash(&a) != typeid(S5).getHash(&b)); + assert(typeid(S5).getHash(&b) == typeid(S5).getHash(&c)); +} + +void main() +{ + test1(); + test2(); + test3(); + test4(); + test5(); +} diff --git a/gcc/testsuite/gdc.test/runnable/test21429.d b/gcc/testsuite/gdc.test/runnable/test21429.d new file mode 100644 index 00000000000..c1d58113ff7 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/test21429.d @@ -0,0 +1,131 @@ +mixin template OD(string s) +{ + + string opDispatch(string name)() if (name == s) + { + return name; + } +} + +mixin template ODA(string s) +{ + void opDispatch(string name)(int x) if (name == s) + { + this.val = x; + } +} + +struct T +{ + mixin OD!"x"; + mixin OD!"y"; +} + +struct TAssign +{ + int val; + mixin ODA!"x"; + mixin ODA!"y"; +} + +struct U +{ + mixin OD!"z"; +} + + +template adder() +{ + int opBinary(string s : "+")(int x) { return x; } +} + +template subtracter() +{ + int opBinary(string s : "-")(int x) { return -x; } +} + + +struct Arithmetic +{ + mixin adder; + mixin subtracter; + +} + +template adderRight() +{ + int opBinaryRight(string s : "+")(int x){ return x; } +} + + +template subtracterRight() +{ + int opBinaryRight(string s: "-")(int x){ return -x; } +} + +struct ArithmeticRight +{ + mixin adderRight; + mixin subtracterRight; +} + +template mixinOpAssign(string op) +{ + void opOpAssign(string s)(int x) if(s == op) + { + val = x; + lastOp = s; + } +} + +struct AssignOverloads +{ + int val; + string lastOp; + mixin mixinOpAssign!"+"; + mixin mixinOpAssign!"-"; +} + + +void main() +{ + + T t; + string s = t.x(); + assert(s == "x"); + assert(t.y == "y"); + + //explicit call should work + assert(t.opDispatch!"x" == "x"); + + + //TODO: fix these + Arithmetic a; + assert((a + 5) == 5); + assert((a - 7) == -7); + + + ArithmeticRight ar; + assert((5 + ar) == 5); + assert((7 - ar) == -7); + + + U u; + //should work for a single mixin + assert(u.z == "z"); + + TAssign ta; + ta.x = 5; + assert(ta.val == 5); + ta.y = 10; + assert(ta.val == 10); + + AssignOverloads oa; + oa += 5; + assert(oa.val == 5); + assert(oa.lastOp == "+"); + oa -= 10; + assert(oa.val == 10); + assert(oa.lastOp == "-"); + +} diff --git a/gcc/testsuite/gdc.test/runnable/test21435.d b/gcc/testsuite/gdc.test/runnable/test21435.d new file mode 100644 index 00000000000..e7c2eaca556 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/test21435.d @@ -0,0 +1,73 @@ +// https://github.com/dlang/dmd/issues/21435 + +void check_align() +{ + pragma(inline, false); + int x; + (cast(size_t)&x & 3) == 0 || assert(false); +} + +char* getenv_setargv(char* env) +{ + auto p = env; + auto q = env; + auto r = env; + auto s = env; + auto t = env; + auto u = env; + auto ptr = env; + + while (1) + { + auto c = *ptr++; + switch (c) + { + case 0: + check_align(); + return env; + + case 'a': + *p++ = 'A'; + continue; + + case 'b': + *q++ = 'B'; + continue; + + case 'c': + *r++ = 'C'; + continue; + + case 'd': + *s++ = 'D'; + continue; + + case 'e': + *t++ = 'E'; + continue; + + case 'f': + *u++ = 'F'; + continue; + + case 'g': + *p++ = 'G'; + continue; + + case 'h': + *p++ = 'H'; + continue; + + default: + *p++ = c; + continue; + } + } +} + +void main() +{ + char[10] m = "abcdefghi"; + auto str = getenv_setargv(m.ptr); + str[0] || assert(false); +} diff --git a/gcc/testsuite/gdc.test/runnable/test21660.d b/gcc/testsuite/gdc.test/runnable/test21660.d new file mode 100644 index 00000000000..9a0efd80ab1 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/test21660.d @@ -0,0 +1,31 @@ +// REQUIRED_ARGS: -preview=bitfields +struct S +{ + int a : 3; + int b : 3; +} + +struct T +{ + uint a : 1; + union { + uint b : 2; + struct { + uint : 2; + uint c : 3; + } + } +} + +void main() +{ + S s = S(1, 2); + assert(s.a == 1 && s.b == 2); + S t = S(b:1, a:2); + assert(t.a == 2 && t.b == 1); + + T u = T(1, 2, 3); + assert(u.a == 1 && u.b == 2 && u.c == 3); + T v = T(a:1, b:2, c:3); + assert(v.a == 1 && v.b == 2 && v.c == 3); +} diff --git a/gcc/testsuite/gdc.test/runnable/test42.d b/gcc/testsuite/gdc.test/runnable/test42.d index d89c152aa48..1ccd9c347f4 100644 --- a/gcc/testsuite/gdc.test/runnable/test42.d +++ b/gcc/testsuite/gdc.test/runnable/test42.d @@ -3638,10 +3638,10 @@ shared class Bug5504b void test5504() { - immutable Bug5504 c; + immutable Bug5504 c = new immutable Bug5504; c.foo(10); c.xx!(int).hoo(10); - shared Bug5504b d; + shared Bug5504b d = new shared Bug5504b; d.foo(10); d.xx!(int).hoo(10); } diff --git a/gcc/testsuite/gdc.test/runnable/testaa2.d b/gcc/testsuite/gdc.test/runnable/testaa2.d index 3d199d98412..74da97d55e7 100644 --- a/gcc/testsuite/gdc.test/runnable/testaa2.d +++ b/gcc/testsuite/gdc.test/runnable/testaa2.d @@ -310,9 +310,9 @@ void testinenum() // https://github.com/dlang/dmd/issues/21258 void test21258() { - alias AliasSeq(TList...) = TList; + alias AliasSeq(TList...) = TList; - struct S { int x; } // use a local type to not generate required TypeInfo elsewhere + struct S { int x; } // use a local type to not generate required TypeInfo elsewhere foreach (T; AliasSeq!(S[int])) enum E { a = T.init, } // bug report uses bad syntax here, but this crashed, too } @@ -329,6 +329,53 @@ void test21207() /************************************************/ +void testEvaluationOrder() +{ + static int last; + int[int] aa; + int[4] arr; + + int seqi(int n, int i, int ln = __LINE__) + { + n += ln * 100; + assert(n > last); + last = n; + return i; + } + ref int[int] seqaa(int n, int ln = __LINE__) + { + n += ln * 100; + assert(n > last); + last = n; + return aa; + } + seqaa(1)[seqi(2, 0)] = seqi(3, 1); // aa[0] = 1 + int x = seqaa(1)[seqi(2, 0)]; // x = aa[0] + seqaa(1)[seqi(2, 1)] = seqaa(3)[seqi(4, 0)]; // aa[1] = aa[0] + assert(seqi(1, 0) in seqaa(2)); // 0 in aa + + // only executed once? + auto naa = seqaa(1).dup; + auto len = seqaa(1).length; + auto keys = seqaa(1).keys; + auto values = seqaa(1).values; + auto hash = hashOf(seqaa(1)); + seqaa(1).rehash; + seqaa(1).clear; + + version (none) + seqaa(1) = [seqi(2, 1) : seqi(3, 1), seqi(4, 2) : seqi(5, 4)]; // aa = [1:1, 2:4] + else + aa = [1:1, 2:4]; + + assert(seqaa(1).remove(seqi(2, 1))); // aa.remove(1) + + assert(seqaa(1) == seqaa(2)); // aa == aa + assert(!(seqaa(1) != seqaa(2))); // aa != aa +} + +/************************************************/ + int main() { testaa(); @@ -339,6 +386,7 @@ int main() testinout(); testinenum(); test21258(); + testEvaluationOrder(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/testaa3.d b/gcc/testsuite/gdc.test/runnable/testaa3.d index 4aac1fba1a4..2aadc35afc0 100644 --- a/gcc/testsuite/gdc.test/runnable/testaa3.d +++ b/gcc/testsuite/gdc.test/runnable/testaa3.d @@ -314,6 +314,29 @@ int[int] testRetx() void aafunc(int[int] aa) {} +void testTypeinfo() +{ + int[int] a = [ 1:1, 2:4, 3:9, 4:16 ]; + int[int] b = [ 1:1, 2:4, 3:9, 4:16 ]; + assert(a == b); + + hash_t ahash1 = hashOf(a); + hash_t ahash2 = typeid(a).getHash(&a); + assert(ahash1 == ahash2); + hash_t bhash1 = hashOf(a); + hash_t bhash2 = typeid(a).getHash(&a); + assert(bhash1 == bhash2); + assert(ahash1 == bhash1); + assert(typeid(a).equals(&a, &b)); + + string[string] c = [ "1":"one", "2":"two", "3":"three" ]; + hash_t chash1 = hashOf(c); + hash_t chash2 = typeid(c).getHash(&c); + assert(chash1 == chash2); + assert(chash1 != ahash2); + assert(typeid(c).equals(&c, &c)); +} + /***************************************************/ // https://issues.dlang.org/show_bug.cgi?id=12214 @@ -370,6 +393,7 @@ void main() assert(testRet()); static assert(testRet()); + testTypeinfo(); test12220(); test12403(); } diff --git a/gcc/testsuite/gdc.test/runnable/testscope2.d b/gcc/testsuite/gdc.test/runnable/testscope2.d index a1164dc4beb..6c520e3196f 100644 --- a/gcc/testsuite/gdc.test/runnable/testscope2.d +++ b/gcc/testsuite/gdc.test/runnable/testscope2.d @@ -2,7 +2,7 @@ /* TEST_OUTPUT: --- -foo1 ulong function(return ref int* delegate() return p) ref return +foo1 ulong function(return ref int* delegate() return p) return ref foo2 int function(return ref int delegate() p) ref foo3 int function(ref inout(int*) p) ref foo4 int function(return ref inout(int*) p) ref @@ -18,7 +18,7 @@ struct SS ref ulong foo1(return ref int* delegate() return p) return; ref int foo2(return ref int delegate() p); ref int foo3(inout ref int* p); - ref int foo4(return inout ref int* p); + ref int foo4(return ref inout int* p); } pragma(msg, "foo1 ", typeof(&SS.foo1)); diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE index 62d90e5770b..a9235246e93 100644 --- a/libphobos/libdruntime/MERGE +++ b/libphobos/libdruntime/MERGE @@ -1,4 +1,4 @@ -1017635a9636ef6a7a4bda35b1e091875d829871 +e1f6680f50d147846316c2fa3363461a2aa7ac1d The first line of this file holds the git revision number of the last merge done from the dlang/dmd repository. diff --git a/libphobos/libdruntime/Makefile.am b/libphobos/libdruntime/Makefile.am index 0e9b71bfa5d..17fa2ceaa96 100644 --- a/libphobos/libdruntime/Makefile.am +++ b/libphobos/libdruntime/Makefile.am @@ -184,13 +184,13 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \ core/internal/array/construction.d core/internal/array/duplication.d \ core/internal/array/equality.d core/internal/array/operations.d \ core/internal/array/utils.d core/internal/atomic.d \ - core/internal/attributes.d core/internal/container/array.d \ - core/internal/container/common.d core/internal/container/hashtab.d \ - core/internal/container/treap.d core/internal/convert.d \ - core/internal/dassert.d core/internal/destruction.d \ - core/internal/entrypoint.d core/internal/gc/bits.d \ - core/internal/gc/blkcache.d core/internal/gc/blockmeta.d \ - core/internal/gc/impl/conservative/gc.d \ + core/internal/attributes.d core/internal/cast_.d \ + core/internal/container/array.d core/internal/container/common.d \ + core/internal/container/hashtab.d core/internal/container/treap.d \ + core/internal/convert.d core/internal/dassert.d \ + core/internal/destruction.d core/internal/entrypoint.d \ + core/internal/gc/bits.d core/internal/gc/blkcache.d \ + core/internal/gc/blockmeta.d core/internal/gc/impl/conservative/gc.d \ core/internal/gc/impl/manual/gc.d core/internal/gc/impl/proto/gc.d \ core/internal/gc/os.d core/internal/gc/pooltable.d \ core/internal/gc/proxy.d core/internal/hash.d core/internal/lifetime.d \ @@ -221,11 +221,10 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \ gcc/sections/macho.d gcc/sections/package.d gcc/sections/pecoff.d \ gcc/simd.d gcc/unwind/arm.d gcc/unwind/arm_common.d gcc/unwind/c6x.d \ gcc/unwind/generic.d gcc/unwind/package.d gcc/unwind/pe.d object.d \ - rt/aApply.d rt/aApplyR.d rt/aaA.d rt/adi.d rt/arraycat.d rt/cast_.d \ - rt/config.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/profilegc.d rt/sections.d rt/tlsgc.d rt/util/typeinfo.d \ - rt/util/utility.d + rt/aApply.d rt/aApplyR.d rt/arraycat.d rt/config.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/profilegc.d rt/sections.d \ + rt/tlsgc.d rt/util/typeinfo.d rt/util/utility.d DRUNTIME_DSOURCES_STDCXX = core/stdcpp/allocator.d core/stdcpp/array.d \ core/stdcpp/exception.d core/stdcpp/memory.d core/stdcpp/new_.d \ @@ -324,15 +323,15 @@ DRUNTIME_DSOURCES_OPENBSD = core/sys/openbsd/dlfcn.d \ DRUNTIME_DSOURCES_POSIX = core/sys/posix/aio.d \ core/sys/posix/arpa/inet.d core/sys/posix/config.d \ - core/sys/posix/dirent.d core/sys/posix/dlfcn.d core/sys/posix/fcntl.d \ - core/sys/posix/grp.d core/sys/posix/iconv.d core/sys/posix/inttypes.d \ - core/sys/posix/libgen.d core/sys/posix/locale.d \ - core/sys/posix/mqueue.d core/sys/posix/net/if_.d \ - core/sys/posix/netdb.d core/sys/posix/netinet/in_.d \ - core/sys/posix/netinet/tcp.d core/sys/posix/poll.d \ - core/sys/posix/pthread.d core/sys/posix/pwd.d core/sys/posix/sched.d \ - core/sys/posix/semaphore.d core/sys/posix/setjmp.d \ - core/sys/posix/signal.d core/sys/posix/spawn.d \ + core/sys/posix/dirent.d core/sys/posix/dlfcn.d core/sys/posix/endian.d \ + core/sys/posix/fcntl.d core/sys/posix/grp.d core/sys/posix/iconv.d \ + core/sys/posix/inttypes.d core/sys/posix/libgen.d \ + core/sys/posix/locale.d core/sys/posix/mqueue.d \ + core/sys/posix/net/if_.d core/sys/posix/netdb.d \ + core/sys/posix/netinet/in_.d core/sys/posix/netinet/tcp.d \ + core/sys/posix/poll.d core/sys/posix/pthread.d core/sys/posix/pwd.d \ + core/sys/posix/sched.d core/sys/posix/semaphore.d \ + core/sys/posix/setjmp.d core/sys/posix/signal.d core/sys/posix/spawn.d \ core/sys/posix/stdc/time.d core/sys/posix/stdio.d \ core/sys/posix/stdlib.d core/sys/posix/string.d \ core/sys/posix/strings.d core/sys/posix/sys/filio.d \ diff --git a/libphobos/libdruntime/Makefile.in b/libphobos/libdruntime/Makefile.in index 8543c6e53ed..bf86bc0e1ae 100644 --- a/libphobos/libdruntime/Makefile.in +++ b/libphobos/libdruntime/Makefile.in @@ -205,7 +205,7 @@ am__objects_1 = core/atomic.lo core/attribute.lo core/bitop.lo \ core/internal/array/equality.lo \ core/internal/array/operations.lo core/internal/array/utils.lo \ core/internal/atomic.lo core/internal/attributes.lo \ - core/internal/container/array.lo \ + core/internal/cast_.lo core/internal/container/array.lo \ core/internal/container/common.lo \ core/internal/container/hashtab.lo \ core/internal/container/treap.lo core/internal/convert.lo \ @@ -248,12 +248,11 @@ am__objects_1 = core/atomic.lo core/attribute.lo core/bitop.lo \ gcc/sections/pecoff.lo gcc/simd.lo gcc/unwind/arm.lo \ gcc/unwind/arm_common.lo gcc/unwind/c6x.lo \ gcc/unwind/generic.lo gcc/unwind/package.lo gcc/unwind/pe.lo \ - object.lo rt/aApply.lo rt/aApplyR.lo rt/aaA.lo rt/adi.lo \ - rt/arraycat.lo rt/cast_.lo rt/config.lo rt/critical_.lo \ - rt/deh.lo rt/dmain2.lo rt/ehalloc.lo rt/invariant_.lo \ - rt/lifetime.lo rt/memory.lo rt/minfo.lo rt/monitor_.lo \ - rt/profilegc.lo rt/sections.lo rt/tlsgc.lo rt/util/typeinfo.lo \ - rt/util/utility.lo + object.lo rt/aApply.lo rt/aApplyR.lo rt/arraycat.lo \ + rt/config.lo rt/critical_.lo rt/deh.lo rt/dmain2.lo \ + rt/ehalloc.lo rt/invariant_.lo rt/lifetime.lo rt/memory.lo \ + rt/minfo.lo rt/monitor_.lo rt/profilegc.lo rt/sections.lo \ + rt/tlsgc.lo rt/util/typeinfo.lo rt/util/utility.lo am__objects_2 = core/stdc/libgdruntime_la-errno_.lo \ etc/valgrind/libgdruntime_la-valgrind_.lo am__objects_3 = core/sys/elf/package.lo @@ -265,31 +264,32 @@ am__objects_4 = core/stdcpp/allocator.lo core/stdcpp/array.lo \ core/stdcpp/vector.lo core/stdcpp/xutility.lo am__objects_5 = core/sys/posix/aio.lo core/sys/posix/arpa/inet.lo \ core/sys/posix/config.lo core/sys/posix/dirent.lo \ - core/sys/posix/dlfcn.lo core/sys/posix/fcntl.lo \ - core/sys/posix/grp.lo core/sys/posix/iconv.lo \ - core/sys/posix/inttypes.lo core/sys/posix/libgen.lo \ - core/sys/posix/locale.lo core/sys/posix/mqueue.lo \ - core/sys/posix/net/if_.lo core/sys/posix/netdb.lo \ - core/sys/posix/netinet/in_.lo core/sys/posix/netinet/tcp.lo \ - core/sys/posix/poll.lo core/sys/posix/pthread.lo \ - core/sys/posix/pwd.lo core/sys/posix/sched.lo \ - core/sys/posix/semaphore.lo core/sys/posix/setjmp.lo \ - core/sys/posix/signal.lo core/sys/posix/spawn.lo \ - core/sys/posix/stdc/time.lo core/sys/posix/stdio.lo \ - core/sys/posix/stdlib.lo core/sys/posix/string.lo \ - core/sys/posix/strings.lo core/sys/posix/sys/filio.lo \ - core/sys/posix/sys/ioccom.lo core/sys/posix/sys/ioctl.lo \ - core/sys/posix/sys/ipc.lo core/sys/posix/sys/mman.lo \ - core/sys/posix/sys/msg.lo core/sys/posix/sys/resource.lo \ - core/sys/posix/sys/select.lo core/sys/posix/sys/shm.lo \ - core/sys/posix/sys/socket.lo core/sys/posix/sys/stat.lo \ - core/sys/posix/sys/statvfs.lo core/sys/posix/sys/time.lo \ - core/sys/posix/sys/ttycom.lo core/sys/posix/sys/types.lo \ - core/sys/posix/sys/uio.lo core/sys/posix/sys/un.lo \ - core/sys/posix/sys/utsname.lo core/sys/posix/sys/wait.lo \ - core/sys/posix/syslog.lo core/sys/posix/termios.lo \ - core/sys/posix/time.lo core/sys/posix/ucontext.lo \ - core/sys/posix/unistd.lo core/sys/posix/utime.lo + core/sys/posix/dlfcn.lo core/sys/posix/endian.lo \ + core/sys/posix/fcntl.lo core/sys/posix/grp.lo \ + core/sys/posix/iconv.lo core/sys/posix/inttypes.lo \ + core/sys/posix/libgen.lo core/sys/posix/locale.lo \ + core/sys/posix/mqueue.lo core/sys/posix/net/if_.lo \ + core/sys/posix/netdb.lo core/sys/posix/netinet/in_.lo \ + core/sys/posix/netinet/tcp.lo core/sys/posix/poll.lo \ + core/sys/posix/pthread.lo core/sys/posix/pwd.lo \ + core/sys/posix/sched.lo core/sys/posix/semaphore.lo \ + core/sys/posix/setjmp.lo core/sys/posix/signal.lo \ + core/sys/posix/spawn.lo core/sys/posix/stdc/time.lo \ + core/sys/posix/stdio.lo core/sys/posix/stdlib.lo \ + core/sys/posix/string.lo core/sys/posix/strings.lo \ + core/sys/posix/sys/filio.lo core/sys/posix/sys/ioccom.lo \ + core/sys/posix/sys/ioctl.lo core/sys/posix/sys/ipc.lo \ + core/sys/posix/sys/mman.lo core/sys/posix/sys/msg.lo \ + core/sys/posix/sys/resource.lo core/sys/posix/sys/select.lo \ + core/sys/posix/sys/shm.lo core/sys/posix/sys/socket.lo \ + core/sys/posix/sys/stat.lo core/sys/posix/sys/statvfs.lo \ + core/sys/posix/sys/time.lo core/sys/posix/sys/ttycom.lo \ + core/sys/posix/sys/types.lo core/sys/posix/sys/uio.lo \ + core/sys/posix/sys/un.lo core/sys/posix/sys/utsname.lo \ + core/sys/posix/sys/wait.lo core/sys/posix/syslog.lo \ + core/sys/posix/termios.lo core/sys/posix/time.lo \ + core/sys/posix/ucontext.lo core/sys/posix/unistd.lo \ + core/sys/posix/utime.lo @DRUNTIME_OS_POSIX_TRUE@am__objects_6 = $(am__objects_5) am__objects_7 = core/sys/darwin/config.lo \ core/sys/darwin/crt_externs.lo core/sys/darwin/dlfcn.lo \ @@ -870,13 +870,13 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \ core/internal/array/construction.d core/internal/array/duplication.d \ core/internal/array/equality.d core/internal/array/operations.d \ core/internal/array/utils.d core/internal/atomic.d \ - core/internal/attributes.d core/internal/container/array.d \ - core/internal/container/common.d core/internal/container/hashtab.d \ - core/internal/container/treap.d core/internal/convert.d \ - core/internal/dassert.d core/internal/destruction.d \ - core/internal/entrypoint.d core/internal/gc/bits.d \ - core/internal/gc/blkcache.d core/internal/gc/blockmeta.d \ - core/internal/gc/impl/conservative/gc.d \ + core/internal/attributes.d core/internal/cast_.d \ + core/internal/container/array.d core/internal/container/common.d \ + core/internal/container/hashtab.d core/internal/container/treap.d \ + core/internal/convert.d core/internal/dassert.d \ + core/internal/destruction.d core/internal/entrypoint.d \ + core/internal/gc/bits.d core/internal/gc/blkcache.d \ + core/internal/gc/blockmeta.d core/internal/gc/impl/conservative/gc.d \ core/internal/gc/impl/manual/gc.d core/internal/gc/impl/proto/gc.d \ core/internal/gc/os.d core/internal/gc/pooltable.d \ core/internal/gc/proxy.d core/internal/hash.d core/internal/lifetime.d \ @@ -907,11 +907,10 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \ gcc/sections/macho.d gcc/sections/package.d gcc/sections/pecoff.d \ gcc/simd.d gcc/unwind/arm.d gcc/unwind/arm_common.d gcc/unwind/c6x.d \ gcc/unwind/generic.d gcc/unwind/package.d gcc/unwind/pe.d object.d \ - rt/aApply.d rt/aApplyR.d rt/aaA.d rt/adi.d rt/arraycat.d rt/cast_.d \ - rt/config.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/profilegc.d rt/sections.d rt/tlsgc.d rt/util/typeinfo.d \ - rt/util/utility.d + rt/aApply.d rt/aApplyR.d rt/arraycat.d rt/config.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/profilegc.d rt/sections.d \ + rt/tlsgc.d rt/util/typeinfo.d rt/util/utility.d DRUNTIME_DSOURCES_STDCXX = core/stdcpp/allocator.d core/stdcpp/array.d \ core/stdcpp/exception.d core/stdcpp/memory.d core/stdcpp/new_.d \ @@ -1009,15 +1008,15 @@ DRUNTIME_DSOURCES_OPENBSD = core/sys/openbsd/dlfcn.d \ DRUNTIME_DSOURCES_POSIX = core/sys/posix/aio.d \ core/sys/posix/arpa/inet.d core/sys/posix/config.d \ - core/sys/posix/dirent.d core/sys/posix/dlfcn.d core/sys/posix/fcntl.d \ - core/sys/posix/grp.d core/sys/posix/iconv.d core/sys/posix/inttypes.d \ - core/sys/posix/libgen.d core/sys/posix/locale.d \ - core/sys/posix/mqueue.d core/sys/posix/net/if_.d \ - core/sys/posix/netdb.d core/sys/posix/netinet/in_.d \ - core/sys/posix/netinet/tcp.d core/sys/posix/poll.d \ - core/sys/posix/pthread.d core/sys/posix/pwd.d core/sys/posix/sched.d \ - core/sys/posix/semaphore.d core/sys/posix/setjmp.d \ - core/sys/posix/signal.d core/sys/posix/spawn.d \ + core/sys/posix/dirent.d core/sys/posix/dlfcn.d core/sys/posix/endian.d \ + core/sys/posix/fcntl.d core/sys/posix/grp.d core/sys/posix/iconv.d \ + core/sys/posix/inttypes.d core/sys/posix/libgen.d \ + core/sys/posix/locale.d core/sys/posix/mqueue.d \ + core/sys/posix/net/if_.d core/sys/posix/netdb.d \ + core/sys/posix/netinet/in_.d core/sys/posix/netinet/tcp.d \ + core/sys/posix/poll.d core/sys/posix/pthread.d core/sys/posix/pwd.d \ + core/sys/posix/sched.d core/sys/posix/semaphore.d \ + core/sys/posix/setjmp.d core/sys/posix/signal.d core/sys/posix/spawn.d \ core/sys/posix/stdc/time.d core/sys/posix/stdio.d \ core/sys/posix/stdlib.d core/sys/posix/string.d \ core/sys/posix/strings.d core/sys/posix/sys/filio.d \ @@ -1257,6 +1256,7 @@ core/internal/array/operations.lo: \ core/internal/array/utils.lo: core/internal/array/$(am__dirstamp) core/internal/atomic.lo: core/internal/$(am__dirstamp) core/internal/attributes.lo: core/internal/$(am__dirstamp) +core/internal/cast_.lo: core/internal/$(am__dirstamp) core/internal/container/$(am__dirstamp): @$(MKDIR_P) core/internal/container @: > core/internal/container/$(am__dirstamp) @@ -1411,10 +1411,7 @@ rt/$(am__dirstamp): @: > rt/$(am__dirstamp) rt/aApply.lo: rt/$(am__dirstamp) rt/aApplyR.lo: rt/$(am__dirstamp) -rt/aaA.lo: rt/$(am__dirstamp) -rt/adi.lo: rt/$(am__dirstamp) rt/arraycat.lo: rt/$(am__dirstamp) -rt/cast_.lo: rt/$(am__dirstamp) rt/config.lo: rt/$(am__dirstamp) rt/critical_.lo: rt/$(am__dirstamp) rt/deh.lo: rt/$(am__dirstamp) @@ -1466,6 +1463,7 @@ core/sys/posix/arpa/inet.lo: core/sys/posix/arpa/$(am__dirstamp) core/sys/posix/config.lo: core/sys/posix/$(am__dirstamp) core/sys/posix/dirent.lo: core/sys/posix/$(am__dirstamp) core/sys/posix/dlfcn.lo: core/sys/posix/$(am__dirstamp) +core/sys/posix/endian.lo: core/sys/posix/$(am__dirstamp) core/sys/posix/fcntl.lo: core/sys/posix/$(am__dirstamp) core/sys/posix/grp.lo: core/sys/posix/$(am__dirstamp) core/sys/posix/iconv.lo: core/sys/posix/$(am__dirstamp) diff --git a/libphobos/libdruntime/core/atomic.d b/libphobos/libdruntime/core/atomic.d index 899e0b054ba..7e97b9d53fa 100644 --- a/libphobos/libdruntime/core/atomic.d +++ b/libphobos/libdruntime/core/atomic.d @@ -154,7 +154,7 @@ void atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref shared T val, auto { static assert (is (V : T), "Can't assign `newval` of type `shared " ~ V.stringof ~ "` to `shared " ~ T.stringof ~ "`."); - core.internal.atomic.atomicStore!ms(cast(T*)&val, cast(V)newval); + core.internal.atomic.atomicStore!ms(cast(T*)&val, *cast(V*)&newval); } /** diff --git a/libphobos/libdruntime/core/checkedint.d b/libphobos/libdruntime/core/checkedint.d index 4c40b9957a9..5b305c759a2 100644 --- a/libphobos/libdruntime/core/checkedint.d +++ b/libphobos/libdruntime/core/checkedint.d @@ -757,9 +757,31 @@ unittest * Returns: * the product */ -pragma(inline, true) +// pragma(inline, true) uint mulu()(uint x, uint y, ref bool overflow) { + version (D_InlineAsm_X86) enum useAsm = true; + else version (D_InlineAsm_X86_64) enum useAsm = true; + else enum useAsm = false; + + static if (useAsm) + { + if (!__ctfe) + { + uint r; + bool o; + asm pure nothrow @nogc @trusted + { + mov EAX, x; + mul y; // EDX:EAX = EAX * y + mov r, EAX; + setc o; + } + overflow |= o; + return r; + } + } + immutable ulong r = ulong(x) * ulong(y); if (r >> 32) overflow = true; @@ -791,6 +813,12 @@ unittest pragma(inline, true) ulong mulu()(ulong x, uint y, ref bool overflow) { + version (D_InlineAsm_X86_64) + { + if (!__ctfe) + return mulu(x, ulong(y), overflow); + } + ulong r = x * y; if (x >> 32 && r / x != y) @@ -799,9 +827,27 @@ ulong mulu()(ulong x, uint y, ref bool overflow) } /// ditto -pragma(inline, true) +// pragma(inline, true) ulong mulu()(ulong x, ulong y, ref bool overflow) { + version (D_InlineAsm_X86_64) + { + if (!__ctfe) + { + ulong r; + bool o; + asm pure nothrow @nogc @trusted + { + mov RAX, x; + mul y; // RDX:RAX = RAX * y + mov r, RAX; + setc o; + } + overflow |= o; + return r; + } + } + immutable ulong r = x * y; if ((x | y) >> 32 && x && diff --git a/libphobos/libdruntime/core/gc/config.d b/libphobos/libdruntime/core/gc/config.d index af077be77bc..32de5ab1409 100644 --- a/libphobos/libdruntime/core/gc/config.d +++ b/libphobos/libdruntime/core/gc/config.d @@ -12,6 +12,8 @@ import core.stdc.stdio : printf; __gshared Config config; +private __gshared bool _initialized; + struct Config { bool disable; // start disabled @@ -31,7 +33,9 @@ struct Config bool initialize() { - return initConfigOptions(this, "gcopt"); + if (!_initialized) + _initialized = initConfigOptions(this, "gcopt"); + return _initialized; } void help() @nogc nothrow diff --git a/libphobos/libdruntime/core/gc/gcinterface.d b/libphobos/libdruntime/core/gc/gcinterface.d index c4e78bae782..6cab62ef995 100644 --- a/libphobos/libdruntime/core/gc/gcinterface.d +++ b/libphobos/libdruntime/core/gc/gcinterface.d @@ -13,6 +13,8 @@ */ module core.gc.gcinterface; +import core.thread.threadbase : ThreadBase; + static import core.memory; alias BlkAttr = core.memory.GC.BlkAttr; alias BlkInfo = core.memory.GC.BlkInfo; @@ -270,4 +272,32 @@ interface GC * Returns: true if successful. */ bool shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic = false) nothrow; + + /** + * Prepare a thread for use with the GC after the GC is initialized. Note + * that you can register an initThread function to call before the GC is + * initialized, see core.gc.registry. + * + * This function is always called *from* the thread as it is being started, + * before any static initializers. The thread object parameter may not yet + * be registered as `ThreadBase.getThis()`, as this is called before that + * happens. + */ + void initThread(ThreadBase thread) nothrow @nogc; + + /** + * Clean up any GC related data from the thread before it exits. There is + * no equivalent of this function as a hook before the GC is initialized. + * That is, the thread init function called before the GC is initialized + * (from the registry hook) should assume this function may not be called + * on termination if the GC is never initialized. + * + * Most times, this is called from the thread that is the given thread + * reference, but it's possible the `thread` parameter is not the same as + * the current thread. + * + * There is no guarantee the thread is still registered as + * `ThreadBase.getThis()`. + */ + void cleanupThread(ThreadBase thread) nothrow @nogc; } diff --git a/libphobos/libdruntime/core/gc/registry.d b/libphobos/libdruntime/core/gc/registry.d index da2dcff1e7d..2e6edf09c3e 100644 --- a/libphobos/libdruntime/core/gc/registry.d +++ b/libphobos/libdruntime/core/gc/registry.d @@ -8,6 +8,7 @@ module core.gc.registry; import core.gc.gcinterface : GC; +import core.thread.threadbase : ThreadBase; /*@nogc nothrow:*/ @@ -21,6 +22,13 @@ import core.gc.gcinterface : GC; */ alias GCFactory = GC function(); +/** + * A function that will initialize a thread before the GC has been initialized. + * Once the GC is initialized, the interface method GC.initThread for each new + * thread. + */ +alias GCThreadInitFunction = void function(ThreadBase base) nothrow @nogc; + /** * Register a GC factory under the given `name`. This function must be called * from a C constructor before druntime is initialized. @@ -28,21 +36,35 @@ alias GCFactory = GC function(); * To use the registered GC, it's name must be specified gcopt runtime option, * e.g. by passing $(TT, --DRT-gcopt=gc:my_gc_name) as application argument. * + * The thread init function will be called *only before* the GC has been + * initialized to the registered GC. It is called as the first step in starting + * the new thread before the thread is registered with the runtime as a running + * thread. This allows any specific thread data that is needed for running the + * GC to be registered with the thread object. + * + * After the GC is initialized, the GC interface function `initThread` is + * called instead. This function should expect the possibility that the + * reciprocal `cleanupThread` method may not be called if the GC is never + * initialized. + * * Params: * name = name of the GC implementation; should be unique * factory = function to instantiate the implementation + * threadInit = function to call from a new thread *before* registration in + * the list of running threads, to set up any GC-specific data. * Note: The registry does not perform synchronization, as registration is * assumed to be executed serially, as is the case for C constructors. * See_Also: * $(LINK2 https://dlang.org/spec/garbage.html#gc_config, Configuring the Garbage Collector) */ -void registerGCFactory(string name, GCFactory factory) nothrow @nogc +void registerGCFactory(string name, GCFactory factory, + GCThreadInitFunction threadInit = null) nothrow @nogc { import core.stdc.stdlib : realloc; auto ptr = cast(Entry*)realloc(entries.ptr, (entries.length + 1) * Entry.sizeof); entries = ptr[0 .. entries.length + 1]; - entries[$ - 1] = Entry(name, factory); + entries[$ - 1] = Entry(name, factory, threadInit); } /** @@ -70,6 +92,22 @@ GC createGCInstance(string name) return null; } +/** + * Get the thread init function used for the selected GC. + * + * Note, this must be called before `createGCInstance`. It is typically called by + * `rt_init`. + */ +GCThreadInitFunction threadInit(string name) nothrow @nogc +{ + foreach (entry; entries) + { + if (entry.name == name) + return entry.threadInit; + } + return null; +} + // list of all registerd GCs const(Entry[]) registeredGCFactories(scope int dummy=0) nothrow @nogc { @@ -82,6 +120,7 @@ struct Entry { string name; GCFactory factory; + GCThreadInitFunction threadInit; } __gshared Entry[] entries; diff --git a/libphobos/libdruntime/core/internal/array/appending.d b/libphobos/libdruntime/core/internal/array/appending.d index 1b2b78ea570..e5c57e67da4 100644 --- a/libphobos/libdruntime/core/internal/array/appending.d +++ b/libphobos/libdruntime/core/internal/array/appending.d @@ -9,8 +9,11 @@ */ module core.internal.array.appending; -/// See $(REF _d_arrayappendcTX, rt,lifetime,_d_arrayappendcTX) -private extern (C) byte[] _d_arrayappendcTX(const TypeInfo ti, ref return scope byte[] px, size_t n) @trusted pure nothrow; +private extern (C) +{ + bool gc_expandArrayUsed(void[] slice, size_t newUsed, bool atomic) pure nothrow; + bool gc_shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic) pure nothrow; +} private enum isCopyingNothrow(T) = __traits(compiles, (ref T rhs) nothrow { T lhs = rhs; }); @@ -25,22 +28,83 @@ private enum isCopyingNothrow(T) = __traits(compiles, (ref T rhs) nothrow { T lh * Bugs: * This function template was ported from a much older runtime hook that bypassed safety, * purity, and throwabilty checks. To prevent breaking existing code, this function template - * is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations. + * is temporarily declared `@trusted` until the implementation can be brought up to modern D expectations. */ ref Tarr _d_arrayappendcTX(Tarr : T[], T)(return ref scope Tarr px, size_t n) @trusted { - // needed for CTFE: https://github.com/dlang/druntime/pull/3870#issuecomment-1178800718 + import core.internal.traits: Unqual; + + alias Unqual_T = Unqual!T; + alias Unqual_Tarr = Unqual_T[]; + enum isshared = is(T == shared); + auto unqual_px = cast(Unqual_Tarr) px; + + // Ignoring additional attributes allows reusing the same generated code + px = cast(Tarr)_d_arrayappendcTX_(unqual_px, n, isshared); + return px; +} + +private ref Tarr _d_arrayappendcTX_(Tarr : T[], T)(return ref scope Tarr px, size_t n, bool isshared) @trusted +{ version (DigitalMars) pragma(inline, false); version (D_TypeInfo) { - auto ti = typeid(Tarr); + // Short circuit if no data is being appended. + if (n == 0) + return px; + + import core.stdc.string : memcpy, memset; + import core.internal.lifetime : __doPostblit; + import core.internal.array.utils: __arrayAlloc, newCapacity, __typeAttrs; + import core.internal.gc.blockmeta : PAGESIZE; + import core.exception: onOutOfMemoryError; + import core.memory: GC; + + alias BlkAttr = GC.BlkAttr; + + enum sizeelem = T.sizeof; + auto length = px.length; + auto newlength = length + n; + auto newsize = newlength * sizeelem; + auto size = length * sizeelem; + + if (!gc_expandArrayUsed(px, newsize, isshared)) + { + // could not set the size, we must reallocate. + auto newcap = newCapacity(newlength, sizeelem); + auto attrs = __typeAttrs!T(cast(void*)px.ptr) | BlkAttr.APPENDABLE; + + T* ptr = cast(T*)GC.malloc(newcap, attrs, typeid(T)); + if (ptr is null) + { + onOutOfMemoryError(); + assert(0); + } + + if (newsize != newcap) + { + // For small blocks that are always fully scanned, if we allocated more + // capacity than was requested, we are responsible for zeroing that + // memory. + // TODO: should let the GC figure this out, as this property may + // not always hold. + if (!(attrs & BlkAttr.NO_SCAN) && newcap < PAGESIZE) + memset(ptr + newlength, 0, newcap - newsize); + + gc_shrinkArrayUsed(ptr[0 .. newlength], newcap, isshared); + } + + memcpy(ptr, px.ptr, size); - // _d_arrayappendcTX takes the `px` as a ref byte[], but its length - // should still be the original length - auto pxx = (cast(byte*)px.ptr)[0 .. px.length]; - ._d_arrayappendcTX(ti, pxx, n); - px = (cast(T*)pxx.ptr)[0 .. pxx.length]; + // do potsblit processing. + __doPostblit!T(ptr[0 .. length]); + px = ptr[0 .. newlength]; + return px; + } + + // we were able to expand in place, just update the length + px = px.ptr[0 .. newlength]; return px; } else @@ -50,9 +114,10 @@ ref Tarr _d_arrayappendcTX(Tarr : T[], T)(return ref scope Tarr px, size_t n) @t version (D_ProfileGC) { /** - * TraceGC wrapper around $(REF _d_arrayappendT, core,internal,array,appending). + * TraceGC wrapper around _d_arrayappendcTX. */ - ref Tarr _d_arrayappendcTXTrace(Tarr : T[], T)(return ref scope Tarr px, size_t n, string file = __FILE__, int line = __LINE__, string funcname = __FUNCTION__) @trusted + ref Tarr _d_arrayappendcTXTrace(Tarr : T[], T)(return ref scope Tarr px, size_t n, + string file = __FILE__, int line = __LINE__, string funcname = __FUNCTION__) @trusted { version (D_TypeInfo) { @@ -72,39 +137,61 @@ ref Tarr _d_arrayappendT(Tarr : T[], T)(return ref scope Tarr x, scope Tarr y) @ version (DigitalMars) pragma(inline, false); import core.stdc.string : memcpy; - import core.internal.traits : hasElaborateCopyConstructor, Unqual; + import core.internal.traits : Unqual; - enum hasPostblit = __traits(hasPostblit, T); auto length = x.length; _d_arrayappendcTX(x, y.length); // Only call `copyEmplace` if `T` has a copy ctor and no postblit. - static if (hasElaborateCopyConstructor!T && !hasPostblit) + static if (__traits(hasCopyConstructor, T)) { import core.lifetime : copyEmplace; - foreach (i, ref elem; y) - copyEmplace(elem, x[length + i]); + size_t i; + try + { + for (i = 0; i < y.length; ++i) + copyEmplace(y[i], x[length + i]); + } + catch (Exception o) + { + /* Destroy, in reverse order, what we've constructed so far + */ + while (i--) + { + auto elem = cast(Unqual!T*) &x[length + i]; + destroy(*elem); + } + + throw o; + } } else { if (y.length) { // blit all elements at once - auto xptr = cast(Unqual!T *)&x[length]; - immutable size = T.sizeof; - - memcpy(xptr, cast(Unqual!T *)&y[0], y.length * size); + memcpy(cast(void*)&x[length], cast(void*)&y[0], y.length * T.sizeof); // call postblits if they exist - static if (hasPostblit) + static if (__traits(hasPostblit, T)) { - auto eptr = xptr + y.length; - for (auto ptr = xptr; ptr < eptr; ptr++) - ptr.__xpostblit(); - } - } + import core.internal.lifetime : __doPostblit; + size_t i = 0; + try __doPostblit(x[length .. $], i); + catch (Exception o) + { + // Destroy, in reverse order, what we've constructed so far + while (i--) + { + auto elem = cast(Unqual!T*) &x[length + i]; + destroy(*elem); + } + + throw o; + } + }} } return x; @@ -205,3 +292,66 @@ version (D_ProfileGC) _d_arrayappendT(str, "c"); assert(str == "abc"); } + +@safe nothrow unittest +{ + static class FailedPostblitException : Exception { this() nothrow @safe { super(null); } } + static size_t inner_postblit_cnt = 0; + static size_t inner_dtor_cnt = 0; + static size_t outer_postblit_cnt = 0; + static size_t outer_dtor_cnt = 0; + static struct Inner + { + char id; + + @safe: + this(this) + { + ++inner_postblit_cnt; + if (id == '2') + throw new FailedPostblitException(); + } + + ~this() nothrow + { + ++inner_dtor_cnt; + } + } + + static struct Outer + { + Inner inner1, inner2, inner3; + + nothrow @safe: + this(char first, char second, char third) + { + inner1 = Inner(first); + inner2 = Inner(second); + inner3 = Inner(third); + } + + this(this) + { + ++outer_postblit_cnt; + } + + ~this() + { + ++outer_dtor_cnt; + } + } + + Outer[3] arr = [Outer('1', '1', '1'), Outer('1', '2', '3'), Outer('3', '3', '3')]; + + try { + Outer[] arrApp; + arrApp ~= arr; + } + catch (FailedPostblitException) {} + catch (Exception) assert(false); + + assert(inner_postblit_cnt == 5); + assert(inner_dtor_cnt == 4); + assert(outer_postblit_cnt == 1); + assert(outer_dtor_cnt == 1); +} diff --git a/libphobos/libdruntime/core/internal/array/capacity.d b/libphobos/libdruntime/core/internal/array/capacity.d index 11bd357d7c2..42e6d44ec32 100644 --- a/libphobos/libdruntime/core/internal/array/capacity.d +++ b/libphobos/libdruntime/core/internal/array/capacity.d @@ -17,7 +17,65 @@ extern(C) { bool gc_expandArrayUsed(void[] slice, size_t newUsed, bool atomic) nothrow pure; size_t gc_reserveArrayCapacity(void[] slice, size_t request, bool atomic) nothrow pure; bool gc_shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic) nothrow pure; + void[] gc_getArrayUsed(void *ptr, bool atomic) nothrow pure; } + +/** +Shrink the "allocated" length of an array to be the exact size of the array. + +It doesn't matter what the current allocated length of the array is, the +user is telling the runtime that he knows what he is doing. + +Params: + T = the type of the elements in the array (this should be unqualified) + arr = array to shrink. Its `.length` is element length, not byte length, despite `void` type + isshared = true if the underlying data is shared +*/ +void _d_arrayshrinkfit(Tarr: T[], T)(Tarr arr, bool isshared) @trusted +{ + import core.exception : onFinalizeError; + import core.internal.traits: hasElaborateDestructor; + + debug(PRINTF) printf("_d_arrayshrinkfit, elemsize = %zd, arr.ptr = %p arr.length = %zd\n", T.sizeof, arr.ptr, arr.length); + auto reqlen = arr.length; + + auto curArr = cast(Tarr)gc_getArrayUsed(arr.ptr, isshared); + if (curArr.ptr is null) + // not a valid GC pointer + return; + + // align the array. + auto offset = arr.ptr - curArr.ptr; + auto curlen = curArr.length - offset; + if (curlen <= reqlen) + // invalid situation, or no change. + return; + + // if the type has a destructor, destroy elements we are about to remove. + static if(is(T == struct) && hasElaborateDestructor!T) + { + try + { + // Finalize the elements that are being removed + + // Due to the fact that the delete operator calls destructors + // for arrays from the last element to the first, we maintain + // compatibility here by doing the same. + for (auto curP = arr.ptr + curlen - 1; curP >= arr.ptr + reqlen; curP--) + { + // call destructor + curP.__xdtor(); + } + } + catch (Exception e) + { + onFinalizeError(typeid(T), e); + } + } + + gc_shrinkArrayUsed(arr[0 .. reqlen], curlen * T.sizeof, isshared); +} + /** Set the array capacity. @@ -60,6 +118,7 @@ do alias BlkAttr = GC.BlkAttr; auto size = T.sizeof; + bool overflow = false; const reqsize = mulu(size, newcapacity, overflow); if (overflow) diff --git a/libphobos/libdruntime/core/internal/array/construction.d b/libphobos/libdruntime/core/internal/array/construction.d index 76811ef3ffd..b4012d2f1fa 100644 --- a/libphobos/libdruntime/core/internal/array/construction.d +++ b/libphobos/libdruntime/core/internal/array/construction.d @@ -65,7 +65,7 @@ Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from, char* ma enforceRawArraysConformable("initialization", T.sizeof, vFrom, vTo); - static if (hasElaborateCopyConstructor!T) + static if (__traits(hasCopyConstructor, T)) { size_t i; try @@ -88,8 +88,30 @@ Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from, char* ma } else { - // blit all elements at once - memcpy(cast(void*) to.ptr, from.ptr, to.length * T.sizeof); + if (to.length) + { + // blit all elements at once + memcpy(cast(void*) to.ptr, from.ptr, to.length * T.sizeof); + + // call postblits if they exist + static if (__traits(hasPostblit, T)) + { + import core.internal.lifetime : __doPostblit; + size_t i = 0; + try __doPostblit(to, i); + catch (Exception o) + { + // Destroy, in reverse order, what we've constructed so far + while (i--) + { + auto elem = cast(Unqual!T*) &to[i]; + destroy(*elem); + } + + throw o; + } + } + } } return to; @@ -346,6 +368,7 @@ T[] _d_newarrayUPureNothrow(T)(size_t length, bool isShared=false) pure nothrow T[] _d_newarrayU(T)(size_t length, bool isShared=false) @trusted { + import core.checkedint : mulu; import core.exception : onOutOfMemoryError; import core.internal.traits : Unqual; import core.internal.array.utils : __arrayAlloc; @@ -353,52 +376,24 @@ T[] _d_newarrayU(T)(size_t length, bool isShared=false) @trusted alias UnqT = Unqual!T; size_t elemSize = T.sizeof; - size_t arraySize; debug(PRINTF) printf("_d_newarrayU(length = x%zu, size = %zu)\n", length, elemSize); if (length == 0 || elemSize == 0) return null; - version (D_InlineAsm_X86) + bool overflow = false; + const arraySize = mulu(elemSize, length, overflow); + if (!overflow) { - asm pure nothrow @nogc + if (auto arr = __arrayAlloc!UnqT(arraySize)) { - mov EAX, elemSize ; - mul EAX, length ; - mov arraySize, EAX ; - jnc Lcontinue ; + debug(PRINTF) printf("p = %p\n", arr.ptr); + return (cast(T*) arr.ptr)[0 .. length]; } } - else version (D_InlineAsm_X86_64) - { - asm pure nothrow @nogc - { - mov RAX, elemSize ; - mul RAX, length ; - mov arraySize, RAX ; - jnc Lcontinue ; - } - } - else - { - import core.checkedint : mulu; - bool overflow = false; - arraySize = mulu(elemSize, length, overflow); - if (!overflow) - goto Lcontinue; - } - -Loverflow: onOutOfMemoryError(); assert(0); - -Lcontinue: - auto arr = __arrayAlloc!UnqT(arraySize); - if (!arr.ptr) - goto Loverflow; - debug(PRINTF) printf("p = %p\n", arr.ptr); - return (cast(T*) arr.ptr)[0 .. length]; } /// ditto @@ -615,3 +610,66 @@ version (D_ProfileGC) assert(0, "Cannot create new multi-dimensional array if compiling without support for runtime type information!"); } } + + +/** +Allocate an array literal + +Rely on the caller to do the initialization of the array. + +--- +int[] getArr() +{ + return [10, 20]; + // auto res = cast(int*) _d_arrayliteralTX(typeid(int[]), 2); + // res[0] = 10; + // res[1] = 20; + // return res[0..2]; +} +--- + +Params: + T = unqualified type of array elements + length = `.length` of array literal + +Returns: pointer to allocated array +*/ +void* _d_arrayliteralTX(T)(size_t length) @trusted pure nothrow +{ + const allocsize = length * T.sizeof; + + if (allocsize == 0) + return null; + else + { + import core.memory : GC; + import core.internal.traits : hasIndirections, hasElaborateDestructor; + alias BlkAttr = GC.BlkAttr; + + /* Same as in core.internal.array.utils.__typeAttrs!T, + * but don't use a nested template function call here to avoid + * possible linking errors. + */ + uint attrs = BlkAttr.APPENDABLE; + static if (!hasIndirections!T) + attrs |= BlkAttr.NO_SCAN; + static if (is(T == struct) && hasElaborateDestructor!T) + attrs |= BlkAttr.FINALIZE; + + return GC.malloc(allocsize, attrs, typeid(T)); + } +} + +version (D_ProfileGC) +void* _d_arrayliteralTXTrace(T)(size_t length, string file = __FILE__, int line = __LINE__, string funcname = __FUNCTION__) @trusted pure nothrow +{ + version (D_TypeInfo) + { + import core.internal.array.utils : TraceHook, gcStatsPure, accumulatePure; + mixin(TraceHook!((T[]).stringof, "_d_arrayliteralTX")); + + return _d_arrayliteralTX!T(length); + } + else + assert(0); +} diff --git a/libphobos/libdruntime/core/internal/array/duplication.d b/libphobos/libdruntime/core/internal/array/duplication.d index b5222b3b175..a9b599cfc35 100644 --- a/libphobos/libdruntime/core/internal/array/duplication.d +++ b/libphobos/libdruntime/core/internal/array/duplication.d @@ -362,7 +362,7 @@ U[] _dup(T, U)(T[] a) if (!__traits(isPOD, T)) // https://issues.dlang.org/show_bug.cgi?id=24453 @safe unittest { - static inout(char)[] foo(ref scope return inout(char)[] s) + static inout(char)[] foo(return ref scope inout(char)[] s) { auto bla = s.idup; return s; diff --git a/libphobos/libdruntime/core/internal/array/equality.d b/libphobos/libdruntime/core/internal/array/equality.d index c110d64069f..e0a811bf072 100644 --- a/libphobos/libdruntime/core/internal/array/equality.d +++ b/libphobos/libdruntime/core/internal/array/equality.d @@ -14,41 +14,7 @@ module core.internal.array.equality; // * dynamic arrays, // * (most) arrays of different (unqualified) element types, and // * arrays of structs with custom opEquals. - - // The scalar-only overload takes advantage of known properties of scalars to - // reduce template instantiation. This is expected to be the most common case. -bool __equals(T1, T2)(scope const T1[] lhs, scope const T2[] rhs) -@nogc nothrow pure @trusted -if (__traits(isScalar, T1) && __traits(isScalar, T2)) -{ - const length = lhs.length; - - static if (T1.sizeof == T2.sizeof - // Signedness needs to match for types that promote to int. - // (Actually it would be okay to memcmp bool[] and byte[] but that is - // probably too uncommon to be worth checking for.) - && (T1.sizeof >= 4 || __traits(isUnsigned, T1) == __traits(isUnsigned, T2)) - && !__traits(isFloating, T1) && !__traits(isFloating, T2)) - { - if (__ctfe) - return length == rhs.length && isEqual(lhs.ptr, rhs.ptr, length); - else - { - // This would improperly allow equality of integers and pointers - // but the CTFE branch will stop this function from compiling then. - import core.stdc.string : memcmp; - return length == rhs.length && - (!length || 0 == memcmp(cast(const void*) lhs.ptr, cast(const void*) rhs.ptr, length * T1.sizeof)); - } - } - else - { - return length == rhs.length && isEqual(lhs.ptr, rhs.ptr, length); - } -} - -bool __equals(T1, T2)(scope T1[] lhs, scope T2[] rhs) -if (!__traits(isScalar, T1) || !__traits(isScalar, T2)) +bool __equals(T1, T2)(scope T1[] lhs, scope T2[] rhs) @trusted { if (lhs.length != rhs.length) return false; @@ -56,37 +22,9 @@ if (!__traits(isScalar, T1) || !__traits(isScalar, T2)) if (lhs.length == 0) return true; - static if (useMemcmp!(T1, T2)) - { - if (!__ctfe) - { - static bool trustedMemcmp(scope T1[] lhs, scope T2[] rhs) @trusted @nogc nothrow pure - { - pragma(inline, true); - import core.stdc.string : memcmp; - return memcmp(cast(void*) lhs.ptr, cast(void*) rhs.ptr, lhs.length * T1.sizeof) == 0; - } - return trustedMemcmp(lhs, rhs); - } - else - { - foreach (const i; 0 .. lhs.length) - { - if (at(lhs, i) != at(rhs, i)) - return false; - } - return true; - } - } - else - { - foreach (const i; 0 .. lhs.length) - { - if (at(lhs, i) != at(rhs, i)) - return false; - } - return true; - } + alias PureType = bool function(scope T1[], scope T2[], size_t) @safe pure nothrow @nogc; + + return (cast(PureType)&isEqual!(T1,T2))(lhs, rhs, lhs.length); } /****************************** @@ -94,11 +32,26 @@ if (!__traits(isScalar, T1) || !__traits(isScalar, T2)) * Outlined to enable __equals() to be inlined, as dmd cannot inline loops. */ private -bool isEqual(T1, T2)(scope const T1* t1, scope const T2* t2, size_t length) +bool isEqual(T1, T2)(scope T1[] lhs, scope T2[] rhs, size_t length) { + // Returns a reference to an array element, eliding bounds check and + // casting void to ubyte. + pragma(inline, true) + static ref at(T)(scope T[] r, size_t i) @trusted + // exclude opaque structs due to https://issues.dlang.org/show_bug.cgi?id=20959 + if (!(is(T == struct) && !is(typeof(T.sizeof)))) + { + static if (is(T == void)) + return (cast(ubyte[]) r)[i]; + else + return r[i]; + } + foreach (const i; 0 .. length) - if (t1[i] != t2[i]) + { + if (at(lhs, i) != at(rhs, i)) return false; + } return true; } @@ -173,69 +126,6 @@ bool isEqual(T1, T2)(scope const T1* t1, scope const T2* t2, size_t length) assert(a1 != a2); } - -private: - -// - Recursively folds static array types to their element type, -// - maps void to ubyte, and -// - pointers to size_t. -template BaseType(T) -{ - static if (__traits(isStaticArray, T)) - alias BaseType = BaseType!(typeof(T.init[0])); - else static if (is(immutable T == immutable void)) - alias BaseType = ubyte; - else static if (is(T == E*, E)) - alias BaseType = size_t; - else - alias BaseType = T; -} - -// Use memcmp if the element sizes match and both base element types are integral. -// Due to int promotion, disallow small integers of diverging signed-ness though. -template useMemcmp(T1, T2) -{ - static if (T1.sizeof != T2.sizeof) - enum useMemcmp = false; - else - { - alias B1 = BaseType!T1; - alias B2 = BaseType!T2; - enum useMemcmp = __traits(isIntegral, B1) && __traits(isIntegral, B2) - && !( (B1.sizeof < 4 || B2.sizeof < 4) && __traits(isUnsigned, B1) != __traits(isUnsigned, B2) ); - } -} - -unittest -{ - enum E { foo, bar } - - static assert(useMemcmp!(byte, byte)); - static assert(useMemcmp!(ubyte, ubyte)); - static assert(useMemcmp!(void, const void)); - static assert(useMemcmp!(void, immutable bool)); - static assert(useMemcmp!(void, inout char)); - static assert(useMemcmp!(void, shared ubyte)); - static assert(!useMemcmp!(void, byte)); // differing signed-ness - static assert(!useMemcmp!(char[8], byte[8])); // ditto - - static assert(useMemcmp!(short, short)); - static assert(useMemcmp!(wchar, ushort)); - static assert(!useMemcmp!(wchar, short)); // differing signed-ness - - static assert(useMemcmp!(int, uint)); // no promotion, ignoring signed-ness - static assert(useMemcmp!(dchar, E)); - - static assert(useMemcmp!(immutable void*, size_t)); - static assert(useMemcmp!(double*, ptrdiff_t)); - static assert(useMemcmp!(long[2][3], const(ulong)[2][3])); - - static assert(!useMemcmp!(float, float)); - static assert(!useMemcmp!(double[2], double[2])); - static assert(!useMemcmp!(Object, Object)); - static assert(!useMemcmp!(int[], int[])); -} - // https://issues.dlang.org/show_bug.cgi?id=21094 unittest { @@ -262,16 +152,3 @@ unittest return lhs == rhs; } } - -// Returns a reference to an array element, eliding bounds check and -// casting void to ubyte. -pragma(inline, true) -ref at(T)(T[] r, size_t i) @trusted - // exclude opaque structs due to https://issues.dlang.org/show_bug.cgi?id=20959 - if (!(is(T == struct) && !is(typeof(T.sizeof)))) -{ - static if (is(immutable T == immutable void)) - return (cast(ubyte*) r.ptr)[i]; - else - return r.ptr[i]; -} diff --git a/libphobos/libdruntime/core/internal/array/utils.d b/libphobos/libdruntime/core/internal/array/utils.d index 1d105e6ebf1..3d85e6277c9 100644 --- a/libphobos/libdruntime/core/internal/array/utils.d +++ b/libphobos/libdruntime/core/internal/array/utils.d @@ -157,6 +157,72 @@ void[] __arrayAlloc(T)(size_t arrSize) @trusted return null; } +/** +Given an array of length `size` that needs to be expanded to `newlength`, +compute a new capacity. + +Better version by Dave Fladebo, enhanced by Steven Schveighoffer: +This uses an inverse logorithmic algorithm to pre-allocate a bit more +space for larger arrays. +- The maximum "extra" space is about 80% of the requested space. This is for +PAGE size and smaller. +- As the arrays grow, the relative pre-allocated space shrinks. +- Perhaps most importantly, overall memory usage and stress on the GC +is decreased significantly for demanding environments. +- The algorithm is tuned to avoid any division at runtime. + +Params: + newlength = new `.length` + elemsize = size of the element in the new array +Returns: new capacity for array +*/ +size_t newCapacity(size_t newlength, size_t elemsize) pure nothrow +{ + size_t newcap = newlength * elemsize; + + /* + * Max growth factor numerator is 234, so allow for multiplying by 256. + * But also, the resulting size cannot be more than 2x, so prevent + * growing if 2x would fill up the address space (for 32-bit) + */ + enum largestAllowed = (ulong.max >> 8) & (size_t.max >> 1); + if (!newcap || (newcap & ~largestAllowed)) + return newcap; + + /* + * The calculation for "extra" space depends on the requested capacity. + * We use an inverse logarithm of the new capacity to add an extra 15% + * to 83% capacity. Note that normally we humans think in terms of + * percent, but using 128 instead of 100 for the denominator means we + * can avoid all division by simply bit-shifthing. Since there are only + * 64 bits in a long, the bsr of a size_t is going to be 0 - 63. Using + * a lookup table allows us to precalculate the multiplier based on the + * inverse logarithm. The formula rougly is: + * + * newcap = request * (1.0 + min(0.83, 10.0 / (log(request) + 1))) + */ + import core.bitop; + static immutable multTable = (){ + assert(__ctfe); + ulong[size_t.sizeof * 8] result; + foreach (i; 0 .. result.length) + { + auto factor = 128 + 1280 / (i + 1); + result[i] = factor > 234 ? 234 : factor; + } + return result; + }(); + + auto mult = multTable[bsr(newcap)]; + + // if this were per cent, then the code would look like: + // ((newlength * mult + 99) / 100) * elemsize + newcap = cast(size_t)((newlength * mult + 127) >> 7) * elemsize; + debug(PRINTF) printf("mult: %2.2f, alloc: %2.2f\n",mult/128.0,newcap / cast(double)elemsize); + debug(PRINTF) printf("newcap = %zd, newlength = %zd, elemsize = %zd\n", newcap, newlength, elemsize); + return newcap; +} + uint __typeAttrs(T)(void *copyAttrsFrom = null) { import core.internal.traits : hasIndirections, hasElaborateDestructor; diff --git a/libphobos/libdruntime/core/internal/cast_.d b/libphobos/libdruntime/core/internal/cast_.d new file mode 100644 index 00000000000..c4de76ea626 --- /dev/null +++ b/libphobos/libdruntime/core/internal/cast_.d @@ -0,0 +1,311 @@ +module core.internal.cast_; + +// Needed because ClassInfo.opEquals(Object) does a dynamic cast, +// but we are trying to implement dynamic cast. +bool areClassInfosEqual(scope const ClassInfo a, scope const ClassInfo b) pure nothrow @safe @nogc +{ + // same class if signatures match, works with potential duplicates across binaries + if (a is b) + return true; + + // new fast way + if (a.m_flags & TypeInfo_Class.ClassFlags.hasNameSig) + return a.nameSig[0] == b.nameSig[0] + && a.nameSig[1] == b.nameSig[1] + && a.nameSig[2] == b.nameSig[2] + && a.nameSig[3] == b.nameSig[3]; + + // old slow way for temporary binary compatibility + return a.name == b.name; +} + + +/***** + * Dynamic cast from a class object `o` to class or interface `To`, where `To` is a subtype of `o`. + * Params: + * o = instance of class + * To = class or interface that is a subtype of `o` + * Returns: + * null if `o` is null or `To` is not a subclass type of `o`. Otherwise, return `o`. + */ +private void* _d_dynamic_cast(To)(const return scope Object o) @trusted +{ + void* res = null; + size_t offset = 0; + + if (o && _d_isbaseof2!To(typeid(o), offset)) + { + res = cast(void*) o + offset; + } + return res; +} + +/** + * Dynamic cast `o` to final class `To` only one level down + * Params: + * o = object that is instance of a class + * To = final class that is a subclass type of `o` + * Returns: + * o if it succeeds, null if it fails + */ +private void* _d_paint_cast(To)(const return scope Object o) +{ + /* If o is really an instance of c, just do a paint + */ + auto p = o && cast(void*)(areClassInfosEqual(typeid(o), typeid(To).info)) ? o : null; + debug assert(cast(void*)p is cast(void*)_d_dynamic_cast!To(o)); + return cast(void*)p; +} + +private void* _d_class_cast_impl(const return scope Object o, const ClassInfo c) pure nothrow @safe @nogc +{ + if (!o) + return null; + + ClassInfo oc = typeid(o); + int delta = oc.depth; + + if (delta && c.depth) + { + delta -= c.depth; + if (delta < 0) + return null; + + while (delta--) + oc = oc.base; + if (areClassInfosEqual(oc, c)) + return cast(void*)o; + return null; + } + + // no depth data - support the old way + do + { + if (areClassInfosEqual(oc, c)) + return cast(void*)o; + oc = oc.base; + } while (oc); + return null; +} + +/***** + * Dynamic cast from a class object o to class type `To`, where `To` is a subclass type of `o`. + * Params: + * o = instance of class + * To = a subclass type of o + * Returns: + * null if `o` is null or `To` is not a subclass type of `o`. Otherwise, return `o`. + */ +private void* _d_class_cast(To)(const return scope Object o) +{ + return _d_class_cast_impl(o, typeid(To)); +} + +/************************************* + * Attempts to cast interface Object o to class type `To`. + * Returns o if successful, null if not. + */ +private void* _d_interface_cast(To)(void* p) @trusted +{ + if (!p) + return null; + + Interface* pi = **cast(Interface***) p; + + Object o2 = cast(Object)(p - pi.offset); + void* res = null; + size_t offset = 0; + if (o2 && _d_isbaseof2!To(typeid(o2), offset)) + { + res = cast(void*) o2 + offset; + } + return res; +} + +/** +* Hook that detects the type of cast performed and calls the appropriate function. +* Params: +* o = object that is being casted +* To = type to which the object is being casted +* Returns: +* null if the cast fails, otherwise returns the object casted to the type `To`. +*/ +void* _d_cast(To, From)(From o) @trusted +{ + static if (is(From == class) && is(To == interface)) + { + return _d_dynamic_cast!To(o); + } + + static if (is(From == class) && is(To == class)) + { + static if (is(From FromSupers == super) && is(To ToSupers == super)) + { + /* Check for: + * class A { } + * final class B : A { } + * ... cast(B) A ... + */ + // Multiple inheritance is not allowed, so we can safely assume + // that the second super can only be an interface. + static if (__traits(isFinalClass, To) && is(ToSupers[0] == From) && + ToSupers.length == 1 && FromSupers.length <= 1) + { + return _d_paint_cast!To(o); + } + } + + static if (is (To : From)) + { + static if (is (To == From)) + { + return cast(void*)o; + } + else + { + return _d_class_cast!To(o); + } + } + + return null; + } + + static if (is(From == interface)) + { + static if (is(From == To)) + { + return cast(void*)o; + } + else + { + return _d_interface_cast!To(cast(void*)o); + } + } + else + { + return null; + } +} + +private bool _d_isbaseof2(To)(scope ClassInfo oc, scope ref size_t offset) +{ + auto c = typeid(To).info; + + if (areClassInfosEqual(oc, c)) + return true; + + do + { + if (oc.base && areClassInfosEqual(oc.base, c)) + return true; + + // Bugzilla 2013: Use depth-first search to calculate offset + // from the derived (oc) to the base (c). + foreach (iface; oc.interfaces) + { + if (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof2!To(iface.classinfo, offset)) + { + offset += iface.offset; + return true; + } + } + + oc = oc.base; + } while (oc); + + return false; +} + +@safe pure unittest +{ + interface I {} + + class A {} + class B : A {} + class C : B, I{} + + A ac = new C(); + assert(_d_cast!I(ac) !is null); // A(c) to I + assert(_d_dynamic_cast!I(ac) !is null); + + A ab = new B(); + assert(_d_cast!I(ab) is null); // A(b) to I + assert(_d_dynamic_cast!I(ab) is null); +} + +@safe pure unittest +{ + class A {} + class B : A {} + class C : B {} + final class D : C {} + + C cd = new D(); + assert(_d_cast!D(cd) !is null); // C(d) to D + assert(_d_paint_cast!D(cd) !is null); + + class G {} + final class J {} + A a = new A(); + assert(_d_cast!G(a) is null); // A(a) to G + assert(_d_paint_cast!G(a) is null); + + assert(_d_cast!J(a) is null); // A(a) to J + assert(_d_paint_cast!J(a) is null); +} + +@safe pure unittest +{ + class A {} + class B : A {} + class C : B {} + class D {} + + A ac = new C(); + assert(_d_cast!C(ac) !is null); // A(c) to C + assert(_d_class_cast!C(ac) !is null); + + assert(_d_cast!B(ac) !is null); // A(c) to B + assert(_d_class_cast!B(ac) !is null); + + A ab = new B(); + assert(_d_cast!C(ab) is null); // A(b) to C + assert(_d_class_cast!C(ab) is null); + + A a = new A(); + assert(_d_cast!D(a) is null); // A(a) to D + assert(_d_class_cast!D(a) is null); +} + +@safe pure unittest +{ + interface I1 {} + interface I2 {} + interface I3 {} + class A {} + class B : A, I1, I2 {} + class C : B, I3 {} + + I1 bi = new B(); + assert(_d_cast!I2(bi) !is null); // I1(b) to I2 + assert(_d_interface_cast!I2(cast(void*)bi) !is null); + + assert(_d_cast!A(bi) !is null); // I1(b) to A + assert(_d_interface_cast!A(cast(void*)bi) !is null); + + assert(_d_cast!B(bi) !is null); // I1(b) to B + assert(_d_interface_cast!B(cast(void*)bi) !is null); + + assert(_d_cast!I3(bi) is null); // I1(b) to I3 + assert(_d_interface_cast!I3(cast(void*)bi) is null); + + assert(_d_cast!C(bi) is null); // I1(b) to C + assert(_d_interface_cast!C(cast(void*)bi) is null); + + assert(_d_cast!I1(bi) !is null); // I1(b) to I1 + assert(_d_interface_cast!I1(cast(void*)bi) !is null); + + I3 ci = new C(); + assert(_d_cast!I1(ci) !is null); // I3(c) to I1 + assert(_d_interface_cast!I1(cast(void*)ci) !is null); +} diff --git a/libphobos/libdruntime/core/internal/convert.d b/libphobos/libdruntime/core/internal/convert.d index 7bac8359a77..120fbdfcfe6 100644 --- a/libphobos/libdruntime/core/internal/convert.d +++ b/libphobos/libdruntime/core/internal/convert.d @@ -647,6 +647,7 @@ package template floatSize(T) if (is(T:real) || is(T:ireal)) @trusted pure nothrow @nogc const(ubyte)[] toUbyte(T)(return scope const T[] arr) if (T.sizeof == 1) { + pragma(inline, true); return cast(const(ubyte)[])arr; } @@ -687,6 +688,7 @@ const(ubyte)[] toUbyte(T)(const ref scope T val) if (__traits(isIntegral, T) && { static if (T.sizeof == 1) { + pragma(inline, true); if (__ctfe) { ubyte[] result = ctfe_alloc(1); @@ -722,6 +724,7 @@ const(ubyte)[] toUbyte(T)(const ref scope T val) if (__traits(isIntegral, T) && @trusted pure nothrow @nogc const(ubyte)[] toUbyte(T)(const ref scope T val) if (is(T == __vector)) { + pragma(inline, true); if (!__ctfe) return (cast(const ubyte*) &val)[0 .. T.sizeof]; else static if (is(typeof(val[0]) : void)) @@ -744,6 +747,7 @@ const(ubyte)[] toUbyte(T)(const ref scope T val) if (is(T == __vector)) @trusted pure nothrow @nogc const(ubyte)[] toUbyte(T)(const ref return scope T val) if (is(T == enum)) { + pragma(inline, true); if (__ctfe) { static if (is(T V == enum)){} @@ -767,6 +771,7 @@ nothrow pure @safe unittest @trusted pure nothrow @nogc const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == delegate) || is(T : V*, V) && __traits(getAliasThis, T).length == 0) { + pragma(inline, true); if (__ctfe) { if (val !is null) assert(0, "Unable to compute byte representation of non-null pointer at compile time"); @@ -781,6 +786,7 @@ const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == delegate) || is(T : V*, V @trusted pure nothrow @nogc const(ubyte)[] toUbyte(T)(const return ref scope T val) if (is(T == struct) || is(T == union)) { + pragma(inline, true); if (__ctfe) { ubyte[] bytes = ctfe_alloc(T.sizeof); diff --git a/libphobos/libdruntime/core/internal/gc/blkcache.d b/libphobos/libdruntime/core/internal/gc/blkcache.d index 07027f56eb6..65de25d2f27 100644 --- a/libphobos/libdruntime/core/internal/gc/blkcache.d +++ b/libphobos/libdruntime/core/internal/gc/blkcache.d @@ -63,17 +63,16 @@ else } // free the allocation on thread exit. -@standalone static ~this() +void cleanupBlkCache(void* storage) nothrow @nogc { - if (__blkcache_storage) + if (storage) { + // check if this is the same thread as the current running thread, and + // if so, make sure we don't leave a dangling pointer. + if (__blkcache_storage is storage) + __blkcache_storage = null; import core.stdc.stdlib : free; - import core.thread.threadbase; - auto tBase = ThreadBase.getThis(); - if (tBase !is null) - tBase.tlsGCData = null; - free(__blkcache_storage); - __blkcache_storage = null; + free(storage); } } diff --git a/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d index 64b5bed43b1..8b81de30ab1 100644 --- a/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d +++ b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d @@ -1532,7 +1532,7 @@ class ConservativeGC : GC return false; // try extending the block into subsequent pages. - immutable requiredExtension = newUsed - info.size - LARGEPAD; + immutable requiredExtension = newUsed - (info.size - LARGEPAD); auto extendedSize = extend(info.base, requiredExtension, requiredExtension, null); if (extendedSize == 0) // could not extend, can't satisfy the request @@ -1637,6 +1637,14 @@ class ConservativeGC : GC return existingCapacity - offset; } + + void initThread(ThreadBase t) nothrow @nogc { } + + void cleanupThread(ThreadBase t) nothrow @nogc + { + cleanupBlkCache(t.tlsGCData); + t.tlsGCData = null; + } } @@ -1862,8 +1870,7 @@ struct Gcx roots.removeAll(); ranges.removeAll(); - toscanConservative.reset(); - toscanPrecise.reset(); + scanStackConservative.reset(); // scanStackPrecise overlaps with scanStackConservative } @@ -2427,14 +2434,13 @@ struct Gcx { nothrow: @disable this(this); - auto stackLock = shared(AlignedSpinLock)(SpinLock.Contention.brief); void reset() { _length = 0; if (_p) { - os_mem_unmap(_p, _cap * RANGE.sizeof); + os_mem_unmap(_p, _cap); _p = null; } _cap = 0; @@ -2446,39 +2452,31 @@ struct Gcx void push(RANGE rng) { - if (_length == _cap) grow(); - _p[_length++] = rng; + if ((_length + 1) * RANGE.sizeof > _cap) grow(); + _p[_length] = rng; + _length++; } - RANGE pop() - in { assert(!empty); } - do + void pushReverse(RANGE[] ranges) { - return _p[--_length]; - } - - bool popLocked(ref RANGE rng) - { - if (_length == 0) - return false; + while ((_length + ranges.length) * RANGE.sizeof > _cap) + grow(); - stackLock.lock(); - scope(exit) stackLock.unlock(); - if (_length == 0) - return false; - rng = _p[--_length]; - return true; + // reverse order for depth-first-order traversal + foreach_reverse (ref range; ranges) + _p[_length++] = range; } - ref inout(RANGE) opIndex(size_t idx) inout - in { assert(idx < _length); } + void pop(ref RANGE rng) + in { assert(_length > 0); } do { - return _p[idx]; + rng = _p[--_length]; } - @property size_t length() const { return _length; } - @property bool empty() const { return !length; } + @property bool empty() const { return !_length; } + size_t length() const { return _length; } + RANGE* ptr() { return _p; } private: void grow() @@ -2486,14 +2484,14 @@ struct Gcx pragma(inline, false); enum initSize = 64 * 1024; // Windows VirtualAlloc granularity - immutable ncap = _cap ? 2 * _cap : initSize / RANGE.sizeof; - auto p = cast(RANGE*)os_mem_map(ncap * RANGE.sizeof); + immutable ncap = _cap ? 2 * _cap : initSize; + auto p = cast(RANGE*)os_mem_map(ncap); if (p is null) onOutOfMemoryError(); debug (VALGRIND) makeMemUndefined(p[0..ncap]); if (_p !is null) { p[0 .. _length] = _p[0 .. _length]; - os_mem_unmap(_p, _cap * RANGE.sizeof); + os_mem_unmap(_p, _cap); } _p = p; _cap = ncap; @@ -2501,18 +2499,20 @@ struct Gcx size_t _length; RANGE* _p; - size_t _cap; + size_t _cap; // in bytes } - ToScanStack!(ScanRange!false) toscanConservative; - ToScanStack!(ScanRange!true) toscanPrecise; - + union + { + ToScanStack!(ScanRange!false) scanStackConservative; + ToScanStack!(ScanRange!true) scanStackPrecise; + } template scanStack(bool precise) { static if (precise) - alias scanStack = toscanPrecise; + alias scanStack = scanStackPrecise; else - alias scanStack = toscanConservative; + alias scanStack = scanStackConservative; } /** @@ -2520,12 +2520,10 @@ struct Gcx */ private void mark(bool precise, bool parallel, bool shared_mem)(ScanRange!precise rng) scope nothrow { - alias toscan = scanStack!precise; - debug(MARK_PRINTF) printf("marking range: [%p..%p] (%#llx)\n", rng.pbot, rng.ptop, cast(long)(rng.ptop - rng.pbot)); - // limit the amount of ranges added to the toscan stack + // limit the amount of ranges added to the scan stack enum FANOUT_LIMIT = 32; size_t stackPos; ScanRange!precise[FANOUT_LIMIT] stack = void; @@ -2706,16 +2704,16 @@ struct Gcx { static if (parallel) { - if (!toscan.popLocked(rng)) + if (!scanStackPopLocked(rng)) break; // nothing more to do } else { - if (toscan.empty) + if (scanStack!precise.empty) break; // nothing more to do // pop range from global stack and recurse - rng = toscan.pop(); + scanStack!precise.pop(rng); } } // printf(" pop [%p..%p] (%#zx)\n", p1, p2, cast(size_t)p2 - cast(size_t)p1); @@ -2725,22 +2723,21 @@ struct Gcx rng.pbot += (void*).sizeof; if (rng.pbot < rng.ptop) { - if (stackPos < stack.length) - { - stack[stackPos] = tgt; - stackPos++; - continue; - } - static if (parallel) + if (stackPos >= stack.length) { - toscan.stackLock.lock(); - scope(exit) toscan.stackLock.unlock(); + static if (parallel) + { + scanStackPushLocked!precise(stack); + } + else + { + scanStack!precise.pushReverse(stack); + } + stackPos = 0; } - toscan.push(rng); - // reverse order for depth-first-order traversal - foreach_reverse (ref range; stack) - toscan.push(range); - stackPos = 0; + stack[stackPos] = tgt; + stackPos++; + continue; } LendOfRange: // continue with last found range @@ -3526,8 +3523,9 @@ Lmark: Gcx.instance.numScanThreads = 0; Gcx.instance.scanThreadData = null; Gcx.instance.busyThreads = 0; + Gcx.instance.stackLock = shared(AlignedSpinLock)(SpinLock.Contention.brief); - memset(&Gcx.instance.evStart, 0, Gcx.instance.evStart.sizeof); + memset(&Gcx.instance.evStackFilled, 0, Gcx.instance.evStackFilled.sizeof); memset(&Gcx.instance.evDone, 0, Gcx.instance.evDone.sizeof); } } @@ -3551,12 +3549,12 @@ Lmark: uint numScanThreads; ScanThreadData* scanThreadData; - Event evStart; + Event evStackFilled; Event evDone; shared uint busyThreads; shared uint stoppedThreads; - bool stopGC; + shared bool stopGC; void markParallel() nothrow { @@ -3565,25 +3563,24 @@ Lmark: if (toscanRoots.empty) return; - void** pbot = toscanRoots._p; - void** ptop = toscanRoots._p + toscanRoots._length; + auto pbot = toscanRoots.ptr; + auto ptop = pbot + toscanRoots.length; debug(PARALLEL_PRINTF) printf("markParallel\n"); - size_t pointersPerThread = toscanRoots._length / (numScanThreads + 1); + size_t pointersPerThread = toscanRoots.length / (numScanThreads + 1); if (pointersPerThread > 0) { void pushRanges(bool precise)() { - alias toscan = scanStack!precise; - toscan.stackLock.lock(); + stackLock.lock(); for (int idx = 0; idx < numScanThreads; idx++) { - toscan.push(ScanRange!precise(pbot, pbot + pointersPerThread)); + scanStack!precise.push(ScanRange!precise(pbot, pbot + pointersPerThread)); pbot += pointersPerThread; } - toscan.stackLock.unlock(); + stackLock.unlock(); } if (ConservativeGC.isPrecise) pushRanges!true(); @@ -3592,21 +3589,22 @@ Lmark: } assert(pbot < ptop); - busyThreads.atomicOp!"+="(1); // main thread is busy - - evStart.setIfInitialized(); + evStackFilled.setIfInitialized(); // background threads start now debug(PARALLEL_PRINTF) printf("mark %lld roots\n", cast(ulong)(ptop - pbot)); + void pullLoop(bool precise)() + { + mark!(precise, true, true)(ScanRange!precise(pbot, ptop)); + + while (pullFromScanStackImpl!precise()) + evDone.wait(1.msecs); + } if (ConservativeGC.isPrecise) - mark!(true, true, true)(ScanRange!true(pbot, ptop, null)); + pullLoop!(true)(); else - mark!(false, true, true)(ScanRange!false(pbot, ptop)); - - busyThreads.atomicOp!"-="(1); + pullLoop!(false)(); - debug(PARALLEL_PRINTF) printf("waitForScanDone\n"); - pullFromScanStack(); debug(PARALLEL_PRINTF) printf("waitForScanDone done\n"); } @@ -3654,7 +3652,7 @@ Lmark: if (!scanThreadData) onOutOfMemoryError(); - evStart.initialize(false, false); + evStackFilled.initialize(true, false); evDone.initialize(false, false); version (Posix) @@ -3696,8 +3694,8 @@ Lmark: stopGC = true; while (atomicLoad(stoppedThreads) < startedThreads && !allThreadsDead) { - evStart.setIfInitialized(); - evDone.wait(dur!"msecs"(1)); + evStackFilled.setIfInitialized(); + evDone.wait(1.msecs); } for (int idx = 0; idx < numScanThreads; idx++) @@ -3709,8 +3707,8 @@ Lmark: } } + evStackFilled.terminate(); evDone.terminate(); - evStart.terminate(); cstdlib.free(scanThreadData); // scanThreadData = null; // keep non-null to not start again after shutdown @@ -3723,26 +3721,24 @@ Lmark: { while (!stopGC) { - evStart.wait(); + evStackFilled.wait(); pullFromScanStack(); - evDone.setIfInitialized(); + evDone.setIfInitialized(); // tell main loop we are done } stoppedThreads.atomicOp!"+="(1); + evDone.setIfInitialized(); // wake up main } - void pullFromScanStack() nothrow + bool pullFromScanStack() nothrow { if (ConservativeGC.isPrecise) - pullFromScanStackImpl!true(); + return pullFromScanStackImpl!true(); else - pullFromScanStackImpl!false(); + return pullFromScanStackImpl!false(); } - void pullFromScanStackImpl(bool precise)() nothrow + bool pullFromScanStackImpl(bool precise)() nothrow { - if (atomicLoad(busyThreads) == 0) - return; - version (Posix) debug (PARALLEL_PRINTF) { import core.sys.posix.pthread : pthread_self, pthread_t; @@ -3751,29 +3747,60 @@ Lmark: } ScanRange!precise rng; - alias toscan = scanStack!precise; - while (atomicLoad(busyThreads) > 0) + stackLock.lock(); + while (!scanStack!precise.empty()) { - if (toscan.empty) - { - evDone.wait(dur!"msecs"(1)); - continue; - } - busyThreads.atomicOp!"+="(1); - if (toscan.popLocked(rng)) + scanStack!precise.pop(rng); + if (scanStack!precise.empty) + evStackFilled.reset(); + stackLock.unlock(); + + version (Posix) debug (PARALLEL_PRINTF) { - version (Posix) debug (PARALLEL_PRINTF) - { - printf("scanBackground thread %d scanning range [%p,%lld] from stack\n", - threadId, rng.pbot, cast(long) (rng.ptop - rng.pbot)); - } - mark!(precise, true, true)(rng); + printf("scanBackground thread %d scanning range [%p,%lld] from stack\n", + threadId, rng.pbot, cast(long) (rng.ptop - rng.pbot)); } + mark!(precise, true, true)(rng); + // returns here only if an empty scan stack has been seen + + stackLock.lock(); busyThreads.atomicOp!"-="(1); } + bool cont = busyThreads > 0; + stackLock.unlock(); + version (Posix) debug (PARALLEL_PRINTF) printf("scanBackground thread %d done\n", threadId); + return cont; + } + + auto stackLock = shared(AlignedSpinLock)(SpinLock.Contention.brief); + + void scanStackPushLocked(bool precise)(ScanRange!precise[] ranges) + { + stackLock.lock(); + scope(exit) stackLock.unlock(); + bool wasEmpty = scanStack!precise.empty; + + scanStack!precise.pushReverse(ranges); + + if (wasEmpty) + evStackFilled.setIfInitialized(); + } + + bool scanStackPopLocked(bool precise)(ref ScanRange!precise rng) + { + stackLock.lock(); + scope(exit) stackLock.unlock(); + if (scanStack!precise.empty()) + return false; + + scanStack!precise.pop(rng); + + if (scanStack!precise.empty()) + evStackFilled.reset(); + return true; } } @@ -5366,6 +5393,26 @@ unittest } } +// https://github.com/dlang/dmd/issues/21615 +debug(SENTINEL) {} else // no additional capacity with SENTINEL +@safe unittest +{ + size_t numReallocations = 0; + ubyte[] buffer = new ubyte[4096]; + auto p = &buffer[0]; + foreach (i; 0 .. 1000) { + buffer.length += 4096; + if (p !is &buffer[0]) + { + ++numReallocations; + p = &buffer[0]; + } + } + + // pick a decently small number, it's unclear where this memory will start out. + assert(numReallocations <= 5); +} + /* ============================ MEMSTOMP =============================== */ /// Mark the specified memory region as uninitialized - diff --git a/libphobos/libdruntime/core/internal/gc/impl/manual/gc.d b/libphobos/libdruntime/core/internal/gc/impl/manual/gc.d index 3da92db14e7..5f9c04187ed 100644 --- a/libphobos/libdruntime/core/internal/gc/impl/manual/gc.d +++ b/libphobos/libdruntime/core/internal/gc/impl/manual/gc.d @@ -23,6 +23,8 @@ import core.gc.gcinterface; import core.internal.container.array; +import core.thread.threadbase : ThreadBase; + import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc; static import core.memory; @@ -287,4 +289,12 @@ class ManualGC : GC { return false; } + + void initThread(ThreadBase t) nothrow @nogc + { + } + + void cleanupThread(ThreadBase t) nothrow @nogc + { + } } diff --git a/libphobos/libdruntime/core/internal/gc/impl/proto/gc.d b/libphobos/libdruntime/core/internal/gc/impl/proto/gc.d index cbdcdb8852b..9dd12eab001 100644 --- a/libphobos/libdruntime/core/internal/gc/impl/proto/gc.d +++ b/libphobos/libdruntime/core/internal/gc/impl/proto/gc.d @@ -3,6 +3,10 @@ module core.internal.gc.impl.proto.gc; import core.gc.gcinterface; +import core.gc.registry : GCThreadInitFunction, threadInit; + +import core.thread.threadbase : ThreadBase; + import core.internal.container.array; import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc; @@ -28,11 +32,15 @@ private extern (C) void gc_addRoot(const void* p ) nothrow @nogc; } + class ProtoGC : GC { Array!Root roots; Array!Range ranges; + // stored on first use, which should be called whenever rt_init is called. + private GCThreadInitFunction _initThreadFn; + // Call this function when initializing the real GC // upon ProtoGC term. This function should be called // after the real GC is in place. @@ -261,4 +269,23 @@ class ProtoGC : GC { return false; } + + void initThread(ThreadBase thread) nothrow + { + if (_initThreadFn is null) + { + static void defaultThreadInit(ThreadBase base) nothrow @nogc { } + import core.gc.config; + config.initialize(); + _initThreadFn = .threadInit(config.gc); + if (_initThreadFn is null) + _initThreadFn = &defaultThreadInit; + } + + _initThreadFn(thread); + } + + void cleanupThread(ThreadBase thread) + { + } } diff --git a/libphobos/libdruntime/core/internal/gc/proxy.d b/libphobos/libdruntime/core/internal/gc/proxy.d index d46cb8cd775..9b83eb5d3fe 100644 --- a/libphobos/libdruntime/core/internal/gc/proxy.d +++ b/libphobos/libdruntime/core/internal/gc/proxy.d @@ -266,7 +266,7 @@ extern (C) return instance.shrinkArrayUsed( slice, existingUsed, atomic ); } - GC gc_getProxy() nothrow + GC gc_getProxy() nothrow @nogc { return instance; } diff --git a/libphobos/libdruntime/core/internal/hash.d b/libphobos/libdruntime/core/internal/hash.d index 80fdc320032..e5c6ab1f0f3 100644 --- a/libphobos/libdruntime/core/internal/hash.d +++ b/libphobos/libdruntime/core/internal/hash.d @@ -132,6 +132,7 @@ private template canBitwiseHash(T) size_t hashOf(T)(auto ref T val, size_t seed = 0) if (is(T == enum) && !__traits(isScalar, T)) { + pragma(inline, true); static if (is(T EType == enum)) {} //for EType return hashOf(cast(EType) val, seed); } @@ -140,6 +141,7 @@ if (is(T == enum) && !__traits(isScalar, T)) size_t hashOf(T)(scope const auto ref T val, size_t seed = 0) if (!is(T == enum) && __traits(isStaticArray, T) && canBitwiseHash!T) { + pragma(inline, true); import core.internal.convert : toUbyte; // FIXME: // We would like to to do this: @@ -173,6 +175,7 @@ if (!is(T == enum) && __traits(isStaticArray, T) && canBitwiseHash!T) size_t hashOf(T)(auto ref T val, size_t seed = 0) if (!is(T == enum) && __traits(isStaticArray, T) && !canBitwiseHash!T) { + pragma(inline, true); // FIXME: // We would like to to do this: // @@ -207,10 +210,12 @@ if (is(T == S[], S) && (__traits(isScalar, S) || canBitwiseHash!S)) // excludes 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 { + pragma(inline, true); return bytesHashAlignedBy!ElementType(toUbyte(val), seed); } else //Other types. CTFE unsupported { + pragma(inline, true); 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); } @@ -258,6 +263,7 @@ size_t hashOf(T)(scope const T val) if (__traits(isScalar, T) && !is(T == __vect else static if (is(T EType == enum) && is(typeof(val[0]))) { // Enum type whose base type is vector. + pragma(inline, true); return hashOf(cast(EType) val); } else static if (__traits(isIntegral, T)) @@ -293,6 +299,7 @@ size_t hashOf(T)(scope const T val, size_t seed) if (__traits(isScalar, T) && !i { static if (is(T V : V*)) { + pragma(inline, true); if (__ctfe) { if (val is null) return hashOf(size_t(0), seed); @@ -303,6 +310,7 @@ size_t hashOf(T)(scope const T val, size_t seed) if (__traits(isScalar, T) && !i else static if (is(T EType == enum) && is(typeof(val[0]))) { // Enum type whose base type is vector. + pragma(inline, true); return hashOf(cast(EType) val, seed); } else static if (__traits(isIntegral, val) && T.sizeof <= size_t.sizeof) @@ -379,6 +387,7 @@ if (is(T == __vector)) // excludes enum types } else { + pragma(inline, true); import core.internal.convert : toUbyte; return bytesHashAlignedBy!T(toUbyte(val), seed); } @@ -388,6 +397,7 @@ if (is(T == __vector)) // excludes enum types @trusted @nogc nothrow pure size_t hashOf(T)(scope const T val) if (!is(T == enum) && is(T : typeof(null))) { + pragma(inline, true); return 0; } @@ -395,6 +405,7 @@ size_t hashOf(T)(scope const T val) if (!is(T == enum) && is(T : typeof(null))) @trusted @nogc nothrow pure size_t hashOf(T)(scope const T val, size_t seed) if (!is(T == enum) && is(T : typeof(null))) { + pragma(inline, true); return hashOf(size_t(0), seed); } @@ -572,6 +583,7 @@ if (!is(T == enum) && (is(T == struct) || is(T == union)) @trusted @nogc nothrow pure size_t hashOf(T)(scope const T val, size_t seed = 0) if (!is(T == enum) && is(T == delegate)) { + pragma(inline, true); if (__ctfe) { if (val is null) return hashOf(size_t(0), hashOf(size_t(0), seed)); @@ -586,6 +598,7 @@ size_t hashOf(T)(scope const T val) if (!is(T == enum) && (is(T == interface) || is(T == class)) && canBitwiseHash!T) { + pragma(inline, true); if (__ctfe) if (val is null) return 0; return hashOf(cast(const void*) val); } @@ -596,6 +609,7 @@ size_t hashOf(T)(scope const T val, size_t seed) if (!is(T == enum) && (is(T == interface) || is(T == class)) && canBitwiseHash!T) { + pragma(inline, true); if (__ctfe) if (val is null) return hashOf(size_t(0), seed); return hashOf(cast(const void*) val, seed); } @@ -664,6 +678,7 @@ size_t hashOf(T)(T aa, size_t seed) if (!is(T == enum) && __traits(isAssociative @system pure nothrow @nogc size_t bytesHash()(scope const(void)* buf, size_t len, size_t seed) { + pragma(inline, true); return bytesHashAlignedBy!ubyte((cast(const(ubyte)*) buf)[0 .. len], seed); } @@ -702,6 +717,7 @@ private alias smallBytesHash = fnv; // handle aligned reads, do the conversion here private uint get32bits()(scope const(ubyte)* x) @nogc nothrow pure @system { + pragma(inline, true); version (BigEndian) { return ((cast(uint) x[0]) << 24) | ((cast(uint) x[1]) << 16) | ((cast(uint) x[2]) << 8) | (cast(uint) x[3]); @@ -717,7 +733,7 @@ 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) +private size_t _bytesHash(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes, size_t seed) { auto len = bytes.length; auto data = bytes.ptr; @@ -771,6 +787,34 @@ private size_t bytesHash(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes, return h1; } +// precompile bytesHash into the runtime to also get optimized versions in debug builds +@nogc nothrow pure @trusted +private size_t _bytesHashAligned(scope const(ubyte)[] bytes, size_t seed) +{ + pragma(inline, true); + return _bytesHash!true(bytes, seed); +} +@nogc nothrow pure @trusted +private size_t _bytesHashUnaligned(scope const(ubyte)[] bytes, size_t seed) +{ + pragma(inline, true); + return _bytesHash!false(bytes, seed); +} + +/+ +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) +{ + pragma(inline, true); + static if (dataKnownToBeAligned) + return _bytesHashAligned(bytes, seed); + else + return _bytesHashUnaligned(bytes, seed); +} + // Check that bytesHash works with CTFE pure nothrow @system @nogc unittest { diff --git a/libphobos/libdruntime/core/internal/lifetime.d b/libphobos/libdruntime/core/internal/lifetime.d index fd78de221d1..053b3edc69d 100644 --- a/libphobos/libdruntime/core/internal/lifetime.d +++ b/libphobos/libdruntime/core/internal/lifetime.d @@ -210,14 +210,29 @@ void __doPostblit(T)(T[] arr) { static if (__traits(isStaticArray, T) && is(T : E[], E)) __doPostblit(cast(E[]) arr); - else static if (!__traits(compiles, arr[0].__xpostblit)) + else { - alias Unqual_T = Unqual!T; - foreach (ref elem; (() @trusted => cast(Unqual_T[]) arr)()) + import core.internal.traits : Unqual; + foreach (ref elem; (() @trusted => cast(Unqual!T[]) arr)()) elem.__xpostblit(); } + } +} + +// ditto, but with an index to keep track of how many elements have been postblitted +void __doPostblit(T)(T[] arr, ref size_t i) +{ + // infer static postblit type, run postblit if any + static if (__traits(hasPostblit, T)) + { + static if (__traits(isStaticArray, T) && is(T : E[], E)) + __doPostblit(cast(E[]) arr, i); else - foreach (ref elem; arr) - elem.__xpostblit(); + { + i = 0; + import core.internal.traits : Unqual; + for(auto eptr = cast(Unqual!T*)&arr[0]; i < arr.length; ++i, ++eptr) + eptr.__xpostblit(); + } } } diff --git a/libphobos/libdruntime/core/internal/newaa.d b/libphobos/libdruntime/core/internal/newaa.d index 47283f28030..764d1e7b586 100644 --- a/libphobos/libdruntime/core/internal/newaa.d +++ b/libphobos/libdruntime/core/internal/newaa.d @@ -1,17 +1,21 @@ /** - Turn an Associative Array into a binary compatible struct for static initialization. - - This does not implement all the pieces of - the associative array type in druntime, just enough to create an AA from an - existing range of key/value pairs. - - Copyright: Copyright Digital Mars 2000 - 2015, Steven Schveighoffer 2022. - License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - Authors: Martin Nowak, Steven Schveighoffer -*/ + * template implementation of associative arrays. + * + * Copyright: Copyright Digital Mars 2000 - 2015, Steven Schveighoffer 2022. + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: Martin Nowak, Steven Schveighoffer, Rainer Schuetze + * + * Source: $(DRUNTIMESRC core/internal/_newaa.d) + * + * derived from rt/aaA.d + */ module core.internal.newaa; -import core.memory; +/// AA version for debuggers, bump whenever changing the layout +immutable int _aaVersion = 1; + +import core.internal.util.math : min, max; +import core.internal.traits : substInout; // grow threshold private enum GROW_NUM = 4; @@ -31,26 +35,225 @@ private enum INIT_DEN = SHRINK_DEN * GROW_DEN; private enum INIT_NUM_BUCKETS = 8; // magic hash constants to distinguish empty, deleted, and filled buckets private enum HASH_EMPTY = 0; +private enum HASH_DELETED = 0x1; private enum HASH_FILLED_MARK = size_t(1) << 8 * size_t.sizeof - 1; -private struct Bucket +/// AA wrapper +struct AA(K, V) { - size_t hash; - void *entry; + Impl!(K,V)* impl; + alias impl this; + + @property bool empty() const pure nothrow @nogc @safe + { + pragma(inline, true); + return impl is null || !impl.length; + } + @property size_t length() const pure nothrow @nogc @safe + { + pragma(inline, true); + return impl is null ? 0 : impl.length; + } +} + +/// like core.internal.traits.Unconst, but stripping inout, too +private template Unconstify(T : const U, U) +{ + static if (is(U == inout V, V)) + alias Unconstify = V; + else + alias Unconstify = U; +} + +ref _refAA(K, V)(ref V[K] aa) @trusted +{ + pragma(inline, true); + return *(cast(AA!(substInout!K, substInout!V)*)&aa); +} + +auto _toAA(K, V)(const V[K] aa) @trusted +{ + pragma(inline, true); + return *(cast(const(AA!(K, V))*)&aa); +} + +auto _toAA(K, V)(inout V[K] aa) @trusted +{ + pragma(inline, true); + return *(cast(inout(AA!(K, V))*)&aa); } -struct Impl +// for backward compatibility, but should be deprecated +auto _toAA(K, V)(shared const V[K] aa) @trusted { + pragma(inline, true); + return *(cast(AA!(K, V)*)&aa); +} + +// for backward compatibility, but should be deprecated +auto _toAA(K, V)(shared V[K] aa) @trusted +{ + pragma(inline, true); + return *(cast(AA!(K, V)*)&aa); +} + +// resolve ambiguity for immutable converting to const and shared const +auto _toAA(K, V)(immutable V[K] aa) @trusted +{ + pragma(inline, true); + return *(cast(AA!(K, V)*)&aa); +} + +static struct Entry(K, V) +{ + K key; + V value; +} + +// backward compatibility conversions +private ref compat_key(K, K2)(ref K2 key) +{ + pragma(inline, true); + static if (is(K2 == const(char)[]) && is(K == string)) + return (ref (ref return K2 k2) @trusted => *cast(string*)&k2)(key); + else + return key; +} + +private void _aaMove(V)(ref V src, ref V dst) @trusted +{ + import core.stdc.string : memcpy, memset; + // move without postblit!? + memcpy(&dst, &src, V.sizeof); + static if (__traits(isZeroInit, V)) + memset(&src, 0, V.sizeof); + else + memcpy(&src, &V.init, V.sizeof); +} + +// mimick behaviour of rt.aaA for initialization +Entry!(K, V)* _newEntry(K, V)(ref K key, ref V value) +{ + static if (__traits(compiles, new Entry!(K, V)(key, value))) + { + auto entry = new Entry!(K, V)(key, value); + } + else static if (__traits(compiles, { K k; new Entry!(K, V)(k); })) + { + auto entry = new Entry!(K, V)(key); + _aaMove(value, entry.value); + } + else + { + auto entry = new Entry!(K, V); + _aaMove(key, entry.key); + _aaMove(value, entry.value); + } + return entry; +} + +// mimick behaviour of rt.aaA for initialization +Entry!(K, V)* _newEntry(K, V, K2)(ref K2 key) +{ + static if (__traits(compiles, new Entry!(K, V)(key)) && + !(is(V == struct) && __traits(isNested, V))) // not detected by "compiles" + { + auto entry = new Entry!(K, V)(key); + } + else static if (__traits(compiles, { K2 k; new Entry!(K, V)(k, V.init); })) + { + // with disabled ctor for V + auto entry = new Entry!(K, V)(key, V.init); + } + else + { + // with disabled ctor for K and V + auto entry = new Entry!(K, V); + entry.key = key; + } + static if (!__traits(isZeroInit, V)) + { + () @trusted { (cast(ubyte*)&entry.value)[0..V.sizeof] = 0; }(); + } + return entry; +} + +template pure_hashOf(K) +{ + static if (__traits(compiles, function hash_t(scope const ref K key) pure nothrow @nogc @trusted { return hashOf(cast()key); })) + { + // avoid wrapper call in debug builds if pure nothrow @nogc is inferred + pragma(inline, true) + hash_t pure_hashOf(scope const ref K key) @trusted { return hashOf(cast()key); } + } + else + { + // for backward compatibility, do not require const in hashOf() + hash_t wrap_hashOf(K)(scope const ref K key) @trusted { return hashOf(cast()key); } + enum pure_hashOf = cast(hash_t function(scope ref const K key) pure nothrow @nogc @safe) &wrap_hashOf!K; + } +} + +// for backward compatibilty pretend the comparison is @safe, pure, etc +// this also breaks cyclic inference on recursive data types +template pure_keyEqual(K1, K2 = K1) +{ + static if (__traits(compiles, function bool(ref const K1 k1, ref const K2 k2) pure nothrow @nogc @trusted { return cast()k1 == cast()k2; })) + { + // avoid wrapper call in debug builds if pure nothrow @nogc is inferred + pragma(inline, true) + bool pure_keyEqual(ref const K1 k1, ref const K2 k2) @trusted { return cast()k1 == cast()k2; } + } + else + { + bool keyEqual(ref const K1 k1, ref const K2 k2) @trusted { return cast()k1 == cast()k2; } + enum pure_keyEqual = cast(bool function(ref const K1, ref const K2) pure nothrow @nogc @safe) &keyEqual; + } +} + +private struct Impl(K, V) +{ +private: + alias Bucket = .Bucket!(K, V); + + this(size_t sz /* = INIT_NUM_BUCKETS */) nothrow + { + buckets = allocBuckets(sz); + firstUsed = cast(uint) buckets.length; + + // only for binary compatibility + version(D_TypeInfo) + entryTI = typeid(Entry!(K, V)); + hashFn = delegate size_t (scope ref const K key) nothrow pure @nogc @safe { + return pure_hashOf!K(key); + }; + + keysz = cast(uint) K.sizeof; + valsz = cast(uint) V.sizeof; + valoff = cast(uint) talign(keysz, V.alignof); + + enum ctflags = () { + import core.internal.traits; + Impl.Flags flags; + static if (__traits(hasPostblit, K)) + flags |= flags.keyHasPostblit; + static if (hasIndirections!K || hasIndirections!V) + flags |= flags.hasPointers; + return flags; + } (); + flags = ctflags; + } + Bucket[] buckets; uint used; uint deleted; - const(TypeInfo) entryTI; + const(TypeInfo) entryTI; // only for binary compatibility uint firstUsed; - immutable uint keysz; - immutable uint valsz; - immutable uint valoff; - Flags flags; - size_t delegate(scope const void*) nothrow hashFn; + immutable uint keysz; // only for binary compatibility + immutable uint valsz; // only for binary compatibility + immutable uint valoff; // only for binary compatibility + Flags flags; // only for binary compatibility + size_t delegate(scope ref const K) nothrow pure @nogc @safe hashFn; enum Flags : ubyte { @@ -58,13 +261,150 @@ struct Impl keyHasPostblit = 0x1, hasPointers = 0x2, } + + @property size_t length() const pure nothrow @nogc @safe + { + pragma(inline, true); + assert(used >= deleted); + return used - deleted; + } + + @property size_t dim() const pure nothrow @nogc @safe + { + pragma(inline, true); + return buckets.length; + } + + @property size_t mask() const pure nothrow @nogc @safe + { + pragma(inline, true); + return dim - 1; + } + + // find the first slot to insert a value with hash + size_t findSlotInsert(size_t hash) const pure nothrow @nogc @safe + { + for (size_t i = hash & mask, j = 1;; ++j) + { + if (!buckets[i].filled) + return i; + i = (i + j) & mask; + } + } + + // lookup a key + inout(Bucket)* findSlotLookup(K2)(size_t hash, scope ref const K2 key) inout pure @safe nothrow + { + for (size_t i = hash & mask, j = 1;; ++j) + { + auto b = &buckets[i]; // avoid multiple bounds checks + if (b.hash == hash && b.entry) + if (pure_keyEqual!(K2, K)(key, b.entry.key)) + return b; + if (b.empty) + return null; + i = (i + j) & mask; + } + } + + void grow() pure nothrow @safe + { + // If there are so many deleted entries, that growing would push us + // below the shrink threshold, we just purge deleted entries instead. + if (length * SHRINK_DEN < GROW_FAC * dim * SHRINK_NUM) + resize(dim); + else + resize(GROW_FAC * dim); + } + + void shrink() pure nothrow @safe + { + if (dim > INIT_NUM_BUCKETS) + resize(dim / GROW_FAC); + } + + void resize(size_t ndim) pure nothrow @safe + { + auto obuckets = buckets; + buckets = allocBuckets(ndim); + + foreach (ref b; obuckets[firstUsed .. $]) + if (b.filled) + buckets[findSlotInsert(b.hash)] = b; + + firstUsed = 0; + used -= deleted; + deleted = 0; + obuckets.length = 0; // safe to free b/c impossible to reference, but doesn't really free + } + + void clear() pure nothrow + { + // clear all data, but don't change bucket array length + buckets[firstUsed .. $] = Bucket.init; + deleted = used = 0; + firstUsed = cast(uint) dim; + } + + size_t calcHash(K2)(ref K2 key) const nothrow pure @nogc @safe + { + static if(is(K2* : K*)) // ref compatible? + hash_t hash = pure_hashOf!K(key); + else + hash_t hash = pure_hashOf!K2(key); + // highest bit is set to distinguish empty/deleted from filled buckets + return mix(hash) | HASH_FILLED_MARK; + } + + static Bucket[] allocBuckets(size_t dim) pure nothrow @safe + { + // could allocate with BlkAttr.NO_INTERIOR, but that does not combine + // well with arrays and type info for precise scanning + return new Bucket[dim]; + } } -private struct AAShell +//============================================================================== +// Bucket +//------------------------------------------------------------------------------ + +private struct Bucket(K, V) +{ +private pure nothrow @nogc: + size_t hash; + Entry!(K, V)* entry; + + @property bool empty() const + { + pragma(inline, true); + return hash == HASH_EMPTY; + } + + @property bool deleted() const + { + pragma(inline, true); + return hash == HASH_DELETED; + } + + @property bool filled() const @safe + { + pragma(inline, true); + return cast(ptrdiff_t) hash < 0; + } +} + +//============================================================================== +// Helper functions +//------------------------------------------------------------------------------ + +private size_t talign(size_t tsize, size_t algn) @safe pure nothrow @nogc { - Impl *impl; + immutable mask = algn - 1; + assert(!(mask & algn)); + return (tsize + mask) & ~mask; } +// mix hash to "fix" bad hash functions private size_t mix(size_t h) @safe pure nothrow @nogc { // final mix function of MurmurHash2 @@ -75,74 +415,612 @@ private size_t mix(size_t h) @safe pure nothrow @nogc return h; } -// create a binary-compatible AA structure that can be used directly as an -// associative array. -// NOTE: this must only be called during CTFE -AAShell makeAA(K, V)(V[K] src) @trusted +private size_t nextpow2(const size_t n) pure nothrow @nogc @safe { - assert(__ctfe, "makeAA Must only be called at compile time"); - immutable srclen = src.length; - assert(srclen <= uint.max); - alias E = TypeInfo_AssociativeArray.Entry!(K, V); - if (srclen == 0) - return AAShell.init; - // first, determine the size that would be used if we grew the bucket list - // one element at a time using the standard AA algorithm. - size_t dim = INIT_NUM_BUCKETS; - while (srclen * GROW_DEN > dim * GROW_NUM) - dim = dim * GROW_FAC; - - // used during runtime. - typeof(Impl.hashFn) hashFn = (scope const void* val) { - auto x = cast(K*)val; - return hashOf(*x); - }; + import core.bitop : bsr; - Bucket[] buckets; - // Allocate and fill the buckets - if (__ctfe) - buckets = new Bucket[dim]; + if (!n) + return 1; + + const isPowerOf2 = !((n - 1) & n); + return 1 << (bsr(n) + !isPowerOf2); +} + +pure nothrow @nogc unittest +{ + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 + foreach (const n, const pow2; [1, 1, 2, 4, 4, 8, 8, 8, 8, 16]) + assert(nextpow2(n) == pow2); +} + +//============================================================================== +// API Implementation +//------------------------------------------------------------------------------ + +/** Allocate associative array data. + * Called for `new SomeAA` expression. + * Returns: + * A new associative array. + * Note: + * not supported in CTFE + */ +V[K] _d_aaNew(K, V)() +{ + AA!(K, V) aa; + aa.impl = new Impl!(K,V)(INIT_NUM_BUCKETS); + return *cast(V[K]*)&aa; +} + +/// Determine number of entries in associative array. +/// Note: +/// emulated by the compiler during CTFE +size_t _d_aaLen(K, V)(inout V[K] a) +{ + auto aa = _toAA!(K, V)(a); + return aa ? aa.length : 0; +} + +/****************************** + * Lookup key in aa. + * Called only from implementation of (aa[key]) expressions when value is mutable. + * Params: + * aa = associative array + * key = reference to the key value + * found = returns whether the key was found or a new entry was added + * 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 zero + */ +V* _d_aaGetY(K, V, T : V1[K1], K1, V1, K2)(auto ref scope T aa, auto ref K2 key, out bool found) +{ + ref aax = cast(V[K])cast(V1[K1])aa; // remove outer const from T + return _aaGetX!(K, V)(aax, key, found); +} + +/****************************** + * Lookup key in aa. + * Called only from implementation of require, update and _d_aaGetY + * Params: + * a = associative array + * key = reference 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 V.init + */ +V* _aaGetX(K, V, K2)(auto ref scope V[K] a, auto ref K2 key, out bool found) +{ + ref aa = _refAA!(K, V)(a); + + // lazily alloc implementation + if (aa is null) + { + aa.impl = new Impl!(K, V)(INIT_NUM_BUCKETS); + } + + ref key2 = compat_key!(K)(key); + + // get hash and bucket for key + immutable hash = aa.calcHash(key2); + + // found a value => return it + if (auto p = aa.findSlotLookup(hash, key2)) + { + found = true; + return &p.entry.value; + } + + auto pi = aa.findSlotInsert(hash); + if (aa.buckets[pi].deleted) + --aa.deleted; + // check load factor and possibly grow + else if (++aa.used * GROW_DEN > aa.dim * GROW_NUM) + { + aa.grow(); + pi = aa.findSlotInsert(hash); + assert(aa.buckets[pi].empty); + } + + // update search cache and allocate entry + aa.firstUsed = min(aa.firstUsed, cast(uint)pi); + ref p = aa.buckets[pi]; + p.hash = hash; + p.entry = _newEntry!(K, V)(key2); + return &p.entry.value; +} + +/****************************** + * Lookup key in aa. + * Called only from implementation of (aa[key]) expressions when value is not mutable. + * Params: + * aa = associative array + * key = key value + * Returns: + * pointer to value if present, null otherwise + */ +auto _d_aaGetRvalueX(K, V, K2)(inout V[K] aa, auto ref scope K2 key) +{ + return _d_aaIn(aa, key); +} + +/// ditto +auto _d_aaGetRvalueX(K, V, K2)(shared(V[K]) aa, auto ref scope K2 key) +{ + // accept shared for backward compatibility, should be deprecated + return cast(shared(V)*)_d_aaIn(cast(V[K]) aa, key); +} + +/// ditto +auto _d_aaGetRvalueX(K, V, K2)(shared const(V[K]) aa, auto ref scope K2 key) +{ + // accept shared for backward compatibility, should be deprecated + return cast(const shared(V)*)_d_aaIn(cast(V[K]) aa, key); +} + +/// ditto +auto _d_aaGetRvalueX(K, V, K2)(immutable(V[K]) aa, auto ref scope K2 key) +{ + // resolve ambiguity for immutable converting to const and shared const + return _d_aaIn((() @trusted => cast(V[K]) aa) (), key); +} + +/*********************************** + * Creates a new associative array of the same size and copies the contents of + * the associative array into it. + * Params: + * a = The associative array. + */ +auto _aaDup(T : V[K], K, V)(T a) +{ + auto aa = _toAA!(K, V)(a); + immutable len = aa.length; + if (len == 0) + return null; + + auto impl = new Impl!(K, V)(aa.dim); + // copy the entries + bool sameHash = aa.hashFn == impl.hashFn; // can be different if coming from template/rt + foreach (b; aa.buckets[aa.firstUsed .. $]) + { + if (!b.filled) + continue; + hash_t hash = sameHash ? b.hash : impl.calcHash(b.entry.key); + auto pi = impl.findSlotInsert(hash); + auto p = &impl.buckets[pi]; + p.hash = hash; + p.entry = new Entry!(K, V)(b.entry.key, b.entry.value); + impl.firstUsed = min(impl.firstUsed, cast(uint)pi); + } + impl.used = cast(uint) len; + return () @trusted { return *cast(Unconstify!V[K]*)&impl; }(); +} + +/****************************** + * Lookup key in aa. + * Called only from implementation of (key in aa) expressions. + * Params: + * a = associative array opaque pointer + * key = reference to the key value + * Returns: + * pointer to value if present, null otherwise + */ +auto _d_aaIn(T : V[K], K, V, K2)(inout T a, auto ref scope K2 key) +{ + auto aa = _toAA!(K, V)(a); + if (aa.empty) + return null; + + ref key2 = compat_key!(K)(key); + + immutable hash = aa.calcHash(key2); + if (auto p = aa.findSlotLookup(hash, key2)) + return &p.entry.value; + return null; +} + +// fake purity for backward compatibility with runtime hooks +private extern(C) bool gc_inFinalizer() pure nothrow @safe; + +/// Delete entry scope const AA, return true if it was present +auto _d_aaDel(T : V[K], K, V, K2)(T a, auto ref K2 key) +{ + auto aa = _toAA!(K, V)(a); + if (aa.empty) + return false; + + ref key2 = compat_key!(K)(key); + + immutable hash = aa.calcHash(key2); + if (auto p = aa.findSlotLookup(hash, key2)) + { + // clear entry + p.hash = HASH_DELETED; + p.entry = null; + + ++aa.deleted; + // `shrink` reallocates, and allocating from a finalizer leads to + // InvalidMemoryError: https://issues.dlang.org/show_bug.cgi?id=21442 + if (aa.length * SHRINK_DEN < aa.dim * SHRINK_NUM && !__ctfe && !gc_inFinalizer()) + aa.shrink(); + + return true; + } + return false; +} + +/// Remove all elements from AA. +void _aaClear(K, V)(V[K] a) +{ + auto aa = _toAA!(K, V)(a); + if (!aa.empty) + { + aa.clear(); + } +} + +/// Rehash AA +V[K] _aaRehash(K, V)(V[K] a) +{ + auto aa = _toAA!(K, V)(a); + if (!aa.empty) + aa.resize(nextpow2(INIT_DEN * aa.length / INIT_NUM)); + return a; +} + +/// Return a GC allocated array of all values +auto _aaValues(K, V)(inout V[K] a) +{ + auto aa = _toAA!(K, V)(a); + if (aa.empty) + return null; + + static if (__traits(compiles, { V val = aa.buckets[0].entry.value; } )) + V[] res; // if value has no const indirections + else + typeof([aa.buckets[0].entry.value]) res; // as mutable as it can get + res = new typeof(res[0])[aa.length]; + + if (false) // never execute, but infer function attributes from this operation + res ~= aa.buckets[0].entry.value; + + size_t i = 0; + foreach (b; aa.buckets[aa.firstUsed .. $]) + { + if (!b.filled) + continue; + import core.lifetime; + () @trusted { copyEmplace(b.entry.value, res[i++]); }(); + } + return res; +} + +/// Return a GC allocated array of all keys +auto _aaKeys(K, V)(inout V[K] a) +{ + auto aa = _toAA!(K, V)(a); + if (aa.empty) + return null; + + static if (__traits(compiles, { K key = aa.buckets[0].entry.key; } )) + K[] res; // if key has no const indirections else - assert(0); + typeof([aa.buckets[0].entry.key]) res; // as mutable as it can get + res = new typeof(res[0])[aa.length]; - assert(buckets.length >= dim); + if (false) // never execute, but infer function attributes from this operation + res ~= aa.buckets[0].entry.key; - immutable mask = dim - 1; - assert((dim & mask) == 0); // must be a power of 2 + size_t i = 0; + foreach (b; aa.buckets[aa.firstUsed .. $]) + { + if (!b.filled) + continue; + // res ~= b.entry.key; + import core.lifetime; + () @trusted { copyEmplace(b.entry.key, res[i++]); }(); + } + return res; +} - Bucket* findSlotInsert(immutable size_t hash) +/// foreach opApply over all values +/// Note: +/// emulated by the compiler during CTFE +int _d_aaApply(K, V, DG)(inout V[K] a, DG dg) +{ + auto aa = () @trusted { return cast(AA!(K, V))_toAA!(K, V)(a); }(); + if (aa.empty) + return 0; + + foreach (b; aa.buckets) { - for (size_t i = hash & mask, j = 1;; ++j) + if (!b.filled) + continue; + if (auto res = dg(b.entry.value)) + return res; + } + return 0; +} + +int _d_aaApply(K, V, DG)(shared V[K] a, DG dg) +{ + return _d_aaApply!(K, V, DG)(cast(V[K]) a, dg); +} + +int _d_aaApply(K, V, DG)(shared const V[K] a, DG dg) +{ + return _d_aaApply!(K, V, DG)(cast(const V[K]) a, dg); +} + +int _d_aaApply(K, V, DG)(immutable V[K] a, DG dg) +{ + return _d_aaApply!(K, V, DG)(cast(const V[K]) a, dg); +} + +/// foreach opApply over all key/value pairs +/// Note: +/// emulated by the compiler during CTFE +int _d_aaApply2(K, V, DG)(inout V[K] a, DG dg) +{ + auto aa = () @trusted { return cast(AA!(K, V))_toAA!(K, V)(a); }(); + if (aa.empty) + return 0; + + foreach (b; aa.buckets) + { + if (!b.filled) + continue; + if (auto res = dg(b.entry.key, b.entry.value)) + return res; + } + return 0; +} + +int _d_aaApply2(K, V, DG)(shared V[K] a, DG dg) +{ + return _d_aaApply2!(K, V, DG)(cast(V[K]) a, dg); +} + +int _d_aaApply2(K, V, DG)(shared const V[K] a, DG dg) +{ + return _d_aaApply2!(K, V, DG)(cast(const V[K]) a, dg); +} + +int _d_aaApply2(K, V, DG)(immutable V[K] a, DG dg) +{ + return _d_aaApply2!(K, V, DG)(cast(const V[K]) a, dg); +} + +/** Construct an associative array of type ti from corresponding keys and values. + * Called for an AA literal `[k1:v1, k2:v2]`. + * Params: + * keys = array of keys + * vals = array of values + * Returns: + * A new associative array opaque pointer, or null if `keys` is empty. + */ +Impl!(K, V)* _d_assocarrayliteralTX(K, V)(K[] keys, V[] vals) +{ + assert(keys.length == vals.length); + + immutable length = keys.length; + + if (!length) + return null; + + auto aa = new Impl!(K, V)(nextpow2(INIT_DEN * length / INIT_NUM)); + size_t duplicates = 0; + foreach (i; 0 .. length) + { + immutable hash = aa.calcHash(keys[i]); + + auto p = aa.findSlotLookup!K(hash, keys[i]); + if (p) { - if (buckets[i].hash == HASH_EMPTY) - return &buckets[i]; - i = (i + j) & mask; + static if (__traits(compiles, p.entry.value = vals[i])) // immutable? + p.entry.value = vals[i]; + else + p.entry = _newEntry!(K, V)(keys[i], vals[i]); + duplicates++; + continue; + } + auto pi = aa.findSlotInsert(hash); + p = &aa.buckets[pi]; + p.hash = hash; + p.entry = _newEntry!(K, V)(keys[i], vals[i]); // todo: move key and value? + aa.firstUsed = min(aa.firstUsed, cast(uint)pi); + } + aa.used = cast(uint) (length - duplicates); + return aa; +} + +/// compares 2 AAs for equality +bool _aaEqual(T : AA!(K, V), K, V)(scope T aa1, scope T aa2) +{ + if (aa1 is aa2) + return true; + + immutable len = aa1.length; + if (len != aa2.length) + return false; + + if (!len) // both empty + return true; + + bool sameHash = aa1.hashFn == aa2.hashFn; // can be different if coming from template/rt + // compare the entries + foreach (b1; aa1.buckets[aa1.firstUsed .. $]) + { + if (!b1.filled) + continue; + hash_t hash = sameHash ? b1.hash : aa2.calcHash(b1.entry.key); + auto pb2 = aa2.findSlotLookup!K(hash, b1.entry.key); + if (pb2 is null || !pure_keyEqual!(V, V)(b1.entry.value, pb2.entry.value)) // rarely, inference on opEqual breaks builds here + return false; + } + return true; +} + +/// compares 2 AAs for equality (compiler hook) +bool _d_aaEqual(K, V)(scope const V[K] a1, scope const V[K] a2) +{ + scope aa1 = _toAA!(K, V)(a1); + scope aa2 = _toAA!(K, V)(a2); + return _aaEqual(aa1, aa2); +} + +/// callback from TypeInfo_AssociativeArray.equals (ignore const for now) +bool _aaOpEqual(K, V)(scope /* const */ AA!(K, V)* aa1, scope /* const */ AA!(K, V)* aa2) +{ + return _aaEqual(*aa1, *aa2); +} + +/// compute a hash callback from TypeInfo_AssociativeArray.xtoHash (ignore scope const for now) +hash_t _aaGetHash(K, V)(/* scope const */ AA!(K, V)* paa) +{ + const aa = *paa; + + if (aa.empty) + return 0; + + size_t h; + foreach (b; aa.buckets) + { + // use addition here, so that hash is independent of element order + if (b.filled) + h += hashOf(pure_hashOf!V(b.entry.value), pure_hashOf!K(b.entry.key)); + } + + return h; +} + +/** + * _aaRange implements a ForwardRange + */ +struct AARange(K, V) +{ + alias Key = substInout!K; + alias Value = substInout!V; + + Impl!(Key, Value)* impl; + size_t idx; + alias impl this; +} + +AARange!(K, V) _aaRange(K, V)(V[K] a) +{ + auto aa = _toAA!(K, V)(a); + if (!aa) + return AARange!(K, V)(); + + foreach (i; aa.firstUsed .. aa.dim) + { + if (aa.buckets[i].filled) + return AARange!(K, V)(aa, i); + } + return AARange!(K, V)(aa, aa.dim); +} + +bool _aaRangeEmpty(K, V)(AARange!(K, V) r) +{ + return r.impl is null || r.idx >= r.dim; +} + +K* _aaRangeFrontKey(K, V)(AARange!(K, V) r) +{ + assert(!_aaRangeEmpty(r)); + if (r.idx >= r.dim) + return null; + auto entry = r.buckets[r.idx].entry; + return entry is null ? null : &r.buckets[r.idx].entry.key; +} + +V* _aaRangeFrontValue(K, V)(AARange!(K, V) r) +{ + assert(!_aaRangeEmpty(r)); + if (r.idx >= r.dim) + return null; + + auto entry = r.buckets[r.idx].entry; + return entry is null ? null : &r.buckets[r.idx].entry.value; +} + +void _aaRangePopFront(K, V)(ref AARange!(K, V) r) +{ + if (r.idx >= r.dim) return; + for (++r.idx; r.idx < r.dim; ++r.idx) + { + if (r.buckets[r.idx].filled) + break; + } +} + +// test postblit for AA literals +unittest +{ + import core.memory; + + static struct T + { + ubyte field; + static size_t postblit, dtor; + this(this) + { + ++postblit; + } + + ~this() + { + ++dtor; } } - uint firstUsed = cast(uint) buckets.length; - foreach (k, v; src) - { - immutable h = hashOf(k).mix | HASH_FILLED_MARK; - auto location = findSlotInsert(h); - immutable nfu = cast(uint) (location - &buckets[0]); - if (nfu < firstUsed) - firstUsed = nfu; - *location = Bucket(h, new E(k, v)); - } - - enum flags = () { - import core.internal.traits; - Impl.Flags flags; - static if (__traits(hasPostblit, K)) - flags |= flags.keyHasPostblit; - static if (hasIndirections!E) - flags |= flags.hasPointers; - return flags; - } (); - // return the new implementation - return AAShell(new Impl(buckets, cast(uint)srclen, 0, typeid(E), firstUsed, - K.sizeof, V.sizeof, E.value.offsetof, flags, hashFn)); + T t; + auto aa1 = [0 : t, 1 : t]; + assert(T.dtor == 2 && T.postblit == 4); + aa1[0] = t; + assert(T.dtor == 3 && T.postblit == 5); + + T.dtor = 0; + T.postblit = 0; + + auto aa2 = [0 : t, 1 : t, 0 : t]; // literal with duplicate key => value overwritten + assert(T.dtor == 4 && T.postblit == 6); + + T.dtor = 0; + T.postblit = 0; + + auto aa3 = [t : 0]; + assert(T.dtor == 1 && T.postblit == 2); + aa3[t] = 1; + assert(T.dtor == 1 && T.postblit == 2); + aa3.remove(t); + assert(T.dtor == 1 && T.postblit == 2); + aa3[t] = 2; + assert(T.dtor == 1 && T.postblit == 3); + + // dtor will be called by GC finalizers + aa1 = null; + aa2 = null; + aa3 = null; + auto dtor1 = typeid(TypeInfo_AssociativeArray.Entry!(int, T)).xdtor; + GC.runFinalizers((cast(char*)dtor1)[0 .. 1]); + auto dtor2 = typeid(TypeInfo_AssociativeArray.Entry!(T, int)).xdtor; + GC.runFinalizers((cast(char*)dtor2)[0 .. 1]); + assert(T.dtor == 7 && T.postblit == 3); +} + +// create a binary-compatible AA structure that can be used directly as an +// associative array. +// NOTE: this must only be called during CTFE +AA!(K, V) makeAA(K, V)(V[K] src) @trusted +{ + assert(__ctfe, "makeAA Must only be called at compile time"); + // keys and values are cheap operations in CTFE, so just reuse _d_assocarrayliteralTX + auto impl = _d_assocarrayliteralTX!(K, V)(cast(K[])src.keys, cast(V[])src.values); + auto aa = AA!(K, V)(impl); + return aa; } unittest diff --git a/libphobos/libdruntime/core/internal/traits.d b/libphobos/libdruntime/core/internal/traits.d index 2475559878c..52e3307256b 100644 --- a/libphobos/libdruntime/core/internal/traits.d +++ b/libphobos/libdruntime/core/internal/traits.d @@ -107,11 +107,38 @@ private template substInoutForm(T) { alias substInoutForm = T; // prevent matching to the form of alias-this-ed type } - else static if (is(T : V[K], K, V)) alias substInoutForm = substInout!V[substInout!K]; - else static if (is(T : U[n], U, size_t n)) alias substInoutForm = substInout!U[n]; - else static if (is(T : U[], U)) alias substInoutForm = substInout!U[]; - else static if (is(T : U*, U)) alias substInoutForm = substInout!U*; - else alias substInoutForm = T; + else static if (is(T == V[K], K, V)) alias substInoutForm = substInout!V[substInout!K]; + else static if (is(T == U[n], U, size_t n)) alias substInoutForm = substInout!U[n]; + else static if (is(T == U[], U)) alias substInoutForm = substInout!U[]; + else static if (is(T == U*, U)) alias substInoutForm = substInout!U*; + else alias substInoutForm = T; +} + +unittest +{ + // https://github.com/dlang/dmd/issues/21452 + struct S { int x; } + struct T { int x; alias x this; } + + enum EnumInt { a = 123 } + enum EnumUInt : uint { a = 123 } + enum EnumFloat : float { a = 123 } + enum EnumString : string { a = "123" } + enum EnumStringW : wstring { a = "123" } + enum EnumStruct : S { a = S(7) } + enum EnumAliasThis : T { a = T(7) } + enum EnumDArray : int[] { a = [1] } + enum EnumAArray : int[int] { a = [0 : 1] } + + static assert(substInout!(EnumInt).stringof == "EnumInt"); + static assert(substInout!(inout(EnumUInt)).stringof == "const(EnumUInt)"); + static assert(substInout!(EnumFloat).stringof == "EnumFloat"); + static assert(substInout!(EnumString).stringof == "EnumString"); + static assert(substInout!(inout(EnumStringW)).stringof == "const(EnumStringW)"); + static assert(substInout!(EnumStruct).stringof == "EnumStruct"); + static assert(substInout!(EnumAliasThis).stringof == "EnumAliasThis"); + static assert(substInout!(EnumDArray).stringof == "EnumDArray"); + static assert(substInout!(inout(EnumAArray)[int]).stringof == "const(EnumAArray)[int]"); } /// used to declare an extern(D) function that is defined in a different module diff --git a/libphobos/libdruntime/core/stdc/config.d b/libphobos/libdruntime/core/stdc/config.d index 34f78e03e4a..952ceacb217 100644 --- a/libphobos/libdruntime/core/stdc/config.d +++ b/libphobos/libdruntime/core/stdc/config.d @@ -260,6 +260,23 @@ else version (DigitalMars) else version (Darwin) alias real c_long_double; } + else version (AArch64) + { + version (linux) + alias real c_long_double; + else version (FreeBSD) + alias real c_long_double; + else version (OpenBSD) + alias real c_long_double; + else version (NetBSD) + alias real c_long_double; + else version (DragonFlyBSD) + alias real c_long_double; + else version (Solaris) + alias real c_long_double; + else version (Darwin) + alias real c_long_double; + } } static assert(is(c_long_double), "c_long_double needs to be declared for this platform/architecture."); diff --git a/libphobos/libdruntime/core/stdc/stdarg.d b/libphobos/libdruntime/core/stdc/stdarg.d index c694dfb3ac3..220b27c4cf8 100644 --- a/libphobos/libdruntime/core/stdc/stdarg.d +++ b/libphobos/libdruntime/core/stdc/stdarg.d @@ -130,6 +130,8 @@ else version (AAPCS32) else version (AAPCS64) { alias va_list = core.internal.vararg.aarch64.va_list; + version (DigitalMars) + public import core.internal.vararg.aarch64 : __va_list, __va_list_tag; } else version (RISCV_Any) { diff --git a/libphobos/libdruntime/core/stdcpp/string.d b/libphobos/libdruntime/core/stdcpp/string.d index 0315867da60..3c974177fa3 100644 --- a/libphobos/libdruntime/core/stdcpp/string.d +++ b/libphobos/libdruntime/core/stdcpp/string.d @@ -343,7 +343,7 @@ extern(D): /// inout(T)* data() inout @safe { return _Get_data()._Myptr; } /// - inout(T)[] as_array() scope return inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize]; } + inout(T)[] as_array() return ref scope inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize]; } /// ref inout(T) at(size_type i) inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize][i]; } @@ -1920,7 +1920,7 @@ extern(D): /// inout(T)* data() inout @trusted { return __get_pointer(); } /// - inout(T)[] as_array() scope return inout nothrow @trusted { return __get_pointer()[0 .. size()]; } + inout(T)[] as_array() return ref scope inout nothrow @trusted { return __get_pointer()[0 .. size()]; } /// ref inout(T) at(size_type i) inout nothrow @trusted { return __get_pointer()[0 .. size()][i]; } diff --git a/libphobos/libdruntime/core/sys/linux/sched.d b/libphobos/libdruntime/core/sys/linux/sched.d index 0a940db65ed..97a1478d76a 100644 --- a/libphobos/libdruntime/core/sys/linux/sched.d +++ b/libphobos/libdruntime/core/sys/linux/sched.d @@ -151,6 +151,10 @@ version (CRuntime_Glibc) /* Determine CPU on which the calling thread is running */ int sched_getcpu(); } +else version (CRuntime_Musl) +{ + int sched_getcpu(); +} /* Reassociate the calling thread with namespace referred to by fd */ int setns(int fd, int nstype); diff --git a/libphobos/libdruntime/core/sys/posix/dirent.d b/libphobos/libdruntime/core/sys/posix/dirent.d index cb76573a95b..0e822f556c7 100644 --- a/libphobos/libdruntime/core/sys/posix/dirent.d +++ b/libphobos/libdruntime/core/sys/posix/dirent.d @@ -394,16 +394,14 @@ else version (CRuntime_Musl) struct DIR { + // Managed by OS } - static if ( __USE_FILE_OFFSET64 ) - { - dirent* readdir64(DIR*); - alias readdir64 readdir; - } - else + dirent* readdir(DIR*); + + static if (__USE_LARGEFILE64) { - dirent* readdir(DIR*); + alias readdir64 = readdir; } } else version (CRuntime_UClibc) diff --git a/libphobos/libdruntime/core/sys/posix/dlfcn.d b/libphobos/libdruntime/core/sys/posix/dlfcn.d index 76542c64c8a..ff590b30f7a 100644 --- a/libphobos/libdruntime/core/sys/posix/dlfcn.d +++ b/libphobos/libdruntime/core/sys/posix/dlfcn.d @@ -189,8 +189,7 @@ version (CRuntime_Glibc) void* dli_saddr; } } -else -version (CRuntime_Musl) +else version (CRuntime_Musl) { enum RTLD_LAZY = 1; enum RTLD_NOW = 2; @@ -402,30 +401,6 @@ else version (CRuntime_Bionic) void* dli_saddr; } } -else version (CRuntime_Musl) -{ - enum { - RTLD_LAZY = 1, - RTLD_NOW = 2, - RTLD_NOLOAD = 4, - RTLD_NODELETE = 4096, - RTLD_GLOBAL = 256, - RTLD_LOCAL = 0, - } - int dlclose(void*); - const(char)* dlerror(); - void* dlopen(const scope char*, int); - void* dlsym(void*, const scope char*); - - int dladdr(scope const void *addr, Dl_info *info); - struct Dl_info - { - const(char)* dli_fname; - void* dli_fbase; - const(char)* dli_sname; - void* dli_saddr; - } -} else version (CRuntime_UClibc) { version (X86_Any) diff --git a/libphobos/libdruntime/core/sys/posix/endian.d b/libphobos/libdruntime/core/sys/posix/endian.d new file mode 100644 index 00000000000..3cfd8e4ffbd --- /dev/null +++ b/libphobos/libdruntime/core/sys/posix/endian.d @@ -0,0 +1,60 @@ +/** + * D header file for POSIX. + * + * $(LINK2 https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/endian.h.html posix endian.h) + * + * Standards: The Open Group Base Specifications Issue 8, IEEE Std 1003.1, 2024 Edition + */ +module core.sys.posix.endian; + +version (Posix): +nothrow: +@safe: +@nogc: + +import core.bitop; + +enum LITTLE_ENDIAN = 1234; +enum BIG_ENDIAN = 4321; +enum PDP_ENDIAN = 3412; + +version (LittleEndian) +{ + enum BYTE_ORDER = LITTLE_ENDIAN; + + pragma(inline, true): + ushort htobe16(ushort x) => core.bitop.byteswap(x); + ushort htole16(ushort x) => x; + ushort be16toh(ushort x) => core.bitop.byteswap(x); + ushort le16toh(ushort x) => x; + + uint htobe32(uint x) => core.bitop.bswap(x); + uint htole32(uint x) => x; + uint be32toh(uint x) => core.bitop.bswap(x); + uint le32toh(uint x) => x; + + ulong htobe64(ulong x) => core.bitop.bswap(x); + ulong htole64(ulong x) => x; + ulong be64toh(ulong x) => core.bitop.bswap(x); + ulong le64toh(ulong x) => x; +} +else +{ + enum BYTE_ORDER = BIG_ENDIAN; + + pragma(inline, true): + ushort htobe16(ushort x) => x; + ushort htole16(ushort x) => core.bitop.byteswap(x); + ushort be16toh(ushort x) => x; + ushort le16toh(ushort x) => core.bitop.byteswap(x); + + uint htobe32(uint x) => x; + uint htole32(uint x) => core.bitop.bswap(x); + uint be32toh(uint x) => x; + uint le32toh(uint x) => core.bitop.bswap(x); + + ulong htobe64(ulong x) => x; + ulong htole64(ulong x) => core.bitop.bswap(x); + ulong be64toh(ulong x) => x; + ulong le64toh(ulong x) => core.bitop.bswap(x); +} diff --git a/libphobos/libdruntime/core/sys/posix/sched.d b/libphobos/libdruntime/core/sys/posix/sched.d index ba7ab8984a9..d5798358415 100644 --- a/libphobos/libdruntime/core/sys/posix/sched.d +++ b/libphobos/libdruntime/core/sys/posix/sched.d @@ -66,9 +66,16 @@ version (linux) int sched_priority; int __reserved1; static if (muslRedirTime64) - c_long[2] __reserved2; + c_long[4] __reserved2; else - timespec[2] __reserved2; + { + struct __timespec32 + { + time_t __reserved_1; + c_long __reserved_2; + } + __timespec32[2] __reserved2; + } int __reserved3; } } diff --git a/libphobos/libdruntime/core/sys/posix/stdio.d b/libphobos/libdruntime/core/sys/posix/stdio.d index d3799890261..e2ee0b6085f 100644 --- a/libphobos/libdruntime/core/sys/posix/stdio.d +++ b/libphobos/libdruntime/core/sys/posix/stdio.d @@ -182,33 +182,20 @@ else version (CRuntime_UClibc) } else version (CRuntime_Musl) { - static if ( __USE_FILE_OFFSET64 ) - { - int fgetpos64(FILE*, fpos_t *); - alias fgetpos64 fgetpos; - - FILE* fopen64(const scope char*, const scope char*); - alias fopen64 fopen; - - FILE* freopen64(const scope char*, const scope char*, FILE*); - alias freopen64 freopen; - - int fseek(FILE*, c_long, int); - - int fsetpos64(FILE*, const scope fpos_t*); - alias fsetpos64 fsetpos; + int fgetpos(FILE*, fpos_t *); + FILE* fopen(const scope char*, const scope char*); + FILE* freopen(const scope char*, const scope char*, FILE*); + int fseek(FILE*, c_long, int); + int fsetpos(FILE*, const scope fpos_t*); + FILE* tmpfile(); - FILE* tmpfile64(); - alias tmpfile64 tmpfile; - } - else + static if (__USE_LARGEFILE64) { - int fgetpos(FILE*, fpos_t *); - FILE* fopen(const scope char*, const scope char*); - FILE* freopen(const scope char*, const scope char*, FILE*); - int fseek(FILE*, c_long, int); - int fsetpos(FILE*, const scope fpos_t*); - FILE* tmpfile(); + alias fgetpos64 = fgetpos; + alias fopen64 = fopen; + alias freopen64 = freopen; + alias fsetpos64 = fsetpos; + alias tmpfile64 = tmpfile; } } else version (Solaris) @@ -320,24 +307,13 @@ else version (CRuntime_Musl) { enum L_ctermid = 20; - static if ( __USE_FILE_OFFSET64 ) - { - int fseeko64(FILE*, off_t, int); - alias fseeko64 fseeko; - } - else - { - int fseeko(FILE*, off_t, int); - } + int fseeko(FILE*, off_t, int); + off_t ftello(FILE*); - static if ( __USE_FILE_OFFSET64 ) + static if (__USE_LARGEFILE64) { - off_t ftello64(FILE*); - alias ftello64 ftello; - } - else - { - off_t ftello(FILE*); + alias fseeko64 = fseeko; + alias ftello64 = ftello; } ssize_t getdelim(char**, size_t*, int, FILE*); diff --git a/libphobos/libdruntime/core/sys/posix/stdlib.d b/libphobos/libdruntime/core/sys/posix/stdlib.d index 8dd7b68e869..ea7048ec384 100644 --- a/libphobos/libdruntime/core/sys/posix/stdlib.d +++ b/libphobos/libdruntime/core/sys/posix/stdlib.d @@ -603,17 +603,12 @@ else version (CRuntime_Musl) void srand48(c_long); void srandom(uint); int unlockpt(int); - - static if ( __USE_LARGEFILE64 ) - { - int mkstemp64(char*); - alias mkstemp64 mkstemp; - } - else - { int mkstemp(char*); - } + static if (__USE_LARGEFILE64) + { + alias mkstemp64 = mkstemp; + } } else version (Solaris) { diff --git a/libphobos/libdruntime/core/sys/posix/sys/mman.d b/libphobos/libdruntime/core/sys/posix/sys/mman.d index b1eb9fa8107..42da553c404 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/mman.d +++ b/libphobos/libdruntime/core/sys/posix/sys/mman.d @@ -293,12 +293,13 @@ else version (CRuntime_Bionic) } else version (CRuntime_Musl) { - static if (__USE_LARGEFILE64) void* mmap64(void*, size_t, int, int, int, off_t); - static if (__USE_FILE_OFFSET64) - alias mmap = mmap64; - else - void* mmap(void*, size_t, int, int, int, off_t); + void* mmap(void*, size_t, int, int, int, off_t); int munmap(void*, size_t); + + static if (__USE_LARGEFILE64) + { + alias mmap64 = mmap; + } } else version (CRuntime_UClibc) { diff --git a/libphobos/libdruntime/core/sys/posix/sys/stat.d b/libphobos/libdruntime/core/sys/posix/sys/stat.d index ab79a406750..0dbf4ebb7b6 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/stat.d +++ b/libphobos/libdruntime/core/sys/posix/sys/stat.d @@ -263,8 +263,16 @@ version (linux) { struct stat_t { - c_ulong st_dev; - c_long[3] st_pad1; + version (CRuntime_Musl) + { + dev_t st_dev; + c_long[2] st_pad1; + } + else + { + c_ulong st_dev; + c_long[3] st_pad1; + } ino_t st_ino; mode_t st_mode; nlink_t st_nlink; diff --git a/libphobos/libdruntime/core/sys/posix/sys/statvfs.d b/libphobos/libdruntime/core/sys/posix/sys/statvfs.d index 9405a6d5387..ede7866bae7 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/statvfs.d +++ b/libphobos/libdruntime/core/sys/posix/sys/statvfs.d @@ -97,7 +97,7 @@ else version (CRuntime_Musl) fsfilcnt_t f_files; fsfilcnt_t f_ffree; fsfilcnt_t f_favail; - static if (true /+__BYTE_ORDER == __LITTLE_ENDIAN+/) + version (LittleEndian) { c_ulong f_fsid; byte[2*int.sizeof-c_long.sizeof] __padding; @@ -133,8 +133,8 @@ else version (CRuntime_Musl) int statvfs (const char * file, statvfs_t* buf); int fstatvfs (int fildes, statvfs_t *buf); - alias statvfs statvfs64; - alias fstatvfs fstatvfs64; + alias statvfs64 = statvfs; + alias fstatvfs64 = fstatvfs; } else version (NetBSD) { diff --git a/libphobos/libdruntime/core/thread/threadbase.d b/libphobos/libdruntime/core/thread/threadbase.d index 24bcc822e9b..4f35cc048d8 100644 --- a/libphobos/libdruntime/core/thread/threadbase.d +++ b/libphobos/libdruntime/core/thread/threadbase.d @@ -136,6 +136,10 @@ class ThreadBase package void tlsRTdataInit() nothrow @nogc { m_tlsrtdata = rt_tlsgc_init(); + + // Let the selected GC initialize anything it needs. + import core.internal.gc.proxy : gc_getProxy; + gc_getProxy().initThread(this); } package void initDataStorage() nothrow @@ -149,6 +153,10 @@ class ThreadBase package void destroyDataStorage() nothrow @nogc { + // allow the GC to clean up any resources it allocated for this thread. + import core.internal.gc.proxy : gc_getProxy; + gc_getProxy().cleanupThread(this); + rt_tlsgc_destroy(m_tlsrtdata); m_tlsrtdata = null; } diff --git a/libphobos/libdruntime/object.d b/libphobos/libdruntime/object.d index e9838db4f38..80629ed760a 100644 --- a/libphobos/libdruntime/object.d +++ b/libphobos/libdruntime/object.d @@ -1301,12 +1301,12 @@ class TypeInfo_AssociativeArray : TypeInfo override bool equals(in void* p1, in void* p2) @trusted const { - return !!_aaEqual(this, *cast(const AA*) p1, *cast(const AA*) p2); + return xopEquals(p1, p2); } override hash_t getHash(scope const void* p) nothrow @trusted const { - return _aaGetHash(cast(AA*)p, this); + return xtoHash(p); } // BUG: need to add the rest of the functions @@ -1325,16 +1325,19 @@ class TypeInfo_AssociativeArray : TypeInfo override @property uint flags() nothrow pure const { return 1; } // TypeInfo entry is generated from the type of this template to help rt/aaA.d - static struct Entry(K, V) - { - K key; - V value; - } + private static import core.internal.newaa; + alias Entry(K, V) = core.internal.newaa.Entry!(K, V); TypeInfo value; TypeInfo key; TypeInfo entry; + bool function(scope const void* p1, scope const void* p2) nothrow @safe xopEquals; + hash_t function(scope const void*) nothrow @safe xtoHash; + + alias aaOpEqual(K, V) = core.internal.newaa._aaOpEqual!(K, V); + alias aaGetHash(K, V) = core.internal.newaa._aaGetHash!(K, V); + override @property size_t talign() nothrow pure const { return (char[int]).alignof; @@ -1572,8 +1575,57 @@ class TypeInfo_Delegate : TypeInfo } private extern (C) Object _d_newclass(const TypeInfo_Class ci); -private extern (C) int _d_isbaseof(scope TypeInfo_Class child, - scope const TypeInfo_Class parent) @nogc nothrow pure @safe; // rt.cast_ + +extern(C) int _d_isbaseof(scope ClassInfo oc, scope const ClassInfo c) @nogc nothrow pure @safe +{ + import core.internal.cast_ : areClassInfosEqual; + + if (areClassInfosEqual(oc, c)) + return true; + + do + { + if (oc.base && areClassInfosEqual(oc.base, c)) + return true; + + // Bugzilla 2013: Use depth-first search to calculate offset + // from the derived (oc) to the base (c). + foreach (iface; oc.interfaces) + { + if (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof(iface.classinfo, c)) + return true; + } + + oc = oc.base; + } while (oc); + + return false; +} + +/****************************************** + * Given a pointer: + * If it is an Object, return that Object. + * If it is an interface, return the Object implementing the interface. + * If it is null, return null. + * Else, undefined crash + */ +extern(C) Object _d_toObject(return scope void* p) @nogc nothrow pure @trusted +{ + if (!p) + return null; + + Object o = cast(Object) p; + Interface* pi = **cast(Interface***) p; + + /* Interface.offset lines up with ClassInfo.name.ptr, + * so we rely on pointers never being less than 64K, + * and Objects never being greater. + */ + if (pi.offset < 0x10000) + return cast(Object)(p - pi.offset); + + return o; +} /** * Runtime type information about a class. @@ -2535,8 +2587,27 @@ class Throwable : Object * $(D Throwable) is thrown from inside a $(D catch) block. The originally * caught $(D Exception) will be chained to the new $(D Throwable) via this * field. + * + * If the 0 bit is set in this, it means the next exception is refcounted, + * meaning this object owns that object and should destroy it on + * destruction. + * + * WARNING: This means we are storing an interior pointer to the next in + * chain, to signify that the next in the chain is actually reference + * counted. We rely on the fact that a pointer to a Throwable is not 1-byte + * aligned. It is important to use a local field to indicate reference + * counting since we are not allowed to look at GC-allocated references + * inside the destructor. It is very important to store this as a void*, + * since optimizers can consider an unaligned pointer to not exist. */ - private Throwable nextInChain; + private void* _nextInChainPtr; + + private @property bool _nextIsRefcounted() @trusted scope pure nothrow @nogc const + { + if (__ctfe) + return false; + return (cast(size_t)_nextInChainPtr) & 1; + } private uint _refcount; // 0 : allocated by GC // 1 : allocated by _d_newThrowable() @@ -2549,24 +2620,36 @@ class Throwable : Object * caught $(D Exception) will be chained to the new $(D Throwable) via this * field. */ - @property inout(Throwable) next() @safe inout return scope pure nothrow @nogc { return nextInChain; } + @property inout(Throwable) next() @trusted inout return scope pure nothrow @nogc + { + if (__ctfe) + return cast(inout(Throwable)) _nextInChainPtr; + + return cast(inout(Throwable)) (_nextInChainPtr - _nextIsRefcounted); + } + /** * Replace next in chain with `tail`. * Use `chainTogether` instead if at all possible. */ - @property void next(Throwable tail) @safe scope pure nothrow @nogc + @property void next(Throwable tail) @trusted scope pure nothrow @nogc { + void* newTail = cast(void*)tail; if (tail && tail._refcount) + { ++tail._refcount; // increment the replacement *first* + ++newTail; // indicate ref counting is used + } - auto n = nextInChain; - nextInChain = null; // sever the tail before deleting it + auto n = next; + auto nrc = _nextIsRefcounted; + _nextInChainPtr = null; // sever the tail before deleting it - if (n && n._refcount) + if (nrc) _d_delThrowable(n); // now delete the old tail - nextInChain = tail; // and set the new tail + _nextInChainPtr = newTail; // and set the new tail } /** @@ -2585,7 +2668,7 @@ class Throwable : Object int opApply(scope int delegate(Throwable) dg) { int result = 0; - for (Throwable t = this; t; t = t.nextInChain) + for (Throwable t = this; t; t = t.next) { result = dg(t); if (result) @@ -2608,14 +2691,12 @@ class Throwable : Object return e2; if (!e2) return e1; - if (e2.refcount()) - ++e2.refcount(); - for (auto e = e1; 1; e = e.nextInChain) + for (auto e = e1; 1; e = e.next) { - if (!e.nextInChain) + if (!e.next) { - e.nextInChain = e2; + e.next = e2; break; } } @@ -2625,9 +2706,7 @@ class Throwable : Object @nogc @safe pure nothrow this(string msg, Throwable nextInChain = null) { this.msg = msg; - this.nextInChain = nextInChain; - if (nextInChain && nextInChain._refcount) - ++nextInChain._refcount; + this.next = nextInChain; //this.info = _d_traceContext(); } @@ -2641,8 +2720,9 @@ class Throwable : Object @trusted nothrow ~this() { - if (nextInChain && nextInChain._refcount) - _d_delThrowable(nextInChain); + if (_nextIsRefcounted) + _d_delThrowable(next); + // handle owned traceinfo if (infoDeallocator !is null) { @@ -2771,7 +2851,7 @@ class Exception : Throwable auto e = new Exception("msg"); assert(e.file == __FILE__); assert(e.line == __LINE__ - 2); - assert(e.nextInChain is null); + assert(e._nextInChainPtr is null); assert(e.msg == "msg"); } @@ -2779,7 +2859,7 @@ class Exception : Throwable auto e = new Exception("msg", new Exception("It's an Exception!"), "hello", 42); assert(e.file == "hello"); assert(e.line == 42); - assert(e.nextInChain !is null); + assert(e._nextInChainPtr !is null); assert(e.msg == "msg"); } @@ -2787,7 +2867,7 @@ class Exception : Throwable auto e = new Exception("msg", "hello", 42, new Exception("It's an Exception!")); assert(e.file == "hello"); assert(e.line == 42); - assert(e.nextInChain !is null); + assert(e._nextInChainPtr !is null); assert(e.msg == "msg"); } @@ -2854,7 +2934,7 @@ class Error : Throwable auto e = new Error("msg"); assert(e.file is null); assert(e.line == 0); - assert(e.nextInChain is null); + assert(e._nextInChainPtr is null); assert(e.msg == "msg"); assert(e.bypassedException is null); } @@ -2863,7 +2943,7 @@ class Error : Throwable auto e = new Error("msg", new Exception("It's an Exception!")); assert(e.file is null); assert(e.line == 0); - assert(e.nextInChain !is null); + assert(e._nextInChainPtr !is null); assert(e.msg == "msg"); assert(e.bypassedException is null); } @@ -2872,60 +2952,27 @@ class Error : Throwable auto e = new Error("msg", "hello", 42, new Exception("It's an Exception!")); assert(e.file == "hello"); assert(e.line == 42); - assert(e.nextInChain !is null); + assert(e._nextInChainPtr !is null); assert(e.msg == "msg"); assert(e.bypassedException is null); } } -extern (C) -{ - // from druntime/src/rt/aaA.d - - private struct AA { void* impl; } - // size_t _aaLen(in AA aa) pure nothrow @nogc; - private void* _aaGetY(scope AA* paa, const TypeInfo_AssociativeArray ti, const size_t valsz, const scope void* pkey) pure nothrow; - private void* _aaGetX(scope AA* paa, const TypeInfo_AssociativeArray ti, const size_t valsz, const scope void* pkey, out bool found) pure nothrow; - // inout(void)* _aaGetRvalueX(inout AA aa, in TypeInfo keyti, in size_t valsz, in void* pkey); - inout(void[]) _aaValues(inout AA aa, const size_t keysz, const size_t valsz, const TypeInfo tiValueArray) pure nothrow; - inout(void[]) _aaKeys(inout AA aa, const size_t keysz, const TypeInfo tiKeyArray) pure nothrow; - void* _aaRehash(AA* paa, const scope TypeInfo keyti) pure nothrow; - void _aaClear(AA aa) pure nothrow; - - // alias _dg_t = extern(D) int delegate(void*); - // int _aaApply(AA aa, size_t keysize, _dg_t dg); +public import core.internal.newaa : _d_aaIn, _d_aaDel, _d_aaNew, _d_aaEqual, _d_assocarrayliteralTX; +public import core.internal.newaa : _d_aaLen, _d_aaGetY, _d_aaGetRvalueX, _d_aaApply, _d_aaApply2; +// public import core.exception : onRangeError; // causes extra messages with -transition=fields +extern (C) noreturn _d_arraybounds(string file, uint line) @trusted pure nothrow @nogc; - // alias _dg2_t = extern(D) int delegate(void*, void*); - // int _aaApply2(AA aa, size_t keysize, _dg2_t dg); +private import core.internal.newaa; - private struct AARange { AA impl; size_t idx; } - AARange _aaRange(AA aa) pure nothrow @nogc @safe; - bool _aaRangeEmpty(AARange r) pure nothrow @nogc @safe; - void* _aaRangeFrontKey(AARange r) pure nothrow @nogc @safe; - void* _aaRangeFrontValue(AARange r) pure nothrow @nogc @safe; - void _aaRangePopFront(ref AARange r) pure nothrow @nogc @safe; - - int _aaEqual(scope const TypeInfo tiRaw, scope const AA aa1, scope const AA aa2); - hash_t _aaGetHash(scope const AA* aa, scope const TypeInfo tiRaw) nothrow; - - /* - _d_assocarrayliteralTX marked as pure, because aaLiteral can be called from pure code. - This is a typesystem hole, however this is existing hole. - Early compiler didn't check purity of toHash or postblit functions, if key is a UDT thus - copiler allowed to create AA literal with keys, which have impure unsafe toHash methods. - */ - void* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys, void[] values) pure; -} - -void* aaLiteral(Key, Value)(Key[] keys, Value[] values) @trusted pure +void* aaLiteral(Key, Value)(Key[] keys, Value[] values) { - return _d_assocarrayliteralTX(typeid(Value[Key]), *cast(void[]*)&keys, *cast(void[]*)&values); + return _d_assocarrayliteralTX(keys, values); } // Lower an Associative Array to a newaa struct for static initialization. auto _aaAsStruct(K, V)(V[K] aa) @safe { - import core.internal.newaa : makeAA; assert(__ctfe); return makeAA!(K, V)(aa); } @@ -2939,13 +2986,13 @@ alias AssociativeArray(Key, Value) = Value[Key]; */ void clear(Value, Key)(Value[Key] aa) @trusted { - _aaClear(*cast(AA *) &aa); + _aaClear(aa); } /** ditto */ void clear(Value, Key)(Value[Key]* aa) @trusted { - _aaClear(*cast(AA *) aa); + (*aa).clear(); } /// @@ -2982,32 +3029,30 @@ void clear(Value, Key)(Value[Key]* aa) @trusted * aa = The associative array. * Returns: * The rehashed associative array. + * Note: + * emulated by the compiler during CTFE */ -T rehash(T : Value[Key], Value, Key)(T aa) +Value[Key] rehash(Value, Key)(Value[Key] aa) { - _aaRehash(cast(AA*)&aa, typeid(Value[Key])); - return aa; + return _aaRehash(aa); } /** ditto */ -T rehash(T : Value[Key], Value, Key)(T* aa) +Value[Key] rehash(T : Value[Key], Value, Key)(T* aa) { - _aaRehash(cast(AA*)aa, typeid(Value[Key])); - return *aa; + return (*aa).rehash(); // CTFE only intercepts the non-pointer overload } /** ditto */ -T rehash(T : shared Value[Key], Value, Key)(T aa) +Value[Key] rehash(T : shared Value[Key], Value, Key)(auto ref T aa) { - _aaRehash(cast(AA*)&aa, typeid(Value[Key])); - return aa; + return (cast(Value[Key])aa).rehash(); // CTFE only intercepts the V[K] overload } /** ditto */ -T rehash(T : shared Value[Key], Value, Key)(T* aa) +Value[Key] rehash(T : shared Value[Key], Value, Key)(T* aa) { - _aaRehash(cast(AA*)aa, typeid(Value[Key])); - return *aa; + return (cast(Value[Key])*aa).rehash(); // CTFE only intercepts the non-pointer overload } /*********************************** @@ -3015,46 +3060,20 @@ T rehash(T : shared Value[Key], Value, Key)(T* aa) * the associative array into it. * Params: * aa = The associative array. + * Note: + * emulated by the compiler during CTFE */ -V[K] dup(T : V[K], K, V)(T aa) +auto dup(T : V[K], K, V)(T aa) { - //pragma(msg, "K = ", K, ", V = ", V); - // Bug10720 - check whether V is copyable static assert(is(typeof({ V v = aa[K.init]; })), "cannot call " ~ T.stringof ~ ".dup because " ~ V.stringof ~ " is not copyable"); - V[K] result; - - //foreach (k, ref v; aa) - // result[k] = v; // Bug13701 - won't work if V is not mutable - - ref V duplicateElem(ref K k, ref const V v) @trusted pure nothrow - { - import core.stdc.string : memcpy; - - void* pv = _aaGetY(cast(AA*)&result, typeid(V[K]), V.sizeof, &k); - memcpy(pv, &v, V.sizeof); - return *cast(V*)pv; - } - - foreach (k, ref v; aa) - { - static if (!__traits(hasPostblit, V)) - duplicateElem(k, v); - else static if (__traits(isStaticArray, V)) - _doPostblit(duplicateElem(k, v)[]); - else static if (!is(typeof(v.__xpostblit())) && is(immutable V == immutable UV, UV)) - (() @trusted => *cast(UV*) &duplicateElem(k, v))().__xpostblit(); - else - duplicateElem(k, v).__xpostblit(); - } - - return result; + return _aaDup(aa); } /** ditto */ -V[K] dup(T : V[K], K, V)(T* aa) +auto dup(T : V[K], K, V)(T* aa) { return (*aa).dup; } @@ -3069,14 +3088,14 @@ V[K] dup(T : V[K], K, V)(T* aa) } // this should never be made public. -private AARange _aaToRange(T: V[K], K, V)(ref T aa) pure nothrow @nogc @safe +private auto _aaToRange(K, V)(auto ref inout V[K] aa) @trusted { - // ensure we are dealing with a genuine AA. - static if (is(const(V[K]) == const(T))) - alias realAA = aa; - else - const(V[K]) realAA = aa; - return _aaRange(() @trusted { return *cast(AA*)&realAA; } ()); + import core.internal.traits : substInout; + + alias K2 = substInout!K; + alias V2 = substInout!V; + auto aa2 = cast(V2[K2])aa; + return _aaRange(aa2); } /*********************************** @@ -3106,25 +3125,27 @@ auto byKey(T : V[K], K, V)(T aa) pure nothrow @nogc @safe { import core.internal.traits : substInout; + const(V[K]) aa2 = aa; + static struct Result { - AARange r; + typeof(_aaToRange(aa2)) r; pure nothrow @nogc: @property bool empty() @safe { return _aaRangeEmpty(r); } @property ref front() @trusted { - return *cast(substInout!K*) _aaRangeFrontKey(r); + return *cast(substInout!K*)_aaRangeFrontKey(r); } void popFront() @safe { _aaRangePopFront(r); } @property Result save() { return this; } } - return Result(_aaToRange(aa)); + return Result(_aaToRange(aa2)); } /** ditto */ -auto byKey(T : V[K], K, V)(T* aa) pure nothrow @nogc +auto byKey(K, V)(V[K]* aa) pure nothrow @nogc { return (*aa).byKey(); } @@ -3167,25 +3188,27 @@ auto byValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe { import core.internal.traits : substInout; + const(V[K]) aa2 = aa; + static struct Result { - AARange r; + typeof(_aaToRange(aa2)) r; pure nothrow @nogc: @property bool empty() @safe { return _aaRangeEmpty(r); } @property ref front() @trusted { - return *cast(substInout!V*) _aaRangeFrontValue(r); + return *cast(substInout!V*)_aaRangeFrontValue(r); } void popFront() @safe { _aaRangePopFront(r); } @property Result save() { return this; } } - return Result(_aaToRange(aa)); + return Result(_aaToRange(aa2)); } /** ditto */ -auto byValue(T : V[K], K, V)(T* aa) pure nothrow @nogc +auto byValue(K, V)(V[K]* aa) pure nothrow @nogc { return (*aa).byValue(); } @@ -3240,9 +3263,11 @@ auto byKeyValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe { import core.internal.traits : substInout; + const(V[K]) aa2 = aa; + static struct Result { - AARange r; + typeof(_aaToRange(aa2)) r; pure nothrow @nogc: @property bool empty() @safe { return _aaRangeEmpty(r); } @@ -3252,16 +3277,16 @@ auto byKeyValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe { // We save the pointers here so that the Pair we return // won't mutate when Result.popFront is called afterwards. - private void* keyp; - private void* valp; + private const(substInout!K)* keyp; + private const(substInout!V)* valp; @property ref key() inout @trusted { - return *cast(substInout!K*) keyp; + return *cast(K*)keyp; } @property ref value() inout @trusted { - return *cast(substInout!V*) valp; + return *cast(V*)valp; } } return Pair(_aaRangeFrontKey(r), @@ -3271,7 +3296,7 @@ auto byKeyValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe @property Result save() { return this; } } - return Result(_aaToRange(aa)); + return Result(_aaToRange(aa2)); } /** ditto */ @@ -3304,27 +3329,18 @@ auto byKeyValue(T : V[K], K, V)(T* aa) pure nothrow @nogc * aa = The associative array. * Returns: * A dynamic array containing a copy of the keys. + * Note: + * emulated by the compiler during CTFE */ -Key[] keys(T : Value[Key], Value, Key)(T aa) @property +auto keys(Value, Key)(inout Value[Key] aa) @property { - // ensure we are dealing with a genuine AA. - static if (is(const(Value[Key]) == const(T))) - alias realAA = aa; - else - const(Value[Key]) realAA = aa; - auto res = () @trusted { - auto a = cast(void[])_aaKeys(*cast(inout(AA)*)&realAA, Key.sizeof, typeid(Key[])); - return *cast(Key[]*)&a; - }(); - static if (__traits(hasPostblit, Key)) - _doPostblit(res); - return res; + return _aaKeys!(Key, Value)(aa); } /** ditto */ -Key[] keys(T : Value[Key], Value, Key)(T *aa) @property +auto keys(T : Value[Key], Value, Key)(T *aa) @property { - return (*aa).keys; + return (*aa).keys; // CTFE only intercepts the non-pointer overload } /// @@ -3388,27 +3404,18 @@ Key[] keys(T : Value[Key], Value, Key)(T *aa) @property * aa = The associative array. * Returns: * A dynamic array containing a copy of the values. + * Note: + * emulated by the compiler during CTFE */ -Value[] values(T : Value[Key], Value, Key)(T aa) @property +auto values(Value, Key)(inout Value[Key] aa) @property { - // ensure we are dealing with a genuine AA. - static if (is(const(Value[Key]) == const(T))) - alias realAA = aa; - else - const(Value[Key]) realAA = aa; - auto res = () @trusted { - auto a = cast(void[])_aaValues(*cast(inout(AA)*)&realAA, Key.sizeof, Value.sizeof, typeid(Value[])); - return *cast(Value[]*)&a; - }(); - static if (__traits(hasPostblit, Value)) - _doPostblit(res); - return res; + return _aaValues!(Key, Value)(aa); } /** ditto */ -Value[] values(T : Value[Key], Value, Key)(T *aa) @property +auto values(T : Value[Key], Value, Key)(T *aa) @property { - return (*aa).values; + return (*aa).values; // CTFE only intercepts the non-pointer overload } /// @@ -3508,25 +3515,11 @@ inout(V) get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue) ref V require(K, V)(ref V[K] aa, K key, lazy V value = V.init) { bool found; - // if key is @safe-ly copyable, `require` can infer @safe - static if (isSafeCopyable!K) - { - auto p = () @trusted - { - return cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found); - } (); - } - else - { - auto p = cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found); - } + auto p = _aaGetX(aa, key, found); if (found) return *p; - else - { - *p = value; // Not `return (*p = value)` since if `=` is overloaded - return *p; // this might not return a ref to the left-hand side. - } + *p = value; // Not `return (*p = value)` since if `=` is overloaded + return *p; // this might not return a ref to the left-hand side. } /// @@ -3545,7 +3538,7 @@ private enum bool isSafeCopyable(T) = is(typeof(() @safe { union U { T x; } T *x * Calls `create` if `key` doesn't exist in the associative array, * otherwise calls `update`. * `create` returns a corresponding value for `key`. - * `update` accepts a key parameter. If it returns a value, the value is + * `update` accepts a value parameter. If it returns a value, the value is * set for `key`. * Params: * aa = The associative array. @@ -3553,24 +3546,13 @@ private enum bool isSafeCopyable(T) = is(typeof(() @safe { union U { T x; } T *x * create = The callable to create a value for `key`. * Must return V. * update = The callable to call if `key` exists. - * Takes a K argument, returns a V or void. + * Takes a V argument, returns a V or void. */ void update(K, V, C, U)(ref V[K] aa, K key, scope C create, scope U update) if (is(typeof(create()) : V) && (is(typeof(update(aa[K.init])) : V) || is(typeof(update(aa[K.init])) == void))) { bool found; - // if key is @safe-ly copyable, `update` may infer @safe - static if (isSafeCopyable!K) - { - auto p = () @trusted - { - return cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found); - } (); - } - else - { - auto p = cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found); - } + auto p = _aaGetX(aa, key, found); if (!found) *p = create(); else @@ -4010,10 +3992,6 @@ size_t reserve(T)(ref T[] arr, size_t newcapacity) pure nothrow @trusted a.reserve(10); } -// HACK: This is a lie. `_d_arrayshrinkfit` is not `nothrow`, but this lie is necessary -// for now to prevent breaking code. -private extern (C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) nothrow; - /** Assume that it is safe to append to this array. Appends made to this array after calling this function may append in place, even if the array was a @@ -4031,7 +4009,13 @@ Returns: */ auto ref inout(T[]) assumeSafeAppend(T)(auto ref inout(T[]) arr) nothrow @system { - _d_arrayshrinkfit(typeid(T[]), *(cast(void[]*)&arr)); + import core.internal.array.capacity : _d_arrayshrinkfit; + import core.internal.traits : Unqual; + + alias Unqual_Tarr = Unqual!T[]; + enum isshared = is(T == shared); + _d_arrayshrinkfit(cast(Unqual_Tarr)arr, isshared); + return arr; } @@ -4703,6 +4687,7 @@ version (D_ProfileGC) public import core.internal.array.construction : _d_newarrayTTrace; public import core.internal.array.construction : _d_newarraymTXTrace; public import core.internal.array.capacity: _d_arraysetlengthTTrace; + public import core.internal.array.construction : _d_arrayliteralTXTrace; } public import core.internal.array.appending : _d_arrayappendcTX; public import core.internal.array.comparison : __cmp; @@ -4713,10 +4698,12 @@ public import core.internal.array.construction : _d_arrayctor; public import core.internal.array.construction : _d_arraysetctor; public import core.internal.array.construction : _d_newarrayT; public import core.internal.array.construction : _d_newarraymTX; +public import core.internal.array.construction : _d_arrayliteralTX; public import core.internal.array.arrayassign : _d_arrayassign_l; public import core.internal.array.arrayassign : _d_arrayassign_r; public import core.internal.array.arrayassign : _d_arraysetassign; public import core.internal.array.capacity : _d_arraysetlengthT; +public import core.internal.cast_: _d_cast; public import core.internal.dassert: _d_assert_fail; diff --git a/libphobos/libdruntime/rt/aaA.d b/libphobos/libdruntime/rt/aaA.d deleted file mode 100644 index 3b4bee04058..00000000000 --- a/libphobos/libdruntime/rt/aaA.d +++ /dev/null @@ -1,867 +0,0 @@ -/** - * Implementation of associative arrays. - * - * Copyright: Copyright Digital Mars 2000 - 2015. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Martin Nowak - * Source: $(DRUNTIMESRC rt/_aaA.d) - */ -module rt.aaA; - -/// AA version for debuggers, bump whenever changing the layout -extern (C) immutable int _aaVersion = 1; - -import core.memory : GC; -import core.internal.util.math : min, max; - -// grow threshold -private enum GROW_NUM = 4; -private enum GROW_DEN = 5; -// shrink threshold -private enum SHRINK_NUM = 1; -private enum SHRINK_DEN = 8; -// grow factor -private enum GROW_FAC = 4; -// growing the AA doubles it's size, so the shrink threshold must be -// smaller than half the grow threshold to have a hysteresis -static assert(GROW_FAC * SHRINK_NUM * GROW_DEN < GROW_NUM * SHRINK_DEN); -// initial load factor (for literals), mean of both thresholds -private enum INIT_NUM = (GROW_DEN * SHRINK_NUM + GROW_NUM * SHRINK_DEN) / 2; -private enum INIT_DEN = SHRINK_DEN * GROW_DEN; - -private enum INIT_NUM_BUCKETS = 8; -// magic hash constants to distinguish empty, deleted, and filled buckets -private enum HASH_EMPTY = 0; -private enum HASH_DELETED = 0x1; -private enum HASH_FILLED_MARK = size_t(1) << 8 * size_t.sizeof - 1; - -/// Opaque AA wrapper -struct AA -{ - Impl* impl; - alias impl this; - - private @property bool empty() const pure nothrow @nogc @safe - { - return impl is null || !impl.length; - } -} - -private struct Impl -{ -private: - this(scope const TypeInfo_AssociativeArray ti, size_t sz = INIT_NUM_BUCKETS) nothrow - { - keysz = cast(uint) ti.key.tsize; - valsz = cast(uint) ti.value.tsize; - buckets = allocBuckets(sz); - firstUsed = cast(uint) buckets.length; - valoff = cast(uint) talign(keysz, ti.value.talign); - hashFn = &ti.key.getHash; - - import rt.lifetime : hasPostblit, unqualify; - - if (hasPostblit(unqualify(ti.key))) - flags |= Flags.keyHasPostblit; - if ((ti.key.flags | ti.value.flags) & 1) - flags |= Flags.hasPointers; - - entryTI = ti.entry; - } - - Bucket[] buckets; - uint used; - uint deleted; - const(TypeInfo) entryTI; - uint firstUsed; - immutable uint keysz; - immutable uint valsz; - immutable uint valoff; - Flags flags; - - // function that calculates hash of a key. Set on creation - // the parameter is a pointer to the key. - size_t delegate(scope const void*) nothrow hashFn; - - enum Flags : ubyte - { - none = 0x0, - keyHasPostblit = 0x1, - hasPointers = 0x2, - } - - @property size_t length() const pure nothrow @nogc @safe - { - assert(used >= deleted); - return used - deleted; - } - - @property size_t dim() const pure nothrow @nogc @safe - { - return buckets.length; - } - - @property size_t mask() const pure nothrow @nogc - { - return dim - 1; - } - - // find the first slot to insert a value with hash - inout(Bucket)* findSlotInsert(size_t hash) inout pure nothrow @nogc - { - for (size_t i = hash & mask, j = 1;; ++j) - { - if (!buckets[i].filled) - return &buckets[i]; - i = (i + j) & mask; - } - } - - // lookup a key - inout(Bucket)* findSlotLookup(size_t hash, scope const void* pkey, scope const TypeInfo keyti) inout - { - for (size_t i = hash & mask, j = 1;; ++j) - { - if (buckets[i].hash == hash && keyti.equals(pkey, buckets[i].entry)) - return &buckets[i]; - else if (buckets[i].empty) - return null; - i = (i + j) & mask; - } - } - - void grow(scope const TypeInfo keyti) pure nothrow - { - // If there are so many deleted entries, that growing would push us - // below the shrink threshold, we just purge deleted entries instead. - if (length * SHRINK_DEN < GROW_FAC * dim * SHRINK_NUM) - resize(dim); - else - resize(GROW_FAC * dim); - } - - void shrink(scope const TypeInfo keyti) pure nothrow - { - if (dim > INIT_NUM_BUCKETS) - resize(dim / GROW_FAC); - } - - void resize(size_t ndim) pure nothrow - { - auto obuckets = buckets; - buckets = allocBuckets(ndim); - - foreach (ref b; obuckets[firstUsed .. $]) - if (b.filled) - *findSlotInsert(b.hash) = b; - - firstUsed = 0; - used -= deleted; - deleted = 0; - GC.free(obuckets.ptr); // safe to free b/c impossible to reference - } - - void clear() pure nothrow @trusted - { - import core.stdc.string : memset; - // clear all data, but don't change bucket array length - memset(&buckets[firstUsed], 0, (buckets.length - firstUsed) * Bucket.sizeof); - deleted = used = 0; - firstUsed = cast(uint) dim; - } -} - -//============================================================================== -// Bucket -//------------------------------------------------------------------------------ - -private struct Bucket -{ -private pure nothrow @nogc: - size_t hash; - void* entry; - - @property bool empty() const - { - return hash == HASH_EMPTY; - } - - @property bool deleted() const - { - return hash == HASH_DELETED; - } - - @property bool filled() const @safe - { - return cast(ptrdiff_t) hash < 0; - } -} - -Bucket[] allocBuckets(size_t dim) @trusted pure nothrow -{ - enum attr = GC.BlkAttr.NO_INTERIOR; - immutable sz = dim * Bucket.sizeof; - return (cast(Bucket*) GC.calloc(sz, attr))[0 .. dim]; -} - -//============================================================================== -// Entry -//------------------------------------------------------------------------------ - -private void* allocEntry(scope const Impl* aa, scope const void* pkey) -{ - import rt.lifetime : _d_newitemU; - import core.stdc.string : memcpy, memset; - - immutable akeysz = aa.valoff; - void* res = void; - if (aa.entryTI) - res = _d_newitemU(aa.entryTI); - else - { - auto flags = (aa.flags & Impl.Flags.hasPointers) ? 0 : GC.BlkAttr.NO_SCAN; - res = GC.malloc(akeysz + aa.valsz, flags); - } - - memcpy(res, pkey, aa.keysz); // copy key - memset(res + akeysz, 0, aa.valsz); // zero value - - return res; -} - -private bool hasDtor(const TypeInfo ti) pure nothrow -{ - import rt.lifetime : unqualify; - - if (typeid(ti) is typeid(TypeInfo_Struct)) - if ((cast(TypeInfo_Struct) cast(void*) ti).xdtor) - return true; - if (typeid(ti) is typeid(TypeInfo_StaticArray)) - return hasDtor(unqualify(ti.next)); - - return false; -} - -private immutable(void)* getRTInfo(const TypeInfo ti) pure nothrow -{ - // classes are references - const isNoClass = ti && typeid(ti) !is typeid(TypeInfo_Class); - return isNoClass ? ti.rtInfo() : rtinfoHasPointers; -} - -unittest -{ - void test(K, V)() - { - static struct Entry - { - K key; - V val; - } - auto keyti = typeid(K); - auto valti = typeid(V); - auto valrti = getRTInfo(valti); - auto keyrti = getRTInfo(keyti); - - auto impl = new Impl(typeid(V[K])); - if (valrti is rtinfoNoPointers && keyrti is rtinfoNoPointers) - { - assert(!(impl.flags & Impl.Flags.hasPointers)); - } - else if (valrti is rtinfoHasPointers && keyrti is rtinfoHasPointers) - { - assert(impl.flags & Impl.Flags.hasPointers); - } - else - { - auto rtInfo = cast(size_t*) impl.entryTI.rtInfo(); - auto refInfo = cast(size_t*) typeid(Entry).rtInfo(); - assert(rtInfo[0] == refInfo[0]); // size - enum bytesPerWord = 8 * size_t.sizeof * (void*).sizeof; - size_t words = (rtInfo[0] + bytesPerWord - 1) / bytesPerWord; - foreach (i; 0 .. words) - assert(rtInfo[1 + i] == refInfo[i + 1]); - } - } - test!(long, int)(); - test!(string, string); - test!(ubyte[16], Object); - - static struct Small - { - ubyte[16] guid; - string name; - } - test!(string, Small); - - static struct Large - { - ubyte[1024] data; - string[412] names; - ubyte[1024] moredata; - } - version (OnlyLowMemUnittests) {} else - test!(Large, Large); -} - -//============================================================================== -// Helper functions -//------------------------------------------------------------------------------ - -private size_t talign(size_t tsize, size_t algn) @safe pure nothrow @nogc -{ - immutable mask = algn - 1; - assert(!(mask & algn)); - return (tsize + mask) & ~mask; -} - -// mix hash to "fix" bad hash functions -private size_t mix(size_t h) @safe pure nothrow @nogc -{ - // final mix function of MurmurHash2 - enum m = 0x5bd1e995; - h ^= h >> 13; - h *= m; - h ^= h >> 15; - return h; -} - -private size_t calcHash(scope const void *pkey, scope const Impl* impl) nothrow -{ - immutable hash = impl.hashFn(pkey); - // highest bit is set to distinguish empty/deleted from filled buckets - return mix(hash) | HASH_FILLED_MARK; -} - -private size_t nextpow2(const size_t n) pure nothrow @nogc -{ - import core.bitop : bsr; - - if (!n) - return 1; - - const isPowerOf2 = !((n - 1) & n); - return 1 << (bsr(n) + !isPowerOf2); -} - -pure nothrow @nogc unittest -{ - // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 - foreach (const n, const pow2; [1, 1, 2, 4, 4, 8, 8, 8, 8, 16]) - assert(nextpow2(n) == pow2); -} - -//============================================================================== -// API Implementation -//------------------------------------------------------------------------------ - -/** Allocate associative array data. - * Called for `new SomeAA` expression. - * Params: - * ti = TypeInfo for the associative array - * Returns: - * A new associative array. - */ -extern (C) Impl* _aaNew(const TypeInfo_AssociativeArray ti) -{ - return new Impl(ti); -} - -/// Determine number of entries in associative array. -extern (C) size_t _aaLen(scope const AA aa) pure nothrow @nogc -{ - return aa ? aa.length : 0; -} - -/****************************** - * Lookup *pkey in aa. - * Called only from implementation of (aa[key]) expressions when value is mutable. - * Params: - * paa = associative array opaque pointer - * ti = TypeInfo for the associative array - * valsz = ignored - * pkey = pointer to the key value - * 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* _aaGetY(scope AA* paa, const TypeInfo_AssociativeArray ti, - const size_t valsz, scope const void* pkey) -{ - bool found; - return _aaGetX(paa, ti, valsz, pkey, found); -} - -/****************************** - * Lookup *pkey in aa. - * Called only from implementation of require - * Params: - * paa = 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(scope AA* paa, const TypeInfo_AssociativeArray ti, - const size_t valsz, scope const void* pkey, out bool found) -{ - // lazily alloc implementation - AA aa = *paa; - if (aa is null) - { - aa = new Impl(ti); - *paa = aa; - } - - // get hash and bucket for key - immutable hash = calcHash(pkey, aa); - - // 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) - --aa.deleted; - // check load factor and possibly grow - else if (++aa.used * GROW_DEN > aa.dim * GROW_NUM) - { - aa.grow(ti.key); - p = aa.findSlotInsert(hash); - assert(p.empty); - } - - // update search cache and allocate entry - aa.firstUsed = min(aa.firstUsed, cast(uint)(p - aa.buckets.ptr)); - p.hash = hash; - p.entry = allocEntry(aa, pkey); - // postblit for key - if (aa.flags & Impl.Flags.keyHasPostblit) - { - import rt.lifetime : __doPostblit, unqualify; - - __doPostblit(p.entry, aa.keysz, unqualify(ti.key)); - } - // return pointer to value - return p.entry + aa.valoff; -} - -/****************************** - * Lookup *pkey in aa. - * Called only from implementation of (aa[key]) expressions when value is not mutable. - * Params: - * aa = associative array opaque pointer - * keyti = TypeInfo for the key - * valsz = ignored - * pkey = pointer to the key value - * Returns: - * pointer to value if present, null otherwise - */ -extern (C) inout(void)* _aaGetRvalueX(inout AA aa, scope const TypeInfo keyti, const size_t valsz, - scope const void* pkey) -{ - return _aaInX(aa, keyti, pkey); -} - -/****************************** - * Lookup *pkey in aa. - * Called only from implementation of (key in aa) expressions. - * Params: - * aa = associative array opaque pointer - * keyti = TypeInfo for the key - * pkey = pointer to the key value - * Returns: - * pointer to value if present, null otherwise - */ -extern (C) inout(void)* _aaInX(inout AA aa, scope const TypeInfo keyti, scope const void* pkey) -{ - if (aa.empty) - return null; - - immutable hash = calcHash(pkey, aa); - if (auto p = aa.findSlotLookup(hash, pkey, keyti)) - return p.entry + aa.valoff; - return null; -} - -/// Delete entry scope const AA, return true if it was present -extern (C) bool _aaDelX(AA aa, scope const TypeInfo keyti, scope const void* pkey) -{ - if (aa.empty) - return false; - - immutable hash = calcHash(pkey, aa); - if (auto p = aa.findSlotLookup(hash, pkey, keyti)) - { - // clear entry - p.hash = HASH_DELETED; - p.entry = null; - - ++aa.deleted; - // `shrink` reallocates, and allocating from a finalizer leads to - // InvalidMemoryError: https://issues.dlang.org/show_bug.cgi?id=21442 - if (aa.length * SHRINK_DEN < aa.dim * SHRINK_NUM && !GC.inFinalizer()) - aa.shrink(keyti); - - return true; - } - return false; -} - -/// Remove all elements from AA. -extern (C) void _aaClear(AA aa) pure nothrow @safe -{ - if (!aa.empty) - { - aa.clear(); - } -} - -/// Rehash AA -extern (C) void* _aaRehash(AA* paa, scope const TypeInfo keyti) pure nothrow -{ - AA aa = *paa; - if (!aa.empty) - aa.resize(nextpow2(INIT_DEN * aa.length / INIT_NUM)); - return aa; -} - -/// Return a GC allocated array of all values -extern (C) inout(void[]) _aaValues(inout AA aa, const size_t keysz, const size_t valsz, - const TypeInfo tiValueArray) pure nothrow -{ - if (aa.empty) - return null; - - import rt.lifetime : _d_newarrayU; - - auto res = _d_newarrayU(tiValueArray, aa.length).ptr; - auto pval = res; - - immutable off = aa.valoff; - foreach (b; aa.buckets[aa.firstUsed .. $]) - { - if (!b.filled) - continue; - pval[0 .. valsz] = cast(void[]) b.entry[off .. valsz + off]; - pval += valsz; - } - // postblit is done in object.values - return (cast(inout(void)*) res)[0 .. aa.length]; // fake length, return number of elements -} - -/// Return a GC allocated array of all keys -extern (C) inout(void[]) _aaKeys(inout AA aa, const size_t keysz, const TypeInfo tiKeyArray) pure nothrow -{ - if (aa.empty) - return null; - - import rt.lifetime : _d_newarrayU; - - auto res = _d_newarrayU(tiKeyArray, aa.length).ptr; - auto pkey = res; - - foreach (b; aa.buckets[aa.firstUsed .. $]) - { - if (!b.filled) - continue; - pkey[0 .. keysz] = cast(void[]) b.entry[0 .. keysz]; - pkey += keysz; - } - // postblit is done in object.keys - return (cast(inout(void)*) res)[0 .. aa.length]; // fake length, return number of elements -} - -// opApply callbacks are extern(D) -extern (D) alias dg_t = int delegate(void*); -extern (D) alias dg2_t = int delegate(void*, void*); - -/// foreach opApply over all values -extern (C) int _aaApply(AA aa, const size_t keysz, dg_t dg) -{ - if (aa.empty) - return 0; - - immutable off = aa.valoff; - foreach (b; aa.buckets) - { - if (!b.filled) - continue; - if (auto res = dg(b.entry + off)) - return res; - } - return 0; -} - -/// foreach opApply over all key/value pairs -extern (C) int _aaApply2(AA aa, const size_t keysz, dg2_t dg) -{ - if (aa.empty) - return 0; - - immutable off = aa.valoff; - foreach (b; aa.buckets) - { - if (!b.filled) - continue; - if (auto res = dg(b.entry, b.entry + off)) - return res; - } - return 0; -} - -/** Construct an associative array of type ti from corresponding keys and values. - * Called for an AA literal `[k1:v1, k2:v2]`. - * Params: - * ti = TypeInfo for the associative array - * keys = array of keys - * vals = array of values - * Returns: - * A new associative array opaque pointer, or null if `keys` is empty. - */ -extern (C) Impl* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys, - void[] vals) -{ - assert(keys.length == vals.length); - - immutable keysz = ti.key.tsize; - immutable valsz = ti.value.tsize; - immutable length = keys.length; - - if (!length) - return null; - - auto aa = new Impl(ti, nextpow2(INIT_DEN * length / INIT_NUM)); - - void* pkey = keys.ptr; - void* pval = vals.ptr; - immutable off = aa.valoff; - uint actualLength = 0; - foreach (_; 0 .. length) - { - immutable hash = calcHash(pkey, aa); - - auto p = aa.findSlotLookup(hash, pkey, ti.key); - if (p is null) - { - p = aa.findSlotInsert(hash); - p.hash = hash; - p.entry = allocEntry(aa, pkey); // move key, no postblit - aa.firstUsed = min(aa.firstUsed, cast(uint)(p - aa.buckets.ptr)); - actualLength++; - } - else if (aa.entryTI && hasDtor(ti.value)) - { - // destroy existing value before overwriting it - ti.value.destroy(p.entry + off); - } - // set hash and blit value - auto pdst = p.entry + off; - pdst[0 .. valsz] = pval[0 .. valsz]; // move value, no postblit - - pkey += keysz; - pval += valsz; - } - aa.used = actualLength; - return aa; -} - -/// compares 2 AAs for equality -extern (C) int _aaEqual(scope const TypeInfo tiRaw, scope const AA aa1, scope const AA aa2) -{ - if (aa1 is aa2) - return true; - - immutable len = _aaLen(aa1); - if (len != _aaLen(aa2)) - return false; - - if (!len) // both empty - return true; - - import rt.lifetime : unqualify; - - auto uti = unqualify(tiRaw); - auto ti = *cast(TypeInfo_AssociativeArray*)&uti; - // compare the entries - immutable off = aa1.valoff; - foreach (b1; aa1.buckets) - { - if (!b1.filled) - continue; - auto pb2 = aa2.findSlotLookup(b1.hash, b1.entry, ti.key); - if (pb2 is null || !ti.value.equals(b1.entry + off, pb2.entry + off)) - return false; - } - return true; -} - -/// compute a hash -extern (C) hash_t _aaGetHash(scope const AA* paa, scope const TypeInfo tiRaw) nothrow -{ - const AA aa = *paa; - - if (aa.empty) - return 0; - - import rt.lifetime : unqualify; - - 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; - foreach (b; aa.buckets) - { - // use addition here, so that hash is independent of element order - if (b.filled) - h += hashOf(valHash(b.entry + off), keyHash(b.entry)); - } - - return h; -} - -/** - * _aaRange implements a ForwardRange - */ -struct Range -{ - Impl* impl; - size_t idx; - alias impl this; -} - -extern (C) pure nothrow @nogc @safe -{ - Range _aaRange(return scope AA aa) - { - if (!aa) - return Range(); - - foreach (i; aa.firstUsed .. aa.dim) - { - if (aa.buckets[i].filled) - return Range(aa, i); - } - return Range(aa, aa.dim); - } - - bool _aaRangeEmpty(Range r) - { - return r.impl is null || r.idx >= r.dim; - } - - void* _aaRangeFrontKey(Range r) - { - assert(!_aaRangeEmpty(r)); - if (r.idx >= r.dim) - return null; - return r.buckets[r.idx].entry; - } - - void* _aaRangeFrontValue(Range r) - { - assert(!_aaRangeEmpty(r)); - if (r.idx >= r.dim) - return null; - - auto entry = r.buckets[r.idx].entry; - return entry is null ? - null : - (() @trusted { return entry + r.valoff; } ()); - } - - void _aaRangePopFront(ref Range r) - { - if (r.idx >= r.dim) return; - for (++r.idx; r.idx < r.dim; ++r.idx) - { - if (r.buckets[r.idx].filled) - break; - } - } -} - -// Most tests are now in test_aa.d - -// test postblit for AA literals -unittest -{ - static struct T - { - ubyte field; - static size_t postblit, dtor; - this(this) - { - ++postblit; - } - - ~this() - { - ++dtor; - } - } - - T t; - auto aa1 = [0 : t, 1 : t]; - assert(T.dtor == 0 && T.postblit == 2); - aa1[0] = t; - assert(T.dtor == 1 && T.postblit == 3); - - T.dtor = 0; - T.postblit = 0; - - auto aa2 = [0 : t, 1 : t, 0 : t]; // literal with duplicate key => value overwritten - assert(T.dtor == 1 && T.postblit == 3); - - T.dtor = 0; - T.postblit = 0; - - auto aa3 = [t : 0]; - assert(T.dtor == 0 && T.postblit == 1); - aa3[t] = 1; - assert(T.dtor == 0 && T.postblit == 1); - aa3.remove(t); - assert(T.dtor == 0 && T.postblit == 1); - aa3[t] = 2; - assert(T.dtor == 0 && T.postblit == 2); - - // dtor will be called by GC finalizers - aa1 = null; - aa2 = null; - aa3 = null; - auto dtor1 = typeid(TypeInfo_AssociativeArray.Entry!(int, T)).xdtor; - GC.runFinalizers((cast(char*)dtor1)[0 .. 1]); - auto dtor2 = typeid(TypeInfo_AssociativeArray.Entry!(T, int)).xdtor; - GC.runFinalizers((cast(char*)dtor2)[0 .. 1]); - assert(T.dtor == 6 && T.postblit == 2); -} - -// Ensure the newaa struct layout (used for static initialization) is in sync -unittest -{ - import newaa = core.internal.newaa; - static assert(newaa.Impl.sizeof == Impl.sizeof); - // ensure compatible types and offsets - static foreach (i; 0 .. Impl.tupleof.length) - { - // for bucket array and Flags, we "compatible" types, not exactly the same types. - static if (__traits(identifier, Impl.tupleof[i]) == "buckets" - || __traits(identifier, Impl.tupleof[i]) == "flags") - static assert(Impl.tupleof[i].sizeof == newaa.Impl.tupleof[i].sizeof); - else - static assert(is(typeof(Impl.tupleof[i]) == typeof(newaa.Impl.tupleof[i]))); - - static assert(Impl.tupleof[i].offsetof == newaa.Impl.tupleof[i].offsetof); - } -} diff --git a/libphobos/libdruntime/rt/adi.d b/libphobos/libdruntime/rt/adi.d deleted file mode 100644 index 02e4d7719aa..00000000000 --- a/libphobos/libdruntime/rt/adi.d +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Implementation of dynamic array property support routines. - * - * Copyright: Copyright Digital Mars 2000 - 2015. - * 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 - * Source: $(DRUNTIMESRC rt/_adi.d) - */ - -module rt.adi; - -// debug = adi; // uncomment to turn on debugging printf's - -debug (adi) import core.stdc.stdio : printf; - -/*************************************** - * Support for array equality test. - * Returns: - * 1 equal - * 0 not equal - */ - -extern (C) int _adEq2(void[] a1, void[] a2, TypeInfo ti) -{ - debug(adi) printf("_adEq2(a1.length = %zd, a2.length = %zd)\n", a1.length, a2.length); - if (a1.length != a2.length) - return 0; // not equal - if (!ti.equals(&a1, &a2)) - return 0; - return 1; -} - -@safe unittest -{ - debug(adi) printf("array.Eq unittest\n"); - - struct S(T) { T val; } - alias String = S!string; - alias Float = S!float; - - String[1] a = [String("hello"c)]; - - assert(a != [String("hel")]); - assert(a != [String("helloo")]); - assert(a != [String("betty")]); - assert(a == [String("hello")]); - assert(a != [String("hxxxx")]); - - Float[1] fa = [Float(float.nan)]; - assert(fa != fa); -} - -unittest -{ - debug(adi) printf("struct.Eq unittest\n"); - - static struct TestStruct - { - int value; - - bool opEquals(const TestStruct rhs) const - { - return value == rhs.value; - } - } - - TestStruct[] b = [TestStruct(5)]; - TestStruct[] c = [TestStruct(6)]; - assert(_adEq2(*cast(void[]*)&b, *cast(void[]*)&c, typeid(TestStruct[])) == false); - assert(_adEq2(*cast(void[]*)&b, *cast(void[]*)&b, typeid(TestStruct[])) == true); -} diff --git a/libphobos/libdruntime/rt/cast_.d b/libphobos/libdruntime/rt/cast_.d deleted file mode 100644 index cd110bfc2aa..00000000000 --- a/libphobos/libdruntime/rt/cast_.d +++ /dev/null @@ -1,208 +0,0 @@ -/** - * Implementation of array assignment support routines. - * - * Copyright: Copyright Digital Mars 2004 - 2010. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Walter Bright, Sean Kelly - * Source: $(DRUNTIMESRC rt/_cast_.d) - */ - -/* 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.cast_; - -debug(cast_) import core.stdc.stdio : printf; - -extern (C): -@nogc: -nothrow: -pure: - -// Needed because ClassInfo.opEquals(Object) does a dynamic cast, -// but we are trying to implement dynamic cast. -extern (D) private bool areClassInfosEqual(scope const ClassInfo a, scope const ClassInfo b) @safe -{ - // same class if signatures match, works with potential duplicates across binaries - if (a is b) - return true; - - // new fast way - if (a.m_flags & TypeInfo_Class.ClassFlags.hasNameSig) - return a.nameSig[0] == b.nameSig[0] - && a.nameSig[1] == b.nameSig[1] - && a.nameSig[2] == b.nameSig[2] - && a.nameSig[3] == b.nameSig[3]; - - // old slow way for temporary binary compatibility - return a.name == b.name; -} - -/****************************************** - * Given a pointer: - * If it is an Object, return that Object. - * If it is an interface, return the Object implementing the interface. - * If it is null, return null. - * Else, undefined crash - */ -Object _d_toObject(return scope void* p) -{ - if (!p) - return null; - - Object o = cast(Object) p; - ClassInfo oc = typeid(o); - Interface* pi = **cast(Interface***) p; - - /* Interface.offset lines up with ClassInfo.name.ptr, - * so we rely on pointers never being less than 64K, - * and Objects never being greater. - */ - if (pi.offset < 0x10000) - { - debug(cast_) printf("\tpi.offset = %zd\n", pi.offset); - return cast(Object)(p - pi.offset); - } - return o; -} - -/************************************* - * Attempts to cast interface Object o to class c. - * Returns o if successful, null if not. - */ -void* _d_interface_cast(void* p, ClassInfo c) -{ - debug(cast_) printf("_d_interface_cast(p = %p, c = '%.*s')\n", p, cast(int) c.name.length, c.name.ptr); - if (!p) - return null; - - Interface* pi = **cast(Interface***) p; - - debug(cast_) printf("\tpi.offset = %zd\n", pi.offset); - Object o2 = cast(Object)(p - pi.offset); - void* res = null; - size_t offset = 0; - if (o2 && _d_isbaseof2(typeid(o2), c, offset)) - { - debug(cast_) printf("\toffset = %zd\n", offset); - res = cast(void*) o2 + offset; - } - debug(cast_) printf("\tresult = %p\n", res); - return res; -} - -/***** - * Dynamic cast from a class object `o` to class or interface `c`, where `c` is a subtype of `o`. - * Params: - * o = instance of class - * c = a subclass of o - * Returns: - * null if o is null or c is not a subclass of o. Otherwise, return o. - */ -void* _d_dynamic_cast(Object o, ClassInfo c) -{ - debug(cast_) printf("_d_dynamic_cast(o = %p, c = '%.*s')\n", o, cast(int) c.name.length, c.name.ptr); - - void* res = null; - size_t offset = 0; - if (o && _d_isbaseof2(typeid(o), c, offset)) - { - debug(cast_) printf("\toffset = %zd\n", offset); - res = cast(void*) o + offset; - } - debug(cast_) printf("\tresult = %p\n", res); - return res; -} - -/***** - * Dynamic cast from a class object o to class c, where c is a subclass of o. - * Params: - * o = instance of class - * c = a subclass of o - * Returns: - * null if o is null or c is not a subclass of o. Otherwise, return o. - */ -void* _d_class_cast(Object o, ClassInfo c) -{ - debug(cast_) printf("_d_cast_cast(o = %p, c = '%.*s')\n", o, cast(int) c.name.length, c.name.ptr); - - if (!o) - return null; - - ClassInfo oc = typeid(o); - int delta = oc.depth; - - if (delta && c.depth) - { - delta -= c.depth; - if (delta < 0) - return null; - - while (delta--) - oc = oc.base; - if (areClassInfosEqual(oc, c)) - return cast(void*)o; - return null; - } - - // no depth data - support the old way - do - { - if (areClassInfosEqual(oc, c)) - return cast(void*)o; - oc = oc.base; - } while (oc); - return null; -} - -/** - * Dynamic cast `o` to final class `c` only one level down - * Params: - * o = object that is instance of a class - * c = class to cast it to - * Returns: - * o if it succeeds, null if it fails - */ -void* _d_paint_cast(Object o, ClassInfo c) -{ - /* If o is really an instance of c, just do a paint - */ - auto p = o && cast(void*)(areClassInfosEqual(typeid(o), c)) ? o : null; - debug assert(cast(void*)p is cast(void*)_d_dynamic_cast(o, c)); - return cast(void*)p; -} - -int _d_isbaseof2(scope ClassInfo oc, scope const ClassInfo c, scope ref size_t offset) @safe -{ - if (areClassInfosEqual(oc, c)) - return true; - - do - { - if (oc.base && areClassInfosEqual(oc.base, c)) - return true; - - // Bugzilla 2013: Use depth-first search to calculate offset - // from the derived (oc) to the base (c). - foreach (iface; oc.interfaces) - { - if (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof2(iface.classinfo, c, offset)) - { - offset += iface.offset; - return true; - } - } - - oc = oc.base; - } while (oc); - - return false; -} - -int _d_isbaseof(scope ClassInfo oc, scope const ClassInfo c) @safe -{ - size_t offset = 0; - return _d_isbaseof2(oc, c, offset); -} diff --git a/libphobos/libdruntime/rt/lifetime.d b/libphobos/libdruntime/rt/lifetime.d index 3558976bd08..159df5956b7 100644 --- a/libphobos/libdruntime/rt/lifetime.d +++ b/libphobos/libdruntime/rt/lifetime.d @@ -229,56 +229,6 @@ private uint __typeAttrs(const scope TypeInfo ti, void *copyAttrsFrom = null) pu return attrs; } -/** -Shrink the "allocated" length of an array to be the exact size of the array. - -It doesn't matter what the current allocated length of the array is, the -user is telling the runtime that he knows what he is doing. - -Params: - ti = `TypeInfo` of array type - arr = array to shrink. Its `.length` is element length, not byte length, despite `void` type -*/ -extern(C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) nothrow -{ - debug(PRINTF) printf("_d_arrayshrinkfit, elemsize = %zd, arr.ptr = %p arr.length = %zd\n", ti.next.tsize, arr.ptr, arr.length); - auto tinext = unqualify(ti.next); - auto size = tinext.tsize; // array element size - auto reqsize = arr.length * size; - auto isshared = typeid(ti) is typeid(TypeInfo_Shared); - - auto curArr = gc_getArrayUsed(arr.ptr, isshared); - if (curArr.ptr is null) - // not a valid GC pointer - return; - - // align the array. - auto offset = arr.ptr - curArr.ptr; - auto cursize = curArr.length - offset; - if (cursize <= reqsize) - // invalid situation, or no change. - return; - - // if the type has a destructor, destroy elements we are about to remove. - if (typeid(tinext) is typeid(TypeInfo_Struct)) // avoid a complete dynamic type cast - { - auto sti = cast(TypeInfo_Struct)cast(void*)tinext; - if (sti.xdtor) - { - try - { - finalize_array(arr.ptr + reqsize, cursize - reqsize, sti); - } - catch (Exception e) - { - onFinalizeError(sti, e); - } - } - } - - gc_shrinkArrayUsed(arr.ptr[0 .. reqsize], cursize, isshared); -} - package bool hasPostblit(in TypeInfo ti) nothrow pure { return (&ti.postblit).funcptr !is &TypeInfo.postblit; @@ -335,43 +285,19 @@ extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length) pure noth if (length == 0 || size == 0) return null; - version (D_InlineAsm_X86) + bool overflow = false; + size = mulu(size, length, overflow); + if (!overflow) { - asm pure nothrow @nogc + if (auto ptr = GC.malloc(size, __typeAttrs(tinext) | BlkAttr.APPENDABLE, tinext)) { - mov EAX,size ; - mul EAX,length ; - mov size,EAX ; - jnc Lcontinue ; + debug(PRINTF) printf(" p = %p\n", ptr); + return ptr[0 .. length]; } } - else version (D_InlineAsm_X86_64) - { - asm pure nothrow @nogc - { - mov RAX,size ; - mul RAX,length ; - mov size,RAX ; - jnc Lcontinue ; - } - } - else - { - bool overflow = false; - size = mulu(size, length, overflow); - if (!overflow) - goto Lcontinue; - } -Loverflow: + onOutOfMemoryError(); assert(0); -Lcontinue: - - auto ptr = GC.malloc(size, __typeAttrs(tinext) | BlkAttr.APPENDABLE, tinext); - if (!ptr) - goto Loverflow; - debug(PRINTF) printf(" p = %p\n", ptr); - return ptr[0 .. length]; } /// ditto @@ -628,143 +554,6 @@ extern (C) void rt_finalizeFromGC(void* p, size_t size, uint attr, TypeInfo type } -/** -Given an array of length `size` that needs to be expanded to `newlength`, -compute a new capacity. - -Better version by Dave Fladebo, enhanced by Steven Schveighoffer: -This uses an inverse logorithmic algorithm to pre-allocate a bit more -space for larger arrays. -- The maximum "extra" space is about 80% of the requested space. This is for -PAGE size and smaller. -- As the arrays grow, the relative pre-allocated space shrinks. -- Perhaps most importantly, overall memory usage and stress on the GC -is decreased significantly for demanding environments. -- The algorithm is tuned to avoid any division at runtime. - -Params: - newlength = new `.length` - elemsize = size of the element in the new array -Returns: new capacity for array -*/ -size_t newCapacity(size_t newlength, size_t elemsize) -{ - size_t newcap = newlength * elemsize; - - /* - * Max growth factor numerator is 234, so allow for multiplying by 256. - * But also, the resulting size cannot be more than 2x, so prevent - * growing if 2x would fill up the address space (for 32-bit) - */ - enum largestAllowed = (ulong.max >> 8) & (size_t.max >> 1); - if (!newcap || (newcap & ~largestAllowed)) - return newcap; - - /* - * The calculation for "extra" space depends on the requested capacity. - * We use an inverse logarithm of the new capacity to add an extra 15% - * to 83% capacity. Note that normally we humans think in terms of - * percent, but using 128 instead of 100 for the denominator means we - * can avoid all division by simply bit-shifthing. Since there are only - * 64 bits in a long, the bsr of a size_t is going to be 0 - 63. Using - * a lookup table allows us to precalculate the multiplier based on the - * inverse logarithm. The formula rougly is: - * - * newcap = request * (1.0 + min(0.83, 10.0 / (log(request) + 1))) - */ - import core.bitop; - static immutable multTable = (){ - assert(__ctfe); - ulong[size_t.sizeof * 8] result; - foreach (i; 0 .. result.length) - { - auto factor = 128 + 1280 / (i + 1); - result[i] = factor > 234 ? 234 : factor; - } - return result; - }(); - - auto mult = multTable[bsr(newcap)]; - - // if this were per cent, then the code would look like: - // ((newlength * mult + 99) / 100) * elemsize - newcap = cast(size_t)((newlength * mult + 127) >> 7) * elemsize; - debug(PRINTF) printf("mult: %2.2f, alloc: %2.2f\n",mult/128.0,newcap / cast(double)elemsize); - debug(PRINTF) printf("newcap = %zd, newlength = %zd, elemsize = %zd\n", newcap, newlength, elemsize); - return newcap; -} - - -/** -Extend an array by n elements. - -Caller must initialize those elements. - -Params: - ti = type info of array type (not element type) - px = array to append to, cast to `byte[]` while keeping the same `.length`. Will be updated. - n = number of elements to append -Returns: `px` after being appended to -*/ -extern (C) -byte[] _d_arrayappendcTX(const TypeInfo ti, return scope ref byte[] px, size_t n) @weak -{ - // This is a cut&paste job from _d_arrayappendT(). Should be refactored. - - // Short circuit if no data is being appended. - if (n == 0) - return px; - - - // only optimize array append where ti is not a shared type - auto tinext = unqualify(ti.next); - auto sizeelem = tinext.tsize; // array element size - auto isshared = typeid(ti) is typeid(TypeInfo_Shared); - auto length = px.length; - auto newlength = length + n; - auto newsize = newlength * sizeelem; - auto size = length * sizeelem; - - if (!gc_expandArrayUsed(px.ptr[0 .. size], newsize, isshared)) - { - // could not set the size, we must reallocate. - auto newcap = newCapacity(newlength, sizeelem); - auto attrs = __typeAttrs(tinext, px.ptr) | BlkAttr.APPENDABLE; - auto ptr = cast(byte*) GC.malloc(newcap, attrs, tinext); - if (ptr is null) - { - onOutOfMemoryError(); - assert(0); - } - - if (newsize != newcap) - { - // For small blocks that are always fully scanned, if we allocated more - // capacity than was requested, we are responsible for zeroing that - // memory. - // TODO: should let the GC figure this out, as this property may - // not always hold. - if (!(attrs & BlkAttr.NO_SCAN) && newcap < PAGESIZE) - memset(ptr + newsize, 0, newcap - newsize); - - gc_shrinkArrayUsed(ptr[0 .. newsize], newcap, isshared); - } - - memcpy(ptr, px.ptr, size); - - // do potsblit processing. - __doPostblit(ptr, size, tinext); - - px = ptr[0 .. newlength]; - return px; - } - - // we were able to expand in place, just update the length - px = px.ptr[0 .. newlength]; - return px; -} - - /** Append `dchar` to `char[]`, converting UTF-32 to UTF-8 @@ -906,45 +695,6 @@ extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c) @weak return x; } -/** -Allocate an array literal - -Rely on the caller to do the initialization of the array. - ---- -int[] getArr() -{ - return [10, 20]; - // auto res = cast(int*) _d_arrayliteralTX(typeid(int[]), 2); - // res[0] = 10; - // res[1] = 20; - // return res[0..2]; -} ---- - -Params: - ti = `TypeInfo` of resulting array type - length = `.length` of array literal - -Returns: pointer to allocated array -*/ -extern (C) -void* _d_arrayliteralTX(const TypeInfo ti, size_t length) @weak -{ - auto tinext = unqualify(ti.next); - auto sizeelem = tinext.tsize; // array element size - void* result; - - debug(PRINTF) printf("_d_arrayliteralTX(sizeelem = %zd, length = %zd)\n", sizeelem, length); - if (length == 0 || sizeelem == 0) - return null; - else - { - auto allocsize = length * sizeelem; - return GC.malloc(allocsize, __typeAttrs(tinext) | BlkAttr.APPENDABLE, tinext); - } -} - unittest { diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE index b1bf0c3c930..4bf563baa9f 100644 --- a/libphobos/src/MERGE +++ b/libphobos/src/MERGE @@ -1,4 +1,4 @@ -5a142da0af7b72bfed314278d14464d8f1147391 +f87979028bd0e0f3e67dbf5b0cd1335ca3f7ed67 The first line of this file holds the git revision number of the last merge done from the dlang/phobos repository. diff --git a/libphobos/src/Makefile.am b/libphobos/src/Makefile.am index 7529b360eea..13c35c22ddd 100644 --- a/libphobos/src/Makefile.am +++ b/libphobos/src/Makefile.am @@ -137,10 +137,11 @@ PHOBOS_DSOURCES = etc/c/curl.d etc/c/odbc/odbc32.d etc/c/odbc/odbc64.d \ std/format/internal/write.d std/format/package.d std/format/read.d \ std/format/spec.d std/format/write.d std/functional.d std/getopt.d \ std/int128.d std/internal/attributes.d std/internal/cstring.d \ - std/internal/math/biguintcore.d std/internal/math/biguintnoasm.d \ - std/internal/math/errorfunction.d std/internal/math/gammafunction.d \ - std/internal/memory.d std/internal/scopebuffer.d \ - std/internal/test/dummyrange.d std/internal/test/range.d \ + std/internal/entropy.d std/internal/math/biguintcore.d \ + std/internal/math/biguintnoasm.d std/internal/math/errorfunction.d \ + std/internal/math/gammafunction.d std/internal/memory.d \ + std/internal/scopebuffer.d std/internal/test/dummyrange.d \ + std/internal/test/range.d \ std/internal/test/sumtype_example_overloads.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 \ diff --git a/libphobos/src/Makefile.in b/libphobos/src/Makefile.in index 27ed8788a47..960ca98cc1d 100644 --- a/libphobos/src/Makefile.in +++ b/libphobos/src/Makefile.in @@ -238,6 +238,7 @@ am__dirstamp = $(am__leading_dot)dirstamp @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/int128.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/attributes.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/cstring.lo \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/entropy.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/math/biguintcore.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/math/biguintnoasm.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/math/errorfunction.lo \ @@ -609,10 +610,11 @@ libgphobos_la_LINK = $(LIBTOOL) --tag=D $(libgphobos_la_LIBTOOLFLAGS) \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/internal/write.d std/format/package.d std/format/read.d \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/spec.d std/format/write.d std/functional.d std/getopt.d \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/int128.d std/internal/attributes.d std/internal/cstring.d \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/math/biguintcore.d std/internal/math/biguintnoasm.d \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/math/errorfunction.d std/internal/math/gammafunction.d \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/memory.d std/internal/scopebuffer.d \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/test/dummyrange.d std/internal/test/range.d \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/entropy.d std/internal/math/biguintcore.d \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/math/biguintnoasm.d std/internal/math/errorfunction.d \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/math/gammafunction.d std/internal/memory.d \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/scopebuffer.d std/internal/test/dummyrange.d \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/test/range.d \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/test/sumtype_example_overloads.d std/internal/test/uda.d \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/unicode_comp.d std/internal/unicode_decomp.d \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/unicode_grapheme.d std/internal/unicode_norm.d \ @@ -879,6 +881,7 @@ std/internal/$(am__dirstamp): @: > std/internal/$(am__dirstamp) std/internal/attributes.lo: std/internal/$(am__dirstamp) std/internal/cstring.lo: std/internal/$(am__dirstamp) +std/internal/entropy.lo: std/internal/$(am__dirstamp) std/internal/math/$(am__dirstamp): @$(MKDIR_P) std/internal/math @: > std/internal/math/$(am__dirstamp) diff --git a/libphobos/src/index.dd b/libphobos/src/index.dd index 48122259670..ed8e46ba8bb 100644 --- a/libphobos/src/index.dd +++ b/libphobos/src/index.dd @@ -306,6 +306,11 @@ $(BOOKTABLE , $(TDNW $(MREF std,bigint)) $(TD An arbitrary-precision integer type.) ) + $(TR + $(TDNW $(MREF std,int128)) + $(TDNW $(MREF core,int128)) + $(TD 128 bit-precision integer type.) + ) $(TR $(TDNW $(MREF std,complex)) $(TD A complex number type.) diff --git a/libphobos/src/std/algorithm/iteration.d b/libphobos/src/std/algorithm/iteration.d index 7923eeee944..34d787ec278 100644 --- a/libphobos/src/std/algorithm/iteration.d +++ b/libphobos/src/std/algorithm/iteration.d @@ -8161,6 +8161,13 @@ Returns: A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) of elements of which are an $(REF indexed, std,range) view into `r`. +Note: + The elements of the resulting range reuse the same internal buffer of + permutations, so each element is invalidated by `popFront`. If copies of + intermediate permutations are desired, they need to be individually copied, + such as using `.map!(e => e.array)` to save them in individual, independent + arrays. + See_Also: $(REF nextPermutation, std,algorithm,sorting). */ diff --git a/libphobos/src/std/algorithm/mutation.d b/libphobos/src/std/algorithm/mutation.d index 8a45ecb8131..1dc0e79992b 100644 --- a/libphobos/src/std/algorithm/mutation.d +++ b/libphobos/src/std/algorithm/mutation.d @@ -3120,7 +3120,7 @@ if (isBlitAssignable!T && !is(typeof(lhs.proxySwap(rhs)))) static struct A { int x; - this(scope ref return const A other) + this(scope return ref const A other) { import std.stdio; x = other.x; diff --git a/libphobos/src/std/algorithm/searching.d b/libphobos/src/std/algorithm/searching.d index e43aeb53317..30becba4d94 100644 --- a/libphobos/src/std/algorithm/searching.d +++ b/libphobos/src/std/algorithm/searching.d @@ -807,6 +807,11 @@ if (isInputRange!R && !isInfinite!R) - `needle` is the index into `needles` which matched. - Both are `-1` if there was no match. + Warning: Due to $(LINK2 https://tour.dlang.org/tour/en/gems/unicode, + auto-decoding), the return value of this function may $(I not) correspond + to the array index for strings. To find the index of an element matching + the predicate in a string, use $(REF indexOf, std,string) instead. + See_Also: $(REF indexOf, std,string) +/ auto countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) @@ -1046,6 +1051,11 @@ if (isInputRange!R && $(LREF startsWith)`!pred(haystack)` is `true`. - If `startsWith!pred(haystack)` is not `true` for any element in `haystack`, then `-1` is returned. + + Warning: Due to $(LINK2 https://tour.dlang.org/tour/en/gems/unicode, + auto-decoding), the return value of this function may $(I not) correspond + to the array index for strings. To find the index of an element matching + the predicate in a string, use $(REF indexOf, std,string) instead. +/ ptrdiff_t countUntil(alias pred, R)(R haystack) if (isInputRange!R && diff --git a/libphobos/src/std/ascii.d b/libphobos/src/std/ascii.d index 367c981320a..a69b7bf84d9 100644 --- a/libphobos/src/std/ascii.d +++ b/libphobos/src/std/ascii.d @@ -23,6 +23,7 @@ $(TR $(TD Validation) $(TD $(LREF isDigit) $(LREF isGraphical) $(LREF isHexDigit) + $(LREF isLower) $(LREF isOctalDigit) $(LREF isPrintable) $(LREF isPunctuation) diff --git a/libphobos/src/std/container/rbtree.d b/libphobos/src/std/container/rbtree.d index fe139a56be3..17e9fc42215 100644 --- a/libphobos/src/std/container/rbtree.d +++ b/libphobos/src/std/container/rbtree.d @@ -679,7 +679,7 @@ private struct RBRange(N) /** * Returns the first element in the range */ - @property Elem front() + ref @property Elem front() { return _begin.value; } @@ -687,7 +687,7 @@ private struct RBRange(N) /** * Returns the last element in the range */ - @property Elem back() + ref @property Elem back() { return _end.prev.value; } @@ -737,13 +737,17 @@ private struct RBRange(N) * elements `a` and `b`, $(D less(a, b) == !less(b, a)). $(D less(a, a)) should * always equal `false`. * + * Care should also be taken to not modify elements in the tree (e.g. via `front` / + * `back`, which return by `ref`) in a way which would affect the order defined by + * the `less` predicate. + * * If `allowDuplicates` is set to `true`, then inserting the same element more than * once continues to add more elements. If it is `false`, duplicate elements are * ignored on insertion. If duplicates are allowed, then new elements are * inserted after all existing duplicate elements. */ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) -if (is(typeof(binaryFun!less(T.init, T.init)))) +if (is(typeof((ref const T a) => binaryFun!less(a, a)))) { import std.meta : allSatisfy; import std.range : Take; @@ -1036,7 +1040,7 @@ if (is(typeof(binaryFun!less(T.init, T.init)))) * * Complexity: $(BIGOH 1) */ - inout(Elem) front() inout + ref inout(Elem) front() inout { return _begin.value; } @@ -1046,7 +1050,7 @@ if (is(typeof(binaryFun!less(T.init, T.init)))) * * Complexity: $(BIGOH log(n)) */ - inout(Elem) back() inout + ref inout(Elem) back() inout { return _end.prev.value; } @@ -2256,3 +2260,14 @@ if ( is(typeof(binaryFun!less((ElementType!Stuff).init, (ElementType!Stuff).init t.insert([1, 3, 5, 4, 2]); assert(t[].equal([5, 4, 3, 2, 1])); } + +// should support `less` predicate taking `ref const` +@safe pure unittest +{ + struct S + { + int* value; + } + + cast(void) new RedBlackTree!(S, (ref const S a, ref const S b) => a.value > b.value); +} diff --git a/libphobos/src/std/datetime/date.d b/libphobos/src/std/datetime/date.d index 0f417b15081..ced65c9a920 100644 --- a/libphobos/src/std/datetime/date.d +++ b/libphobos/src/std/datetime/date.d @@ -2131,6 +2131,7 @@ public: $(BOOKTABLE, $(TR $(TD DateTime) $(TD +) $(TD Duration) $(TD -->) $(TD DateTime)) $(TR $(TD DateTime) $(TD -) $(TD Duration) $(TD -->) $(TD DateTime)) + $(TR $(TD Duration) $(TD +) $(TD DateTime) $(TD -->) $(TD DateTime)) ) Params: @@ -2145,6 +2146,14 @@ public: mixin("return retval._addSeconds(" ~ op ~ "seconds);"); } + /// ditto + DateTime opBinaryRight(string op)(Duration duration) const @safe pure nothrow @nogc + if (op == "+") + { + return this + duration; + } + + /// @safe unittest { @@ -2161,6 +2170,9 @@ public: assert(DateTime(2016, 1, 1, 0, 59, 59) - hours(1) == DateTime(2015, 12, 31, 23, 59, 59)); + + assert(DateTime(2015, 12, 31, 23, 59, 59) + hours(1) == + hours(1) + DateTime(2015, 12, 31, 23, 59, 59)); } @safe unittest @@ -6134,13 +6146,14 @@ public: import core.time : Duration; /++ Gives the result of adding or subtracting a $(REF Duration, core,time) - from + from this $(LREF Date). The legal types of arithmetic for $(LREF Date) using this operator are $(BOOKTABLE, $(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date)) $(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date)) + $(TR $(TD Duration) $(TD +) $(TD Date) $(TD -->) $(TD Date)) ) Params: @@ -6155,6 +6168,14 @@ public: mixin("return retval._addDays(" ~ op ~ "days);"); } + + /// ditto + Date opBinaryRight(string op)(Duration duration) const @safe pure nothrow @nogc + if (op == "+") + { + return this + duration; + } + /// @safe unittest { @@ -6165,6 +6186,8 @@ public: assert(Date(2016, 1, 1) - days(1) == Date(2015, 12, 31)); assert(Date(2004, 3, 1) - days(4) == Date(2004, 2, 26)); + + assert(Date(2004, 2, 26) + days(4) == days(4) + Date(2004, 2, 26)); } @safe unittest @@ -8844,6 +8867,7 @@ public: $(BOOKTABLE, $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay)) $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay)) + $(TR $(TD Duration) $(TD +) $(TD TimeOfDay) $(TD -->) $(TD TimeOfDay)) ) Params: @@ -8858,6 +8882,13 @@ public: mixin("return retval._addSeconds(" ~ op ~ "seconds);"); } + /// ditto + TimeOfDay opBinaryRight(string op)(Duration duration) const @safe pure nothrow @nogc + if (op == "+") + { + return this + duration; + } + /// @safe unittest { @@ -8872,6 +8903,8 @@ public: assert(TimeOfDay(12, 12, 12) - minutes(1) == TimeOfDay(12, 11, 12)); assert(TimeOfDay(12, 12, 12) - hours(1) == TimeOfDay(11, 12, 12)); assert(TimeOfDay(0, 0, 0) - seconds(1) == TimeOfDay(23, 59, 59)); + + assert(TimeOfDay(12, 12, 12) + seconds(1) == seconds(1) + TimeOfDay(12, 12, 12)); } @safe unittest diff --git a/libphobos/src/std/datetime/systime.d b/libphobos/src/std/datetime/systime.d index fd2a9e192e4..189b95ce5b1 100644 --- a/libphobos/src/std/datetime/systime.d +++ b/libphobos/src/std/datetime/systime.d @@ -6301,6 +6301,7 @@ public: $(BOOKTABLE, $(TR $(TD SysTime) $(TD +) $(TD Duration) $(TD -->) $(TD SysTime)) $(TR $(TD SysTime) $(TD -) $(TD Duration) $(TD -->) $(TD SysTime)) + $(TR $(TD Duration) $(TD +) $(TD SysTime) $(TD -->) $(TD SysTime)) ) Params: @@ -6316,6 +6317,13 @@ public: return retval; } + /// ditto + SysTime opBinaryRight(string op)(Duration duration) const @safe pure nothrow + if (op == "+") + { + return this + duration; + } + /// @safe unittest { @@ -6333,6 +6341,9 @@ public: assert(SysTime(DateTime(2016, 1, 1, 0, 59, 59)) - hours(1) == SysTime(DateTime(2015, 12, 31, 23, 59, 59))); + + assert(SysTime(DateTime(2015, 12, 31, 23, 59, 59)) + seconds(1) == + seconds(1) + SysTime(DateTime(2015, 12, 31, 23, 59, 59))); } @safe unittest diff --git a/libphobos/src/std/datetime/timezone.d b/libphobos/src/std/datetime/timezone.d index 6a1898b0ef5..7461fcaa33b 100644 --- a/libphobos/src/std/datetime/timezone.d +++ b/libphobos/src/std/datetime/timezone.d @@ -291,7 +291,7 @@ public: // be there, but since PosixTimeZone _does_ use leap seconds if // the time zone file does, we'll test that functionality if the // appropriate files exist. - if (chainPath(PosixTimeZone.defaultTZDatabaseDir, "right", tzName).exists) + if (chainPath(PosixTimeZone.getDefaultTZDatabaseDir(), "right", tzName).exists) { auto leapTZ = PosixTimeZone.getTimeZone("right/" ~ tzName); @@ -2014,7 +2014,8 @@ public: the TZDatabaseDir version to pass an arbitrary path at compile-time, rather than hard-coding it here. Android concatenates all time zone data into a single file called tzdata and stores it in the directory - below. + below. If the TZDIR environment variable is set, it is consulted + before this constant. +/ enum defaultTZDatabaseDir = ""; } @@ -2040,6 +2041,18 @@ public: enum defaultTZDatabaseDir = ""; } + private static string getDefaultTZDatabaseDir() + { + import core.stdc.stdlib : getenv; + import std.string : fromStringz; + + auto dir = getenv("TZDIR"); + if (dir) + return fromStringz(dir).idup; + + return defaultTZDatabaseDir; + } + /++ Returns a $(LREF TimeZone) with the give name per the TZ Database. The @@ -2067,7 +2080,7 @@ public: +/ // TODO make it possible for tzDatabaseDir to be gzipped tar file rather than an uncompressed // directory. - static immutable(PosixTimeZone) getTimeZone(string name, string tzDatabaseDir = defaultTZDatabaseDir) @trusted + static immutable(PosixTimeZone) getTimeZone(string name, string tzDatabaseDir = getDefaultTZDatabaseDir()) @trusted { import std.algorithm.sorting : sort; import std.conv : to; @@ -2418,7 +2431,7 @@ public: Throws: `FileException` if it fails to read from disk. +/ - static string[] getInstalledTZNames(string subName = "", string tzDatabaseDir = defaultTZDatabaseDir) @safe + static string[] getInstalledTZNames(string subName = "", string tzDatabaseDir = getDefaultTZDatabaseDir()) @safe { import std.algorithm.sorting : sort; import std.array : appender; @@ -2510,11 +2523,12 @@ public: {} else { - foreach (DirEntry de; dirEntries(defaultTZDatabaseDir, SpanMode.depth)) + string tzDatabaseDir = getDefaultTZDatabaseDir(); + foreach (DirEntry de; dirEntries(tzDatabaseDir, SpanMode.depth)) { if (de.isFile) { - auto tzName = de.name[defaultTZDatabaseDir.length .. $]; + auto tzName = de.name[tzDatabaseDir.length .. $]; if (!canFind(tzNames, tzName)) assertThrown!DateTimeException(testPTZFailure(tzName)); diff --git a/libphobos/src/std/experimental/allocator/building_blocks/kernighan_ritchie.d b/libphobos/src/std/experimental/allocator/building_blocks/kernighan_ritchie.d index 167cf1bc6bc..3d04bb6f053 100644 --- a/libphobos/src/std/experimental/allocator/building_blocks/kernighan_ritchie.d +++ b/libphobos/src/std/experimental/allocator/building_blocks/kernighan_ritchie.d @@ -111,11 +111,13 @@ struct KRRegion(ParentAllocator = NullAllocator) this(this) @disable; + nothrow @nogc @trusted void[] payload() inout { return (cast(ubyte*) &this)[0 .. size]; } + nothrow @nogc @trusted bool adjacent(in Node* right) const { assert(right); @@ -123,6 +125,7 @@ struct KRRegion(ParentAllocator = NullAllocator) return p.ptr < right && right < p.ptr + p.length + Node.sizeof; } + nothrow @nogc @trusted bool coalesce(void* memoryEnd = null) { // Coalesce the last node before the memory end with any possible gap @@ -139,6 +142,7 @@ struct KRRegion(ParentAllocator = NullAllocator) return true; } + nothrow @nogc @safe Tuple!(void[], Node*) allocateHere(size_t bytes) { assert(bytes >= Node.sizeof); @@ -152,7 +156,7 @@ struct KRRegion(ParentAllocator = NullAllocator) if (leftover >= Node.sizeof) { // There's room for another node - auto newNode = cast(Node*) ((cast(ubyte*) &this) + bytes); + auto newNode = (() @trusted => cast(Node*) ((cast(ubyte*) &this) + bytes))(); newNode.size = leftover; newNode.next = next == &this ? newNode : next; assert(next); @@ -174,8 +178,8 @@ struct KRRegion(ParentAllocator = NullAllocator) else alias parent = ParentAllocator.instance; private void[] payload; private Node* root; - private bool regionMode() const { return bytesUsedRegionMode != size_t.max; } - private void cancelRegionMode() { bytesUsedRegionMode = size_t.max; } + nothrow @nogc @safe private bool regionMode() const { return bytesUsedRegionMode != size_t.max; } + nothrow @nogc @safe private void cancelRegionMode() { bytesUsedRegionMode = size_t.max; } private size_t bytesUsedRegionMode = 0; auto byNodePtr() @@ -257,6 +261,7 @@ struct KRRegion(ParentAllocator = NullAllocator) } } + nothrow @nogc @safe private Node* sortFreelist(Node* root) { // Find a monotonic run @@ -274,6 +279,7 @@ struct KRRegion(ParentAllocator = NullAllocator) return merge(root, tail); } + nothrow @nogc @safe private Node* merge(Node* left, Node* right) { assert(left != right); @@ -290,6 +296,7 @@ struct KRRegion(ParentAllocator = NullAllocator) return result; } + nothrow @nogc @safe private void coalesceAndMakeCircular() { for (auto n = root;;) @@ -368,6 +375,7 @@ struct KRRegion(ParentAllocator = NullAllocator) Otherwise, sorts the free list accumulated so far and switches strategy for future allocations to KR style. */ + nothrow @nogc @safe void switchToFreeList() { if (!regionMode) return; @@ -396,6 +404,7 @@ struct KRRegion(ParentAllocator = NullAllocator) Returns: A word-aligned buffer of `n` bytes, or `null`. */ + nothrow @nogc @safe void[] allocate(size_t n) { if (!n || !root) return null; @@ -413,7 +422,7 @@ struct KRRegion(ParentAllocator = NullAllocator) immutable balance = root.size - actualBytes; if (balance >= Node.sizeof) { - auto newRoot = cast(Node*) (result + actualBytes); + auto newRoot = (() @trusted => cast(Node*) ((cast(ubyte*) result) + actualBytes))(); newRoot.next = root.next; newRoot.size = balance; root = newRoot; @@ -423,7 +432,7 @@ struct KRRegion(ParentAllocator = NullAllocator) root = null; switchToFreeList; } - return result[0 .. n]; + return (() @trusted => result[0 .. n])(); } // Not enough memory, switch to freelist mode and fall through @@ -554,6 +563,7 @@ struct KRRegion(ParentAllocator = NullAllocator) at the front of the free list. These blocks get coalesced, whether `allocateAll` succeeds or fails due to fragmentation. */ + nothrow @nogc @safe void[] allocateAll() { if (regionMode) switchToFreeList; diff --git a/libphobos/src/std/getopt.d b/libphobos/src/std/getopt.d index fc5cdac73ce..0c5dfb9506c 100644 --- a/libphobos/src/std/getopt.d +++ b/libphobos/src/std/getopt.d @@ -542,6 +542,13 @@ private pure Option splitAndGet(string opt) @trusted nothrow assert(olongshort.optLong == "--foo"); } +private string optionValidatorErrorFormat(string msg, size_t idx) +{ + import std.conv : to; + return "getopt validator: " ~ msg ~ " (at position " ~ to!(string)(idx) ~ + ")"; +} + /* This function verifies that the variadic parameters passed in getOpt follow this pattern: @@ -555,9 +562,6 @@ follow this pattern: */ private template optionValidator(A...) { - import std.format : format; - - enum fmt = "getopt validator: %s (at position %d)"; enum isReceiver(T) = is(T == U*, U) || (is(T == function)) || (is(T == delegate)); enum isOptionStr(T) = isSomeString!T || isSomeChar!T; @@ -568,11 +572,11 @@ private template optionValidator(A...) { static if (isReceiver!(A[0])) { - msg = format(fmt, "first argument must be a string or a config", 0); + msg = optionValidatorErrorFormat("first argument must be a string or a config", 0); } else static if (!isOptionStr!(A[0]) && !is(A[0] == config)) { - msg = format(fmt, "invalid argument type: " ~ A[0].stringof, 0); + msg = optionValidatorErrorFormat("invalid argument type: " ~ A[0].stringof, 0); } else { @@ -581,25 +585,25 @@ private template optionValidator(A...) static if (!isReceiver!(A[i]) && !isOptionStr!(A[i]) && !(is(A[i] == config))) { - msg = format(fmt, "invalid argument type: " ~ A[i].stringof, i); + msg = optionValidatorErrorFormat("invalid argument type: " ~ A[i].stringof, i); goto end; } else static if (isReceiver!(A[i]) && !isOptionStr!(A[i-1])) { - msg = format(fmt, "a receiver can not be preceeded by a receiver", i); + msg = optionValidatorErrorFormat("a receiver can not be preceeded by a receiver", i); goto end; } else static if (i > 1 && isOptionStr!(A[i]) && isOptionStr!(A[i-1]) && isSomeString!(A[i-2])) { - msg = format(fmt, "a string can not be preceeded by two strings", i); + msg = optionValidatorErrorFormat("a string can not be preceeded by two strings", i); goto end; } } } static if (!isReceiver!(A[$-1]) && !is(A[$-1] == config)) { - msg = format(fmt, "last argument must be a receiver or a config", + msg = optionValidatorErrorFormat("last argument must be a receiver or a config", A.length -1); } } @@ -614,16 +618,16 @@ private auto getoptTo(R)(string option, string value, size_t idx, string file = __FILE__, size_t line = __LINE__) { import std.conv : to, ConvException; - import std.format : format; try { return to!R(value); } catch (ConvException e) { - throw new ConvException(format("Argument '%s' at position '%u' could " - ~ "not be converted to type '%s' as required by option '%s'.", - value, idx, R.stringof, option), e, file, line); + throw new ConvException("Argument '" ~ value ~ "' at position '" ~ + to!(string)(idx) ~ "' could not be converted to type '" ~ + R.stringof ~ "' as required by option '" ~ option ~ "'.", e, file, + line); } } diff --git a/libphobos/src/std/internal/entropy.d b/libphobos/src/std/internal/entropy.d new file mode 100644 index 00000000000..92b3323250f --- /dev/null +++ b/libphobos/src/std/internal/entropy.d @@ -0,0 +1,918 @@ +// Written in the D programming language. + +/+ + CSPRNG library prototype. + + This code has not been audited. + Do not use for cryptographic purposes. + + The terms $(I entropy) and $(I entropy sources) here do refer to + cryptographically-safe random numbers and higher-level generators of such + — typically powered by an entropy pool provided by the operating system. + + An example of similar usage of said terminology would be the `getentropy()` + function provided by + $(LINK2 https://man.freebsd.org/cgi/man.cgi?query=getentropy&apropos=0&sektion=3&manpath=FreeBSD+14.2-RELEASE&arch=default&format=html, + FreeBSD). + + This library does not interact with any actual low-level entropy sources + by itself. Instead it interfaces with system-provided CSPRNGs that are + typically seeded through aforementioned entropy sources by the operating + system as needed. + + See_also: + $(LINK https://blog.cr.yp.to/20140205-entropy.html), + $(LINK https://cr.yp.to/talks/2014.10.18/slides-djb-20141018-a4.pdf) + + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + Authors: Elias Batek + Source: $(PHOBOSSRC std/internal/entropy.d) + +/ +module std.internal.entropy; + +import std.meta; + +version (OSX) + version = Darwin; +else version (iOS) + version = Darwin; +else version (TVOS) + version = Darwin; +else version (WatchOS) + version = Darwin; + +// Self-test: Detect potentially unsuitable default entropy source. +@safe unittest +{ + auto buffer = new ubyte[](32); + forceEntropySource(defaultEntropySource); + const result = getEntropy(buffer); + + assert( + !result.isUnavailable, + "The default entropy source for the target platform" + ~ " is unavailable on this machine. Please consider" + ~ " patching it to accommodate to your environment." + ); + assert(result.isOK); +} + +// Self-test: Detect faulty implementation. +@system unittest +{ + forceEntropySource(defaultEntropySource); + + bool test() @system + { + static immutable pattern = 0xDEAD_BEEF_1337_0000; + long number = pattern; + const result = getEntropy(&number, number.sizeof); + assert(result.isOK); + return number != pattern; + } + + size_t timesFailed = 0; + foreach (n; 0 .. 3) + if (!test()) + ++timesFailed; + + assert( + timesFailed <= 1, + "Suspicious random data: Potential security issue or really unlucky; please retry." + ); +} + +// Self-test: Detect faulty implementation. +@safe unittest +{ + forceEntropySource(defaultEntropySource); + + bool test() @safe + { + ubyte[32] data; + data[] = 0; + + const result = getEntropy(data[]); + assert(result.isOK); + + size_t zeros = 0; + foreach (b; data) + if (b == 0) + ++zeros; + + enum threshold = 24; + return zeros < threshold; + } + + size_t timesFailed = 0; + foreach (n; 0 .. 3) + if (!test()) + ++timesFailed; + + assert( + timesFailed <= 1, + "Suspicious random data: Potential security issue or really unlucky; please retry." + ); +} + +@nogc nothrow: + +// Flagship function +/++ + Retrieves random data from an applicable system CSPRNG. + + Params: + buffer = An output buffer to store the retrieved entropy in. + The length of it will determine the amount of random data to + be obtained. + + This function (and all overloads) always attempt to fill + the entire buffer. Therefore, they can block, spin or report + an error. + + Returns: + An `EntropyResult` that either reports success + or the type of error that has occurred. + + In case of an error, the data in `buffer` MUST NOT be used. + The recommended way to check for success is through the `isOK()` + helper function. + +/ +EntropyResult getEntropy(scope void[] buffer) @safe +{ + return getEntropyImpl(buffer); +} + +/// +@safe unittest +{ + int[4] bytes; + if (getEntropy(cast(void[]) bytes).isOK) + { + // Success; data in `bytes` may be used. + } + + assert((cast(void[]) bytes).length == bytes.sizeof); +} + +// Convenience overload +/// ditto +EntropyResult getEntropy(scope ubyte[] buffer) @safe +{ + return getEntropy(cast(void[]) buffer); +} + +/// +@safe unittest +{ + ubyte[16] bytes; + if (getEntropy(bytes).isOK) + { + // Success; data in `bytes` may be used. + } +} + +// Convenience wrapper +/// ditto +/++ + Retrieves random data from an applicable system CSPRNG. + + Params: + buffer = An output buffer to store the retrieved entropy in. + length = Length of the provided `buffer`. + Specifying a wrong value here, will lead to memory corruption. + + Returns: + An `EntropyResult` that either reports success + or the type of error that has occurred. + + In case of an error, the data in `buffer` MUST NOT be used. + The recommended way to check for success is through the `isOK()` + helper function. + +/ +EntropyResult getEntropy(scope void* buffer, size_t length) @system +{ + return getEntropy(buffer[0 .. length]); +} + +/// +@system unittest +{ + ubyte[16] bytes; + if (getEntropy(cast(void*) bytes.ptr, bytes.length).isOK) + { + // Success; data in `bytes` may be used. + } +} + +/// +@system unittest +{ + int number = void; + if (getEntropy(&number, number.sizeof).isOK) + { + // Success; value of `number` may be used. + } +} + +/++ + Manually set the entropy source to use for the current thread. + + As a rule of thumb, this SHOULD NOT be done. + + It might be useful in cases where the default entropy source — as chosen by + the maintainer of the used compiler package — is unavailable on a system. + Usually, `EntropySource.tryAll` will be the most reasonable option + in such cases. + + Params: + source = The requested default entropy source to use for the current thread. + + Examples: + + --- + // Using `forceEntropySource` almost always is a bad idea. + // As a rule of thumb, this SHOULD NOT be done. + forceEntropySource(EntropySource.none); + --- + +/ +void forceEntropySource(EntropySource source) @safe +{ + _entropySource = source; +} + +// (In-)Convenience wrapper +/++ + Retrieves random data from the requested entropy source. + + In general, it’s a $(B bad idea) to let users pick sources themselves. + A sane option should be used by default instead. + + This overload only exists because its used by Phobos. + + See_also: + Use `forceEntropySource` instead. + + Params: + buffer = An output buffer to store the retrieved entropy in. + The length of it will determine the amount of entropy to be + obtained. + length = Length of the provided `buffer`. + Specifying a wrong value here, will lead to memory corruption. + source = The entropy source to use for the operation. + + Returns: + An `EntropyResult` that either reports success + or the type of error that has occurred. + + In case of an error, the data in `buffer` MUST NOT be used. + The recommended way to check for success is through the `isOK()` + helper function. + +/ +EntropyResult getEntropy(scope void* buffer, size_t length, EntropySource source) @system +{ + const sourcePrevious = _entropySource; + scope (exit) _entropySource = sourcePrevious; + + _entropySource = source; + return getEntropy(buffer[0 .. length]); +} + +/// +@system unittest +{ + ubyte[4] bytes; + + // `EntropySource.none` always fails. + assert(!getEntropy(bytes.ptr, bytes.length, EntropySource.none).isOK); +} + +/++ + A CSPRNG suitable to retrieve cryptographically-secure random data from. + + (No actual low-level entropy sources are provided on purpose.) + +/ +enum EntropySource +{ + /// Implements a $(I hunting) strategy for finding an entropy source that + /// is available at runtime. + /// + /// Try supported sources one-by-one until one is available. + /// This exists to enable the use of this the entropy library + /// in a backwards compatibility way. + /// + /// It is recommended against using this in places that do not strictly + /// have to to meet compatibility requirements. + /// Like any kind of crypto-agility, this approach may suffer from + /// practical issues. + /// + /// See_also: + /// While the following article focuses on cipher agility in protocols, + /// it elaborates why agility can lead to problems: + /// $(LINK https://web.archive.org/web/20191102211148/https://paragonie.com/blog/2019/10/against-agility-in-cryptography-protocols) + tryAll = -1, + + /// Always fail. + none = 0, + + /// `/dev/urandom` + charDevURandom = 1, + + /// `/dev/random` + charDevRandom = 2, + + /// `getrandom` syscall or wrapper + getrandom = 3, + + /// `arc4random` + arc4random = 4, + + // `getentropy` + getentropy = 5, + + /// Windows legacy CryptoAPI + cryptGenRandom = 6, + + /// Windows Cryptography API: Next Generation (“BCrypt”) + bcryptGenRandom = 7, +} + +/// +enum EntropyStatus +{ + /// success + ok = 0, + + /// catch-all error + unknownError = 1, + + /// An entropy source was unavailable. + unavailable, + + /// A dependency providing the entropy source turned out unavailable. + unavailableLibrary, + + /// The requested entropy source is not supported on this platform. + unavailablePlatform, + + /// Could not retrieve entropy from the selected source. + readError, +} + +/++ + Status report returned by `getEntropy` functions. + + Use the `isOK` helper function to test for success. + +/ +struct EntropyResult +{ + /// + EntropyStatus status; + + /// + EntropySource source; + + /++ + Returns: + A human-readable status message. + +/ + string toString() const @nogc nothrow pure @safe + { + if (status == EntropyStatus.ok) + return "getEntropy(): OK."; + + if (source == EntropySource.none) + { + if (status == EntropyStatus.unavailable) + return "getEntropy(): Error - No suitable entropy source was available."; + } + else if (source == EntropySource.getrandom) + { + if (status == EntropyStatus.unavailableLibrary) + return "getEntropy(): `dlopen(\"libc\")` failed."; + if (status == EntropyStatus.unavailable) + return "getEntropy(): `dlsym(\"libc\", \"getrandom\")` failed."; + if (status == EntropyStatus.readError) + return "getEntropy(): `getrandom()` failed."; + } + else if (source == EntropySource.getentropy) + { + if (status == EntropyStatus.readError) + return "getEntropy(): `getentropy()` failed."; + } + else if (source == EntropySource.charDevURandom) + { + if (status == EntropyStatus.unavailable) + return "getEntropy(): `/dev/urandom` is unavailable."; + if (status == EntropyStatus.readError) + return "getEntropy(): Reading from `/dev/urandom` failed."; + } + else if (source == EntropySource.charDevURandom) + { + if (status == EntropyStatus.unavailable) + return "getEntropy(): `/dev/random` is unavailable."; + if (status == EntropyStatus.readError) + return "getEntropy(): Reading from `/dev/random` failed."; + } + else if (source == EntropySource.bcryptGenRandom) + { + if (status == EntropyStatus.unavailableLibrary) + return "getEntropy(): `LoadLibraryA(\"Bcrypt.dll\")` failed."; + if (status == EntropyStatus.unavailable) + return "getEntropy(): `GetProcAddress(hBcrypt , \"BCryptGenRandom\")` failed."; + if (status == EntropyStatus.readError) + return "getEntropy(): `BCryptGenRandom()` failed."; + } + + // generic errors + { + if (status == EntropyStatus.unavailable || + status == EntropyStatus.unavailableLibrary) + return "getEntropy(): An entropy source was unavailable."; + if (status == EntropyStatus.unavailablePlatform) + return "getEntropy(): The requested entropy source is not supported on this platform."; + if (status == EntropyStatus.readError) + return "getEntropy(): Could not retrieve entropy from the selected source."; + + return "getEntropy(): An unknown error occurred."; + } + } +} + +/// +@safe unittest +{ + ubyte[4] data; + EntropyResult result = getEntropy(data[]); + + if (result.isOK) + { + // Success; data in `bytes` may be used. + } + else + { + // Failure + + if (result.isUnavailable) + { + // System’s entropy source was unavailable. + } + + // Call `toString` to obtain a user-readable error message. + assert(result.toString() !is null); + assert(result.toString().length > 0); + } +} + +/++ + Determines whether an `EntropyResult` reports the success of an operation. + + Params: + value = test subject + + Returns: + `true` on success + +/ +pragma(inline, true) bool isOK(const EntropyResult value) pure @safe +{ + return value.status == EntropyStatus.ok; +} + +/++ + Determines whether an `EntropyResult` reports the unvailability of the + requested entropy source. + + Params: + value = test subject + + Returns: + `true` if entropy source requested to use with the operation was unavailable. + +/ +pragma(inline, true) bool isUnavailable(const EntropyResult value) pure @safe +{ + return ( + value.status == EntropyStatus.unavailable || + value.status == EntropyStatus.unavailableLibrary || + value.status == EntropyStatus.unavailablePlatform + ); +} + +package(std): + +// If the system let us down, we'll let the system down. +pragma(inline, true) void crashOnError(const EntropyResult value) pure @safe +{ + if (value.isOK) + return; + + assert(false, value.toString()); +} + +/+ + Building blocks and implementation helpers + +/ +private +{ + /++ + A “Chunks” implementation that works with `void[]`. + +/ + struct VoidChunks + { + void[] _data; + size_t _chunkSize; + + @nogc nothrow pure @safe: + + this(void[] data, size_t chunkSize) + { + _data = data; + _chunkSize = chunkSize; + } + + bool empty() const + { + return _data.length == 0; + } + + inout(void)[] front() inout + { + if (_data.length < _chunkSize) + return _data; + + return _data[0 .. _chunkSize]; + } + + void popFront() + { + if (_data.length <= _chunkSize) + { + _data = null; + return; + } + + _data = _data[_chunkSize .. $]; + } + } + + struct SrcFunPair(EntropySource source, alias func) + { + enum src = source; + alias fun = func; + } + + template isValidSupportedSource(SupportedSource) + { + enum isValidSupportedSource = ( + is(SupportedSource == SrcFunPair!Args, Args...) && + SupportedSource.src != EntropySource.tryAll && + SupportedSource.src != EntropySource.none + ); + } + + /++ + `getEntropyImpl()` implementation helper. + To be instantiated and mixed in with platform-specific configuration. + + Params: + defaultSource = Default entropy source of the platform + SupportedSources = Sequence of `SrcFunPair` + representing the supported sources of the platform + +/ + mixin template entropyImpl(EntropySource defaultSource, SupportedSources...) + if (allSatisfy!(isValidSupportedSource, SupportedSources)) + { + private: + /// Preconfigured entropy source preset of the platform. + enum defaultEntropySource = defaultSource; + + EntropyResult getEntropyImpl(scope void[] buffer) @safe + { + switch (_entropySource) + { + static foreach (source; SupportedSources) + { + case source.src: + return source.fun(buffer); + } + + case EntropySource.tryAll: + { + const result = _tryEntropySources(buffer); + result.saveSourceForNextUse(); + return result; + } + + case EntropySource.none: + return getEntropyViaNone(buffer); + + default: + return EntropyResult(EntropyStatus.unavailablePlatform, _entropySource); + } + } + + EntropyResult _tryEntropySources(scope void[] buffer) @safe + { + EntropyResult result; + + static foreach (source; SupportedSources) + { + result = source.fun(buffer); + if (!result.isUnavailable) + return result; + } + + result = EntropyResult( + EntropyStatus.unavailable, + EntropySource.none, + ); + + return result; + } + } +} + +version (Darwin) mixin entropyImpl!( + EntropySource.arc4random, + SrcFunPair!(EntropySource.arc4random, getEntropyViaARC4Random), + SrcFunPair!(EntropySource.charDevURandom, getEntropyViaCharDevURandom), + SrcFunPair!(EntropySource.charDevRandom, getEntropyViaCharDevRandom), +); +else version (DragonFlyBSD) mixin entropyImpl!( + EntropySource.getentropy, + SrcFunPair!(EntropySource.getentropy, getEntropyViaGetentropy), + SrcFunPair!(EntropySource.charDevURandom, getEntropyViaCharDevURandom), + SrcFunPair!(EntropySource.charDevRandom, getEntropyViaCharDevRandom), +); +else version (FreeBSD) mixin entropyImpl!( + EntropySource.getentropy, + SrcFunPair!(EntropySource.getentropy, getEntropyViaGetentropy), + SrcFunPair!(EntropySource.charDevURandom, getEntropyViaCharDevURandom), + SrcFunPair!(EntropySource.charDevRandom, getEntropyViaCharDevRandom), +); +else version (linux) mixin entropyImpl!( + EntropySource.getrandom, + SrcFunPair!(EntropySource.getrandom, getEntropyViaGetrandom), + SrcFunPair!(EntropySource.charDevURandom, getEntropyViaCharDevURandom), + SrcFunPair!(EntropySource.charDevRandom, getEntropyViaCharDevRandom), +); +else version (NetBSD) mixin entropyImpl!( + EntropySource.arc4random, + SrcFunPair!(EntropySource.arc4random, getEntropyViaARC4Random), + SrcFunPair!(EntropySource.charDevURandom, getEntropyViaCharDevURandom), + SrcFunPair!(EntropySource.charDevRandom, getEntropyViaCharDevRandom), +); +else version (OpenBSD) mixin entropyImpl!( + EntropySource.arc4random, + SrcFunPair!(EntropySource.arc4random, getEntropyViaARC4Random), + SrcFunPair!(EntropySource.charDevURandom, getEntropyViaCharDevURandom), + SrcFunPair!(EntropySource.charDevRandom, getEntropyViaCharDevRandom), +); +else version (Posix) mixin entropyImpl!( + EntropySource.charDevURandom, + SrcFunPair!(EntropySource.charDevURandom, getEntropyViaCharDevURandom), + SrcFunPair!(EntropySource.charDevRandom, getEntropyViaCharDevRandom), +); +else version (Windows) mixin entropyImpl!( + EntropySource.bcryptGenRandom, + SrcFunPair!(EntropySource.bcryptGenRandom, getEntropyViaBCryptGenRandom), +); +else mixin entropyImpl!( + EntropySource.none, +); + +private +{ + static EntropySource _entropySource = defaultEntropySource; + + void saveSourceForNextUse(const EntropyResult result) @safe + { + if (!result.isOK) + return; + + _entropySource = result.source; + } +} + +version (all) +{ +private: + + EntropyResult getEntropyViaNone(scope void[]) @safe + { + return EntropyResult(EntropyStatus.unavailable, EntropySource.none); + } +} + +version (Posix) +{ +private: + + EntropyResult getEntropyViaCharDevURandom(scope void[] buffer) @trusted + { + const status = getEntropyViaCharDev(buffer, "/dev/urandom".ptr); + return EntropyResult(status, EntropySource.charDevURandom); + } + + EntropyResult getEntropyViaCharDevRandom(scope void[] buffer) @trusted + { + const status = getEntropyViaCharDev(buffer, "/dev/random".ptr); + return EntropyResult(status, EntropySource.charDevRandom); + } + + EntropyStatus getEntropyViaCharDev(scope void[] buffer, const(char)* charDevName) @system + { + import core.stdc.stdio : fclose, fopen, fread; + + auto charDev = fopen(charDevName, "r"); + if (charDev is null) + return EntropyStatus.unavailable; + + scope (exit) + fclose(charDev); + + const bytesRead = fread(buffer.ptr, 1, buffer.length, charDev); + if (bytesRead != buffer.length) + return EntropyStatus.readError; + + return EntropyStatus.ok; + } +} + +version (linux) +{ +private: + + EntropyResult getEntropyViaGetrandom(scope void[] buffer) @trusted + { + const status = syscallGetrandom(buffer, 0); + return EntropyResult(status, EntropySource.getrandom); + } + + EntropyStatus syscallGetrandom(scope void[] buffer, uint flags) @system + { + import core.sys.linux.errno : EINTR, ENOSYS, errno; + import core.sys.linux.sys.syscall : SYS_getrandom; + import core.sys.linux.unistd : syscall; + + while (buffer.length > 0) + { + const got = syscall(SYS_getrandom, buffer.ptr, buffer.length, flags); + + if (got == -1) + { + switch (errno) + { + case EINTR: + break; // That’s fine. + case ENOSYS: + return EntropyStatus.unavailable; + default: + return EntropyStatus.readError; + } + } + + if (got > 0) + buffer = buffer[got .. $]; + } + + return EntropyStatus.ok; + } +} + +// BSD +private +{ + version (Darwin) + version = SecureARC4Random; + version (DragonFlyBSD) + version = UseGetentropy; + version (FreeBSD) + version = UseGetentropy; + version (NetBSD) + version = SecureARC4Random; + version (OpenBSD) + version = SecureARC4Random; + + version (SecureARC4Random) + { + EntropyResult getEntropyViaARC4Random(scope void[] buffer) @trusted + { + arc4random_buf(buffer.ptr, buffer.length); + return EntropyResult(EntropyStatus.ok, EntropySource.arc4random); + } + + private extern(C) void arc4random_buf(scope void* buf, size_t nbytes) @system; + } + + version (UseGetentropy) + { + EntropyResult getEntropyViaGetentropy(scope void[] buffer) @trusted + { + const status = callGetentropy(buffer); + return EntropyResult(status, EntropySource.getentropy); + } + + private EntropyStatus callGetentropy(scope void[] buffer) @system + { + /+ + genentropy(3): + The maximum buflen permitted is 256 bytes. + +/ + foreach (chunk; VoidChunks(buffer, 256)) + { + const status = getentropy(buffer.ptr, buffer.length); + if (status != 0) + return EntropyStatus.readError; + } + + return EntropyStatus.ok; + } + + private extern(C) int getentropy(scope void* buf, size_t buflen) @system; + } +} + +version (Windows) +{ + import core.sys.windows.bcrypt : BCryptGenRandom, BCRYPT_USE_SYSTEM_PREFERRED_RNG; + import core.sys.windows.windef : HMODULE, PUCHAR, ULONG; + import core.sys.windows.ntdef : NT_SUCCESS; + +private: + + EntropyResult getEntropyViaBCryptGenRandom(scope void[] buffer) @trusted + { + const loaded = loadBcrypt(); + if (loaded != EntropyStatus.ok) + return EntropyResult(loaded, EntropySource.bcryptGenRandom); + + const status = callBcryptGenRandom(buffer); + return EntropyResult(status, EntropySource.bcryptGenRandom); + } + + EntropyStatus callBcryptGenRandom(scope void[] buffer) @system + { + foreach (chunk; VoidChunks(buffer, ULONG.max)) + { + assert(chunk.length <= ULONG.max, "Bad chunk length."); + + const gotRandom = ptrBCryptGenRandom( + null, + cast(PUCHAR) buffer.ptr, + cast(ULONG) buffer.length, + BCRYPT_USE_SYSTEM_PREFERRED_RNG, + ); + + if (!NT_SUCCESS(gotRandom)) + return EntropyStatus.readError; + } + + return EntropyStatus.ok; + } + + static + { + HMODULE hBcrypt = null; + typeof(BCryptGenRandom)* ptrBCryptGenRandom; + } + + EntropyStatus loadBcrypt() @system + { + import core.sys.windows.winbase : GetProcAddress, LoadLibraryA; + + if (hBcrypt !is null) + return EntropyStatus.ok; + + hBcrypt = LoadLibraryA("Bcrypt.dll"); + if (!hBcrypt) + return EntropyStatus.unavailableLibrary; + + ptrBCryptGenRandom = cast(typeof(ptrBCryptGenRandom)) GetProcAddress(hBcrypt, "BCryptGenRandom"); + if (!ptrBCryptGenRandom) + return EntropyStatus.unavailable; + + return EntropyStatus.ok; + } + + // Will free `Bcrypt.dll`. + void freeBcrypt() @system + { + import core.sys.windows.winbase : FreeLibrary; + + if (hBcrypt is null) + return; + + if (!FreeLibrary(hBcrypt)) + { + return; // Error + } + + hBcrypt = null; + ptrBCryptGenRandom = null; + } + + static ~this() @system + { + freeBcrypt(); + } +} diff --git a/libphobos/src/std/internal/math/gammafunction.d b/libphobos/src/std/internal/math/gammafunction.d index 703ecb1e6b3..d5f5dd2e814 100644 --- a/libphobos/src/std/internal/math/gammafunction.d +++ b/libphobos/src/std/internal/math/gammafunction.d @@ -1643,7 +1643,12 @@ real digamma(real x) negative = 0; nz = 0.0; - if ( x <= 0.0 ) + if ( x == 0.0 ) + { + return signbit(x) == 1 ? real.infinity : -real.infinity; + } + + if ( x < 0.0 ) { negative = 1; q = x; @@ -1718,6 +1723,10 @@ done: assert(digamma(1.0)== -EULERGAMMA); assert(feqrel(digamma(0.25), -PI/2 - 3* LN2 - EULERGAMMA) >= real.mant_dig-7); assert(feqrel(digamma(1.0L/6), -PI/2 *sqrt(3.0L) - 2* LN2 -1.5*log(3.0L) - EULERGAMMA) >= real.mant_dig-7); + assert(digamma(-0.0) == real.infinity); + assert(!digamma(nextDown(-0.0)).isNaN()); + assert(digamma(+0.0) == -real.infinity); + assert(!digamma(nextUp(+0.0)).isNaN()); assert(digamma(-5.0).isNaN()); assert(feqrel(digamma(2.5), -EULERGAMMA - 2*LN2 + 2.0 + 2.0L/3) >= real.mant_dig-9); assert(isIdentical(digamma(NaN(0xABC)), NaN(0xABC))); diff --git a/libphobos/src/std/mathspecial.d b/libphobos/src/std/mathspecial.d index 64ab9bf3714..10c4a2d5310 100644 --- a/libphobos/src/std/mathspecial.d +++ b/libphobos/src/std/mathspecial.d @@ -29,6 +29,7 @@ * NAN = $(RED NAN) * SUP = $0 * GAMMA = Γ + * PSI = Ψ * THETA = θ * INTEGRAL = ∫ * INTEGRATE = $(BIG ∫$(SMALL $1)$2) @@ -37,8 +38,10 @@ * BIGSUM = $(BIG Σ $2$(SMALL $1)) * CHOOSE = $(BIG () $(SMALL $1)$(SMALL $2) $(BIG )) * PLUSMN = ± + * MNPLUS = ∓ * INFIN = ∞ * PLUSMNINF = ±∞ + * MNPLUSINF = ∓∞ * PI = π * LT = < * GT = > @@ -166,19 +169,47 @@ real beta(real x, real y) assert(isIdentical(beta(2, NaN(0xABC)), NaN(0xABC))); } -/** Digamma function +/** Digamma function, $(PSI)(x) * - * The digamma function is the logarithmic derivative of the gamma function. + * $(PSI)(x), is the logarithmic derivative of the gamma function, $(GAMMA)(x). * - * digamma(x) = d/dx logGamma(x) + * $(PSI)(x) = $(SUP d)$(SUB /, dx) ln|$(GAMMA)(x)| (the derivative of `logGamma(x)`) * - * See_Also: $(LREF logmdigamma), $(LREF logmdigammaInverse). + * Params: + * x = the domain value + * + * Returns: + * It returns $(PSI)(x). + * + * $(TABLE_SV + * $(SVH x, digamma(x) ) + * $(SV integer < 0, $(NAN) ) + * $(SV $(PLUSMN)0.0, $(MNPLUSINF) ) + * $(SV +$(INFIN), +$(INFIN) ) + * $(SV -$(INFIN), $(NAN) ) + * $(SV $(NAN), $(NAN) ) + * ) + * + * See_Also: $(LREF logmdigamma), $(LREF logmdigammaInverse). */ real digamma(real x) { return std.internal.math.gammafunction.digamma(x); } +/// +@safe unittest +{ + const euler = 0.57721_56649_01532_86060_65121L; + + assert(isClose(digamma(1), -euler)); + assert(digamma(+0.) == -real.infinity); + assert(digamma(-0.) == +real.infinity); + assert(digamma(+real.infinity) == +real.infinity); + assert(isNaN(digamma(-1))); + assert(isNaN(digamma(-real.infinity))); +} + /** Log Minus Digamma function * * logmdigamma(x) = log(x) - digamma(x) diff --git a/libphobos/src/std/numeric.d b/libphobos/src/std/numeric.d index 918984fd52e..345d3b7a87b 100644 --- a/libphobos/src/std/numeric.d +++ b/libphobos/src/std/numeric.d @@ -3733,7 +3733,16 @@ public: } else if (range.length == 1) { - buf[0] = range[0]; + static if (isNumeric!(ElementType!R)) + { + buf[0].re = range[0]; + buf[0].im = 0; + } + else + { + buf[0].re = range[0].re; + buf[0].im = range[0].im; + } return; } else if (range.length == 2) @@ -3931,6 +3940,26 @@ void inverseFft(Ret, R)(R range, Ret buf) assert(isClose(twoInv[1].im, 0, 0.0, 1e-10)); } +// https://github.com/dlang/phobos/issues/10796 +@system unittest +{ + import std.algorithm; + import std.range; + static struct C { float re, im; } // User-defined complex + + float[8] arr = [1,2,3,4,5,6,7,8]; + C[8] fft1; + fft(arr[], fft1[]); + assert(isClose(fft1[].map!"a.re", + [36.0, -4, -4, -4, -4, -4, -4, -4], 1e-4)); + assert(isClose(fft1[].map!"a.im", + [0, 9.6568, 4, 1.6568, 0, -1.6568, -4, -9.6568], 1e-4)); + + auto inv = inverseFft(fft1[]); + assert(isClose(inv[].map!"a.re", arr[], 1e-6)); + assert(inv[].map!"a.im".maxElement < 1e-10); +} + // Swaps the real and imaginary parts of a complex number. This is useful // for inverse FFTs. C swapRealImag(C)(C input) @@ -4112,11 +4141,24 @@ struct Stride(R) // using a generic slow DFT. This seems to be the best base case. (Size 1 // can be coded inline as buf[0] = range[0]). void slowFourier2(Ret, R)(R range, Ret buf) +if (isComplexLike!(ElementType!Ret)) +in (range.length == 2) +in (buf.length == 2) { - assert(range.length == 2); - assert(buf.length == 2); - buf[0] = range[0] + range[1]; - buf[1] = range[0] - range[1]; + static if (isNumeric!(ElementType!R)) + { + buf[0].re = range[0] + range[1]; + buf[0].im = 0; + buf[1].re = range[0] - range[1]; + buf[1].im = 0; + } + else + { + buf[0].re = range[0].re + range[1].re; + buf[0].im = range[0].im + range[1].im; + buf[1].re = range[0].re - range[1].re; + buf[1].im = range[0].im - range[1].im; + } } // Hard-coded base case for FFT of size 4. Doesn't work as well as the size diff --git a/libphobos/src/std/random.d b/libphobos/src/std/random.d index fb4e5469088..0176b0ae653 100644 --- a/libphobos/src/std/random.d +++ b/libphobos/src/std/random.d @@ -1773,43 +1773,9 @@ else } version (linux) -{ - // `getrandom()` was introduced in Linux 3.17. - - // Shim for missing bindings in druntime - version (none) - import core.sys.linux.sys.random : getrandom; - else - { - import core.sys.posix.sys.types : ssize_t; - extern extern(C) ssize_t getrandom( - void* buf, - size_t buflen, - uint flags, - ) @system nothrow @nogc; - } -} - + version = SeedUseGetEntropy; version (Windows) -{ - pragma(lib, "Bcrypt.lib"); - - private bool bcryptGenRandom(T)(out T result) @trusted - { - import core.sys.windows.windef : PUCHAR, ULONG; - import core.sys.windows.ntdef : NT_SUCCESS; - import core.sys.windows.bcrypt : BCryptGenRandom, BCRYPT_USE_SYSTEM_PREFERRED_RNG; - - const gotRandom = BCryptGenRandom( - null, - cast(PUCHAR) &result, - ULONG(T.sizeof), - BCRYPT_USE_SYSTEM_PREFERRED_RNG, - ); - - return NT_SUCCESS(gotRandom); - } -} + version = SeedUseGetEntropy; /** A "good" seed for initializing random number engines. Initializing @@ -1851,36 +1817,15 @@ how excellent the source of entropy is. */ @property uint unpredictableSeed() @trusted nothrow @nogc { - version (linux) + version (SeedUseGetEntropy) { - uint buffer; - - /* - getrandom(2): - If the _urandom_ source has been initialized, reads of up to - 256 bytes will always return as many bytes as requested and - will not be interrupted by signals. No such guarantees apply - for larger buffer sizes. - */ - static assert(buffer.sizeof <= 256); - - const status = (() @trusted => getrandom(&buffer, buffer.sizeof, 0))(); - assert(status == buffer.sizeof); + import std.internal.entropy : crashOnError, EntropySource, getEntropy; + uint buffer; + const status = (() @trusted => getEntropy(&buffer, buffer.sizeof, EntropySource.tryAll))(); + crashOnError(status); return buffer; } - else version (Windows) - { - uint result; - if (!bcryptGenRandom!uint(result)) - { - version (none) - return fallbackSeed(); - else - assert(false, "BCryptGenRandom() failed."); - } - return result; - } else version (AnyARC4Random) { return arc4random(); @@ -1930,36 +1875,15 @@ if (isUnsigned!UIntType) /// ditto @property UIntType unpredictableSeed() @nogc nothrow @trusted { - version (linux) + version (SeedUseGetEntropy) { - UIntType buffer; - - /* - getrandom(2): - If the _urandom_ source has been initialized, reads of up to - 256 bytes will always return as many bytes as requested and - will not be interrupted by signals. No such guarantees apply - for larger buffer sizes. - */ - static assert(buffer.sizeof <= 256); - - const status = (() @trusted => getrandom(&buffer, buffer.sizeof, 0))(); - assert(status == buffer.sizeof); + import std.internal.entropy : crashOnError, EntropySource, getEntropy; + UIntType buffer; + const status = (() @trusted => getEntropy(&buffer, buffer.sizeof, EntropySource.tryAll))(); + crashOnError(status); return buffer; } - else version (Windows) - { - UIntType result; - if (!bcryptGenRandom!UIntType(result)) - { - version (none) - return fallbackSeed(); - else - assert(false, "BCryptGenRandom() failed."); - } - return result; - } else version (AnyARC4Random) { static if (UIntType.sizeof <= uint.sizeof) diff --git a/libphobos/src/std/range/package.d b/libphobos/src/std/range/package.d index 82580599a7d..4620b98071e 100644 --- a/libphobos/src/std/range/package.d +++ b/libphobos/src/std/range/package.d @@ -3881,9 +3881,11 @@ pure @safe nothrow @nogc unittest } /++ - Convenience function which calls + `drop` is a convenience function which calls $(REF popFrontN, std, range, primitives)`(range, n)` and returns `range`. - `drop` makes it easier to pop elements from a range + Unlike `popFrontN`, the range argument is passed by copy, not by `ref`. + + `drop` makes it easier to pop elements from a range rvalue and then pass it to another function within a single expression, whereas `popFrontN` would require multiple statements. @@ -3916,7 +3918,10 @@ if (isInputRange!R) { import std.algorithm.comparison : equal; - assert([0, 2, 1, 5, 0, 3].drop(3) == [5, 0, 3]); + auto a = [0, 2, 1, 5, 0, 3]; + assert(a.drop(3) == [5, 0, 3]); + assert(a.length == 6); // original unchanged + assert("hello world".drop(6) == "world"); assert("hello world".drop(50).empty); assert("hello world".take(6).drop(3).equal("lo ")); @@ -3993,8 +3998,8 @@ if (isBidirectionalRange!R) `range` with `n` elements dropped See_Also: - $(REF popFrontExcatly, std, range, primitives), - $(REF popBackExcatly, std, range, primitives) + $(REF popFrontExactly, std, range, primitives), + $(REF popBackExactly, std, range, primitives) +/ R dropExactly(R)(R range, size_t n) if (isInputRange!R) @@ -4030,9 +4035,11 @@ if (isBidirectionalRange!R) } /++ - Convenience function which calls - `range.popFront()` and returns `range`. `dropOne` - makes it easier to pop an element from a range + `dropOne` is a convenience function which calls + `range.popFront()` and returns `range`. + Unlike `popFront`, the range argument is passed by copy, not by `ref`. + + `dropOne` makes it easier to pop an element from a range rvalue and then pass it to another function within a single expression, whereas `popFront` would require multiple statements. diff --git a/libphobos/src/std/string.d b/libphobos/src/std/string.d index bcc9d7c13af..12661e71328 100644 --- a/libphobos/src/std/string.d +++ b/libphobos/src/std/string.d @@ -6864,6 +6864,11 @@ if (isConvertibleToString!Range) * tabsize = column spacing of tabs in firstindent[] and indent[] * Returns: * resulting paragraph as an allocated string + * Bugs: + * Columns are counted as the number of code points in the string. This may + * not correspond with the actual number of columns displayed if the string + * contains combining characters, modifiers, zero-width characters, or + * double-width characters. */ S wrap(S)(S s, in size_t columns = 80, S firstindent = null, diff --git a/libphobos/src/std/traits.d b/libphobos/src/std/traits.d index 9bdd9775f9c..400df02aaac 100644 --- a/libphobos/src/std/traits.d +++ b/libphobos/src/std/traits.d @@ -7611,6 +7611,8 @@ enum bool isSomeFunction(alias T) = /** Detect whether `T` is a callable object, which can be called with the function call operator `$(LPAREN)...$(RPAREN)`. + +$(NOTE Implicit Function Template Instantiation is *not* attempted - see below.) */ template isCallable(alias callable) { @@ -7634,7 +7636,7 @@ template isCallable(alias callable) } } -/// Functions, lambdas, and aggregate types with (static) opCall. +/// Functions, function pointers, delegates, lambdas. @safe unittest { void f() { } @@ -7643,6 +7645,17 @@ template isCallable(alias callable) static assert( isCallable!f); static assert( isCallable!g); + auto fp = &f; + static assert( isCallable!fp); + static assert( isCallable!((int x) {})); + + int x; + static assert(!isCallable!x); +} + +/// Aggregate types with (static) opCall. +@safe unittest +{ class C { int opCall(int) { return 0; } } auto c = new C; struct S { static int opCall(int) { return 0; } } @@ -7657,7 +7670,7 @@ template isCallable(alias callable) static assert(!isCallable!I); } -/// Templates +/// Template functions are only detected if they are instantiable with `!()`. @safe unittest { void f()() { } @@ -7669,9 +7682,11 @@ template isCallable(alias callable) static assert( isCallable!g); static assert( isCallable!S1); static assert( isCallable!S2); + + static assert(!isCallable!((x) {})); } -/// Overloaded functions and function templates. +/// Overloaded functions and function templates instantiable with `!()`. @safe unittest { static struct Wrapper diff --git a/libphobos/src/std/typecons.d b/libphobos/src/std/typecons.d index d7f86d17aed..cd2772109c0 100644 --- a/libphobos/src/std/typecons.d +++ b/libphobos/src/std/typecons.d @@ -3836,22 +3836,22 @@ struct Nullable(T) * Returns: * A `string` if `writer` and `fmt` are not set; `void` otherwise. */ - string toString() + string toString()() { import std.array : appender; auto app = appender!string(); auto spec = singleSpec("%s"); - toString(app, spec); + this.toString(app, spec); return app.data; } /// ditto - string toString() const + string toString()() const { import std.array : appender; auto app = appender!string(); auto spec = singleSpec("%s"); - toString(app, spec); + this.toString(app, spec); return app.data; } @@ -6677,8 +6677,8 @@ private static: if (atts & FA.property) poatts ~= " @property"; if (atts & FA.safe ) poatts ~= " @safe"; if (atts & FA.trusted ) poatts ~= " @trusted"; - if (atts & FA.scope_ ) poatts ~= " scope"; if (atts & FA.return_ ) poatts ~= " return"; + if (atts & FA.scope_ ) poatts ~= " scope"; return poatts; } enum postAtts = make_postAtts(); @@ -10786,18 +10786,18 @@ private template replaceTypeInFunctionTypeUnless(alias pred, From, To, fun) { if (i) result ~= ", "; + if (storageClasses[i] & ParameterStorageClass.return_) + result ~= "return "; if (storageClasses[i] & ParameterStorageClass.scope_) result ~= "scope "; - if (storageClasses[i] & ParameterStorageClass.in_) - result ~= "in "; if (storageClasses[i] & ParameterStorageClass.out_) result ~= "out "; if (storageClasses[i] & ParameterStorageClass.ref_) result ~= "ref "; + if (storageClasses[i] & ParameterStorageClass.in_) + result ~= "in "; if (storageClasses[i] & ParameterStorageClass.lazy_) result ~= "lazy "; - if (storageClasses[i] & ParameterStorageClass.return_) - result ~= "return "; result ~= "PX[" ~ i.stringof ~ "]"; } diff --git a/libphobos/testsuite/libphobos.aa/test_aa.d b/libphobos/testsuite/libphobos.aa/test_aa.d index 5c3ba05d83d..4f8cac52d3c 100644 --- a/libphobos/testsuite/libphobos.aa/test_aa.d +++ b/libphobos/testsuite/libphobos.aa/test_aa.d @@ -41,6 +41,8 @@ void main() testTombstonePurging(); testClear(); testTypeInfoCollect(); + testNew(); + testAliasThis(); } void testKeysValues1() @@ -947,3 +949,42 @@ void testTypeInfoCollect() s = null; // clear any reference to the entry GC.collect(); // used to segfault. } + +void testNew() +{ + auto aa = new long[int]; // call _d_newAA + assert(aa.length == 0); + foreach (i; 0 .. 100) + aa[i] = i * 2; + assert(aa.length == 100); + + // not supported in CTFE (it doesn't do much anyway): + // static auto aa = new long[int]; +} + +void testAliasThis() +{ + static struct S + { + __gshared int numCopies; + + ubyte[long] aa; + S* next; + + this(this) { numCopies++; } + + alias aa this; + } + S s; + long key = 1; + s.aa[1] = 1; // create and insert + assert(S.numCopies == 0); + if (auto p = 1 in s) + *p = 2; + if (auto p = key in s) + *p = 3; + if (auto p = () { return 1; }() in s) + *p = 4; + s.remove(1); + assert(S.numCopies == 0); +} diff --git a/libphobos/testsuite/libphobos.init_fini/custom_gc.d b/libphobos/testsuite/libphobos.init_fini/custom_gc.d index b85aa889da9..1aee31c6acb 100644 --- a/libphobos/testsuite/libphobos.init_fini/custom_gc.d +++ b/libphobos/testsuite/libphobos.init_fini/custom_gc.d @@ -4,6 +4,8 @@ import core.stdc.stdlib : calloc, malloc, realloc; static import core.memory; +import core.thread.threadbase : ThreadBase; + extern (C) __gshared string[] rt_options = ["gcopt=gc:malloc"]; extern (C) pragma(crt_constructor) void register_mygc() @@ -209,6 +211,14 @@ nothrow @nogc: return false; } + void initThread(ThreadBase thread) nothrow @nogc + { + } + + void cleanupThread(ThreadBase thread) nothrow @nogc + { + } + private: // doesn't care for alignment static void* sentinelAdd(void* p, size_t value)