diff --git a/gcc/d/Make-lang.in b/gcc/d/Make-lang.in index ae575cf465ed329146d7889a8f5a5e7103bedf71..f28761e4b37012e7b6292db11c7ff8962f7d61c4 100644 --- a/gcc/d/Make-lang.in +++ b/gcc/d/Make-lang.in @@ -88,7 +88,6 @@ D_FRONTEND_OBJS = \ d/ast_node.o \ d/astcodegen.o \ d/astenums.o \ - d/basicmangle.o \ d/blockexit.o \ d/builtin.o \ d/canthrow.o \ @@ -104,7 +103,6 @@ D_FRONTEND_OBJS = \ d/cond.o \ d/constfold.o \ d/cparse.o \ - d/cppmangle.o \ d/ctfeexpr.o \ d/ctorflow.o \ d/cxxfrontend.o \ @@ -117,7 +115,6 @@ D_FRONTEND_OBJS = \ d/dimport.o \ d/dinterpret.o \ d/dmacro.o \ - d/dmangle.o \ d/dmodule.o \ d/doc.o \ d/dscope.o \ @@ -155,6 +152,9 @@ D_FRONTEND_OBJS = \ d/lambdacomp.o \ d/lexer.o \ d/location.o \ + d/mangle-basic.o \ + d/mangle-cpp.o \ + d/mangle-package.o \ d/mtype.o \ d/mustuse.o \ d/nogc.o \ @@ -420,6 +420,10 @@ d/common-%.o: d/dmd/common/%.d $(DCOMPILE) $(D_INCLUDES) $< $(DPOSTCOMPILE) +d/mangle-%.o: d/dmd/mangle/%.d + $(DCOMPILE) $(D_INCLUDES) $< + $(DPOSTCOMPILE) + d/root-%.o: d/dmd/root/%.d $(DCOMPILE) $(D_INCLUDES) $< $(DPOSTCOMPILE) diff --git a/gcc/d/d-builtins.cc b/gcc/d/d-builtins.cc index 538600cd027841655ee00688b25126dc8eba5485..f7e7a801a00d1dadc80ce19d6a8ad6ff4cffb954 100644 --- a/gcc/d/d-builtins.cc +++ b/gcc/d/d-builtins.cc @@ -777,7 +777,7 @@ maybe_set_builtin_1 (Dsymbol *d) if (ad != NULL) { /* Recursively search through attribute decls. */ - Dsymbols *decls = ad->include (NULL); + Dsymbols *decls = dmd::include (ad, NULL); if (decls && decls->length) { for (size_t i = 0; i < decls->length; i++) diff --git a/gcc/d/d-diagnostic.cc b/gcc/d/d-diagnostic.cc index b8c04dce303fa6c95e6c8c89c51ae7d26965c2d8..4749585d93054cbe311e0ee3c87b237f6f4f16af 100644 --- a/gcc/d/d-diagnostic.cc +++ b/gcc/d/d-diagnostic.cc @@ -241,7 +241,7 @@ verrorReport (const Loc& loc, const char *format, va_list ap, ErrorKind kind, } else if (kind == ErrorKind::warning) { - if (global.gag || global.params.warnings == DIAGNOSTICoff) + if (global.gag || global.params.useWarnings == DIAGNOSTICoff) { if (global.gag) global.gaggedWarnings++; @@ -250,7 +250,7 @@ verrorReport (const Loc& loc, const char *format, va_list ap, ErrorKind kind, } /* Warnings don't count if not treated as errors. */ - if (global.params.warnings == DIAGNOSTICerror) + if (global.params.useWarnings == DIAGNOSTICerror) global.warnings++; diag_kind = DK_WARNING; @@ -314,7 +314,7 @@ verrorReportSupplemental (const Loc& loc, const char* format, va_list ap, } else if (kind == ErrorKind::warning) { - if (global.params.warnings == DIAGNOSTICoff || global.gag) + if (global.params.useWarnings == DIAGNOSTICoff || global.gag) return; } else if (kind == ErrorKind::deprecation) diff --git a/gcc/d/d-frontend.cc b/gcc/d/d-frontend.cc index 90a22041bb0f19f0b0ccf78e438895aa5298fe85..587579d9f80433d0519cb75c077db76bbe4288ec 100644 --- a/gcc/d/d-frontend.cc +++ b/gcc/d/d-frontend.cc @@ -79,11 +79,11 @@ eval_builtin (const Loc &loc, FuncDeclaration *fd, Expressions *arguments) /* Build and return typeinfo type for TYPE. */ Type * -getTypeInfoType (const Loc &loc, Type *type, Scope *sc, bool genObjCode) +getTypeInfoType (const Loc &loc, Type *type, Scope *sc) { gcc_assert (type->ty != TY::Terror); check_typeinfo_type (loc, sc); - create_typeinfo (type, sc ? sc->_module->importedFrom : NULL, genObjCode); + create_typeinfo (type, sc ? sc->_module->importedFrom : NULL); return type->vtinfo->type; } diff --git a/gcc/d/d-lang.cc b/gcc/d/d-lang.cc index b6724c3afbcf364c704be9012b14de9204a3b850..467e886530417307a23b93d9f62a8c6ad3e39480 100644 --- a/gcc/d/d-lang.cc +++ b/gcc/d/d-lang.cc @@ -302,7 +302,7 @@ d_init_options (unsigned int, cl_decoded_option *decoded_options) /* Warnings and deprecations are disabled by default. */ global.params.useDeprecated = DIAGNOSTICinform; - global.params.warnings = DIAGNOSTICoff; + global.params.useWarnings = DIAGNOSTICoff; global.params.v.errorLimit = flag_max_errors; global.params.v.messageStyle = MessageStyle::gnu; @@ -772,7 +772,7 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value, case OPT_Wall: if (value) - global.params.warnings = DIAGNOSTICinform; + global.params.useWarnings = DIAGNOSTICinform; break; case OPT_Wdeprecated: @@ -781,7 +781,7 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value, case OPT_Werror: if (value) - global.params.warnings = DIAGNOSTICerror; + global.params.useWarnings = DIAGNOSTICerror; break; case OPT_Wspeculative: @@ -907,7 +907,7 @@ d_post_options (const char ** fn) /* Error about use of deprecated features. */ if (global.params.useDeprecated == DIAGNOSTICinform - && global.params.warnings == DIAGNOSTICerror) + && global.params.useWarnings == DIAGNOSTICerror) global.params.useDeprecated = DIAGNOSTICerror; if (flag_excess_precision == EXCESS_PRECISION_DEFAULT) @@ -933,6 +933,7 @@ d_post_options (const char ** fn) /* The front-end parser only has access to `compileEnv', synchronize its fields with params. */ global.compileEnv.previewIn = global.params.previewIn; + global.compileEnv.transitionIn = global.params.v.vin; global.compileEnv.ddocOutput = global.params.ddoc.doOutput; global.compileEnv.cCharLookupTable = IdentifierCharLookup::forTable (IdentifierTable::C11); diff --git a/gcc/d/d-target.cc b/gcc/d/d-target.cc index ed7158528b6fa9d259a7a356312a49fbffe80980..e5d7e2b183004a0d4aad9b003cfbd603aa5a1025 100644 --- a/gcc/d/d-target.cc +++ b/gcc/d/d-target.cc @@ -588,3 +588,23 @@ Target::preferPassByRef (Type *param_type) return (tb->ty == TY::Tstruct || tb->ty == TY::Tsarray); } + +/* Returns true if the specified bit-field FIELD contributes to the alignment + of the containing aggregate. */ + +bool +TargetC::contributesToAggregateAlignment(BitFieldDeclaration *field) +{ + if (this->bitFieldStyle == TargetC::BitFieldStyle::MS) + return true; + + if (PCC_BITFIELD_TYPE_MATTERS) + { + /* Named bit-fields contribute to alignment. Some targets also apply the + same rules to unnamed bit-fields too. */ + if (!field->isAnonymous () || targetm.align_anon_bitfield ()) + return true; + } + + return false; +} diff --git a/gcc/d/d-tree.h b/gcc/d/d-tree.h index 5a5a81c091e2097d32608b8988f28e1035d18f49..eeecef33e1dd4055dd6179e4c87367461b5127a6 100644 --- a/gcc/d/d-tree.h +++ b/gcc/d/d-tree.h @@ -703,7 +703,7 @@ extern tree get_classinfo_decl (ClassDeclaration *); extern void check_typeinfo_type (const Loc &, Scope *, Expression * = NULL); extern tree build_typeinfo (const Loc &, Type *, Expression * = NULL); extern tree build_typeinfo (Expression *, Type *); -extern void create_typeinfo (Type *, Module *, bool = true); +extern void create_typeinfo (Type *, Module *); extern void create_tinfo_types (Module *); extern void layout_cpp_typeinfo (ClassDeclaration *); extern tree get_cpp_typeinfo_decl (ClassDeclaration *); diff --git a/gcc/d/decl.cc b/gcc/d/decl.cc index af984968c64aa15fe639b1d6638373c8143dab6d..88dc5251bdafc982e504ade7cd103f3a764b20c3 100644 --- a/gcc/d/decl.cc +++ b/gcc/d/decl.cc @@ -350,7 +350,7 @@ public: void visit (AttribDeclaration *d) final override { - Dsymbols *ds = d->include (NULL); + Dsymbols *ds = dmd::include (d, NULL); if (!ds) return; @@ -2416,7 +2416,7 @@ layout_struct_initializer (StructDeclaration *sd) { StructLiteralExp *sle = StructLiteralExp::create (sd->loc, sd, NULL); - if (!sd->fill (sd->loc, *sle->elements, true)) + if (!dmd::fill (sd, sd->loc, *sle->elements, true)) gcc_unreachable (); sle->type = sd->type; diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE index acb7d98123f83f1bc90324fe0616009d2eaffb88..f660884c3b9590a9f318b1d3025a5c9b36456315 100644 --- a/gcc/d/dmd/MERGE +++ b/gcc/d/dmd/MERGE @@ -1,4 +1,4 @@ -4ccb01fde535c7ad6ad4bdae2516c99420751814 +2b89c2909de239bd603d6f36379658fe902667db 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 a2c940fce01520fb15f827e529d0131271268400..1e96152d04a7ab9aa84a3de2103e9cfe40ccacc3 100644 --- a/gcc/d/dmd/README.md +++ b/gcc/d/dmd/README.md @@ -41,6 +41,7 @@ Note that these groups have no strict meaning, the category assignments are a bi | [frontend.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/frontend.d) | An interface for using DMD as a library | | [errors.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/errors.d) | Error reporting implementation | | [errorsink.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/errorsink.d) | Error reporting interface | +| [sarif.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/sarif.d) | Generates SARIF reports for errors and warnings. | | [target.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/target.d) | Manage target-specific parameters for cross-compiling (for LDC/GDC) | | [compiler.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/compiler.d) | Describe a back-end compiler and implements compiler-specific actions | | [deps.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/deps.d) | Implement the `-deps` and `-makedeps` switches | @@ -199,13 +200,13 @@ Note that these groups have no strict meaning, the category assignments are a bi | File | Purpose | |-------------------------------------------------------------------------------|------------------------------------------------------| -| [lib.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib.d) | Abstract library class | -| [libelf.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/libelf.d) | Library in ELF format (Unix) | -| [libmach.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/libmach.d) | Library in Mach-O format (macOS) | -| [libmscoff.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/libmscoff.d) | Library in COFF format (32/64-bit Windows) | -| [scanelf.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/scanelf.d) | Extract symbol names from a library in ELF format | -| [scanmach.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/scanmach.d) | Extract symbol names from a library in Mach-O format | -| [scanmscoff.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/scanmscoff.d) | Extract symbol names from a library in COFF format | +| [lib/package.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/package.d) | Abstract library class | +| [lib/elf.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/elf.d) | Library in ELF format (Unix) | +| [lib/mach.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/mach.d) | Library in Mach-O format (macOS) | +| [lib/mscoff.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/mscoff.d) | Library in COFF format (32/64-bit Windows) | +| [lib/scanelf.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/scanelf.d) | Extract symbol names from a library in ELF format | +| [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 | ### Code generation / back-end interfacing @@ -233,10 +234,10 @@ Note that these groups have no strict meaning, the category assignments are a bi | File | Purpose | |-----------------------------------------------------------------------------------|------------------------------------------------------------------| -| [cppmangle.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cppmangle.d) | C++ name mangling | -| [cppmanglewin.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cppmanglewin.d) | C++ name mangling for Windows | -| [basicmangle.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/basicmangle.d) | D name mangling for basic types | -| [dmangle.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dmangle.d) | D [name mangling](https://dlang.org/spec/abi.html#name_mangling) | +| [mangle/cpp.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mangle/cpp.d) | C++ name mangling | +| [mangle/cppwin.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mangle/cppwin.d) | C++ name mangling for Windows | +| [mangle/basic.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mangle/basic.d) | D name mangling for basic types | +| [mangle/package.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mangle/package.d) | D [name mangling](https://dlang.org/spec/abi.html#name_mangling) | ### Linking diff --git a/gcc/d/dmd/access.d b/gcc/d/dmd/access.d index f29755bbfdf669f3cac51e3c171078e8bf370f48..e778ed92cae8149212567d6b063ee67acbbc40ae 100644 --- a/gcc/d/dmd/access.d +++ b/gcc/d/dmd/access.d @@ -22,6 +22,7 @@ import dmd.dstruct; import dmd.dsymbol; import dmd.errors; import dmd.expression; +import dmd.funcsem : overloadApply; import dmd.location; import dmd.tokens; diff --git a/gcc/d/dmd/aggregate.d b/gcc/d/dmd/aggregate.d index 78cb87f2fe7d9b772bc950c9aa0d0da733e9063a..25f54b57c91d29e0e94a4895576a8d0768275a30 100644 --- a/gcc/d/dmd/aggregate.d +++ b/gcc/d/dmd/aggregate.d @@ -25,7 +25,7 @@ import dmd.declaration; import dmd.dscope; import dmd.dstruct; import dmd.dsymbol; -import dmd.dsymbolsem; +import dmd.dsymbolsem : dsymbolSemantic, determineFields, search, determineSize, include; import dmd.dtemplate; import dmd.errors; import dmd.expression; @@ -99,7 +99,7 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol StorageClass storage_class; /// uint structsize; /// size of struct uint alignsize; /// size of struct for alignment purposes - VarDeclarations fields; /// VarDeclaration fields + VarDeclarations fields; /// VarDeclaration fields including flattened AnonDeclaration members Dsymbol deferred; /// any deferred semantic2() or semantic3() symbol /// specifies whether this is a D, C++, Objective-C or anonymous struct/class/interface @@ -187,70 +187,12 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol return fields.length - isNested() - (vthis2 !is null); } - /*************************************** - * Collect all instance fields, then determine instance size. - * Returns: - * false if failed to determine the size. - */ - extern (D) final bool determineSize(const ref Loc loc) - { - //printf("AggregateDeclaration::determineSize() %s, sizeok = %d\n", toChars(), sizeok); - - // The previous instance size finalizing had: - if (type.ty == Terror || errors) - return false; // failed already - if (sizeok == Sizeok.done) - return true; // succeeded - - if (!members) - { - .error(loc, "%s `%s` unknown size", kind, toPrettyChars); - return false; - } - - if (_scope) - dsymbolSemantic(this, null); - - // Determine the instance size of base class first. - if (auto cd = isClassDeclaration()) - { - cd = cd.baseClass; - if (cd && !cd.determineSize(loc)) - goto Lfail; - } - - // Determine instance fields when sizeok == Sizeok.none - if (!this.determineFields()) - goto Lfail; - if (sizeok != Sizeok.done) - finalizeSize(); - - // this aggregate type has: - if (type.ty == Terror) - return false; // marked as invalid during the finalizing. - if (sizeok == Sizeok.done) - return true; // succeeded to calculate instance size. - - Lfail: - // There's unresolvable forward reference. - if (type != Type.terror) - error(loc, "%s `%s` no size because of forward reference", kind, toPrettyChars); - // Don't cache errors from speculative semantic, might be resolvable later. - // https://issues.dlang.org/show_bug.cgi?id=16574 - if (!global.gag) - { - type = Type.terror; - errors = true; - } - return false; - } - abstract void finalizeSize(); override final uinteger_t size(const ref Loc loc) { //printf("+AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok); - bool ok = determineSize(loc); + bool ok = determineSize(this, loc); //printf("-AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok); return ok ? structsize : SIZE_INVALID; } @@ -336,161 +278,6 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol return errors; } - /*************************************** - * Fill out remainder of elements[] with default initializers for fields[]. - * Params: - * loc = location - * elements = explicit arguments which given to construct object. - * ctorinit = true if the elements will be used for default initialization. - * Returns: - * false if any errors occur. - * Otherwise, returns true and the missing arguments will be pushed in elements[]. - */ - final bool fill(const ref Loc loc, ref Expressions elements, bool ctorinit) - { - //printf("AggregateDeclaration::fill() %s\n", toChars()); - assert(sizeok == Sizeok.done); - const nfields = nonHiddenFields(); - bool errors = false; - - size_t dim = elements.length; - elements.setDim(nfields); - foreach (size_t i; dim .. nfields) - elements[i] = null; - - // Fill in missing any elements with default initializers - foreach (i; 0 .. nfields) - { - if (elements[i]) - continue; - - auto vd = fields[i]; - auto vx = vd; - if (vd._init && vd._init.isVoidInitializer()) - vx = null; - - // Find overlapped fields with the hole [vd.offset .. vd.offset.size()]. - size_t fieldi = i; - foreach (j; 0 .. nfields) - { - if (i == j) - continue; - auto v2 = fields[j]; - if (!vd.isOverlappedWith(v2)) - continue; - - if (elements[j]) - { - vx = null; - break; - } - if (v2._init && v2._init.isVoidInitializer()) - continue; - - version (all) - { - /* Prefer first found non-void-initialized field - * union U { int a; int b = 2; } - * U u; // Error: overlapping initialization for field a and b - */ - if (!vx) - { - vx = v2; - fieldi = j; - } - else if (v2._init) - { - .error(loc, "overlapping initialization for field `%s` and `%s`", v2.toChars(), vd.toChars()); - errors = true; - } - } - else - { - // fixes https://issues.dlang.org/show_bug.cgi?id=1432 by enabling this path always - - /* Prefer explicitly initialized field - * union U { int a; int b = 2; } - * U u; // OK (u.b == 2) - */ - if (!vx || !vx._init && v2._init) - { - vx = v2; - fieldi = j; - } - else if (vx != vd && !vx.isOverlappedWith(v2)) - { - // Both vx and v2 fills vd, but vx and v2 does not overlap - } - else if (vx._init && v2._init) - { - .error(loc, "overlapping default initialization for field `%s` and `%s`", - v2.toChars(), vd.toChars()); - errors = true; - } - else - assert(vx._init || !vx._init && !v2._init); - } - } - if (vx) - { - Expression e; - if (vx.type.size() == 0) - { - e = null; - } - else if (vx._init) - { - assert(!vx._init.isVoidInitializer()); - if (vx.inuse) // https://issues.dlang.org/show_bug.cgi?id=18057 - { - .error(loc, "%s `%s` recursive initialization of field", vx.kind(), vx.toPrettyChars()); - errors = true; - } - else - e = vx.getConstInitializer(false); - } - else - { - if ((vx.storage_class & STC.nodefaultctor) && !ctorinit) - { - .error(loc, "field `%s.%s` must be initialized because it has no default constructor", - type.toChars(), vx.toChars()); - errors = true; - } - /* https://issues.dlang.org/show_bug.cgi?id=12509 - * Get the element of static array type. - */ - Type telem = vx.type; - if (telem.ty == Tsarray) - { - /* We cannot use Type::baseElemOf() here. - * If the bottom of the Tsarray is an enum type, baseElemOf() - * will return the base of the enum, and its default initializer - * would be different from the enum's. - */ - TypeSArray tsa; - while ((tsa = telem.toBasetype().isTypeSArray()) !is null) - telem = tsa.next; - if (telem.ty == Tvoid) - telem = Type.tuns8.addMod(telem.mod); - } - if (telem.needsNested() && ctorinit) - e = telem.defaultInit(loc); - else - e = telem.defaultInitLiteral(loc); - } - elements[fieldi] = e; - } - } - foreach (e; elements) - { - if (e && e.op == EXP.error) - return false; - } - - return !errors; - } - override final Type getType() { /* Apply storage classes to forward references. (Issue 22254) @@ -793,6 +580,7 @@ public uint alignmember(structalign_t alignment, uint memalignsize, uint offset) /**************************************** * Place a field (mem) into an aggregate (agg), which can be a struct, union or class * Params: + * loc = source location for error messages * nextoffset = location just past the end of the previous field in the aggregate. * Updated to be just past the end of this field to be placed, i.e. the future nextoffset * memsize = size of field @@ -805,8 +593,8 @@ public uint alignmember(structalign_t alignment, uint memalignsize, uint offset) * aligned offset to place field at * */ -public uint placeField(ref uint nextoffset, uint memsize, uint memalignsize, - structalign_t alignment, ref uint aggsize, ref uint aggalignsize, bool isunion) @safe pure nothrow +public uint placeField(Loc loc, ref uint nextoffset, uint memsize, uint memalignsize, + structalign_t alignment, ref uint aggsize, ref uint aggalignsize, bool isunion) @trusted nothrow { static if (0) { @@ -829,7 +617,12 @@ public uint placeField(ref uint nextoffset, uint memsize, uint memalignsize, bool overflow; const sz = addu(memsize, actualAlignment, overflow); addu(ofs, sz, overflow); - if (overflow) assert(0); + if (overflow) + { + error(loc, "max object size %u exceeded from adding field size %u + alignment adjustment %u + field offset %u when placing field in aggregate", + uint.max, memsize, actualAlignment, ofs); + return 0; + } // Skip no-op for noreturn without custom aligment if (memalignsize != 0 || !alignment.isDefault()) diff --git a/gcc/d/dmd/aggregate.h b/gcc/d/dmd/aggregate.h index c972f0a6631ffba505e089e7b377fb07ed042ee4..8fd12e1d168ad494d97e43c3b8812d7a998e842e 100644 --- a/gcc/d/dmd/aggregate.h +++ b/gcc/d/dmd/aggregate.h @@ -45,6 +45,7 @@ namespace dmd { FuncDeclaration *search_toString(StructDeclaration *sd); void semanticTypeInfoMembers(StructDeclaration *sd); + bool fill(StructDeclaration* sd, const Loc &loc, Expressions &elements, bool ctorinit); } enum class ClassKind : uint8_t @@ -119,7 +120,6 @@ public: virtual Scope *newScope(Scope *sc); virtual void finalizeSize() = 0; uinteger_t size(const Loc &loc) override final; - bool fill(const Loc &loc, Expressions &elements, bool ctorinit); Type *getType() override final; bool isDeprecated() const override final; // is aggregate deprecated? bool isNested() const; diff --git a/gcc/d/dmd/attrib.d b/gcc/d/dmd/attrib.d index db14f9a1cc3f2111c2f00f6524ad7a52510a0b45..0b5cd9d41c5d042ee15ef090ffaac2a87c166fab 100644 --- a/gcc/d/dmd/attrib.d +++ b/gcc/d/dmd/attrib.d @@ -32,8 +32,7 @@ import dmd.declaration; import dmd.dmodule; import dmd.dscope; import dmd.dsymbol; -import dmd.dsymbolsem; -import dmd.errors; +import dmd.dsymbolsem : setScope, addMember, include; import dmd.expression; import dmd.func; import dmd.globals; @@ -73,14 +72,6 @@ extern (C++) abstract class AttribDeclaration : Dsymbol this.decl = decl; } - Dsymbols* include(Scope* sc) - { - if (errors) - return null; - - return decl; - } - /**************************************** * Create a new scope if one or more given attributes * are different from the sc's. @@ -113,21 +104,13 @@ extern (C++) abstract class AttribDeclaration : Dsymbol return sc2; } - /**************************************** - * A hook point to supply scope for members. - * addMember, setScope, importAll, semantic, semantic2 and semantic3 will use this. - */ - Scope* newScope(Scope* sc) - { - return sc; - } override void addComment(const(char)* comment) { //printf("AttribDeclaration::addComment %s\n", comment); if (comment) { - include(null).foreachDsymbol( s => s.addComment(comment) ); + this.include(null).foreachDsymbol( s => s.addComment(comment) ); } } @@ -138,23 +121,23 @@ extern (C++) abstract class AttribDeclaration : Dsymbol override bool oneMember(out Dsymbol ps, Identifier ident) { - Dsymbols* d = include(null); + Dsymbols* d = this.include(null); return Dsymbol.oneMembers(d, ps, ident); } override final bool hasPointers() { - return include(null).foreachDsymbol( (s) { return s.hasPointers(); } ) != 0; + return this.include(null).foreachDsymbol( (s) { return s.hasPointers(); } ) != 0; } override final bool hasStaticCtorOrDtor() { - return include(null).foreachDsymbol( (s) { return s.hasStaticCtorOrDtor(); } ) != 0; + return this.include(null).foreachDsymbol( (s) { return s.hasStaticCtorOrDtor(); } ) != 0; } override final void checkCtorConstInit() { - include(null).foreachDsymbol( s => s.checkCtorConstInit() ); + this.include(null).foreachDsymbol( s => s.checkCtorConstInit() ); } /**************************************** @@ -202,28 +185,6 @@ extern (C++) class StorageClassDeclaration : AttribDeclaration return new StorageClassDeclaration(stc, Dsymbol.arraySyntaxCopy(decl)); } - override Scope* newScope(Scope* sc) - { - StorageClass scstc = sc.stc; - /* These sets of storage classes are mutually exclusive, - * so choose the innermost or most recent one. - */ - if (stc & (STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.manifest)) - scstc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.manifest); - if (stc & (STC.auto_ | STC.scope_ | STC.static_ | STC.manifest | STC.gshared)) - scstc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.manifest | STC.gshared); - if (stc & (STC.const_ | STC.immutable_ | STC.manifest)) - scstc &= ~(STC.const_ | STC.immutable_ | STC.manifest); - if (stc & (STC.gshared | STC.shared_)) - scstc &= ~(STC.gshared | STC.shared_); - if (stc & (STC.safe | STC.trusted | STC.system)) - scstc &= ~(STC.safe | STC.trusted | STC.system); - scstc |= stc; - //printf("scstc = x%llx\n", scstc); - return createNewScope(sc, scstc, sc.linkage, sc.cppmangle, - sc.visibility, sc.explicitVisibility, sc.aligndecl, sc.inlining); - } - override final bool oneMember(out Dsymbol ps, Identifier ident) { bool t = Dsymbol.oneMembers(decl, ps, ident); @@ -286,25 +247,6 @@ extern (C++) final class DeprecatedDeclaration : StorageClassDeclaration return new DeprecatedDeclaration(msg.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl)); } - /** - * Provides a new scope with `STC.deprecated_` and `Scope.depdecl` set - * - * Calls `StorageClassDeclaration.newScope` (as it must be called or copied - * in any function overriding `newScope`), then set the `Scope`'s depdecl. - * - * Returns: - * Always a new scope, to use for this `DeprecatedDeclaration`'s members. - */ - override Scope* newScope(Scope* sc) - { - auto scx = super.newScope(sc); - // The enclosing scope is deprecated as well - if (scx == sc) - scx = sc.push(); - scx.depdecl = this; - return scx; - } - override void accept(Visitor v) { v.visit(this); @@ -339,11 +281,6 @@ extern (C++) final class LinkDeclaration : AttribDeclaration return new LinkDeclaration(loc, linkage, Dsymbol.arraySyntaxCopy(decl)); } - override Scope* newScope(Scope* sc) - { - return createNewScope(sc, sc.stc, this.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility, - sc.aligndecl, sc.inlining); - } override const(char)* toChars() const { @@ -386,12 +323,6 @@ extern (C++) final class CPPMangleDeclaration : AttribDeclaration return new CPPMangleDeclaration(loc, cppmangle, Dsymbol.arraySyntaxCopy(decl)); } - override Scope* newScope(Scope* sc) - { - return createNewScope(sc, sc.stc, LINK.cpp, cppmangle, sc.visibility, sc.explicitVisibility, - sc.aligndecl, sc.inlining); - } - override const(char)* toChars() const { return toString().ptr; @@ -462,18 +393,6 @@ extern (C++) final class CPPNamespaceDeclaration : AttribDeclaration this.loc, this.ident, this.exp, Dsymbol.arraySyntaxCopy(this.decl), this.cppnamespace); } - /** - * Returns: - * A copy of the parent scope, with `this` as `namespace` and C++ linkage - */ - override Scope* newScope(Scope* sc) - { - auto scx = sc.copy(); - scx.linkage = LINK.cpp; - scx.namespace = this; - return scx; - } - override const(char)* toChars() const { return toString().ptr; @@ -545,13 +464,6 @@ extern (C++) final class VisibilityDeclaration : AttribDeclaration return new VisibilityDeclaration(this.loc, visibility, Dsymbol.arraySyntaxCopy(decl)); } - override Scope* newScope(Scope* sc) - { - if (pkg_identifiers) - dsymbolSemantic(this, sc); - return createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, this.visibility, 1, sc.aligndecl, sc.inlining); - } - override const(char)* kind() const { return "visibility attribute"; @@ -621,11 +533,6 @@ extern (C++) final class AlignDeclaration : AttribDeclaration Dsymbol.arraySyntaxCopy(decl)); } - override Scope* newScope(Scope* sc) - { - return createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility, this, sc.inlining); - } - override void accept(Visitor v) { v.visit(this); @@ -694,17 +601,6 @@ extern (C++) final class PragmaDeclaration : AttribDeclaration return new PragmaDeclaration(loc, ident, Expression.arraySyntaxCopy(args), Dsymbol.arraySyntaxCopy(decl)); } - override Scope* newScope(Scope* sc) - { - if (ident == Id.Pinline) - { - // We keep track of this pragma inside scopes, - // then it's evaluated on demand in function semantic - return createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility, sc.aligndecl, this); - } - return sc; - } - override const(char)* kind() const { return "pragma"; @@ -757,18 +653,6 @@ extern (C++) class ConditionalDeclaration : AttribDeclaration } } - // Decide if 'then' or 'else' code should be included - override Dsymbols* include(Scope* sc) - { - //printf("ConditionalDeclaration::include(sc = %p) scope = %p\n", sc, _scope); - - if (errors) - return null; - - assert(condition); - return condition.include(_scope ? _scope : sc) ? decl : elsedecl; - } - override final void addComment(const(char)* comment) { /* Because addComment is called by the parser, if we called @@ -797,8 +681,8 @@ extern (C++) class ConditionalDeclaration : AttribDeclaration extern (C++) final class StaticIfDeclaration : ConditionalDeclaration { ScopeDsymbol scopesym; /// enclosing symbol (e.g. module) where symbols will be inserted - private bool addisdone = false; /// true if members have been added to scope - private bool onStack = false; /// true if a call to `include` is currently active + bool addisdone = false; /// true if members have been added to scope + bool onStack = false; /// true if a call to `include` is currently active extern (D) this(const ref Loc loc, Condition condition, Dsymbols* decl, Dsymbols* elsedecl) @safe { @@ -812,42 +696,6 @@ extern (C++) final class StaticIfDeclaration : ConditionalDeclaration return new StaticIfDeclaration(loc, condition.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl), Dsymbol.arraySyntaxCopy(elsedecl)); } - /**************************************** - * Different from other AttribDeclaration subclasses, include() call requires - * the completion of addMember and setScope phases. - */ - override Dsymbols* include(Scope* sc) - { - //printf("StaticIfDeclaration::include(sc = %p) scope = %p\n", sc, _scope); - - if (errors || onStack) - return null; - onStack = true; - scope(exit) onStack = false; - - if (sc && condition.inc == Include.notComputed) - { - assert(scopesym); // addMember is already done - assert(_scope); // setScope is already done - Dsymbols* d = ConditionalDeclaration.include(_scope); - if (d && !addisdone) - { - // Add members lazily. - d.foreachDsymbol( s => s.addMember(_scope, scopesym) ); - - // Set the member scopes lazily. - d.foreachDsymbol( s => s.setScope(_scope) ); - - addisdone = true; - } - return d; - } - else - { - return ConditionalDeclaration.include(sc); - } - } - override const(char)* kind() const { return "static if"; @@ -914,43 +762,6 @@ extern (C++) final class StaticForeachDeclaration : AttribDeclaration return false; } - override Dsymbols* include(Scope* sc) - { - if (errors || onStack) - return null; - if (cached) - { - assert(!onStack); - return cache; - } - onStack = true; - scope(exit) onStack = false; - - if (_scope) - { - sfe.prepare(_scope); // lower static foreach aggregate - } - if (!sfe.ready()) - { - return null; // TODO: ok? - } - - // expand static foreach - import dmd.statementsem: makeTupleForeach; - Dsymbols* d = makeTupleForeach(_scope, true, true, sfe.aggrfe, decl, sfe.needExpansion).decl; - if (d) // process generated declarations - { - // Add members lazily. - d.foreachDsymbol( s => s.addMember(_scope, scopesym) ); - - // Set the member scopes lazily. - d.foreachDsymbol( s => s.setScope(_scope) ); - } - cached = true; - cache = d; - return d; - } - override void addComment(const(char)* comment) { // do nothing @@ -1005,13 +816,6 @@ extern(C++) final class ForwardingAttribDeclaration : AttribDeclaration sym.symtab = new DsymbolTable(); } - /************************************** - * Use the ForwardingScopeDsymbol as the parent symbol for members. - */ - override Scope* newScope(Scope* sc) - { - return sc.push(sym); - } override inout(ForwardingAttribDeclaration) isForwardingAttribDeclaration() inout { @@ -1024,7 +828,6 @@ extern(C++) final class ForwardingAttribDeclaration : AttribDeclaration } } - /*********************************************************** * Mixin declarations, like: * mixin("int x"); @@ -1088,18 +891,6 @@ extern (C++) final class UserAttributeDeclaration : AttribDeclaration return new UserAttributeDeclaration(Expression.arraySyntaxCopy(this.atts), Dsymbol.arraySyntaxCopy(decl)); } - override Scope* newScope(Scope* sc) - { - Scope* sc2 = sc; - if (atts && atts.length) - { - // create new one for changes - sc2 = sc.copy(); - sc2.userAttribDecl = this; - } - return sc2; - } - extern (D) static Expressions* concat(Expressions* udas1, Expressions* udas2) { Expressions* udas; @@ -1128,70 +919,6 @@ extern (C++) final class UserAttributeDeclaration : AttribDeclaration { v.visit(this); } - - /** - * Check if the provided expression references `core.attribute.gnuAbiTag` - * - * This should be called after semantic has been run on the expression. - * Semantic on UDA happens in semantic2 (see `dmd.semantic2`). - * - * Params: - * e = Expression to check (usually from `UserAttributeDeclaration.atts`) - * - * Returns: - * `true` if the expression references the compiler-recognized `gnuAbiTag` - */ - static bool isGNUABITag(Expression e) - { - if (global.params.cplusplus < CppStdRevision.cpp11) - return false; - - auto ts = e.type ? e.type.isTypeStruct() : null; - if (!ts) - return false; - if (ts.sym.ident != Id.udaGNUAbiTag || !ts.sym.parent) - return false; - // Can only be defined in druntime - Module m = ts.sym.parent.isModule(); - if (!m || !m.isCoreModule(Id.attribute)) - return false; - return true; - } - - /** - * Called from a symbol's semantic to check if `gnuAbiTag` UDA - * can be applied to them - * - * Directly emits an error if the UDA doesn't work with this symbol - * - * Params: - * sym = symbol to check for `gnuAbiTag` - * linkage = Linkage of the symbol (Declaration.link or sc.link) - */ - static void checkGNUABITag(Dsymbol sym, LINK linkage) - { - if (global.params.cplusplus < CppStdRevision.cpp11) - return; - - foreachUdaNoSemantic(sym, (exp) { - if (isGNUABITag(exp)) - { - if (sym.isCPPNamespaceDeclaration() || sym.isNspace()) - { - .error(exp.loc, "`@%s` cannot be applied to namespaces", Id.udaGNUAbiTag.toChars()); - sym.errors = true; - } - else if (linkage != LINK.cpp) - { - .error(exp.loc, "`@%s` can only apply to C++ symbols", Id.udaGNUAbiTag.toChars()); - sym.errors = true; - } - // Only one `@gnuAbiTag` is allowed by semantic2 - return 1; // break - } - return 0; // continue - }); - } } /** @@ -1243,7 +970,6 @@ int foreachUdaNoSemantic(Dsymbol sym, int delegate(Expression) dg) return 0; } - /** * Returns: true if the given expression is an enum from `core.attribute` named `id` */ diff --git a/gcc/d/dmd/attrib.h b/gcc/d/dmd/attrib.h index d4c41ec94f36f3f3ebc61592cb3c5c7305ec7b06..d2d18dba18d94b5df014b794d0f2ff1aab247be0 100644 --- a/gcc/d/dmd/attrib.h +++ b/gcc/d/dmd/attrib.h @@ -28,9 +28,6 @@ class AttribDeclaration : public Dsymbol { public: Dsymbols *decl; // array of Dsymbol's - - virtual Dsymbols *include(Scope *sc); - virtual Scope *newScope(Scope *sc); void addComment(const utf8_t *comment) override; const char *kind() const override; bool oneMember(Dsymbol *&ps, Identifier *ident) override; @@ -48,7 +45,6 @@ public: StorageClass stc; StorageClassDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; bool oneMember(Dsymbol *&ps, Identifier *ident) override final; StorageClassDeclaration *isStorageClassDeclaration() override { return this; } @@ -62,7 +58,6 @@ public: const char *msgstr; DeprecatedDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -73,7 +68,6 @@ public: static LinkDeclaration *create(const Loc &loc, LINK p, Dsymbols *decl); LinkDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; const char *toChars() const override; void accept(Visitor *v) override { v->visit(this); } }; @@ -84,7 +78,6 @@ public: CPPMANGLE cppmangle; CPPMangleDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; const char *toChars() const override; void accept(Visitor *v) override { v->visit(this); } }; @@ -95,7 +88,6 @@ public: Expression *exp; CPPNamespaceDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; const char *toChars() const override; void accept(Visitor *v) override { v->visit(this); } }; @@ -107,7 +99,6 @@ public: DArray<Identifier*> pkg_identifiers; VisibilityDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; const char *kind() const override; const char *toPrettyChars(bool unused) override; VisibilityDeclaration *isVisibilityDeclaration() override { return this; } @@ -121,7 +112,6 @@ public: structalign_t salign; AlignDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -146,7 +136,6 @@ public: Expressions *args; // array of Expression's PragmaDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; const char *kind() const override; void accept(Visitor *v) override { v->visit(this); } }; @@ -159,7 +148,6 @@ public: ConditionalDeclaration *syntaxCopy(Dsymbol *s) override; bool oneMember(Dsymbol *&ps, Identifier *ident) override final; - Dsymbols *include(Scope *sc) override; void addComment(const utf8_t *comment) override final; void accept(Visitor *v) override { v->visit(this); } }; @@ -172,7 +160,6 @@ public: d_bool onStack; StaticIfDeclaration *syntaxCopy(Dsymbol *s) override; - Dsymbols *include(Scope *sc) override; StaticIfDeclaration *isStaticIfDeclaration() override { return this; } const char *kind() const override; void accept(Visitor *v) override { v->visit(this); } @@ -189,7 +176,6 @@ public: StaticForeachDeclaration *syntaxCopy(Dsymbol *s) override; bool oneMember(Dsymbol *&ps, Identifier *ident) override; - Dsymbols *include(Scope *sc) override; void addComment(const utf8_t *comment) override; const char *kind() const override; void accept(Visitor *v) override { v->visit(this); } @@ -200,7 +186,6 @@ class ForwardingAttribDeclaration final : public AttribDeclaration public: ForwardingScopeDsymbol *sym; - Scope *newScope(Scope *sc) override; ForwardingAttribDeclaration *isForwardingAttribDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -230,7 +215,6 @@ public: Expressions *atts; UserAttributeDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; const char *kind() const override; void accept(Visitor *v) override { v->visit(this); } }; diff --git a/gcc/d/dmd/blockexit.d b/gcc/d/dmd/blockexit.d index 75293bce0fcde216cf2bffa600ba0810f4ca990b..dc3e379e8d4be878b7c76ef73d5e1fc1a651d01d 100644 --- a/gcc/d/dmd/blockexit.d +++ b/gcc/d/dmd/blockexit.d @@ -80,26 +80,25 @@ int blockExit(Statement s, FuncDeclaration func, ErrorSink eSink) void visitExp(ExpStatement s) { result = BE.fallthru; - if (s.exp) + if (!s.exp) + return; + + if (s.exp.op == EXP.halt) { - if (s.exp.op == EXP.halt) + result = BE.halt; + return; + } + if (AssertExp a = s.exp.isAssertExp()) + { + if (a.e1.toBool().hasValue(false)) // if it's an assert(0) { result = BE.halt; return; } - if (AssertExp a = s.exp.isAssertExp()) - { - if (a.e1.toBool().hasValue(false)) // if it's an assert(0) - { - result = BE.halt; - return; - } - } - if (s.exp.type && s.exp.type.toBasetype().isTypeNoreturn()) - result = BE.halt; - - result |= canThrow(s.exp, func, eSink); } + if (s.exp.type && s.exp.type.toBasetype().isTypeNoreturn()) + result = BE.halt; + result |= canThrow(s.exp, func, eSink); } void visitDtorExp(DtorExpStatement s) @@ -120,39 +119,39 @@ int blockExit(Statement s, FuncDeclaration func, ErrorSink eSink) Statement slast = null; foreach (s; *cs.statements) { - if (s) + if (!s) + continue; + + //printf("result = x%x\n", result); + //printf("s: %s\n", s.toChars()); + if (result & BE.fallthru && slast) { - //printf("result = x%x\n", result); - //printf("s: %s\n", s.toChars()); - if (result & BE.fallthru && slast) + slast = slast.last(); + if (slast && (slast.isCaseStatement() || slast.isDefaultStatement()) && (s.isCaseStatement() || s.isDefaultStatement())) { - slast = slast.last(); - if (slast && (slast.isCaseStatement() || slast.isDefaultStatement()) && (s.isCaseStatement() || s.isDefaultStatement())) + // Allow if last case/default was empty + CaseStatement sc = slast.isCaseStatement(); + DefaultStatement sd = slast.isDefaultStatement(); + auto sl = (sc ? sc.statement : (sd ? sd.statement : null)); + + if (sl && (!sl.hasCode() || sl.isErrorStatement())) { - // Allow if last case/default was empty - CaseStatement sc = slast.isCaseStatement(); - DefaultStatement sd = slast.isDefaultStatement(); - auto sl = (sc ? sc.statement : (sd ? sd.statement : null)); - - if (sl && (!sl.hasCode() || sl.isErrorStatement())) - { - } - else if (func.getModule().filetype != FileType.c) - { - const(char)* gototype = s.isCaseStatement() ? "case" : "default"; - // https://issues.dlang.org/show_bug.cgi?id=22999 - global.errorSink.error(s.loc, "switch case fallthrough - use 'goto %s;' if intended", gototype); - } + } + else if (func.getModule().filetype != FileType.c) + { + const(char)* gototype = s.isCaseStatement() ? "case" : "default"; + // https://issues.dlang.org/show_bug.cgi?id=22999 + global.errorSink.error(s.loc, "switch case fallthrough - use 'goto %s;' if intended", gototype); } } + } - if ((result & BE.fallthru) || s.comeFrom()) - { - result &= ~BE.fallthru; - result |= blockExit(s, func, eSink); - } - slast = s; + if ((result & BE.fallthru) || s.comeFrom()) + { + result &= ~BE.fallthru; + result |= blockExit(s, func, eSink); } + slast = s; } } @@ -161,13 +160,12 @@ int blockExit(Statement s, FuncDeclaration func, ErrorSink eSink) result = BE.fallthru; foreach (s; *uls.statements) { - if (s) - { - int r = blockExit(s, func, eSink); - result |= r & ~(BE.break_ | BE.continue_ | BE.fallthru); - if ((r & (BE.fallthru | BE.continue_ | BE.break_)) == 0) - result &= ~BE.fallthru; - } + if (!s) + continue; + int r = blockExit(s, func, eSink); + result |= r & ~(BE.break_ | BE.continue_ | BE.fallthru); + if ((r & (BE.fallthru | BE.continue_ | BE.break_)) == 0) + result &= ~BE.fallthru; } } diff --git a/gcc/d/dmd/canthrow.d b/gcc/d/dmd/canthrow.d index dfc91e1c85285a6c4fc99532d17c1bc22fea6747..caf58a20634cc1853707bf937f9a7d67a8bb5475 100644 --- a/gcc/d/dmd/canthrow.d +++ b/gcc/d/dmd/canthrow.d @@ -20,8 +20,10 @@ import dmd.astenums; import dmd.blockexit : BE, checkThrow; import dmd.declaration; import dmd.dsymbol; +import dmd.dsymbolsem : include; import dmd.errorsink; import dmd.expression; +import dmd.expressionsem : errorSupplementalInferredAttr; import dmd.func; import dmd.globals; import dmd.init; diff --git a/gcc/d/dmd/clone.d b/gcc/d/dmd/clone.d index 8746baacd0ef6bd30ba721ee8f6d5c7210475a5e..bbfb1ee9f87d175e4aca052d4aeb25f4e3de74c0 100644 --- a/gcc/d/dmd/clone.d +++ b/gcc/d/dmd/clone.d @@ -51,7 +51,7 @@ import dmd.tokens; * Returns: * merged storage class */ -StorageClass mergeFuncAttrs(StorageClass s1, const FuncDeclaration f) pure +StorageClass mergeFuncAttrs(StorageClass s1, const FuncDeclaration f) pure @safe { if (!f) return s1; @@ -101,46 +101,45 @@ StorageClass mergeFuncAttrs(StorageClass s1, const FuncDeclaration f) pure */ FuncDeclaration hasIdentityOpAssign(AggregateDeclaration ad, Scope* sc) { - if (Dsymbol assign = search_function(ad, Id.assign)) - { - /* check identity opAssign exists - */ - scope er = new NullExp(ad.loc, ad.type); // dummy rvalue - scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue - el.type = ad.type; - const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it. - sc = sc.push(); - sc.tinst = null; - sc.minst = null; + Dsymbol assign = search_function(ad, Id.assign); + if (!assign) + return null; - auto a = new Expressions(1); - (*a)[0] = er; - auto f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, ArgumentList(a), FuncResolveFlag.quiet); - if (!f) - { - (*a)[0] = el; - f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, ArgumentList(a), FuncResolveFlag.quiet); - } + /* check identity opAssign exists + */ + scope er = new NullExp(ad.loc, ad.type); // dummy rvalue + scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue + el.type = ad.type; + const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it. + sc = sc.push(); + sc.tinst = null; + sc.minst = null; - sc = sc.pop(); - global.endGagging(errors); - if (f) - { - if (f.errors) - return null; - auto fparams = f.getParameterList(); - if (fparams.length) - { - auto fparam0 = fparams[0]; - if (fparam0.type.toDsymbol(null) != ad) - f = null; - } - } - // BUGS: This detection mechanism cannot find some opAssign-s like follows: - // struct S { void opAssign(ref immutable S) const; } - return f; + auto a = new Expressions(1); + (*a)[0] = er; + auto f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, ArgumentList(a), FuncResolveFlag.quiet); + if (!f) + { + (*a)[0] = el; + f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, ArgumentList(a), FuncResolveFlag.quiet); } - return null; + + sc = sc.pop(); + global.endGagging(errors); + if (!f) + return null; + if (f.errors) + return null; + auto fparams = f.getParameterList(); + if (fparams.length) + { + auto fparam0 = fparams[0]; + if (fparam0.type.toDsymbol(null) != ad) + f = null; + } + // BUGS: This detection mechanism cannot find some opAssign-s like follows: + // struct S { void opAssign(ref immutable S) const; } + return f; } /******************************************* @@ -418,16 +417,26 @@ FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) */ bool needOpEquals(StructDeclaration sd) { + bool dontneed() + { + //printf("\tdontneed\n"); + return false; + } + bool need() + { + //printf("\tneed\n"); + return true; + } //printf("StructDeclaration::needOpEquals() %s\n", sd.toChars()); if (sd.isUnionDeclaration()) { /* If a union has only one field, treat it like a struct */ if (sd.fields.length != 1) - goto Ldontneed; + return dontneed(); } if (sd.hasIdentityEquals) - goto Lneed; + return need(); /* If any of the fields has an opEquals, then we * need it too. */ @@ -444,28 +453,23 @@ bool needOpEquals(StructDeclaration sd) if (ts.sym.isUnionDeclaration() && ts.sym.fields.length != 1) continue; if (needOpEquals(ts.sym)) - goto Lneed; + return need(); } if (tvbase.isFloating()) { // This is necessray for: // 1. comparison of +0.0 and -0.0 should be true. // 2. comparison of NANs should be false always. - goto Lneed; + return need(); } if (tvbase.ty == Tarray) - goto Lneed; + return need(); if (tvbase.ty == Taarray) - goto Lneed; + return need(); if (tvbase.ty == Tclass) - goto Lneed; + return need(); } -Ldontneed: - //printf("\tdontneed\n"); - return false; -Lneed: - //printf("\tneed\n"); - return true; + return dontneed(); } /******************************************* @@ -478,48 +482,50 @@ Lneed: private FuncDeclaration hasIdentityOpEquals(AggregateDeclaration ad, Scope* sc) { FuncDeclaration f; - if (Dsymbol eq = search_function(ad, Id.eq)) + Dsymbol eq = search_function(ad, Id.eq); + if (!eq) + return null; + + /* check identity opEquals exists + */ + scope er = new NullExp(ad.loc, null); // dummy rvalue + scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue + auto a = new Expressions(1); + + bool hasIt(Type tthis) { - /* check identity opEquals exists - */ - scope er = new NullExp(ad.loc, null); // dummy rvalue - scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue - auto a = new Expressions(1); + const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it + sc = sc.push(); + sc.tinst = null; + sc.minst = null; - bool hasIt(Type tthis) + FuncDeclaration rfc(Expression e) { - const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it - sc = sc.push(); - sc.tinst = null; - sc.minst = null; - - FuncDeclaration rfc(Expression e) - { - (*a)[0] = e; - (*a)[0].type = tthis; - return resolveFuncCall(ad.loc, sc, eq, null, tthis, ArgumentList(a), FuncResolveFlag.quiet); - } + (*a)[0] = e; + (*a)[0].type = tthis; + return resolveFuncCall(ad.loc, sc, eq, null, tthis, ArgumentList(a), FuncResolveFlag.quiet); + } - f = rfc(er); - if (!f) - f = rfc(el); + f = rfc(er); + if (!f) + f = rfc(el); - sc = sc.pop(); - global.endGagging(errors); + sc = sc.pop(); + global.endGagging(errors); - return f !is null; - } + return f !is null; + } - if (hasIt(ad.type) || - hasIt(ad.type.constOf()) || - hasIt(ad.type.immutableOf()) || - hasIt(ad.type.sharedOf()) || - hasIt(ad.type.sharedConstOf())) - { - if (f.errors) - return null; - } + if (hasIt(ad.type) || + hasIt(ad.type.constOf()) || + hasIt(ad.type.immutableOf()) || + hasIt(ad.type.sharedOf()) || + hasIt(ad.type.sharedConstOf())) + { + if (f.errors) + return null; } + return f; } @@ -732,7 +738,7 @@ FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) Expression e2 = new IdentifierExp(loc, Id.p); Expression e = new CallExp(loc, new DotIdExp(loc, e1, Id.cmp), e2); fop.fbody = new ReturnStatement(loc, e); - uint errors = global.startGagging(); // Do not report errors + const errors = global.startGagging(); // Do not report errors Scope* sc2 = sc.push(); sc2.stc = 0; sc2.linkage = LINK.d; @@ -757,11 +763,21 @@ FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) */ private bool needToHash(StructDeclaration sd) { + bool dontneed() + { + //printf("\tdontneed\n"); + return false; + } + bool need() + { + //printf("\tneed\n"); + return true; + } //printf("StructDeclaration::needToHash() %s\n", sd.toChars()); if (sd.isUnionDeclaration()) - goto Ldontneed; + return dontneed(); if (sd.xhash) - goto Lneed; + return need(); /* If any of the fields has an toHash, then we * need it too. @@ -779,28 +795,23 @@ private bool needToHash(StructDeclaration sd) if (ts.sym.isUnionDeclaration()) continue; if (needToHash(ts.sym)) - goto Lneed; + return need(); } if (tvbase.isFloating()) { /* This is necessary because comparison of +0.0 and -0.0 should be true, * i.e. not a bit compare. */ - goto Lneed; + return need(); } if (tvbase.ty == Tarray) - goto Lneed; + return need(); if (tvbase.ty == Taarray) - goto Lneed; + return need(); if (tvbase.ty == Tclass) - goto Lneed; + return need(); } -Ldontneed: - //printf("\tdontneed\n"); - return false; -Lneed: - //printf("\tneed\n"); - return true; + return dontneed(); } /****************************************** diff --git a/gcc/d/dmd/common/outbuffer.d b/gcc/d/dmd/common/outbuffer.d index f3801cb69cae2c00d4c163f83e8ee5579c711618..7e5c47409e36ac32980fb62fd7172fdc1482ade9 100644 --- a/gcc/d/dmd/common/outbuffer.d +++ b/gcc/d/dmd/common/outbuffer.d @@ -247,7 +247,7 @@ struct OutBuffer /** * Writes a 16 bit value, no reserve check. */ - nothrow + nothrow @safe void write16n(int v) { auto x = cast(ushort) v; @@ -367,7 +367,7 @@ struct OutBuffer } // Position buffer to accept the specified number of bytes at offset - void position(size_t where, size_t nbytes) nothrow + void position(size_t where, size_t nbytes) nothrow @safe { if (where + nbytes > data.length) { @@ -785,10 +785,11 @@ struct OutBuffer Returns: `true` iff the operation succeeded. */ - extern(D) bool moveToFile(const char* filename) @system + extern(D) bool moveToFile(const char[] filename) @system { bool result = true; - const bool identical = this[] == FileMapping!(const ubyte)(filename)[]; + const filenameZ = (filename ~ "\0").ptr; + const bool identical = this[] == FileMapping!(const ubyte)(filenameZ)[]; if (fileMapping && fileMapping.active) { @@ -801,7 +802,7 @@ struct OutBuffer { // Resize to fit to get rid of the slack bytes at the end fileMapping.resize(offset); - result = fileMapping.moveToFile(filename); + result = fileMapping.moveToFile(filenameZ); } // Can't call destroy() here because the file mapping is already closed. data = null; @@ -810,12 +811,12 @@ struct OutBuffer else { if (!identical) - writeFile(filename, this[]); + writeFile(filenameZ, this[]); destroy(); } return identical - ? result && touchFile(filename) + ? result && touchFile(filenameZ) : result; } } diff --git a/gcc/d/dmd/cond.d b/gcc/d/dmd/cond.d index 265492f1a62daffb43d65a4c89961bce3659adad..cffc412367d26ec5601130d9857004f653752998 100644 --- a/gcc/d/dmd/cond.d +++ b/gcc/d/dmd/cond.d @@ -24,7 +24,7 @@ import dmd.dscope; import dmd.dsymbol; import dmd.errors; import dmd.expression; -import dmd.expressionsem; +import dmd.expressionsem : expressionSemantic, evalStaticCondition, resolveProperties; import dmd.globals; import dmd.identifier; import dmd.location; @@ -160,34 +160,33 @@ extern (C++) final class StaticForeach : RootObject sc = sc.endCTFE(); el = el.optimize(WANTvalue); el = el.ctfeInterpret(); - if (el.op == EXP.int64) + if (el.op != EXP.int64) { - Expressions *es = void; - if (auto ale = aggr.isArrayLiteralExp()) - { - // Directly use the elements of the array for the TupleExp creation - es = ale.elements; - } - else - { - const length = cast(size_t)el.toInteger(); - es = new Expressions(length); - foreach (i; 0 .. length) - { - auto index = new IntegerExp(loc, i, Type.tsize_t); - auto value = new IndexExp(aggr.loc, aggr, index); - (*es)[i] = value; - } - } - aggrfe.aggr = new TupleExp(aggr.loc, es); - aggrfe.aggr = aggrfe.aggr.expressionSemantic(sc); - aggrfe.aggr = aggrfe.aggr.optimize(WANTvalue); - aggrfe.aggr = aggrfe.aggr.ctfeInterpret(); + aggrfe.aggr = ErrorExp.get(); + return; + } + + Expressions *es; + if (auto ale = aggr.isArrayLiteralExp()) + { + // Directly use the elements of the array for the TupleExp creation + es = ale.elements; } else { - aggrfe.aggr = ErrorExp.get(); + const length = cast(size_t)el.toInteger(); + es = new Expressions(length); + foreach (i; 0 .. length) + { + auto index = new IntegerExp(loc, i, Type.tsize_t); + auto value = new IndexExp(aggr.loc, aggr, index); + (*es)[i] = value; + } } + aggrfe.aggr = new TupleExp(aggr.loc, es); + aggrfe.aggr = aggrfe.aggr.expressionSemantic(sc); + aggrfe.aggr = aggrfe.aggr.optimize(WANTvalue); + aggrfe.aggr = aggrfe.aggr.ctfeInterpret(); } /***************************************** @@ -383,7 +382,7 @@ extern (C++) final class StaticForeach : RootObject // Run 'typeof' gagged to avoid duplicate errors and if it fails just create // an empty foreach to expose them. - uint olderrors = global.startGagging(); + const olderrors = global.startGagging(); ety = ety.typeSemantic(aloc, sc); if (global.endGagging(olderrors)) s2.push(createForeach(aloc, pparams[1], null)); @@ -571,31 +570,32 @@ extern (C++) final class DebugCondition : DVCondition override int include(Scope* sc) { //printf("DebugCondition::include() level = %d, debuglevel = %d\n", level, global.params.debuglevel); - if (inc == Include.notComputed) + if (inc != Include.notComputed) { - inc = Include.no; - bool definedInModule = false; - if (ident) + return inc == Include.yes; + } + inc = Include.no; + bool definedInModule = false; + if (ident) + { + if (mod.debugids && findCondition(*mod.debugids, ident)) { - if (mod.debugids && findCondition(*mod.debugids, ident)) - { - inc = Include.yes; - definedInModule = true; - } - else if (findCondition(global.debugids, ident)) - inc = Include.yes; - else - { - if (!mod.debugidsNot) - mod.debugidsNot = new Identifiers(); - mod.debugidsNot.push(ident); - } + inc = Include.yes; + definedInModule = true; } - else if (level <= global.params.debuglevel || level <= mod.debuglevel) + else if (findCondition(global.debugids, ident)) inc = Include.yes; - if (!definedInModule) - printDepsConditional(sc, this, "depsDebug "); + else + { + if (!mod.debugidsNot) + mod.debugidsNot = new Identifiers(); + mod.debugidsNot.push(ident); + } } + else if (level <= global.params.debuglevel || level <= mod.debuglevel) + inc = Include.yes; + if (!definedInModule) + printDepsConditional(sc, this, "depsDebug "); return (inc == Include.yes); } @@ -852,34 +852,36 @@ extern (C++) final class VersionCondition : DVCondition { //printf("VersionCondition::include() level = %d, versionlevel = %d\n", level, global.params.versionlevel); //if (ident) printf("\tident = '%s'\n", ident.toChars()); - if (inc == Include.notComputed) + if (inc != Include.notComputed) { - inc = Include.no; - bool definedInModule = false; - if (ident) + return inc == Include.yes; + } + + inc = Include.no; + bool definedInModule = false; + if (ident) + { + if (mod.versionids && findCondition(*mod.versionids, ident)) { - if (mod.versionids && findCondition(*mod.versionids, ident)) - { - inc = Include.yes; - definedInModule = true; - } - else if (findCondition(global.versionids, ident)) - inc = Include.yes; - else - { - if (!mod.versionidsNot) - mod.versionidsNot = new Identifiers(); - mod.versionidsNot.push(ident); - } + inc = Include.yes; + definedInModule = true; } - else if (level <= global.params.versionlevel || level <= mod.versionlevel) + else if (findCondition(global.versionids, ident)) inc = Include.yes; - if (!definedInModule && - (!ident || (!isReserved(ident.toString()) && ident != Id._unittest && ident != Id._assert))) + else { - printDepsConditional(sc, this, "depsVersion "); + if (!mod.versionidsNot) + mod.versionidsNot = new Identifiers(); + mod.versionidsNot.push(ident); } } + else if (level <= global.params.versionlevel || level <= mod.versionlevel) + inc = Include.yes; + if (!definedInModule && + (!ident || (!isReserved(ident.toString()) && ident != Id._unittest && ident != Id._assert))) + { + printDepsConditional(sc, this, "depsVersion "); + } return (inc == Include.yes); } @@ -927,31 +929,33 @@ extern (C++) final class StaticIfCondition : Condition return 0; } - if (inc == Include.notComputed) + if (inc != Include.notComputed) { - if (!sc) - { - error(loc, "`static if` conditional cannot be at global scope"); - inc = Include.no; - return 0; - } + return inc == Include.yes; + } - import dmd.staticcond; - bool errors; + if (!sc) + { + error(loc, "`static if` conditional cannot be at global scope"); + inc = Include.no; + return 0; + } - bool result = evalStaticCondition(sc, exp, exp, errors); + import dmd.staticcond; + bool errors; - // Prevent repeated condition evaluation. - // See: fail_compilation/fail7815.d - if (inc != Include.notComputed) - return (inc == Include.yes); - if (errors) - return errorReturn(); - if (result) - inc = Include.yes; - else - inc = Include.no; - } + bool result = evalStaticCondition(sc, exp, exp, errors); + + // Prevent repeated condition evaluation. + // See: fail_compilation/fail7815.d + if (inc != Include.notComputed) + return (inc == Include.yes); + if (errors) + return errorReturn(); + if (result) + inc = Include.yes; + else + inc = Include.no; return (inc == Include.yes); } diff --git a/gcc/d/dmd/cxxfrontend.d b/gcc/d/dmd/cxxfrontend.d index 7cb4011e9203bcdd4aa0a280d873ae501a80b28f..b74b11b67341b7a8f2c31dfffea9568f365bdc06 100644 --- a/gcc/d/dmd/cxxfrontend.d +++ b/gcc/d/dmd/cxxfrontend.d @@ -45,24 +45,24 @@ Expressions* getAttributes(UserAttributeDeclaration a) } /*********************************************************** - * cppmangle.d + * mangle/cpp.d */ const(char)* toCppMangleItanium(Dsymbol s) { - import dmd.cppmangle; - return dmd.cppmangle.toCppMangleItanium(s); + import dmd.mangle.cpp; + return dmd.mangle.cpp.toCppMangleItanium(s); } const(char)* cppTypeInfoMangleItanium(Dsymbol s) { - import dmd.cppmangle; - return dmd.cppmangle.cppTypeInfoMangleItanium(s); + import dmd.mangle.cpp; + return dmd.mangle.cpp.cppTypeInfoMangleItanium(s); } const(char)* cppThunkMangleItanium(FuncDeclaration fd, int offset) { - import dmd.cppmangle; - return dmd.cppmangle.cppThunkMangleItanium(fd, offset); + import dmd.mangle.cpp; + return dmd.mangle.cpp.cppThunkMangleItanium(fd, offset); } /*********************************************************** @@ -75,36 +75,36 @@ Expression ctfeInterpret(Expression e) } /*********************************************************** - * dmangle.d + * mangle/package.d */ const(char)* mangleExact(FuncDeclaration fd) { - import dmd.dmangle; - return dmd.dmangle.mangleExact(fd); + import dmd.mangle; + return dmd.mangle.mangleExact(fd); } void mangleToBuffer(Type t, ref OutBuffer buf) { - import dmd.dmangle; - return dmd.dmangle.mangleToBuffer(t, buf); + import dmd.mangle; + return dmd.mangle.mangleToBuffer(t, buf); } void mangleToBuffer(Expression e, ref OutBuffer buf) { - import dmd.dmangle; - return dmd.dmangle.mangleToBuffer(e, buf); + import dmd.mangle; + return dmd.mangle.mangleToBuffer(e, buf); } void mangleToBuffer(Dsymbol s, ref OutBuffer buf) { - import dmd.dmangle; - return dmd.dmangle.mangleToBuffer(s, buf); + import dmd.mangle; + return dmd.mangle.mangleToBuffer(s, buf); } void mangleToBuffer(TemplateInstance ti, ref OutBuffer buf) { - import dmd.dmangle; - return dmd.dmangle.mangleToBuffer(ti, buf); + import dmd.mangle; + return dmd.mangle.mangleToBuffer(ti, buf); } /*********************************************************** @@ -172,6 +172,12 @@ void importAll(Dsymbol d, Scope* sc) return dmd.dsymbolsem.importAll(d, sc); } +Dsymbols* include(Dsymbol d, Scope* sc) +{ + import dmd.dsymbolsem; + return dmd.dsymbolsem.include(d, sc); +} + /*********************************************************** * dtemplate.d */ @@ -250,6 +256,13 @@ Expression expressionSemantic(Expression e, Scope* sc) return dmd.expressionsem.expressionSemantic(e, sc); } +bool fill(StructDeclaration sd, const ref Loc loc, + ref Expressions elements, bool ctorinit) +{ + import dmd.expressionsem; + return dmd.expressionsem.fill(sd, loc, elements, ctorinit); +} + /*********************************************************** * funcsem.d */ diff --git a/gcc/d/dmd/dcast.d b/gcc/d/dmd/dcast.d index ef84af9de5a31eacef05d3dc496e05d5752aecc9..34b120b3412566845bbe91dc98524d764474b930 100644 --- a/gcc/d/dmd/dcast.d +++ b/gcc/d/dmd/dcast.d @@ -46,6 +46,7 @@ import dmd.root.ctfloat; import dmd.common.outbuffer; import dmd.root.rmem; import dmd.root.utf; +import dmd.safe : setUnsafe; import dmd.tokens; import dmd.typesem; @@ -704,7 +705,7 @@ MATCH implicitConvTo(Expression e, Type t) return MATCH.nomatch; m = MATCH.constant; } - if (e.hexString && tn.isIntegral && (tn.size == e.sz || (!e.committed && (e.len % tn.size) == 0))) + if (e.type != t && e.hexString && tn.isIntegral && (tn.size == e.sz || (!e.committed && (e.len % tn.size) == 0))) { m = MATCH.convert; return m; diff --git a/gcc/d/dmd/dclass.d b/gcc/d/dmd/dclass.d index 74074d7078cda125859491ee5713ee4cdbe1ba27..c99d27559b22970ce251c497840aa98df2fe09d7 100644 --- a/gcc/d/dmd/dclass.d +++ b/gcc/d/dmd/dclass.d @@ -23,10 +23,9 @@ import dmd.gluelayer; import dmd.declaration; import dmd.dscope; import dmd.dsymbol; -import dmd.dsymbolsem; +import dmd.dsymbolsem : dsymbolSemantic, addMember, search, setFieldOffset; import dmd.errors; import dmd.func; -import dmd.funcsem; import dmd.id; import dmd.identifier; import dmd.location; @@ -34,7 +33,7 @@ import dmd.mtype; import dmd.objc; import dmd.root.rmem; import dmd.target; -import dmd.typesem; +import dmd.typesem : covariant, immutableOf, sarrayOf; import dmd.visitor; /*********************************************************** @@ -616,6 +615,7 @@ extern (C++) class ClassDeclaration : AggregateDeclaration final bool isFuncHidden(FuncDeclaration fd) { + import dmd.funcsem : overloadApply; //printf("ClassDeclaration.isFuncHidden(class = %s, fd = %s)\n", toChars(), fd.toPrettyChars()); Dsymbol s = this.search(Loc.initial, fd.ident, SearchOpt.ignoreAmbiguous | SearchOpt.ignoreErrors); if (!s) diff --git a/gcc/d/dmd/declaration.d b/gcc/d/dmd/declaration.d index 2d3592bf8eb4c3b7be76f76355cb5b9da16a90c7..995995400a55f0d78425f6a7e7a05d4290905a09 100644 --- a/gcc/d/dmd/declaration.d +++ b/gcc/d/dmd/declaration.d @@ -22,18 +22,18 @@ import dmd.delegatize; import dmd.dscope; import dmd.dstruct; import dmd.dsymbol; -import dmd.dsymbolsem; +import dmd.dsymbolsem : dsymbolSemantic, aliasSemantic; import dmd.dtemplate; import dmd.errors; import dmd.expression; import dmd.func; -import dmd.funcsem; +import dmd.funcsem : overloadApply, getLevelAndCheck; import dmd.globals; import dmd.gluelayer; import dmd.id; import dmd.identifier; import dmd.init; -import dmd.initsem; +import dmd.initsem : initializerToExpression, initializerSemantic; import dmd.intrange; import dmd.location; import dmd.mtype; @@ -42,177 +42,13 @@ import dmd.rootobject; import dmd.root.filename; import dmd.target; import dmd.tokens; -import dmd.typesem; +import dmd.typesem : toDsymbol, typeSemantic, size, hasPointers; import dmd.visitor; version (IN_GCC) {} else version (IN_LLVM) {} else version = MARS; -/************************************ - * Check to see the aggregate type is nested and its context pointer is - * accessible from the current scope. - * Returns true if error occurs. - */ -bool checkFrameAccess(Loc loc, Scope* sc, AggregateDeclaration ad, size_t iStart = 0) -{ - Dsymbol sparent = ad.toParentLocal(); - Dsymbol sparent2 = ad.toParent2(); - Dsymbol s = sc.func; - if (ad.isNested() && s) - { - //printf("ad = %p %s [%s], parent:%p\n", ad, ad.toChars(), ad.loc.toChars(), ad.parent); - //printf("sparent = %p %s [%s], parent: %s\n", sparent, sparent.toChars(), sparent.loc.toChars(), sparent.parent,toChars()); - //printf("sparent2 = %p %s [%s], parent: %s\n", sparent2, sparent2.toChars(), sparent2.loc.toChars(), sparent2.parent,toChars()); - if (!ensureStaticLinkTo(s, sparent) || sparent != sparent2 && !ensureStaticLinkTo(s, sparent2)) - { - error(loc, "cannot access frame pointer of `%s`", ad.toPrettyChars()); - return true; - } - } - - bool result = false; - for (size_t i = iStart; i < ad.fields.length; i++) - { - VarDeclaration vd = ad.fields[i]; - Type tb = vd.type.baseElemOf(); - if (tb.ty == Tstruct) - { - result |= checkFrameAccess(loc, sc, (cast(TypeStruct)tb).sym); - } - } - return result; -} - -/*********************************************** - * Mark variable v as modified if it is inside a constructor that var - * is a field in. - * Also used to allow immutable globals to be initialized inside a static constructor. - * Returns: - * true if it's an initialization of v - */ -bool modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1) -{ - //printf("modifyFieldVar(var = %s)\n", var.toChars()); - Dsymbol s = sc.func; - while (1) - { - FuncDeclaration fd = null; - if (s) - fd = s.isFuncDeclaration(); - if (fd && - ((fd.isCtorDeclaration() && var.isField()) || - ((fd.isStaticCtorDeclaration() || fd.isCrtCtor) && !var.isField())) && - fd.toParentDecl() == var.toParent2() && - (!e1 || e1.op == EXP.this_)) - { - bool result = true; - - var.ctorinit = true; - //printf("setting ctorinit\n"); - - if (var.isField() && sc.ctorflow.fieldinit.length && !sc.intypeof) - { - assert(e1); - auto mustInit = ((var.storage_class & STC.nodefaultctor) != 0 || - var.type.needsNested()); - - const dim = sc.ctorflow.fieldinit.length; - auto ad = fd.isMemberDecl(); - assert(ad); - size_t i; - for (i = 0; i < dim; i++) // same as findFieldIndexByName in ctfeexp.c ? - { - if (ad.fields[i] == var) - break; - } - assert(i < dim); - auto fieldInit = &sc.ctorflow.fieldinit[i]; - const fi = fieldInit.csx; - - if (fi & CSX.this_ctor) - { - if (var.type.isMutable() && e1.type.isMutable()) - result = false; - else - { - const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod); - .error(loc, "%s field `%s` initialized multiple times", modStr, var.toChars()); - .errorSupplemental(fieldInit.loc, "Previous initialization is here."); - } - } - else if (sc.inLoop || (fi & CSX.label)) - { - if (!mustInit && var.type.isMutable() && e1.type.isMutable()) - result = false; - else - { - const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod); - .error(loc, "%s field `%s` initialization is not allowed in loops or after labels", modStr, var.toChars()); - } - } - - fieldInit.csx |= CSX.this_ctor; - fieldInit.loc = e1.loc; - if (var.overlapped) // https://issues.dlang.org/show_bug.cgi?id=15258 - { - foreach (j, v; ad.fields) - { - if (v is var || !var.isOverlappedWith(v)) - continue; - v.ctorinit = true; - sc.ctorflow.fieldinit[j].csx = CSX.this_ctor; - } - } - } - else if (fd != sc.func) - { - if (var.type.isMutable()) - result = false; - else if (sc.func.fes) - { - const(char)* p = var.isField() ? "field" : var.kind(); - .error(loc, "%s %s `%s` initialization is not allowed in foreach loop", - MODtoChars(var.type.mod), p, var.toChars()); - } - else - { - const(char)* p = var.isField() ? "field" : var.kind(); - .error(loc, "%s %s `%s` initialization is not allowed in nested function `%s`", - MODtoChars(var.type.mod), p, var.toChars(), sc.func.toChars()); - } - } - else if (fd.isStaticCtorDeclaration() && !fd.isSharedStaticCtorDeclaration() && - var.type.isImmutable()) - { - .error(loc, "%s %s `%s` initialization is not allowed in `static this`", - MODtoChars(var.type.mod), var.kind(), var.toChars()); - errorSupplemental(loc, "Use `shared static this` instead."); - } - else if (fd.isStaticCtorDeclaration() && !fd.isSharedStaticCtorDeclaration() && - var.type.isConst()) - { - // @@@DEPRECATED_2.116@@@ - // Turn this into an error, merging with the branch above - .deprecation(loc, "%s %s `%s` initialization is not allowed in `static this`", - MODtoChars(var.type.mod), var.kind(), var.toChars()); - deprecationSupplemental(loc, "Use `shared static this` instead."); - } - return result; - } - else - { - if (s) - { - s = s.toParentP(var.toParent2()); - continue; - } - } - break; - } - return false; -} - /****************************************** */ void ObjectNotFound(Loc loc, Identifier id) @@ -289,149 +125,6 @@ extern (C++) abstract class Declaration : Dsymbol return sz; } - /** - * Issue an error if an attempt to call a disabled method is made - * - * If the declaration is disabled but inside a disabled function, - * returns `true` but do not issue an error message. - * - * Params: - * loc = Location information of the call - * sc = Scope in which the call occurs - * isAliasedDeclaration = if `true` searches overload set - * - * Returns: - * `true` if this `Declaration` is `@disable`d, `false` otherwise. - */ - extern (D) final bool checkDisabled(Loc loc, Scope* sc, bool isAliasedDeclaration = false) - { - if (!(storage_class & STC.disable)) - return false; - - if (sc.func && sc.func.storage_class & STC.disable) - return true; - - if (auto p = toParent()) - { - if (auto postblit = isPostBlitDeclaration()) - { - /* https://issues.dlang.org/show_bug.cgi?id=21885 - * - * If the generated postblit is disabled, it - * means that one of the fields has a disabled - * postblit. Print the first field that has - * a disabled postblit. - */ - if (postblit.isGenerated()) - { - auto sd = p.isStructDeclaration(); - assert(sd); - for (size_t i = 0; i < sd.fields.length; i++) - { - auto structField = sd.fields[i]; - if (structField.overlapped) - continue; - Type tv = structField.type.baseElemOf(); - if (tv.ty != Tstruct) - continue; - auto sdv = (cast(TypeStruct)tv).sym; - if (!sdv.postblit) - continue; - if (sdv.postblit.isDisabled()) - { - .error(loc, "%s `%s` is not copyable because field `%s` is not copyable", p.kind, p.toPrettyChars, structField.toChars()); - return true; - } - } - } - .error(loc, "%s `%s` is not copyable because it has a disabled postblit", p.kind, p.toPrettyChars); - return true; - } - } - - // if the function is @disabled, maybe there - // is an overload in the overload set that isn't - if (isAliasedDeclaration) - { - if (FuncDeclaration fd = isFuncDeclaration()) - { - for (FuncDeclaration ovl = fd; ovl; ovl = cast(FuncDeclaration)ovl.overnext) - if (!(ovl.storage_class & STC.disable)) - return false; - } - } - - if (auto ctor = isCtorDeclaration()) - { - if (ctor.isCpCtor && ctor.isGenerated()) - { - .error(loc, "generating an `inout` copy constructor for `struct %s` failed, therefore instances of it are uncopyable", parent.toPrettyChars()); - return true; - } - } - .error(loc, "%s `%s` cannot be used because it is annotated with `@disable`", kind, toPrettyChars); - return true; - } - - /************************************* - * Check to see if declaration can be modified in this context (sc). - * Issue error if not. - * Params: - * loc = location for error messages - * e1 = `null` or `this` expression when this declaration is a field - * sc = context - * flag = if the first bit is set it means do not issue error message for - * invalid modification; if the second bit is set, it means that - this declaration is a field and a subfield of it is modified. - * Returns: - * Modifiable.yes or Modifiable.initialization - */ - extern (D) final Modifiable checkModify(Loc loc, Scope* sc, Expression e1, ModifyFlags flag) - { - VarDeclaration v = isVarDeclaration(); - if (v && v.canassign) - return Modifiable.initialization; - - if (isParameter() || isResult()) - { - for (Scope* scx = sc; scx; scx = scx.enclosing) - { - if (scx.func == parent && scx.contract != Contract.none) - { - const(char)* s = isParameter() && parent.ident != Id.ensure ? "parameter" : "result"; - if (!(flag & ModifyFlags.noError)) - error(loc, "%s `%s` cannot modify %s `%s` in contract", kind, toPrettyChars, s, toChars()); - return Modifiable.initialization; // do not report type related errors - } - } - } - - if (e1 && e1.op == EXP.this_ && isField()) - { - VarDeclaration vthis = e1.isThisExp().var; - for (Scope* scx = sc; scx; scx = scx.enclosing) - { - if (scx.func == vthis.parent && scx.contract != Contract.none) - { - if (!(flag & ModifyFlags.noError)) - error(loc, "%s `%s` cannot modify parameter `this` in contract", kind, toPrettyChars); - return Modifiable.initialization; // do not report type related errors - } - } - } - - if (v && (v.isCtorinit() || isField())) - { - // It's only modifiable if inside the right constructor - if ((storage_class & (STC.foreach_ | STC.ref_)) == (STC.foreach_ | STC.ref_)) - return Modifiable.initialization; - if (flag & ModifyFlags.fieldAssign) - return Modifiable.yes; - return modifyFieldVar(loc, sc, v, e1) ? Modifiable.initialization : Modifiable.yes; - } - return Modifiable.yes; - } - final bool isStatic() const pure nothrow @nogc @safe { return (storage_class & STC.static_) != 0; @@ -923,6 +616,15 @@ extern (C++) final class AliasDeclaration : Declaration 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 (!(adFlags & ignoreRead)) adFlags |= wasRead; // can never assign to this AliasDeclaration again @@ -930,16 +632,16 @@ extern (C++) final class AliasDeclaration : Declaration if (inuse == 1 && type && _scope) { inuse = 2; - uint olderrors = global.errors; + 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) - goto Lerr; + return err(); if (s) { s = s.toAlias(); if (global.errors != olderrors) - goto Lerr; + return err(); aliassym = s; inuse = 0; } @@ -947,9 +649,9 @@ extern (C++) final class AliasDeclaration : Declaration { Type t = type.typeSemantic(loc, _scope); if (t.ty == Terror) - goto Lerr; + return err(); if (global.errors != olderrors) - goto Lerr; + return err(); //printf("t = %s\n", t.toChars()); inuse = 0; } @@ -957,14 +659,7 @@ extern (C++) final class AliasDeclaration : Declaration if (inuse) { .error(loc, "%s `%s` recursive alias declaration", kind, toPrettyChars); - - Lerr: - // 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; + return err(); } if (semanticRun >= PASS.semanticdone) @@ -1350,7 +1045,7 @@ extern (C++) class VarDeclaration : Declaration auto vbitoffset = v.offset * 8; // Bitsize of types are overridden by any bit-field widths. - ulong tbitsize = void; + ulong tbitsize; if (auto bf = isBitFieldDeclaration()) { bitoffset += bf.bitOffset; @@ -1359,7 +1054,7 @@ extern (C++) class VarDeclaration : Declaration else tbitsize = tsz * 8; - ulong vbitsize = void; + ulong vbitsize; if (auto vbf = v.isBitFieldDeclaration()) { vbitoffset += vbf.bitOffset; @@ -1395,118 +1090,6 @@ extern (C++) class VarDeclaration : Declaration return edtor && !(storage_class & STC.nodtor); } - /****************************************** - * If a variable has a scope destructor call, return call for it. - * Otherwise, return NULL. - */ - extern (D) final Expression callScopeDtor(Scope* sc) - { - //printf("VarDeclaration::callScopeDtor() %s\n", toChars()); - - // Destruction of STC.field's is handled by buildDtor() - if (storage_class & (STC.nodtor | STC.ref_ | STC.out_ | STC.field)) - { - return null; - } - - if (iscatchvar) - return null; // destructor is built by `void semantic(Catch c, Scope* sc)`, not here - - Expression e = null; - // Destructors for structs and arrays of structs - Type tv = type.baseElemOf(); - if (tv.ty == Tstruct) - { - StructDeclaration sd = (cast(TypeStruct)tv).sym; - if (!sd.dtor || sd.errors) - return null; - - const sz = type.size(); - assert(sz != SIZE_INVALID); - if (!sz) - return null; - - if (type.toBasetype().ty == Tstruct) - { - // v.__xdtor() - e = new VarExp(loc, this); - - /* This is a hack so we can call destructors on const/immutable objects. - * Need to add things like "const ~this()" and "immutable ~this()" to - * fix properly. - */ - e.type = e.type.mutableOf(); - - // Enable calling destructors on shared objects. - // The destructor is always a single, non-overloaded function, - // and must serve both shared and non-shared objects. - e.type = e.type.unSharedOf; - - e = new DotVarExp(loc, e, sd.dtor, false); - e = new CallExp(loc, e); - } - else - { - // __ArrayDtor(v[0 .. n]) - e = new VarExp(loc, this); - - const sdsz = sd.type.size(); - assert(sdsz != SIZE_INVALID && sdsz != 0); - const n = sz / sdsz; - SliceExp se = new SliceExp(loc, e, new IntegerExp(loc, 0, Type.tsize_t), - new IntegerExp(loc, n, Type.tsize_t)); - - // Prevent redundant bounds check - se.upperIsInBounds = true; - se.lowerIsLessThanUpper = true; - - // This is a hack so we can call destructors on const/immutable objects. - se.type = sd.type.arrayOf(); - - e = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayDtor), se); - } - return e; - } - // Destructors for classes - if (storage_class & (STC.auto_ | STC.scope_) && !(storage_class & STC.parameter)) - { - for (ClassDeclaration cd = type.isClassHandle(); cd; cd = cd.baseClass) - { - /* We can do better if there's a way with onstack - * classes to determine if there's no way the monitor - * could be set. - */ - //if (cd.isInterfaceDeclaration()) - // error("interface `%s` cannot be scope", cd.toChars()); - - if (onstack) // if any destructors - { - // delete'ing C++ classes crashes (and delete is deprecated anyway) - if (cd.classKind == ClassKind.cpp) - { - // Don't call non-existant dtor - if (!cd.dtor) - break; - - e = new VarExp(loc, this); - e.type = e.type.mutableOf().unSharedOf(); // Hack for mutable ctor on immutable instances - e = new DotVarExp(loc, e, cd.dtor, false); - e = new CallExp(loc, e); - break; - } - - // delete this; - Expression ec; - ec = new VarExp(loc, this); - e = new DeleteExp(loc, ec, true); - e.type = Type.tvoid; - break; - } - } - } - return e; - } - /******************************************* * If variable has a constant expression initializer, get it. * Otherwise, return null. @@ -1516,7 +1099,7 @@ extern (C++) class VarDeclaration : Declaration assert(type && _init); // Ungag errors when not speculative - uint oldgag = global.gag; + const oldgag = global.gag; if (global.gag) { Dsymbol sym = isMember(); @@ -1539,25 +1122,6 @@ extern (C++) class VarDeclaration : Declaration return e; } - /******************************************* - * Helper function for the expansion of manifest constant. - */ - extern (D) final Expression expandInitializer(Loc loc) - { - assert((storage_class & STC.manifest) && _init); - - auto e = getConstInitializer(); - if (!e) - { - .error(loc, "cannot make expression out of initializer for `%s`", toChars()); - return ErrorExp.get(); - } - - e = e.copy(); - e.loc = loc; // for better error message - return e; - } - override final void checkCtorConstInit() { version (none) @@ -1762,7 +1326,7 @@ extern (C++) final class SymbolDeclaration : Declaration */ private Identifier getTypeInfoIdent(Type t) { - import dmd.dmangle; + import dmd.mangle; import core.stdc.stdlib; import dmd.root.rmem; // _init_10TypeInfo_%s diff --git a/gcc/d/dmd/declaration.h b/gcc/d/dmd/declaration.h index c19aa0692fa362df6f2e2021532db7c53c05881b..bdefd2d9f864cc5f029b7c5bc0bf181526ab78ea 100644 --- a/gcc/d/dmd/declaration.h +++ b/gcc/d/dmd/declaration.h @@ -590,13 +590,6 @@ public: // Things that should really go into Scope - // 1 if there's a return exp; statement - // 2 if there's a throw statement - // 4 if there's an assert(0) - // 8 if there's inline asm - // 16 if there are multiple return statements - int hasReturnExp; - VarDeclaration *nrvo_var; // variable to replace with shidden Symbol *shidden; // hidden pointer passed to function @@ -679,6 +672,12 @@ public: bool dllImport(bool v); bool dllExport() const; bool dllExport(bool v); + bool hasReturnExp() const; + bool hasReturnExp(bool v); + bool hasInlineAsm() const; + bool hasInlineAsm(bool v); + bool hasMultipleReturnExp() const; + bool hasMultipleReturnExp(bool v); // Data for a function declaration that is needed for the Objective-C // integration. diff --git a/gcc/d/dmd/dimport.d b/gcc/d/dmd/dimport.d index 4c4c063cbf2731fd72897a9e9873482b9c8b7380..96e821aea6af2c2f1454c97617e041324d998d12 100644 --- a/gcc/d/dmd/dimport.d +++ b/gcc/d/dmd/dimport.d @@ -14,7 +14,6 @@ module dmd.dimport; import dmd.arraytypes; import dmd.dmodule; import dmd.dsymbol; -import dmd.errors; import dmd.identifier; import dmd.location; import dmd.visitor; @@ -84,16 +83,6 @@ extern (C++) final class Import : Dsymbol this.visibility = Visibility.Kind.private_; // default to private } - extern (D) void addAlias(Identifier name, Identifier _alias) - { - if (isstatic) - .error(loc, "%s `%s` cannot have an import bind list", kind, toPrettyChars); - if (!aliasId) - this.ident = null; // make it an anonymous import - names.push(name); - aliases.push(_alias); - } - override const(char)* kind() const { return isstatic ? "static import" : "import"; @@ -110,9 +99,13 @@ extern (C++) final class Import : Dsymbol assert(!s); auto si = new Import(loc, packages, id, aliasId, isstatic); si.comment = comment; + assert(!(isstatic && names.length)); + if (names.length && !si.aliasId) + si.ident = null; for (size_t i = 0; i < names.length; i++) { - si.addAlias(names[i], aliases[i]); + si.names.push(names[i]); + si.aliases.push(aliases[i]); } return si; } diff --git a/gcc/d/dmd/dmodule.d b/gcc/d/dmd/dmodule.d index eda20a2141ad3b83a6224fc24bb5f7f7bc4fb036..7996d3441934ffe4bf9b106f98a0df84b20e4705 100644 --- a/gcc/d/dmd/dmodule.d +++ b/gcc/d/dmd/dmodule.d @@ -30,7 +30,7 @@ import dmd.dmacro; import dmd.doc; import dmd.dscope; import dmd.dsymbol; -import dmd.dsymbolsem; +import dmd.dsymbolsem : dsymbolSemantic, importAll, load, include; import dmd.errors; import dmd.errorsink; import dmd.expression; @@ -632,7 +632,7 @@ extern (C++) final class Module : Package const name = srcfile.toString(); if (FileName.equals(name, "object.d")) { - ObjectNotFound(loc, ident); + ObjectNotFound(Loc.initial, ident); } else if (FileName.ext(this.arg) || !loc.isValid()) { @@ -800,7 +800,6 @@ extern (C++) final class Module : Package { const bool doUnittests = global.params.parsingUnittestsRequired(); scope p = new Parser!AST(this, buf, cast(bool) docfile, global.errorSink, &global.compileEnv, doUnittests); - p.transitionIn = global.params.v.vin; p.nextToken(); p.parseModuleDeclaration(); md = p.md; @@ -1408,7 +1407,7 @@ private const(char)[] processSource (const(ubyte)[] src, Module mod) { enum SourceEncoding { utf16, utf32} enum Endian { little, big} - immutable loc = mod.getLoc(); + immutable loc = mod.loc; /* * Convert a buffer from UTF32 to UTF8 diff --git a/gcc/d/dmd/doc.d b/gcc/d/dmd/doc.d index 84048af033796ab9a561732f2e830f6c1a15a301..21391dc1838877f04d12f718996264e49698164f 100644 --- a/gcc/d/dmd/doc.d +++ b/gcc/d/dmd/doc.d @@ -2274,10 +2274,9 @@ void removeBlankLineMacro(ref OutBuffer buf, ref size_t iAt, ref size_t i) * thematic break. If the replacement is made `i` changes to * point to the closing parenthesis of the `$(HR)` macro. * iLineStart = the index within `buf` that the thematic break's line starts at - * loc = the current location within the file * Returns: whether a thematic break was replaced */ -bool replaceMarkdownThematicBreak(ref OutBuffer buf, ref size_t i, size_t iLineStart, const ref Loc loc) +bool replaceMarkdownThematicBreak(ref OutBuffer buf, ref size_t i, size_t iLineStart) { const slice = buf[]; @@ -2378,11 +2377,10 @@ void removeAnyAtxHeadingSuffix(ref OutBuffer buf, size_t i) * iEnd = the index within `buf` of the character after the last * heading character. Is incremented by the length of the * inserted heading macro when this function ends. - * loc = the location of the Ddoc within the file * headingLevel = the level (1-6) of heading to end. Is set to `0` when this * function ends. */ -void endMarkdownHeading(ref OutBuffer buf, size_t iStart, ref size_t iEnd, const ref Loc loc, ref int headingLevel) +void endMarkdownHeading(ref OutBuffer buf, size_t iStart, ref size_t iEnd, ref int headingLevel) { char[5] heading = "$(H0 "; heading[3] = cast(char) ('0' + headingLevel); @@ -2439,12 +2437,11 @@ size_t endAllListsAndQuotes(ref OutBuffer buf, ref size_t i, ref MarkdownList[] * e.g. `*very* **nice**` becomes `$(EM very) $(STRONG nice)`. * Params: * buf = an OutBuffer containing the DDoc - * loc = the current location within the file * inlineDelimiters = the collection of delimiters found within a paragraph. When this function returns its length will be reduced to `downToLevel`. * downToLevel = the length within `inlineDelimiters`` to reduce emphasis to * Returns: the number of characters added to the buffer by the replacements */ -size_t replaceMarkdownEmphasis(ref OutBuffer buf, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, int downToLevel = 0) +size_t replaceMarkdownEmphasis(ref OutBuffer buf, ref MarkdownDelimiter[] inlineDelimiters, int downToLevel = 0) { size_t replaceEmphasisPair(ref MarkdownDelimiter start, ref MarkdownDelimiter end) { @@ -2823,10 +2820,9 @@ struct MarkdownList * i = the index within `buf` of the list item. If this function succeeds `i` will be adjusted to fit the inserted macro. * iPrecedingBlankLine = the index within `buf` of the preceeding blank line. If non-zero and a new list was started, the preceeding blank line is removed and this value is set to `0`. * nestedLists = a set of nested lists. If this function succeeds it may contain a new nested list. - * loc = the location of the Ddoc within the file * Returns: `true` if a list was created */ - bool startItem(ref OutBuffer buf, ref size_t iLineStart, ref size_t i, ref size_t iPrecedingBlankLine, ref MarkdownList[] nestedLists, const ref Loc loc) + bool startItem(ref OutBuffer buf, ref size_t iLineStart, ref size_t i, ref size_t iPrecedingBlankLine, ref MarkdownList[] nestedLists) { buf.remove(iStart, iContentStart - iStart); @@ -3001,14 +2997,13 @@ struct MarkdownLink * buf = an OutBuffer containing the DDoc * i = the index within `buf` that points to the `]` character of the potential link. * If this function succeeds it will be adjusted to fit the inserted link macro. - * loc = the current location within the file * inlineDelimiters = previously parsed Markdown delimiters, including emphasis and link/image starts * delimiterIndex = the index within `inlineDelimiters` of the nearest link/image starting delimiter * linkReferences = previously parsed link references. When this function returns it may contain * additional previously unparsed references. * Returns: whether a reference link was found and replaced at `i` */ - static bool replaceLink(ref OutBuffer buf, ref size_t i, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, int delimiterIndex, ref MarkdownLinkReferences linkReferences) + static bool replaceLink(ref OutBuffer buf, ref size_t i, ref MarkdownDelimiter[] inlineDelimiters, int delimiterIndex, ref MarkdownLinkReferences linkReferences) { const delimiter = inlineDelimiters[delimiterIndex]; MarkdownLink link; @@ -3017,7 +3012,7 @@ struct MarkdownLink if (iEnd > i) { i = delimiter.iStart; - link.storeAndReplaceDefinition(buf, i, iEnd, linkReferences, loc); + link.storeAndReplaceDefinition(buf, i, iEnd, linkReferences); inlineDelimiters.length = delimiterIndex; return true; } @@ -3029,7 +3024,7 @@ struct MarkdownLink if (iEnd > i) { const label = link.label; - link = linkReferences.lookupReference(label, buf, i, loc); + link = linkReferences.lookupReference(label, buf, i); // check rightFlanking to avoid replacing things like int[string] if (!link.href.length && !delimiter.rightFlanking) link = linkReferences.lookupSymbol(label); @@ -3041,7 +3036,7 @@ struct MarkdownLink if (iEnd == i) return false; - immutable delta = replaceMarkdownEmphasis(buf, loc, inlineDelimiters, delimiterIndex); + immutable delta = replaceMarkdownEmphasis(buf, inlineDelimiters, delimiterIndex); iEnd += delta; i += delta; link.replaceLink(buf, i, iEnd, delimiter); @@ -3058,10 +3053,9 @@ struct MarkdownLink * delimiterIndex = the index within `inlineDelimiters` of the nearest link/image starting delimiter * linkReferences = previously parsed link references. When this function returns it may contain * additional previously unparsed references. - * loc = the current location in the file * Returns: whether a reference link was found and replaced at `i` */ - static bool replaceReferenceDefinition(ref OutBuffer buf, ref size_t i, ref MarkdownDelimiter[] inlineDelimiters, int delimiterIndex, ref MarkdownLinkReferences linkReferences, const ref Loc loc) + static bool replaceReferenceDefinition(ref OutBuffer buf, ref size_t i, ref MarkdownDelimiter[] inlineDelimiters, int delimiterIndex, ref MarkdownLinkReferences linkReferences) { const delimiter = inlineDelimiters[delimiterIndex]; MarkdownLink link; @@ -3070,7 +3064,7 @@ struct MarkdownLink return false; i = delimiter.iStart; - link.storeAndReplaceDefinition(buf, i, iEnd, linkReferences, loc); + link.storeAndReplaceDefinition(buf, i, iEnd, linkReferences); inlineDelimiters.length = delimiterIndex; return true; } @@ -3443,9 +3437,8 @@ struct MarkdownLink * iEnd = the index within `buf` that points just after the end of the definition * linkReferences = previously parsed link references. When this function returns it may contain * an additional reference. - * loc = the current location in the file */ - private void storeAndReplaceDefinition(ref OutBuffer buf, ref size_t i, size_t iEnd, ref MarkdownLinkReferences linkReferences, const ref Loc loc) + private void storeAndReplaceDefinition(ref OutBuffer buf, ref size_t i, size_t iEnd, ref MarkdownLinkReferences linkReferences) { // Remove the definition and trailing whitespace iEnd = skipChars(buf, iEnd, " \t\r\n"); @@ -3578,14 +3571,13 @@ struct MarkdownLinkReferences * label = the label to find the reference for * buf = an OutBuffer containing the DDoc * i = the index within `buf` to start searching for references at - * loc = the current location in the file * Returns: a link. If the `href` member has a value then the reference is valid. */ - MarkdownLink lookupReference(string label, ref OutBuffer buf, size_t i, const ref Loc loc) + MarkdownLink lookupReference(string label, ref OutBuffer buf, size_t i) { const lowercaseLabel = label.toLowercase(); if (lowercaseLabel !in references) - extractReferences(buf, i, loc); + extractReferences(buf, i); if (lowercaseLabel in references) return references[lowercaseLabel]; @@ -3633,10 +3625,9 @@ struct MarkdownLinkReferences * Params: * buf = an OutBuffer containing the DDoc * i = the index within `buf` to start looking at - * loc = the current location in the file * Returns: whether a reference was extracted */ - private void extractReferences(ref OutBuffer buf, size_t i, const ref Loc loc) + private void extractReferences(ref OutBuffer buf, size_t i) { static bool isFollowedBySpace(ref OutBuffer buf, size_t i) { @@ -3724,7 +3715,7 @@ struct MarkdownLinkReferences break; case ']': if (delimiters.length && !inCode && - MarkdownLink.replaceReferenceDefinition(buf, i, delimiters, cast(int) delimiters.length - 1, this, loc)) + MarkdownLink.replaceReferenceDefinition(buf, i, delimiters, cast(int) delimiters.length - 1, this)) --i; break; default: @@ -3897,19 +3888,18 @@ size_t parseTableDelimiterRow(ref OutBuffer buf, const size_t iStart, bool inQuo * buf = an OutBuffer containing the DDoc * iStart = the index within `buf` that the table header row starts at, inclusive * iEnd = the index within `buf` that the table header row ends at, exclusive - * loc = the current location in the file * inQuote = whether the table is inside a quote * inlineDelimiters = delimiters containing columns separators and any inline emphasis * columnAlignments = the parsed alignments for each column * Returns: the number of characters added by starting the table, or `0` if unchanged */ -size_t startTable(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc loc, bool inQuote, ref MarkdownDelimiter[] inlineDelimiters, out TableColumnAlignment[] columnAlignments) +size_t startTable(ref OutBuffer buf, size_t iStart, size_t iEnd, bool inQuote, ref MarkdownDelimiter[] inlineDelimiters, out TableColumnAlignment[] columnAlignments) { const iDelimiterRowEnd = parseTableDelimiterRow(buf, iEnd + 1, inQuote, columnAlignments); if (iDelimiterRowEnd) { size_t delta; - if (replaceTableRow(buf, iStart, iEnd, loc, inlineDelimiters, columnAlignments, true, delta)) + if (replaceTableRow(buf, iStart, iEnd, inlineDelimiters, columnAlignments, true, delta)) { buf.remove(iEnd + delta, iDelimiterRowEnd - iEnd); buf.insert(iEnd + delta, "$(TBODY "); @@ -3930,7 +3920,6 @@ size_t startTable(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc l * buf = an OutBuffer containing the DDoc * iStart = the index within `buf` that the table row starts at, inclusive * iEnd = the index within `buf` that the table row ends at, exclusive - * loc = the current location in the file * inlineDelimiters = delimiters containing columns separators and any inline emphasis * columnAlignments = alignments for each column * headerRow = if `true` then the number of columns will be enforced to match @@ -3939,7 +3928,7 @@ size_t startTable(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc l * delta = the number of characters added by replacing the row, or `0` if unchanged * Returns: `true` if a table row was found and replaced */ -bool replaceTableRow(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, TableColumnAlignment[] columnAlignments, bool headerRow, out size_t delta) +bool replaceTableRow(ref OutBuffer buf, size_t iStart, size_t iEnd, ref MarkdownDelimiter[] inlineDelimiters, TableColumnAlignment[] columnAlignments, bool headerRow, out size_t delta) { delta = 0; @@ -3965,7 +3954,7 @@ bool replaceTableRow(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Lo void replaceTableCell(size_t iCellStart, size_t iCellEnd, int cellIndex, int di) { - const eDelta = replaceMarkdownEmphasis(buf, loc, inlineDelimiters, di); + const eDelta = replaceMarkdownEmphasis(buf, inlineDelimiters, di); delta += eDelta; iCellEnd += eDelta; @@ -4083,15 +4072,14 @@ size_t endTable(ref OutBuffer buf, size_t i, ref TableColumnAlignment[] columnAl * buf = an OutBuffer containing the DDoc * iStart = the index within `buf` that the table row starts at, inclusive * iEnd = the index within `buf` that the table row ends at, exclusive - * loc = the current location in the file * inlineDelimiters = delimiters containing columns separators and any inline emphasis * columnAlignments = alignments for each column; upon return is set to length `0` * Returns: the number of characters added by replacing the row, or `0` if unchanged */ -size_t endRowAndTable(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, ref TableColumnAlignment[] columnAlignments) +size_t endRowAndTable(ref OutBuffer buf, size_t iStart, size_t iEnd, ref MarkdownDelimiter[] inlineDelimiters, ref TableColumnAlignment[] columnAlignments) { size_t delta; - replaceTableRow(buf, iStart, iEnd, loc, inlineDelimiters, columnAlignments, false, delta); + replaceTableRow(buf, iStart, iEnd, inlineDelimiters, columnAlignments, false, delta); delta += endTable(buf, iEnd + delta, columnAlignments); return delta; } @@ -4161,19 +4149,19 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of } if (headingLevel) { - i += replaceMarkdownEmphasis(buf, loc, inlineDelimiters); - endMarkdownHeading(buf, iParagraphStart, i, loc, headingLevel); + i += replaceMarkdownEmphasis(buf, inlineDelimiters); + endMarkdownHeading(buf, iParagraphStart, i, headingLevel); removeBlankLineMacro(buf, iPrecedingBlankLine, i); ++i; iParagraphStart = skipChars(buf, i, " \t\r\n"); } if (tableRowDetected && !columnAlignments.length) - i += startTable(buf, iLineStart, i, loc, lineQuoted, inlineDelimiters, columnAlignments); + i += startTable(buf, iLineStart, i, lineQuoted, inlineDelimiters, columnAlignments); else if (columnAlignments.length) { size_t delta; - if (replaceTableRow(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments, false, delta)) + if (replaceTableRow(buf, iLineStart, i, inlineDelimiters, columnAlignments, false, delta)) i += delta; else i += endTable(buf, i, columnAlignments); @@ -4188,7 +4176,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of i += endTable(buf, i, columnAlignments); if (!lineQuoted && quoteLevel) endAllListsAndQuotes(buf, i, nestedLists, quoteLevel, quoteMacroLevel); - i += replaceMarkdownEmphasis(buf, loc, inlineDelimiters); + i += replaceMarkdownEmphasis(buf, inlineDelimiters); // if we don't already know about this paragraph break then // insert a blank line and record the paragraph break @@ -4304,7 +4292,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of if (quoteLevel < lineQuoteLevel) { - i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments); + i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments); if (nestedLists.length) { const indent = getMarkdownIndent(buf, iLineStart, i); @@ -4427,7 +4415,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of if (!headingLevel) break; - i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments); + i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments); if (!lineQuoted && quoteLevel) i += endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel); @@ -4468,7 +4456,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of const list = MarkdownList.parseItem(buf, iLineStart, i); if (list.isValid) { - if (replaceMarkdownThematicBreak(buf, i, iLineStart, loc)) + if (replaceMarkdownThematicBreak(buf, i, iLineStart)) { removeBlankLineMacro(buf, iPrecedingBlankLine, i); iParagraphStart = skipChars(buf, i+1, " \t\r\n"); @@ -4592,7 +4580,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of } else { - i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments); + i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments); if (!lineQuoted && quoteLevel) { const delta = endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel); @@ -4624,9 +4612,9 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of case '_': { - if (leadingBlank && !inCode && replaceMarkdownThematicBreak(buf, i, iLineStart, loc)) + if (leadingBlank && !inCode && replaceMarkdownThematicBreak(buf, i, iLineStart)) { - i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments); + i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments); if (!lineQuoted && quoteLevel) i += endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel); removeBlankLineMacro(buf, iPrecedingBlankLine, i); @@ -4654,7 +4642,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of break; } - i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments); + i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments); if (!lineQuoted && quoteLevel) { const delta = endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel); @@ -4664,7 +4652,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of } list.macroLevel = macroLevel; - list.startItem(buf, iLineStart, i, iPrecedingBlankLine, nestedLists, loc); + list.startItem(buf, iLineStart, i, iPrecedingBlankLine, nestedLists); break; } } @@ -4683,9 +4671,9 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of if (leadingBlank) { // Check for a thematic break - if (replaceMarkdownThematicBreak(buf, i, iLineStart, loc)) + if (replaceMarkdownThematicBreak(buf, i, iLineStart)) { - i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments); + i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments); if (!lineQuoted && quoteLevel) i += endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel); removeBlankLineMacro(buf, iPrecedingBlankLine, i); @@ -4764,7 +4752,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of if (delimiter.type == '[' || delimiter.type == '!') { if (delimiter.isValid && - MarkdownLink.replaceLink(buf, i, loc, inlineDelimiters, d, linkReferences)) + MarkdownLink.replaceLink(buf, i, inlineDelimiters, d, linkReferences)) { // if we removed a reference link then we're at line start if (i <= delimiter.iStart) @@ -4862,10 +4850,10 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of --downToLevel; if (headingLevel && headingMacroLevel >= macroLevel) { - endMarkdownHeading(buf, iParagraphStart, i, loc, headingLevel); + endMarkdownHeading(buf, iParagraphStart, i, headingLevel); removeBlankLineMacro(buf, iPrecedingBlankLine, i); } - i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments); + i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments); while (nestedLists.length && nestedLists[$-1].macroLevel >= macroLevel) { i = buf.insert(i, ")\n)"); @@ -4873,7 +4861,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of } if (quoteLevel && quoteMacroLevel >= macroLevel) i += endAllMarkdownQuotes(buf, i, quoteLevel); - i += replaceMarkdownEmphasis(buf, loc, inlineDelimiters, downToLevel); + i += replaceMarkdownEmphasis(buf, inlineDelimiters, downToLevel); --macroLevel; quoteMacroLevel = 0; @@ -4949,11 +4937,11 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of size_t i = buf.length; if (headingLevel) { - endMarkdownHeading(buf, iParagraphStart, i, loc, headingLevel); + endMarkdownHeading(buf, iParagraphStart, i, headingLevel); removeBlankLineMacro(buf, iPrecedingBlankLine, i); } - i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments); - i += replaceMarkdownEmphasis(buf, loc, inlineDelimiters); + i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments); + i += replaceMarkdownEmphasis(buf, inlineDelimiters); endAllListsAndQuotes(buf, i, nestedLists, quoteLevel, quoteMacroLevel); } diff --git a/gcc/d/dmd/dscope.d b/gcc/d/dmd/dscope.d index 7936086bdd06d392943a1a1d6403a7b8ae8af293..05cc8f156e11869336a0413da66b19ccc4aaf40d 100644 --- a/gcc/d/dmd/dscope.d +++ b/gcc/d/dmd/dscope.d @@ -253,13 +253,13 @@ extern (C++) struct Scope } /// Copy flags from scope `other` - extern(D) void copyFlagsFrom(Scope* other) + extern(D) void copyFlagsFrom(Scope* other) @safe { this.bitFields = other.bitFields; } /// Set all scope flags to their initial value - extern(D) void resetAllFlags() + extern(D) void resetAllFlags() @safe { this.bitFields = 0; } @@ -353,24 +353,23 @@ extern (C++) struct Scope error(loc, "one path skips constructor"); const fies = ctorflow.fieldinit; - if (this.ctorflow.fieldinit.length && fies.length) + if (!this.ctorflow.fieldinit.length || !fies.length) + return; + FuncDeclaration f = func; + if (fes) + f = fes.func; + auto ad = f.isMemberDecl(); + assert(ad); + foreach (i, v; ad.fields) { - FuncDeclaration f = func; - if (fes) - f = fes.func; - auto ad = f.isMemberDecl(); - assert(ad); - foreach (i, v; ad.fields) + bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested()); + auto fieldInit = &this.ctorflow.fieldinit[i]; + const fiesCurrent = fies[i]; + if (fieldInit.loc is Loc.init) + fieldInit.loc = fiesCurrent.loc; + if (!mergeFieldInit(this.ctorflow.fieldinit[i].csx, fiesCurrent.csx) && mustInit) { - bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested()); - auto fieldInit = &this.ctorflow.fieldinit[i]; - const fiesCurrent = fies[i]; - if (fieldInit.loc is Loc.init) - fieldInit.loc = fiesCurrent.loc; - if (!mergeFieldInit(this.ctorflow.fieldinit[i].csx, fiesCurrent.csx) && mustInit) - { - error(loc, "one path skips field `%s`", v.toChars()); - } + error(loc, "one path skips field `%s`", v.toChars()); } } } diff --git a/gcc/d/dmd/dstruct.d b/gcc/d/dmd/dstruct.d index 8b4ac7d88464b57e7f595b819b0b901f330d6a85..d7b1ace34f0a01c01166c9053f03a35944f60abd 100644 --- a/gcc/d/dmd/dstruct.d +++ b/gcc/d/dmd/dstruct.d @@ -23,7 +23,7 @@ import dmd.declaration; import dmd.dmodule; import dmd.dscope; import dmd.dsymbol; -import dmd.dsymbolsem; +import dmd.dsymbolsem : search, setFieldOffset; import dmd.dtemplate; import dmd.errors; import dmd.expression; @@ -37,7 +37,7 @@ import dmd.mtype; import dmd.opover; import dmd.target; import dmd.tokens; -import dmd.typesem; +import dmd.typesem : isZeroInit, merge, size, hasPointers; import dmd.typinf; import dmd.visitor; @@ -66,126 +66,6 @@ FuncDeclaration search_toString(StructDeclaration sd) return fd; } -/*************************************** - * Request additional semantic analysis for TypeInfo generation. - * Params: - * sc = context - * t = type that TypeInfo is being generated for - */ -extern (D) void semanticTypeInfo(Scope* sc, Type t) -{ - if (sc) - { - if (sc.intypeof) - return; - if (!sc.needsCodegen()) - return; - } - - if (!t) - return; - - void visitVector(TypeVector t) - { - semanticTypeInfo(sc, t.basetype); - } - - void visitAArray(TypeAArray t) - { - semanticTypeInfo(sc, t.index); - semanticTypeInfo(sc, t.next); - } - - void visitStruct(TypeStruct t) - { - //printf("semanticTypeInfo.visit(TypeStruct = %s)\n", t.toChars()); - StructDeclaration sd = t.sym; - - /* Step 1: create TypeInfoDeclaration - */ - if (!sc) // inline may request TypeInfo. - { - Scope scx; - scx.eSink = global.errorSink; - scx._module = sd.getModule(); - getTypeInfoType(sd.loc, t, &scx); - sd.requestTypeInfo = true; - } - else if (!sc.minst) - { - // don't yet have to generate TypeInfo instance if - // the typeid(T) expression exists in speculative scope. - } - else - { - getTypeInfoType(sd.loc, t, sc); - sd.requestTypeInfo = true; - - // https://issues.dlang.org/show_bug.cgi?id=15149 - // if the typeid operand type comes from a - // result of auto function, it may be yet speculative. - // unSpeculative(sc, sd); - } - - /* Step 2: If the TypeInfo generation requires sd.semantic3, run it later. - * This should be done even if typeid(T) exists in speculative scope. - * Because it may appear later in non-speculative scope. - */ - if (!sd.members) - return; // opaque struct - if (!sd.xeq && !sd.xcmp && !sd.postblit && !sd.tidtor && !sd.xhash && !search_toString(sd)) - return; // none of TypeInfo-specific members - - // If the struct is in a non-root module, run semantic3 to get - // correct symbols for the member function. - if (sd.semanticRun >= PASS.semantic3) - { - // semantic3 is already done - } - else if (TemplateInstance ti = sd.isInstantiated()) - { - if (ti.minst && !ti.minst.isRoot()) - Module.addDeferredSemantic3(sd); - } - else - { - if (sd.inNonRoot()) - { - //printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd.toChars(), sd.inNonRoot()); - Module.addDeferredSemantic3(sd); - } - } - } - - void visitTuple(TypeTuple t) - { - if (t.arguments) - { - foreach (arg; *t.arguments) - { - semanticTypeInfo(sc, arg.type); - } - } - } - - /* Note structural similarity of this Type walker to that in isSpeculativeType() - */ - - Type tb = t.toBasetype(); - switch (tb.ty) - { - case Tvector: visitVector(tb.isTypeVector()); break; - case Taarray: visitAArray(tb.isTypeAArray()); break; - case Tstruct: visitStruct(tb.isTypeStruct()); break; - case Ttuple: visitTuple (tb.isTypeTuple()); break; - - case Tclass: - case Tenum: break; - - default: semanticTypeInfo(sc, tb.nextOf()); break; - } -} - enum StructFlags : int { none = 0x0, @@ -350,8 +230,18 @@ extern (C++) class StructDeclaration : AggregateDeclaration // Determine if struct is all zeros or not zeroInit = true; + auto lastOffset = -1; foreach (vd; fields) { + // First skip zero sized fields + if (vd.type.size(vd.loc) == 0) + continue; + + // only consider first sized member of an (anonymous) union + if (vd.overlapped && vd.offset == lastOffset) + continue; + lastOffset = vd.offset; + if (vd._init) { if (vd._init.isVoidInitializer()) @@ -360,10 +250,6 @@ extern (C++) class StructDeclaration : AggregateDeclaration */ continue; - // Zero size fields are zero initialized - if (vd.type.size(vd.loc) == 0) - continue; - // Examine init to see if it is all 0s. auto exp = vd.getConstInitializer(); if (!exp || !_isZeroInit(exp)) diff --git a/gcc/d/dmd/dsymbol.d b/gcc/d/dmd/dsymbol.d index f5ec1ce1251c9afa60c71a6c76ecf64c26204c68..af32f7afa1e8d5305a31dcccb18a837531aa997d 100644 --- a/gcc/d/dmd/dsymbol.d +++ b/gcc/d/dmd/dsymbol.d @@ -275,13 +275,13 @@ extern (C++) class Dsymbol : ASTNode final extern (D) this() nothrow @safe { //printf("Dsymbol::Dsymbol(%p)\n", this); - loc = Loc(null, 0, 0); + loc = Loc.initial; } final extern (D) this(Identifier ident) nothrow @safe { //printf("Dsymbol::Dsymbol(%p, ident)\n", this); - this.loc = Loc(null, 0, 0); + this.loc = Loc.initial; this.ident = ident; } @@ -344,19 +344,6 @@ extern (C++) class Dsymbol : ASTNode return toChars(); } - final const(Loc) getLoc() - { - if (!loc.isValid()) // avoid bug 5861. - if (const m = getModule()) - return Loc(m.srcfile.toChars(), 0, 0); - return loc; - } - - final const(char)* locToChars() - { - return getLoc().toChars(); - } - override bool equals(const RootObject o) const { if (this == o) @@ -618,7 +605,7 @@ extern (C++) class Dsymbol : ASTNode final Ungag ungagSpeculative() const { - uint oldgag = global.gag; + const oldgag = global.gag; if (global.gag && !isSpeculative() && !toParent2().isFuncDeclaration()) global.gag = 0; return Ungag(oldgag); @@ -1303,7 +1290,7 @@ public: } else { - .error(s1.loc, "%s `%s` conflicts with %s `%s` at %s", s1.kind, s1.toPrettyChars, s2.kind(), s2.toPrettyChars(), s2.locToChars()); + .error(s1.loc, "%s `%s` conflicts with %s `%s` at %s", s1.kind, s1.toPrettyChars, s2.kind(), s2.toPrettyChars(), s2.loc.toChars()); } } diff --git a/gcc/d/dmd/dsymbol.h b/gcc/d/dmd/dsymbol.h index 2c5f0e579b3af7974373056a7a2cdf1c4e0b5e97..7b33ed2910bfe00da252e8813449f702433e28fb 100644 --- a/gcc/d/dmd/dsymbol.h +++ b/gcc/d/dmd/dsymbol.h @@ -139,20 +139,6 @@ enum class PASS : uint8_t obj // toObjFile() run }; -enum -{ - PASSinit, // initial state - PASSsemantic, // semantic() started - PASSsemanticdone, // semantic() done - PASSsemantic2, // semantic2() started - PASSsemantic2done, // semantic2() done - PASSsemantic3, // semantic3() started - PASSsemantic3done, // semantic3() done - PASSinline, // inline started - PASSinlinedone, // inline done - PASSobj // toObjFile() run -}; - /* Flags for symbol search */ typedef unsigned SearchOptFlags; @@ -209,8 +195,6 @@ public: CPPNamespaceDeclaration* cppnamespace(CPPNamespaceDeclaration* ns); UserAttributeDeclaration* userAttribDecl(UserAttributeDeclaration* uad); virtual const char *toPrettyCharsHelper(); // helper to print fully qualified (template) arguments - Loc getLoc(); - const char *locToChars(); bool equals(const RootObject * const o) const override; bool isAnonymous() const; Module *getModule(); @@ -446,6 +430,7 @@ namespace dmd { void addMember(Dsymbol *dsym, Scope *sc, ScopeDsymbol *sds); Dsymbol *search(Dsymbol *d, const Loc &loc, Identifier *ident, SearchOptFlags flags = (SearchOptFlags)SearchOpt::localsOnly); + Dsymbols *include(Dsymbol *d, Scope *sc); void setScope(Dsymbol *d, Scope *sc); void importAll(Dsymbol *d, Scope *sc); } diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d index 2958d955f4fa399136a1556d20616ccc164cbc48..173532af397c810c2b9dcd5df219c84b01fe82bd 100644 --- a/gcc/d/dmd/dsymbolsem.d +++ b/gcc/d/dmd/dsymbolsem.d @@ -67,6 +67,7 @@ import dmd.root.filename; import dmd.common.outbuffer; import dmd.root.rmem; import dmd.rootobject; +import dmd.safe; import dmd.semantic2; import dmd.semantic3; import dmd.sideeffect; @@ -313,7 +314,7 @@ Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool find Loc loc = e.loc; Type tthis = (e.op == EXP.type ? e.type : null); const flags = cast(DotExpFlag) (DotExpFlag.noAliasThis | (gag * DotExpFlag.gag)); - uint olderrors = gag ? global.startGagging() : 0; + const olderrors = gag ? global.startGagging() : 0; e = dotExp(ad.type, sc, e, ad.aliasthis.ident, flags); if (!e || findOnly) return gag && global.endGagging(olderrors) ? null : e; @@ -1434,7 +1435,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor */ if (!inferred) { - uint errors = global.errors; + const errors = global.errors; dsym.inuse++; // Bug 20549. Don't try this on modules or packages, syntaxCopy // could crash (inf. recursion) on a mod/pkg referencing itself @@ -1831,7 +1832,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor { //printf("MixinDeclaration::compileIt(loc = %d) %s\n", cd.loc.linnum, cd.exp.toChars()); OutBuffer buf; - if (expressionsToString(buf, sc, cd.exps)) + if (expressionsToString(buf, sc, cd.exps, cd.loc, null, true)) return null; const errors = global.errors; @@ -1841,7 +1842,6 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor const bool doUnittests = global.params.parsingUnittestsRequired(); auto loc = adjustLocForMixin(str, cd.loc, global.params.mixinOut); scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); - p.transitionIn = global.params.v.vin; p.nextToken(); auto d = p.parseDeclDefs(0); @@ -2214,7 +2214,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor tm.argsym.parent = scy.parent; Scope* argscope = scy.push(tm.argsym); - uint errorsave = global.errors; + const errorsave = global.errors; // Declare each template parameter as an alias for the argument type tm.declareParameters(argscope); @@ -2359,7 +2359,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor ns.semanticRun = PASS.semantic; ns.parent = sc.parent; // Link does not matter here, if the UDA is present it will error - UserAttributeDeclaration.checkGNUABITag(ns, LINK.cpp); + checkGNUABITag(ns, LINK.cpp); if (!ns.members) { @@ -2842,7 +2842,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (sd.semanticRun >= PASS.semanticdone) return; - int errors = global.errors; + const errors = global.errors; //printf("+StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok); Scope* scx = null; @@ -2909,7 +2909,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor return; sd.semanticRun = PASS.semantic; - UserAttributeDeclaration.checkGNUABITag(sd, sc.linkage); + checkGNUABITag(sd, sc.linkage); if (!sd.members) // if opaque declaration { @@ -3004,7 +3004,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor Dsymbol scall = sd.search(Loc.initial, Id.call); if (scall) { - uint xerrors = global.startGagging(); + const xerrors = global.startGagging(); sc = sc.push(); sc.tinst = null; sc.minst = null; @@ -3089,7 +3089,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (cldec.semanticRun >= PASS.semanticdone) return; - int errors = global.errors; + const errors = global.errors; //printf("+ClassDeclaration.dsymbolSemantic(%s), type = %p, sizeok = %d, this = %p\n", toChars(), type, sizeok, this); @@ -3149,7 +3149,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor return; } cldec.semanticRun = PASS.semantic; - UserAttributeDeclaration.checkGNUABITag(cldec, sc.linkage); + checkGNUABITag(cldec, sc.linkage); checkMustUseReserved(cldec); if (cldec.baseok < Baseok.done) @@ -3752,7 +3752,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor //printf("InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type); if (idec.semanticRun >= PASS.semanticdone) return; - int errors = global.errors; + const errors = global.errors; //printf("+InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type); @@ -3861,7 +3861,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (!idec.baseclasses.length && sc.linkage == LINK.cpp) idec.classKind = ClassKind.cpp; idec.cppnamespace = sc.namespace; - UserAttributeDeclaration.checkGNUABITag(idec, sc.linkage); + checkGNUABITag(idec, sc.linkage); checkMustUseReserved(idec); if (sc.linkage == LINK.objc) @@ -4082,7 +4082,7 @@ private extern(C++) class AddMemberVisitor : Visitor Scope* sc; ScopeDsymbol sds; - this(Scope* sc, ScopeDsymbol sds) + this(Scope* sc, ScopeDsymbol sds) @safe { this.sc = sc; this.sds = sds; @@ -4715,6 +4715,15 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList visit(cast(Dsymbol)sds); } + override void visit(StructDeclaration sd) + { + // need to visit auto-generated methods as well + if (sd.xeq) visit(sd.xeq); + if (sd.xcmp) visit(sd.xcmp); + if (sd.xhash) visit(sd.xhash); + visit(cast(ScopeDsymbol)sd); + } + override void visit(AttribDeclaration ad) { ad.include(null).foreachDsymbol( s => s.accept(this) ); @@ -4762,7 +4771,7 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList printf("\timplement template instance %s '%s'\n", tempdecl.parent.toChars(), tempinst.toChars()); printf("\ttempdecl %s\n", tempdecl.toChars()); } - uint errorsave = global.errors; + const errorsave = global.errors; tempinst.inst = tempinst; tempinst.parent = tempinst.enclosing ? tempinst.enclosing : tempdecl.parent; @@ -5927,67 +5936,6 @@ private void writeMixin(const(char)[] s, ref const Loc loc, ref int lines, ref O ++lines; } -/** - * Check signature of `pragma(printf)` function, print error if invalid. - * - * printf/scanf-like functions must be of the form: - * extern (C/C++) T printf([parameters...], const(char)* format, ...); - * or: - * extern (C/C++) T vprintf([parameters...], const(char)* format, va_list); - * - * Params: - * funcdecl = function to check - * f = function type - * sc = scope - */ -void checkPrintfScanfSignature(FuncDeclaration funcdecl, TypeFunction f, Scope* sc) -{ - static bool isPointerToChar(Parameter p) - { - if (auto tptr = p.type.isTypePointer()) - { - return tptr.next.ty == Tchar; - } - return false; - } - - bool isVa_list(Parameter p) - { - return p.type.equals(target.va_listType(funcdecl.loc, sc)); - } - - const nparams = f.parameterList.length; - const p = (funcdecl.printf ? Id.printf : Id.scanf).toChars(); - if (!(f.linkage == LINK.c || f.linkage == LINK.cpp)) - { - .error(funcdecl.loc, "`pragma(%s)` function `%s` must have `extern(C)` or `extern(C++)` linkage," - ~" not `extern(%s)`", - p, funcdecl.toChars(), f.linkage.linkageToChars()); - } - if (f.parameterList.varargs == VarArg.variadic) - { - if (!(nparams >= 1 && isPointerToChar(f.parameterList[nparams - 1]))) - { - .error(funcdecl.loc, "`pragma(%s)` function `%s` must have" - ~ " signature `%s %s([parameters...], const(char)*, ...)` not `%s`", - p, funcdecl.toChars(), f.next.toChars(), funcdecl.toChars(), funcdecl.type.toChars()); - } - } - else if (f.parameterList.varargs == VarArg.none) - { - if(!(nparams >= 2 && isPointerToChar(f.parameterList[nparams - 2]) && - isVa_list(f.parameterList[nparams - 1]))) - .error(funcdecl.loc, "`pragma(%s)` function `%s` must have"~ - " signature `%s %s([parameters...], const(char)*, va_list)`", - p, funcdecl.toChars(), f.next.toChars(), funcdecl.toChars()); - } - else - { - .error(funcdecl.loc, "`pragma(%s)` function `%s` must have C-style variadic `...` or `va_list` parameter", - p, funcdecl.toChars()); - } -} - /********************************************* * Search for ident as member of d. * Params: @@ -6045,7 +5993,7 @@ private extern(C++) class SearchVisitor : Visitor SearchOptFlags flags; Dsymbol result; - this(const ref Loc loc, Identifier ident, SearchOptFlags flags) + this(const ref Loc loc, Identifier ident, SearchOptFlags flags) @safe { this.loc = loc; this.ident = ident; @@ -6522,7 +6470,7 @@ private extern(C++) class SearchVisitor : Visitor return setResult(m.searchCacheSymbol); } - uint errors = global.errors; + const errors = global.errors; m.insearch = true; visit(cast(ScopeDsymbol)m); @@ -6651,7 +6599,7 @@ private extern(C++) class SetScopeVisitor : Visitor alias visit = typeof(super).visit; Scope* sc; - this(Scope* sc) + this(Scope* sc) @safe { this.sc = sc; } @@ -6794,7 +6742,7 @@ extern(C++) class ImportAllVisitor : Visitor alias visit = typeof(super).visit; Scope* sc; - this(Scope* sc) + this(Scope* sc) @safe { this.sc = sc; } @@ -6873,7 +6821,7 @@ extern(C++) class ImportAllVisitor : Visitor (*m.members)[0].ident != Id.object || (*m.members)[0].isImport() is null)) { - auto im = new Import(Loc.initial, null, Id.object, null, 0); + auto im = new Import(m.loc, null, Id.object, null, 0); m.members.shift(im); } if (!m.symtab) @@ -6960,7 +6908,7 @@ extern (D) bool load(Import imp, Scope* sc) { if (p.isPkgMod == PKG.unknown) { - uint preverrors = global.errors; + const preverrors = global.errors; imp.mod = Module.load(imp.loc, imp.packages, imp.id); if (!imp.mod) p.isPkgMod = PKG.package_; @@ -7037,7 +6985,7 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor FieldState* fieldState; bool isunion; - this(AggregateDeclaration ad, FieldState* fieldState, bool isunion) + this(AggregateDeclaration ad, FieldState* fieldState, bool isunion) @safe { this.ad = ad; this.fieldState = fieldState; @@ -7126,7 +7074,7 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor assert(sz != SIZE_INVALID && sz < uint.max); uint memsize = cast(uint)sz; // size of member uint memalignsize = target.fieldalign(t); // size of member for alignment purposes - vd.offset = placeField( + vd.offset = placeField(vd.loc, fieldState.offset, memsize, memalignsize, vd.alignment, ad.structsize, ad.alignsize, @@ -7173,12 +7121,19 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor error(bfd.loc, "bit field width %d is larger than type", bfd.fieldWidth); const style = target.c.bitFieldStyle; + if (style != TargetC.BitFieldStyle.MS && style != TargetC.BitFieldStyle.Gcc_Clang) + assert(0, "unsupported bit-field style"); + + const isMicrosoftStyle = style == TargetC.BitFieldStyle.MS; + const contributesToAggregateAlignment = target.c.contributesToAggregateAlignment(bfd); void startNewField() { if (log) printf("startNewField()\n"); uint alignsize; - if (style == TargetC.BitFieldStyle.Gcc_Clang) + if (isMicrosoftStyle) + alignsize = memsize; // not memalignsize + else { if (bfd.fieldWidth > 32) alignsize = memalignsize; @@ -7189,15 +7144,13 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor else alignsize = 1; } - else - alignsize = memsize; // not memalignsize uint dummy; - bfd.offset = placeField( + bfd.offset = placeField(bfd.loc, fieldState.offset, memsize, alignsize, bfd.alignment, ad.structsize, - (anon && style == TargetC.BitFieldStyle.Gcc_Clang) ? dummy : ad.alignsize, + contributesToAggregateAlignment ? ad.alignsize : dummy, isunion); fieldState.inFlight = true; @@ -7206,45 +7159,30 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor fieldState.fieldSize = memsize; } - if (style == TargetC.BitFieldStyle.Gcc_Clang) + if (ad.alignsize == 0) + ad.alignsize = 1; + if (!isMicrosoftStyle && contributesToAggregateAlignment && ad.alignsize < memalignsize) + ad.alignsize = memalignsize; + + if (bfd.fieldWidth == 0) { - if (bfd.fieldWidth == 0) + if (!isMicrosoftStyle && !isunion) { - if (!isunion) - { - // Use type of zero width field to align to next field - fieldState.offset = (fieldState.offset + memalignsize - 1) & ~(memalignsize - 1); - ad.structsize = fieldState.offset; - } - - fieldState.inFlight = false; - return; + // Use type of zero width field to align to next field + fieldState.offset = (fieldState.offset + memalignsize - 1) & ~(memalignsize - 1); + ad.structsize = fieldState.offset; } - - if (ad.alignsize == 0) - ad.alignsize = 1; - if (!anon && - ad.alignsize < memalignsize) - ad.alignsize = memalignsize; - } - else if (style == TargetC.BitFieldStyle.MS) - { - if (ad.alignsize == 0) - ad.alignsize = 1; - if (bfd.fieldWidth == 0) + else if (isMicrosoftStyle && fieldState.inFlight && !isunion) { - if (fieldState.inFlight && !isunion) - { - // documentation says align to next int - //const alsz = cast(uint)Type.tint32.size(); - const alsz = memsize; // but it really does this - fieldState.offset = (fieldState.offset + alsz - 1) & ~(alsz - 1); - ad.structsize = fieldState.offset; - } - - fieldState.inFlight = false; - return; + // documentation says align to next int + //const alsz = cast(uint)Type.tint32.size(); + const alsz = memsize; // but it really does this + fieldState.offset = (fieldState.offset + alsz - 1) & ~(alsz - 1); + ad.structsize = fieldState.offset; } + + fieldState.inFlight = false; + return; } if (!fieldState.inFlight) @@ -7252,7 +7190,7 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor //printf("not in flight\n"); startNewField(); } - else if (style == TargetC.BitFieldStyle.Gcc_Clang) + else if (!isMicrosoftStyle) { // If the bit-field spans more units of alignment than its type // and is at the alignment boundary, start a new field at the @@ -7277,7 +7215,7 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor } } } - else if (style == TargetC.BitFieldStyle.MS) + else { if (memsize != fieldState.fieldSize || fieldState.bitOffset + bfd.fieldWidth > fieldState.fieldSize * 8) @@ -7286,14 +7224,14 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor startNewField(); } } - else - assert(0); bfd.offset = fieldState.fieldOffset; bfd.bitOffset = fieldState.bitOffset; const pastField = bfd.bitOffset + bfd.fieldWidth; - if (style == TargetC.BitFieldStyle.Gcc_Clang) + if (isMicrosoftStyle) + fieldState.fieldSize = memsize; + else { auto size = (pastField + 7) / 8; fieldState.fieldSize = size; @@ -7307,8 +7245,6 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor else ad.structsize = bfd.offset + size; } - else - fieldState.fieldSize = memsize; //printf("at end: ad.structsize = %d\n", cast(int)ad.structsize); //print(fieldState); @@ -7395,7 +7331,7 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor /* Given the anon 'member's size and alignment, * go ahead and place it. */ - anond.anonoffset = placeField( + anond.anonoffset = placeField(anond.loc, fieldState.offset, anond.anonstructsize, anond.anonalignsize, alignment, ad.structsize, ad.alignsize, @@ -7412,3 +7348,500 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor } } } + +extern(D) Scope* newScope(Dsymbol d, Scope* sc) +{ + scope nsv = new NewScopeVisitor(sc); + d.accept(nsv); + return nsv.sc; +} + +private extern(C++) class NewScopeVisitor : Visitor +{ + alias visit = typeof(super).visit; + Scope* sc; + this(Scope* sc) + { + this.sc = sc; + } + + /**************************************** + * A hook point to supply scope for members. + * addMember, setScope, importAll, semantic, semantic2 and semantic3 will use this. + */ + override void visit(AttribDeclaration dc){} + + override void visit(StorageClassDeclaration swt) + { + StorageClass scstc = sc.stc; + /* These sets of storage classes are mutually exclusive, + * so choose the innermost or most recent one. + */ + if (swt.stc & (STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.manifest)) + scstc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.manifest); + if (swt.stc & (STC.auto_ | STC.scope_ | STC.static_ | STC.manifest | STC.gshared)) + scstc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.manifest | STC.gshared); + if (swt.stc & (STC.const_ | STC.immutable_ | STC.manifest)) + scstc &= ~(STC.const_ | STC.immutable_ | STC.manifest); + if (swt.stc & (STC.gshared | STC.shared_)) + scstc &= ~(STC.gshared | STC.shared_); + if (swt.stc & (STC.safe | STC.trusted | STC.system)) + scstc &= ~(STC.safe | STC.trusted | STC.system); + scstc |= swt.stc; + //printf("scstc = x%llx\n", scstc); + sc = swt.createNewScope(sc, scstc, sc.linkage, sc.cppmangle, + sc.visibility, sc.explicitVisibility, sc.aligndecl, sc.inlining); + } + + /** + * Provides a new scope with `STC.deprecated_` and `Scope.depdecl` set + * + * Calls `StorageClassDeclaration.newScope` (as it must be called or copied + * in any function overriding `newScope`), then set the `Scope`'s depdecl. + * + * Returns: + * Always a new scope, to use for this `DeprecatedDeclaration`'s members. + */ + override void visit(DeprecatedDeclaration dpd) + { + auto oldsc = sc; + visit((cast(StorageClassDeclaration)dpd)); + auto scx = sc; + sc = oldsc; + // The enclosing scope is deprecated as well + if (scx == sc) + scx = sc.push(); + scx.depdecl = dpd; + sc = scx; + } + + override void visit(LinkDeclaration lid) + { + sc= lid.createNewScope(sc, sc.stc, lid.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility, + sc.aligndecl, sc.inlining); + } + + override void visit(CPPMangleDeclaration cpmd) + { + sc = cpmd.createNewScope(sc, sc.stc, LINK.cpp, cpmd.cppmangle, sc.visibility, sc.explicitVisibility, + sc.aligndecl, sc.inlining); + } + + /** + * Returns: + * A copy of the parent scope, with `this` as `namespace` and C++ linkage + *///override Scope* visit(Scope* sc) + override void visit(CPPNamespaceDeclaration scd) + { + auto scx = sc.copy(); + scx.linkage = LINK.cpp; + scx.namespace = scd; + sc = scx; + } + + override void visit(VisibilityDeclaration atbd) + { + if (atbd.pkg_identifiers) + dsymbolSemantic(atbd, sc); + + sc = atbd.createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, atbd.visibility, 1, sc.aligndecl, sc.inlining); + } + + override void visit(AlignDeclaration visd) + { + sc = visd.createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, sc.visibility, + sc.explicitVisibility, visd, sc.inlining); + } + + override void visit(PragmaDeclaration prd) + { + if (prd.ident == Id.Pinline) + { + // We keep track of this pragma inside scopes, + // then it's evaluated on demand in function semantic + sc = prd.createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility, sc.aligndecl, prd); // @suppress(dscanner.style.long_line) + } + } + + /************************************** + * Use the ForwardingScopeDsymbol as the parent symbol for members. + */ + override void visit(ForwardingAttribDeclaration fad) + { + sc = sc.push(fad.sym); + } + + override void visit(UserAttributeDeclaration uac) + { + Scope* sc2 = sc; + if (uac.atts && uac.atts.length) + { + // create new one for changes + sc2 = sc.copy(); + sc2.userAttribDecl = uac; + } + sc = sc2; + } +} + + +extern(C++) Dsymbols* include(Dsymbol d, Scope* sc) +{ + scope icv = new IncludeVisitor(sc); + d.accept(icv); + return icv.symbols; +} + +extern(C++) class IncludeVisitor : Visitor +{ + alias visit = typeof(super).visit; + Scope* sc; + Dsymbols* symbols; + this(Scope* sc) + { + this.sc = sc; + } + + override void visit(AttribDeclaration ad) + { + if (ad.errors) + { + symbols = null; + return; + } + symbols = ad.decl; + return; + } + +// Decide if 'then' or 'else' code should be included + override void visit(ConditionalDeclaration cdc) + { + //printf("ConditionalDeclaration::include(sc = %p) scope = %p\n", sc, _scope); + + if (cdc.errors) + { + symbols = null; + return; + } + assert(cdc.condition); + symbols = cdc.condition.include(cdc._scope ? cdc._scope : sc) ? cdc.decl : cdc.elsedecl; + } + + override void visit(StaticIfDeclaration sif) + { + /**************************************** + * Different from other AttribDeclaration subclasses, include() call requires + * the completion of addMember and setScope phases. + */ + //printf("StaticIfDeclaration::include(sc = %p) scope = %p\n", sc, _scope); + if (sif.errors || sif.onStack) + { + symbols = null; + return; + } + sif.onStack = true; + scope(exit) sif.onStack = false; + + if (sc && sif.condition.inc == Include.notComputed) + { + assert(sif.scopesym); // addMember is already done + assert(sif._scope); // setScope is already done + + Scope* saved_scope = sc; + sc = sif._scope; + visit(cast(ConditionalDeclaration) sif); + Dsymbols* d = symbols; + sc = saved_scope; + + if (d && !sif.addisdone) + { + // Add members lazily. + d.foreachDsymbol( s => s.addMember(sif._scope, sif.scopesym) ); + + // Set the member scopes lazily. + d.foreachDsymbol( s => s.setScope(sif._scope) ); + + sif.addisdone = true; + } + symbols = d; + return; + } + else + { + visit(cast(ConditionalDeclaration)sif); + } + } + + override void visit(StaticForeachDeclaration sfd) + { + if (sfd.errors || sfd.onStack) + { + symbols = null; + return; + } + if (sfd.cached) + { + assert(!sfd.onStack); + symbols = sfd.cache; + return; + } + sfd.onStack = true; + scope(exit) sfd.onStack = false; + + if (sfd._scope) + { + sfd.sfe.prepare(sfd._scope); // lower static foreach aggregate + } + if (!sfd.sfe.ready()) + { + symbols = null;// TODO: ok? + return; + } + + // expand static foreach + import dmd.statementsem: makeTupleForeach; + Dsymbols* d = makeTupleForeach(sfd._scope, true, true, sfd.sfe.aggrfe, sfd.decl, sfd.sfe.needExpansion).decl; + if (d) // process generated declarations + { + // Add members lazily. + d.foreachDsymbol( s => s.addMember(sfd._scope, sfd.scopesym) ); + + // Set the member scopes lazily. + d.foreachDsymbol( s => s.setScope(sfd._scope) ); + } + sfd.cached = true; + sfd.cache = d; + symbols = d; + } +} + +/** + * Called from a symbol's semantic to check if `gnuAbiTag` UDA + * can be applied to them + * + * Directly emits an error if the UDA doesn't work with this symbol + * + * Params: + * sym = symbol to check for `gnuAbiTag` + * linkage = Linkage of the symbol (Declaration.link or sc.link) + */ +void checkGNUABITag(Dsymbol sym, LINK linkage) +{ + if (global.params.cplusplus < CppStdRevision.cpp11) + return; + + foreachUdaNoSemantic(sym, (exp) { + if (!isGNUABITag(exp)) + return 0; // continue + if (sym.isCPPNamespaceDeclaration() || sym.isNspace()) + { + .error(exp.loc, "`@%s` cannot be applied to namespaces", Id.udaGNUAbiTag.toChars()); + sym.errors = true; + } + else if (linkage != LINK.cpp) + { + .error(exp.loc, "`@%s` can only apply to C++ symbols", Id.udaGNUAbiTag.toChars()); + sym.errors = true; + } + // Only one `@gnuAbiTag` is allowed by semantic2 + return 1; // break + }); +} + +/** + * Check if the provided expression references `core.attribute.gnuAbiTag` + * + * This should be called after semantic has been run on the expression. + * Semantic on UDA happens in semantic2 (see `dmd.semantic2`). + * + * Params: + * e = Expression to check (usually from `UserAttributeDeclaration.atts`) + * + * Returns: + * `true` if the expression references the compiler-recognized `gnuAbiTag` + */ +bool isGNUABITag(Expression e) +{ + if (global.params.cplusplus < CppStdRevision.cpp11) + return false; + + auto ts = e.type ? e.type.isTypeStruct() : null; + if (!ts) + return false; + if (ts.sym.ident != Id.udaGNUAbiTag || !ts.sym.parent) + return false; + // Can only be defined in druntime + Module m = ts.sym.parent.isModule(); + if (!m || !m.isCoreModule(Id.attribute)) + return false; + return true; +} + +/****************************************** + * If a variable has a scope destructor call, return call for it. + * Otherwise, return NULL. + */ +private Expression callScopeDtor(VarDeclaration vd, Scope* sc) +{ + //printf("VarDeclaration::callScopeDtor() %s\n", toChars()); + + // Destruction of STC.field's is handled by buildDtor() + if (vd.storage_class & (STC.nodtor | STC.ref_ | STC.out_ | STC.field)) + { + return null; + } + + if (vd.iscatchvar) + return null; // destructor is built by `void semantic(Catch c, Scope* sc)`, not here + + Expression e = null; + // Destructors for structs and arrays of structs + Type tv = vd.type.baseElemOf(); + if (tv.ty == Tstruct) + { + StructDeclaration sd = (cast(TypeStruct)tv).sym; + if (!sd.dtor || sd.errors) + return null; + + const sz = vd.type.size(); + assert(sz != SIZE_INVALID); + if (!sz) + return null; + + if (vd.type.toBasetype().ty == Tstruct) + { + // v.__xdtor() + e = new VarExp(vd.loc, vd); + + /* This is a hack so we can call destructors on const/immutable objects. + * Need to add things like "const ~this()" and "immutable ~this()" to + * fix properly. + */ + e.type = e.type.mutableOf(); + + // Enable calling destructors on shared objects. + // The destructor is always a single, non-overloaded function, + // and must serve both shared and non-shared objects. + e.type = e.type.unSharedOf; + + e = new DotVarExp(vd.loc, e, sd.dtor, false); + e = new CallExp(vd.loc, e); + } + else + { + // __ArrayDtor(v[0 .. n]) + e = new VarExp(vd.loc, vd); + + const sdsz = sd.type.size(); + assert(sdsz != SIZE_INVALID && sdsz != 0); + const n = sz / sdsz; + SliceExp se = new SliceExp(vd.loc, e, new IntegerExp(vd.loc, 0, Type.tsize_t), + new IntegerExp(vd.loc, n, Type.tsize_t)); + + // Prevent redundant bounds check + se.upperIsInBounds = true; + se.lowerIsLessThanUpper = true; + + // This is a hack so we can call destructors on const/immutable objects. + se.type = sd.type.arrayOf(); + + e = new CallExp(vd.loc, new IdentifierExp(vd.loc, Id.__ArrayDtor), se); + } + return e; + } + // Destructors for classes + if (!(vd.storage_class & (STC.auto_ | STC.scope_) && !(vd.storage_class & STC.parameter))) + return null; + + for (ClassDeclaration cd = vd.type.isClassHandle(); cd; cd = cd.baseClass) + { + /* We can do better if there's a way with onstack + * classes to determine if there's no way the monitor + * could be set. + */ + //if (cd.isInterfaceDeclaration()) + // error("interface `%s` cannot be scope", cd.toChars()); + + if (!vd.onstack) // if any destructors + continue; + // delete'ing C++ classes crashes (and delete is deprecated anyway) + if (cd.classKind == ClassKind.cpp) + { + // Don't call non-existant dtor + if (!cd.dtor) + break; + + e = new VarExp(vd.loc, vd); + e.type = e.type.mutableOf().unSharedOf(); // Hack for mutable ctor on immutable instances + e = new DotVarExp(vd.loc, e, cd.dtor, false); + e = new CallExp(vd.loc, e); + break; + } + + // delete this; + Expression ec; + ec = new VarExp(vd.loc, vd); + e = new DeleteExp(vd.loc, ec, true); + e.type = Type.tvoid; + break; + } + return e; +} + +/*************************************** + * Collect all instance fields, then determine instance size. + * Returns: + * false if failed to determine the size. + */ +bool determineSize(AggregateDeclaration ad, const ref Loc loc) +{ + //printf("AggregateDeclaration::determineSize() %s, sizeok = %d\n", toChars(), sizeok); + + // The previous instance size finalizing had: + if (ad.type.ty == Terror || ad.errors) + return false; // failed already + if (ad.sizeok == Sizeok.done) + return true; // succeeded + + if (!ad.members) + { + .error(loc, "%s `%s` unknown size", ad.kind, ad.toPrettyChars); + return false; + } + + if (ad._scope) + dsymbolSemantic(ad, null); + + // Determine the instance size of base class first. + if (auto cd = ad.isClassDeclaration()) + { + cd = cd.baseClass; + if (cd && !cd.determineSize(loc)) + goto Lfail; + } + + // Determine instance fields when sizeok == Sizeok.none + if (!ad.determineFields()) + goto Lfail; + if (ad.sizeok != Sizeok.done) + ad.finalizeSize(); + + // this aggregate type has: + if (ad.type.ty == Terror) + return false; // marked as invalid during the finalizing. + if (ad.sizeok == Sizeok.done) + return true; // succeeded to calculate instance size. + +Lfail: + // There's unresolvable forward reference. + if (ad.type != Type.terror) + error(loc, "%s `%s` no size because of forward reference", ad.kind, ad.toPrettyChars); + // Don't cache errors from speculative semantic, might be resolvable later. + // https://issues.dlang.org/show_bug.cgi?id=16574 + if (!global.gag) + { + ad.type = Type.terror; + ad.errors = true; + } + return false; +} diff --git a/gcc/d/dmd/dtemplate.d b/gcc/d/dmd/dtemplate.d index fe601207408fba8181a5616d3a482ca2cd70597c..d46e4661a0874a82cd5e28867d6073f1e144549c 100644 --- a/gcc/d/dmd/dtemplate.d +++ b/gcc/d/dmd/dtemplate.d @@ -50,36 +50,36 @@ import dmd.dcast; import dmd.dclass; import dmd.declaration; import dmd.dinterpret; -import dmd.dmangle; import dmd.dmodule; import dmd.dscope; import dmd.dsymbol; -import dmd.dsymbolsem; +import dmd.dsymbolsem : dsymbolSemantic, checkDeprecated, aliasSemantic, search, search_correct, setScope, importAll, include; import dmd.errors; import dmd.errorsink; import dmd.expression; -import dmd.expressionsem; +import dmd.expressionsem : resolveLoc, expressionSemantic, resolveProperties; import dmd.func; -import dmd.funcsem; +import dmd.funcsem : functionSemantic, leastAsSpecialized, overloadApply; import dmd.globals; import dmd.hdrgen; import dmd.id; import dmd.identifier; import dmd.impcnvtab; import dmd.init; -import dmd.initsem; import dmd.location; +import dmd.mangle; import dmd.mtype; import dmd.opover; import dmd.optimize; import dmd.root.array; import dmd.common.outbuffer; import dmd.rootobject; -import dmd.semantic2; -import dmd.semantic3; -import dmd.templatesem; +import dmd.semantic3 : semantic3; +import dmd.templatesem : matchWithInstance, formatParamsWithTiargs, leastAsSpecialized, declareParameter; import dmd.tokens; -import dmd.typesem; +import dmd.typesem : hasPointers, typeSemantic, merge, merge2, resolve, toDsymbol, + addStorageClass, isBaseOf, equivalent, sarrayOf, constOf, mutableOf, unSharedOf, + unqualify, aliasthisOf, castMod, substWildTo, addMod; import dmd.visitor; import dmd.templateparamsem; @@ -1695,7 +1695,9 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, ref TemplateParameters pa edim = s ? getValue(s) : getValue(e); } } - if (tp && tp.matchArg(sc, t.dim, i, ¶meters, dedtypes, null) || edim && edim.toInteger() == t.dim.toInteger()) + 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; @@ -3235,10 +3237,6 @@ extern (C++) class TemplateParameter : ASTNode return DYNCAST.templateparameter; } - /* Create dummy argument based on parameter. - */ - abstract RootObject dummyArg(); - override void accept(Visitor v) { v.visit(this); @@ -3319,19 +3317,6 @@ extern (C++) class TemplateTypeParameter : TemplateParameter return defaultType !is null; } - override final RootObject dummyArg() - { - Type t = specType; - if (!t) - { - // Use this for alias-parameter's too (?) - if (!tdummy) - tdummy = new TypeIdentifier(loc, ident); - t = tdummy; - } - return t; - } - override void accept(Visitor v) { v.visit(this); @@ -3439,34 +3424,35 @@ extern (C++) final class TemplateValueParameter : TemplateParameter override RootObject defaultArg(const ref Loc instLoc, Scope* sc) { Expression e = defaultValue; - if (e) - { - 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()) + 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) { - 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(); - } + // 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); } + if ((e = resolveProperties(sc, e)) is null) + return null; + e = e.resolveLoc(instLoc, sc); // use the instantiated loc + e = e.optimize(WANTvalue); + return e; } @@ -3475,24 +3461,6 @@ extern (C++) final class TemplateValueParameter : TemplateParameter return defaultValue !is null; } - override RootObject dummyArg() - { - Expression e = specValue; - if (!e) - { - // Create a dummy value - auto pe = cast(void*)valType in edummies; - if (!pe) - { - e = valType.defaultInit(Loc.initial); - edummies[cast(void*)valType] = e; - } - else - e = *pe; - } - return e; - } - override void accept(Visitor v) { v.visit(this); @@ -3578,18 +3546,6 @@ extern (C++) final class TemplateAliasParameter : TemplateParameter return defaultAlias !is null; } - override RootObject dummyArg() - { - RootObject s = specAlias; - if (!s) - { - if (!sdummy) - sdummy = new Dsymbol(); - s = sdummy; - } - return s; - } - override void accept(Visitor v) { v.visit(this); @@ -3667,11 +3623,6 @@ extern (C++) final class TemplateTupleParameter : TemplateParameter return false; } - override RootObject dummyArg() - { - return null; - } - override void accept(Visitor v) { v.visit(this); @@ -3704,7 +3655,22 @@ extern (C++) class TemplateInstance : ScopeDsymbol 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 - TemplateInstance inst; // refer to existing instance + + /** + 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() @@ -3716,7 +3682,15 @@ extern (C++) class TemplateInstance : ScopeDsymbol TemplateInstances* deferred; - Module memberOf; // if !null, then this TemplateInstance appears in memberOf.members[] + /** + 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. @@ -3914,7 +3888,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol // 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.warnings == DiagnosticReporting.error) || + (cl == Classification.warning && global.params.useWarnings == DiagnosticReporting.error) || (cl == Classification.deprecation && global.params.useDeprecated == DiagnosticReporting.error)) cur.errors = true; @@ -3998,66 +3972,63 @@ extern (C++) class TemplateInstance : ScopeDsymbol if (enclosing != ti.enclosing) { //printf("test2 enclosing %s ti.enclosing %s\n", enclosing ? enclosing.toChars() : "", ti.enclosing ? ti.enclosing.toChars() : ""); - goto Lnotequals; + return false; } //printf("parent = %s, ti.parent = %s\n", parent.toPrettyChars(), ti.parent.toPrettyChars()); if (!arrayObjectMatch(tdtypes, ti.tdtypes)) - goto Lnotequals; + return false; /* Template functions may have different instantiations based on * "auto ref" parameters. */ - if (auto fd = ti.toAlias().isFuncDeclaration()) - { - if (!fd.errors) - { - auto resolvedArgs = fd.type.isTypeFunction().resolveNamedArgs( - ArgumentList(this.fargs, this.fnames), null); + auto fd = ti.toAlias().isFuncDeclaration(); + if (!fd) + return true; + if (fd.errors) + return true; - // 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 resolvedArgs = fd.type.isTypeFunction().resolveNamedArgs( + ArgumentList(this.fargs, this.fnames), null); - Expression[] args = resolvedArgs ? (*resolvedArgs)[] : []; + // 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 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" - { - 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) - goto Lnotequals; - if (farg.isLvalue()) - { - if (!(fparam.storageClass & STC.ref_)) - goto Lnotequals; // auto ref's don't match - } - else - { - if (fparam.storageClass & STC.ref_) - goto Lnotequals; // auto ref's don't match - } - } - } + 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; - - Lnotequals: - return false; } extern (D) final size_t toHash() @@ -4209,77 +4180,75 @@ extern (C++) class TemplateInstance : ScopeDsymbol // Elide codegen because there's no instantiation from any root modules. return false; } - else - { - // 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) + // 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) { - // 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 { - 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; - } + 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; + // Elide codegen if `this` doesn't need it. + if (tithis.minst && !tithis.minst.isRoot() && !tithis.minst.rootImports()) + return ThreeState.no; - return ThreeState.none; - } + return ThreeState.none; + } - if (const needsCodegen = needsCodegenRootOnly(this, tinst)) - return needsCodegen == ThreeState.yes ? true : false; + 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) + // 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 { - const needsCodegen = needsCodegenRootOnly(tnext, tnext.tinst); // sets tnext.minst - if (tnext.minst) // not speculative + if (needsCodegen == ThreeState.no) { - 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; + 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; } + + // 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; } /********************************************** @@ -4538,9 +4507,13 @@ extern (C++) class TemplateInstance : ScopeDsymbol // 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++) { RootObject o = (*tiargs)[j]; @@ -4634,7 +4607,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol { if (ea.checkValue()) // check void expression ea = ErrorExp.get(); - uint olderrs = global.errors; + const olderrs = global.errors; ea = ea.ctfeInterpret(); if (global.errors != olderrs) ea = ErrorExp.get(); @@ -4846,7 +4819,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol printf("TemplateInstance.findBestMatch()\n"); } - uint errs = global.errors; + const errs = global.errors; TemplateDeclaration td_last = null; Objects dedtypes; @@ -5061,135 +5034,6 @@ extern (C++) class TemplateInstance : ScopeDsymbol return (errs == global.errors); } - /***************************************************** - * Determine if template instance is really a template function, - * and that template function needs to infer types from the function - * arguments. - * - * Like findBestMatch, iterate possible template candidates, - * but just looks only the necessity of type inference. - */ - extern (D) final bool needsTypeInference(Scope* sc, int flag = 0) - { - //printf("TemplateInstance.needsTypeInference() %s\n", toChars()); - if (semanticRun != PASS.initial) - return false; - - uint olderrs = global.errors; - Objects dedtypes; - size_t count = 0; - - 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 any of the overloaded template declarations need inference, - * then return true - */ - if (!td.onemember) - return 0; - if (auto td2 = td.onemember.isTemplateDeclaration()) - { - if (!td2.onemember || !td2.onemember.isFuncDeclaration()) - return 0; - if (tiargs.length >= td.parameters.length - (td.isVariadic() ? 1 : 0)) - return 0; - return 1; - } - auto fd = td.onemember.isFuncDeclaration(); - if (!fd || fd.type.ty != Tfunction) - return 0; - - foreach (tp; *td.parameters) - { - if (tp.isTemplateThisParameter()) - return 1; - } - - /* Determine if the instance arguments, tiargs, are all that is necessary - * to instantiate the template. - */ - //printf("tp = %p, td.parameters.length = %d, tiargs.length = %d\n", tp, td.parameters.length, tiargs.length); - auto tf = fd.type.isTypeFunction(); - if (tf.parameterList.length) - { - auto tp = td.isVariadic(); - if (tp && td.parameters.length > 1) - return 1; - - if (!tp && tiargs.length < td.parameters.length) - { - // Can remain tiargs be filled by default arguments? - foreach (size_t i; tiargs.length .. td.parameters.length) - { - if (!(*td.parameters)[i].hasDefaultArg()) - return 1; - } - } - - foreach (i, fparam; tf.parameterList) - { - // 'auto ref' needs inference. - if (fparam.storageClass & STC.auto_) - return 1; - } - } - - if (!flag) - { - /* Calculate the need for overload resolution. - * When only one template can match with tiargs, inference is not necessary. - */ - dedtypes.setDim(td.parameters.length); - dedtypes.zero(); - 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; - } - } - MATCH m = matchWithInstance(sc, td, this, dedtypes, ArgumentList(), 0); - if (m == MATCH.nomatch) - return 0; - } - - /* If there is more than one function template which matches, we may - * need type inference (see https://issues.dlang.org/show_bug.cgi?id=4430) - */ - return ++count > 1 ? 1 : 0; - }); - if (r) - return true; - } - - if (olderrs != global.errors) - { - if (!global.gag) - { - errorSupplemental(loc, "while looking for match for `%s`", toChars()); - semanticRun = PASS.semanticdone; - inst = this; - } - errors = true; - } - //printf("false\n"); - return false; - } - /***************************************** * Determines if a TemplateInstance will need a nested * generation of the TemplateDeclaration. @@ -6012,7 +5856,7 @@ MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, Templ /* If a function is really property-like, and then * it's CTFEable, ei will be a literal expression. */ - uint olderrors = global.startGagging(); + const olderrors = global.startGagging(); ei = resolveProperties(sc, ei); ei = ei.ctfeInterpret(); if (global.endGagging(olderrors) || ei.op == EXP.error) diff --git a/gcc/d/dmd/dtoh.d b/gcc/d/dmd/dtoh.d index 6d39464d57118081ce5d2fca095f1beeb0c40203..296e13c63d51aaeea36bae93fe7a0ab6782e5925 100644 --- a/gcc/d/dmd/dtoh.d +++ b/gcc/d/dmd/dtoh.d @@ -512,7 +512,7 @@ public: } } - if (global.params.warnings != DiagnosticReporting.off || canFix) + if (global.params.useWarnings != DiagnosticReporting.off || canFix) { // Warn about identifiers that are keywords in C++. if (auto kc = keywordClass(ident)) diff --git a/gcc/d/dmd/dversion.d b/gcc/d/dmd/dversion.d index 2e3b35264dbe2f4e0ddfd25d92c9d69f69ac164f..36e9cf80189dce0acd9aa4df56337baa7fc97be2 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.errors; import dmd.globals; import dmd.identifier; import dmd.location; diff --git a/gcc/d/dmd/enumsem.d b/gcc/d/dmd/enumsem.d index f2eb34c83f91fd231752d01291cfac29cec9c50e..99211e4985484381d9eda6360783726da27a24cc 100644 --- a/gcc/d/dmd/enumsem.d +++ b/gcc/d/dmd/enumsem.d @@ -30,7 +30,6 @@ import dmd.declaration; import dmd.denum; import dmd.dimport; import dmd.dinterpret; -import dmd.dmangle; import dmd.dmodule; import dmd.dscope; import dmd.dstruct; @@ -119,7 +118,7 @@ void enumSemantic(Scope* sc, EnumDeclaration ed) ed.cppnamespace = sc.namespace; ed.semanticRun = PASS.semantic; - UserAttributeDeclaration.checkGNUABITag(ed, sc.linkage); + checkGNUABITag(ed, sc.linkage); checkMustUseReserved(ed); if (!ed.members && !ed.memtype) // enum ident; diff --git a/gcc/d/dmd/errors.h b/gcc/d/dmd/errors.h index 7a9683b9e67e19123546d6a4be5b561a3f9352b2..448a4f674a8546d7a39c4ae706ffc99bff9b0530 100644 --- a/gcc/d/dmd/errors.h +++ b/gcc/d/dmd/errors.h @@ -42,9 +42,6 @@ D_ATTRIBUTE_FORMAT(1, 2) void message(const char *format, ...); D_ATTRIBUTE_FORMAT(2, 3) void message(const Loc& loc, const char *format, ...); D_ATTRIBUTE_FORMAT(1, 2) void tip(const char *format, ...); -D_ATTRIBUTE_FORMAT(2, 0) void verrorReport(const Loc& loc, const char *format, va_list ap, ErrorKind kind, const char *p1 = nullptr, const char *p2 = nullptr); -D_ATTRIBUTE_FORMAT(2, 0) void verrorReportSupplemental(const Loc& loc, const char* format, va_list ap, ErrorKind kind); - #if defined(__GNUC__) || defined(__clang__) #define D_ATTRIBUTE_NORETURN __attribute__((noreturn)) #elif _MSC_VER diff --git a/gcc/d/dmd/escape.d b/gcc/d/dmd/escape.d index fb3fb9b96457a757b12e9747a69f4e48995fe2da..e1b2ef791a7f113ed6df88788e443387a6c8c504 100644 --- a/gcc/d/dmd/escape.d +++ b/gcc/d/dmd/escape.d @@ -34,6 +34,7 @@ import dmd.location; import dmd.mtype; import dmd.printast; import dmd.rootobject; +import dmd.safe; import dmd.tokens; import dmd.typesem : hasPointers, parameterStorageClass; import dmd.visitor; diff --git a/gcc/d/dmd/expression.d b/gcc/d/dmd/expression.d index a72df72adb4e2ab06e81acbe720e6088a93a75db..3c79b02caa026586eda134010582f14224aed1ba 100644 --- a/gcc/d/dmd/expression.d +++ b/gcc/d/dmd/expression.d @@ -54,29 +54,6 @@ import dmd.visitor; enum LOGSEMANTIC = false; -/// Return value for `checkModifiable` -enum Modifiable -{ - /// Not modifiable - no, - /// Modifiable (the type is mutable) - yes, - /// Modifiable because it is initialization - initialization, -} -/** - * Specifies how the checkModify deals with certain situations - */ -enum ModifyFlags -{ - /// Issue error messages on invalid modifications of the variable - none, - /// No errors are emitted for invalid modifications - noError = 0x1, - /// The modification occurs for a subfield of the current variable - fieldAssign = 0x2, -} - /**************************************** * Find the last non-comma expression. * Params: @@ -478,7 +455,7 @@ extern (C++) abstract class Expression : ASTNode dinteger_t toInteger() { //printf("Expression %s\n", EXPtoString(op).ptr); - if (!type.isTypeError()) + if (!type || !type.isTypeError()) error(loc, "integer constant expression expected instead of `%s`", toChars()); return 0; } @@ -1880,7 +1857,7 @@ extern (C++) final class InterpExp : Expression enum char NoPostfix = 0; - extern (D) this(const ref Loc loc, InterpolatedSet* set, char postfix = NoPostfix) scope + extern (D) this(const ref Loc loc, InterpolatedSet* set, char postfix = NoPostfix) scope @safe { super(loc, EXP.interpolated); this.interpolatedSet = set; @@ -4575,7 +4552,7 @@ extern (C++) class CatAssignExp : BinAssignExp { Expression lowering; // lowered druntime hook `_d_arrayappend{cTX,T}` - extern (D) this(const ref Loc loc, Expression e1, Expression e2) + extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.concatenateAssign, e1, e2); } @@ -5286,7 +5263,7 @@ extern (C++) final class ObjcClassReferenceExp : Expression { ClassDeclaration classDeclaration; - extern (D) this(const ref Loc loc, ClassDeclaration classDeclaration) + extern (D) this(const ref Loc loc, ClassDeclaration classDeclaration) @safe { super(loc, EXP.objcClassReference); this.classDeclaration = classDeclaration; diff --git a/gcc/d/dmd/expression.h b/gcc/d/dmd/expression.h index ad792817c1b91257ed525bc4e9506d78f1ae5cbe..c353a191a662067ef7652e9285a23747ee2b1bec 100644 --- a/gcc/d/dmd/expression.h +++ b/gcc/d/dmd/expression.h @@ -71,19 +71,6 @@ enum #define WANTvalue 0 // default #define WANTexpand 1 // expand const/immutable variables if possible -/** - * Specifies how the checkModify deals with certain situations - */ -enum class ModifyFlags -{ - /// Issue error messages on invalid modifications of the variable - none, - /// No errors are emitted for invalid modifications - noError = 0x1, - /// The modification occurs for a subfield of the current variable - fieldAssign = 0x2, -}; - class Expression : public ASTNode { public: diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d index 57336987764b7c1ad9497ead3e13dc9274baf7c7..b26ce2380f9a2995bdeb92867dcdedc8ecefcf75 100644 --- a/gcc/d/dmd/expressionsem.d +++ b/gcc/d/dmd/expressionsem.d @@ -36,7 +36,6 @@ import dmd.denum; import dmd.deps; import dmd.dimport; import dmd.dinterpret; -import dmd.dmangle; import dmd.dmodule; import dmd.dstruct; import dmd.dsymbolsem; @@ -60,6 +59,7 @@ import dmd.initsem; import dmd.inline; import dmd.intrange; import dmd.location; +import dmd.mangle; import dmd.mtype; import dmd.mustuse; import dmd.nspace; @@ -81,6 +81,7 @@ import dmd.semantic3; import dmd.sideeffect; import dmd.safe; import dmd.target; +import dmd.templatesem : matchWithInstance; import dmd.tokens; import dmd.traits; import dmd.typesem; @@ -139,16 +140,26 @@ private bool isNeedThisScope(Scope* sc, Declaration d) * buf = append generated string to buffer * sc = context * exps = array of Expressions + * loc = location of the pragma / mixin where this conversion was requested, for supplemental error + * fmt = format string for supplemental error. May contain 1 `%s` which prints the faulty expression + * expandTuples = whether tuples should be expanded rather than printed as tuple syntax * Returns: * true on error */ -bool expressionsToString(ref OutBuffer buf, Scope* sc, Expressions* exps) +bool expressionsToString(ref OutBuffer buf, Scope* sc, Expressions* exps, + Loc loc, const(char)* fmt, bool expandTuples) { if (!exps) return false; foreach (ex; *exps) { + bool error() + { + if (loc != Loc.initial && fmt) + errorSupplemental(loc, fmt, ex.toChars()); + return true; + } if (!ex) continue; auto sc2 = sc.startCTFE(); @@ -161,15 +172,16 @@ bool expressionsToString(ref OutBuffer buf, Scope* sc, Expressions* exps) // allowed to contain types as well as expressions auto e4 = ctfeInterpretForPragmaMsg(e3); if (!e4 || e4.op == EXP.error) - return true; + return error(); // expand tuple - if (auto te = e4.isTupleExp()) - { - if (expressionsToString(buf, sc, te.exps)) - return true; - continue; - } + if (expandTuples) + if (auto te = e4.isTupleExp()) + { + if (expressionsToString(buf, sc, te.exps, loc, fmt, true)) + return error(); + continue; + } // char literals exp `.toStringExp` return `null` but we cant override it // because in most contexts we don't want the conversion to succeed. IntegerExp ie = e4.isIntegerExp(); @@ -180,9 +192,11 @@ bool expressionsToString(ref OutBuffer buf, Scope* sc, Expressions* exps) e4 = new ArrayLiteralExp(ex.loc, tsa, ie); } - if (StringExp se = e4.toStringExp()) + StringExp se = e4.toStringExp(); + + if (se && se.type.nextOf().ty.isSomeChar) buf.writestring(se.toUTF8(sc).peekString()); - else + else if (!(se && se.len == 0)) // don't print empty array literal `[]` buf.writestring(e4.toString()); } return false; @@ -313,14 +327,11 @@ StringExp semanticString(Scope *sc, Expression exp, const char* s) return null; } - auto se = e.toStringExp(); - if (!se) - { - error(exp.loc, "`string` expected for %s, not `(%s)` of type `%s`", - s, exp.toChars(), exp.type.toChars()); - return null; - } - return se; + if (auto se = e.toStringExp()) + return se; + error(exp.loc, "`string` expected for %s, not `(%s)` of type `%s`", + s, exp.toChars(), exp.type.toChars()); + return null; } /**************************************** @@ -328,17 +339,16 @@ StringExp semanticString(Scope *sc, Expression exp, const char* s) */ StringExp toUTF8(StringExp se, Scope* sc) { - if (se.sz != 1) - { - // Convert to UTF-8 string - se.committed = false; - Expression e = castTo(se, sc, Type.tchar.arrayOf()); - e = e.optimize(WANTvalue); - auto result = e.isStringExp(); - assert(result.sz == 1); - return result; - } - return se; + if (se.sz == 1) + return se; + // Convert to UTF-8 string + se.committed = false; + Expression e = castTo(se, sc, Type.tchar.arrayOf()); + e = e.optimize(WANTvalue); + auto result = e.isStringExp(); + assert(result); + assert(result.sz == 1); + return result; } /******************************** * The type for a unary expression is incompatible. @@ -346,7 +356,7 @@ StringExp toUTF8(StringExp se, Scope* sc) * Returns: * ErrorExp */ -extern (D) Expression incompatibleTypes(UnaExp e) +private Expression incompatibleTypes(UnaExp e) { if (e.e1.type.toBasetype() == Type.terror) return e.e1; @@ -508,82 +518,76 @@ private Expression checkOpAssignTypes(BinExp binExp, Scope* sc) e2 = e2.castTo(sc, t1); } } - if (op == EXP.mulAssign) + if (op == EXP.mulAssign && t2.isFloating()) { - if (t2.isFloating()) + if (t1.isReal()) { - if (t1.isReal()) - { - if (t2.isImaginary() || t2.isComplex()) - { - e2 = e2.castTo(sc, t1); - } - } - else if (t1.isImaginary()) + if (t2.isImaginary() || t2.isComplex()) { - if (t2.isImaginary() || t2.isComplex()) - { - switch (t1.ty) - { - case Timaginary32: - t2 = Type.tfloat32; - break; - - case Timaginary64: - t2 = Type.tfloat64; - break; - - case Timaginary80: - t2 = Type.tfloat80; - break; - - default: - assert(0); - } - e2 = e2.castTo(sc, t2); - } + e2 = e2.castTo(sc, t1); } } - } - else if (op == EXP.divAssign) - { - if (t2.isImaginary()) + else if (t1.isImaginary()) { - if (t1.isReal()) - { - // x/iv = i(-x/v) - // Therefore, the result is 0 - e2 = new CommaExp(loc, e2, new RealExp(loc, CTFloat.zero, t1)); - e2.type = t1; - Expression e = new AssignExp(loc, e1, e2); - e.type = t1; - return e; - } - else if (t1.isImaginary()) + if (t2.isImaginary() || t2.isComplex()) { - Type t3; switch (t1.ty) { case Timaginary32: - t3 = Type.tfloat32; + t2 = Type.tfloat32; break; case Timaginary64: - t3 = Type.tfloat64; + t2 = Type.tfloat64; break; case Timaginary80: - t3 = Type.tfloat80; + t2 = Type.tfloat80; break; default: assert(0); } - e2 = e2.castTo(sc, t3); - Expression e = new AssignExp(loc, e1, e2); - e.type = t1; - return e; + e2 = e2.castTo(sc, t2); + } + } + } + else if (op == EXP.divAssign && t2.isImaginary()) + { + if (t1.isReal()) + { + // x/iv = i(-x/v) + // Therefore, the result is 0 + e2 = new CommaExp(loc, e2, new RealExp(loc, CTFloat.zero, t1)); + e2.type = t1; + Expression e = new AssignExp(loc, e1, e2); + e.type = t1; + return e; + } + else if (t1.isImaginary()) + { + Type t3; + switch (t1.ty) + { + case Timaginary32: + t3 = Type.tfloat32; + break; + + case Timaginary64: + t3 = Type.tfloat64; + break; + + case Timaginary80: + t3 = Type.tfloat80; + break; + + default: + assert(0); } + e2 = e2.castTo(sc, t3); + Expression e = new AssignExp(loc, e1, e2); + e.type = t1; + return e; } } else if (op == EXP.modAssign) @@ -710,7 +714,7 @@ Expression resolveOpDollar(Scope* sc, ArrayExp ae, Expression* pe0) (*fargs)[0] = ie.lwr; (*fargs)[1] = ie.upr; - uint xerrors = global.startGagging(); + const xerrors = global.startGagging(); sc = sc.push(); FuncDeclaration fslice = resolveFuncCall(ae.loc, sc, slice, tiargs, ae.e1.type, ArgumentList(fargs), FuncResolveFlag.quiet); sc = sc.pop(); @@ -1210,7 +1214,7 @@ private Expression resolveUFCS(Scope* sc, CallExp ce) // check them for issues. Expressions* originalArguments = Expression.arraySyntaxCopy(ce.arguments); - uint errors = global.startGagging(); + const errors = global.startGagging(); e = ce.expressionSemantic(sc); if (!global.endGagging(errors)) return e; @@ -2014,6 +2018,71 @@ void checkOverriddenDtor(FuncDeclaration f, Scope* sc, const ref Loc loc, } } +/// Print the reason why `fd` was inferred `@system` as a supplemental error +/// Params: +/// fd = function to check +/// maxDepth = up to how many functions deep to report errors +/// deprecation = print deprecations instead of errors +/// stc = storage class of attribute to check +public void errorSupplementalInferredAttr(FuncDeclaration fd, int maxDepth, bool deprecation, STC stc) +{ + auto errorFunc = deprecation ? &deprecationSupplemental : &errorSupplemental; + + AttributeViolation* s; + const(char)* attr; + if (stc & STC.safe) + { + s = fd.safetyViolation; + attr = "@safe"; + } + else if (stc & STC.pure_) + { + s = fd.pureViolation; + attr = "pure"; + } + else if (stc & STC.nothrow_) + { + s = fd.nothrowViolation; + attr = "nothrow"; + } + else if (stc & STC.nogc) + { + s = fd.nogcViolation; + attr = "@nogc"; + } + + if (!s) + return; + + if (s.fmtStr) + { + errorFunc(s.loc, deprecation ? + "which wouldn't be `%s` because of:" : + "which wasn't inferred `%s` because of:", attr); + if (stc == STC.nogc || stc == STC.pure_) + { + auto f = (cast(Dsymbol) s.arg0).isFuncDeclaration(); + errorFunc(s.loc, s.fmtStr, f.kind(), f.toPrettyChars(), s.arg1 ? s.arg1.toChars() : ""); + } + else + { + errorFunc(s.loc, s.fmtStr, + s.arg0 ? s.arg0.toChars() : "", s.arg1 ? s.arg1.toChars() : "", s.arg2 ? s.arg2.toChars() : ""); + } + } + else if (auto sa = s.arg0.isDsymbol()) + { + if (FuncDeclaration fd2 = sa.isFuncDeclaration()) + { + if (maxDepth > 0) + { + errorFunc(s.loc, "which calls `%s`", fd2.toPrettyChars()); + errorSupplementalInferredAttr(fd2, maxDepth - 1, deprecation, stc); + } + } + } +} + /******************************************* * Accessing variable v. * Check for purity and safety violations. @@ -2102,23 +2171,21 @@ private bool checkPurity(VarDeclaration v, const ref Loc loc, Scope* sc) FuncDeclaration ff = s.isFuncDeclaration(); if (!ff) break; - if (ff.isNested() || ff.isThis()) - { - if (ff.type.isImmutable() || - ff.type.isShared() && !MODimplicitConv(ff.type.mod, v.type.mod)) - { - OutBuffer ffbuf; - OutBuffer vbuf; - MODMatchToBuffer(&ffbuf, ff.type.mod, v.type.mod); - MODMatchToBuffer(&vbuf, v.type.mod, ff.type.mod); - error(loc, "%s%s `%s` cannot access %sdata `%s`", - ffbuf.peekChars(), ff.kind(), ff.toPrettyChars(), vbuf.peekChars(), v.toChars()); - err = true; - break; - } - continue; + if (!ff.isNested() && !ff.isThis()) + break; + if (ff.type.isImmutable() || + ff.type.isShared() && !MODimplicitConv(ff.type.mod, v.type.mod)) + { + OutBuffer ffbuf; + OutBuffer vbuf; + MODMatchToBuffer(&ffbuf, ff.type.mod, v.type.mod); + MODMatchToBuffer(&vbuf, v.type.mod, ff.type.mod); + error(loc, "%s%s `%s` cannot access %sdata `%s`", + ffbuf.peekChars(), ff.kind(), ff.toPrettyChars(), vbuf.peekChars(), v.toChars()); + err = true; + break; } - break; + continue; } } @@ -2246,30 +2313,28 @@ private bool checkNogc(FuncDeclaration f, ref Loc loc, Scope* sc) if (f.isNogc()) return false; - if (isRootTraitsCompilesScope(sc) ? sc.func.isNogcBypassingInference() : sc.func.setGCCall(f)) - { - if (loc.linnum == 0) // e.g. implicitly generated dtor - loc = sc.func.loc; - - // Lowered non-@nogc'd hooks will print their own error message inside of nogc.d (NOGCVisitor.visit(CallExp e)), - // so don't print anything to avoid double error messages. - if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT - || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX - || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT)) - { - error(loc, "`@nogc` %s `%s` cannot call non-@nogc %s `%s`", - sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars()); + if (isRootTraitsCompilesScope(sc) ? !sc.func.isNogcBypassingInference() : !sc.func.setGCCall(f)) + return false; - if (!f.isDtorDeclaration) - f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc); - } + if (loc.linnum == 0) // e.g. implicitly generated dtor + loc = sc.func.loc; - f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().isNogc, "non-@nogc"); + // Lowered non-@nogc'd hooks will print their own error message inside of nogc.d (NOGCVisitor.visit(CallExp e)), + // so don't print anything to avoid double error messages. + if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT + || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX + || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT)) + { + error(loc, "`@nogc` %s `%s` cannot call non-@nogc %s `%s`", + sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars()); - return true; + if (!f.isDtorDeclaration) + f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc); } - return false; + f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().isNogc, "non-@nogc"); + + return true; } /******************************************** @@ -2361,37 +2426,35 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = return e.expressionSemantic(sc); } } + for (size_t i = 0; i < os.a.length; i++) { - for (size_t i = 0; i < os.a.length; i++) + FuncDeclaration f = resolveFuncCall(loc, sc, os.a[i], tiargs, tthis, ArgumentList(), FuncResolveFlag.quiet); + if (!f) + continue; + if (f.errors) + return ErrorExp.get(); + fd = f; + assert(fd.type.ty == Tfunction); + auto tf = fd.type.isTypeFunction(); + if (!tf.isRef && e2) { - if (FuncDeclaration f = resolveFuncCall(loc, sc, os.a[i], tiargs, tthis, ArgumentList(), FuncResolveFlag.quiet)) - { - if (f.errors) - return ErrorExp.get(); - fd = f; - assert(fd.type.ty == Tfunction); - auto tf = fd.type.isTypeFunction(); - if (!tf.isRef && e2) - { - error(loc, "%s is not an lvalue", e1.toChars()); - return ErrorExp.get(); - } - } + error(loc, "%s is not an lvalue", e1.toChars()); + return ErrorExp.get(); } - if (fd) + } + if (fd) + { + Expression e = new CallExp(loc, e1); + if (e2) { - Expression e = new CallExp(loc, e1); - if (e2) + e = new AssignExp(loc, e, e2); + if (saveAtts) { - e = new AssignExp(loc, e, e2); - if (saveAtts) - { - (cast(BinExp)e).att1 = saveAtts.att1; - (cast(BinExp)e).att2 = saveAtts.att2; - } + (cast(BinExp)e).att1 = saveAtts.att1; + (cast(BinExp)e).att2 = saveAtts.att2; } - return e.expressionSemantic(sc); } + return e.expressionSemantic(sc); } if (e2) goto Leprop; @@ -2479,33 +2542,31 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = return e.expressionSemantic(sc); } } + FuncDeclaration fd = resolveFuncCall(loc, sc, s, tiargs, tthis, ArgumentList(), FuncResolveFlag.quiet); + if (fd && fd.type) { - FuncDeclaration fd = resolveFuncCall(loc, sc, s, tiargs, tthis, ArgumentList(), FuncResolveFlag.quiet); - if (fd && fd.type) + if (fd.errors) + return ErrorExp.get(); + TypeFunction tf = fd.type.isTypeFunction(); + if (!e2 || tf.isRef) { - if (fd.errors) - return ErrorExp.get(); - TypeFunction tf = fd.type.isTypeFunction(); - if (!e2 || tf.isRef) + Expression e = new CallExp(loc, e1); + if (e2) { - Expression e = new CallExp(loc, e1); - if (e2) + e = new AssignExp(loc, e, e2); + if (saveAtts) { - e = new AssignExp(loc, e, e2); - if (saveAtts) - { - (cast(BinExp)e).att1 = saveAtts.att1; - (cast(BinExp)e).att2 = saveAtts.att2; - } + (cast(BinExp)e).att1 = saveAtts.att1; + (cast(BinExp)e).att2 = saveAtts.att2; } - return e.expressionSemantic(sc); } + return e.expressionSemantic(sc); } } - if (FuncDeclaration fd = s.isFuncDeclaration()) + if (FuncDeclaration fd2 = s.isFuncDeclaration()) { // Keep better diagnostic message for invalid property usage of functions - assert(fd.type.ty == Tfunction); + assert(fd2.type.ty == Tfunction); Expression e = new CallExp(loc, e1, e2); return e.expressionSemantic(sc); } @@ -2564,20 +2625,19 @@ private bool checkRightThis(Expression e, Scope* sc) { if (e.op == EXP.error) return true; - if (e.op == EXP.variable && e.type.ty != Terror) - { - VarExp ve = cast(VarExp)e; - if (isNeedThisScope(sc, ve.var)) - { - //printf("checkRightThis sc.intypeof = %d, ad = %p, func = %p, fdthis = %p\n", - // sc.intypeof, sc.getStructClassScope(), func, fdthis); - auto t = ve.var.isThis(); - assert(t); - error(e.loc, "accessing non-static variable `%s` requires an instance of `%s`", ve.var.toChars(), t.toChars()); - return true; - } - } - return false; + if (e.op != EXP.variable || e.type.ty == Terror) + return false; + + VarExp ve = cast(VarExp)e; + if (!isNeedThisScope(sc, ve.var)) + return false; + + //printf("checkRightThis sc.intypeof = %d, ad = %p, func = %p, fdthis = %p\n", + // sc.intypeof, sc.getStructClassScope(), func, fdthis); + auto t = ve.var.isThis(); + assert(t); + error(e.loc, "accessing non-static variable `%s` requires an instance of `%s`", ve.var.toChars(), t.toChars()); + return true; } Expression resolveProperties(Scope* sc, Expression e) @@ -2998,29 +3058,22 @@ private bool functionParameters(const ref Loc loc, Scope* sc, else (*arguments)[i] = arg; } - else + else if (arg.isDefaultInitExp()) { - if (arg.isDefaultInitExp()) - { - arg = arg.resolveLoc(loc, sc); - (*arguments)[i] = arg; - } + arg = arg.resolveLoc(loc, sc); + (*arguments)[i] = arg; } - if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nparams) // https://dlang.org/spec/function.html#variadic { //printf("\t\tvarargs == 2, p.type = '%s'\n", p.type.toChars()); + if (MATCH m = arg.implicitConvTo(p.type)) { - MATCH m; - if ((m = arg.implicitConvTo(p.type)) > MATCH.nomatch) - { - if (p.type.nextOf() && arg.implicitConvTo(p.type.nextOf()) >= m) - goto L2; - else if (nargs != nparams) - return errorArgs(); - goto L1; - } + if (p.type.nextOf() && arg.implicitConvTo(p.type.nextOf()) >= m) + goto L2; + else if (nargs != nparams) + return errorArgs(); + goto L1; } L2: Type tb = p.type.toBasetype(); @@ -3783,37 +3836,37 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { if (!e.type) e.type = Type.tfloat64; - else if (e.type.isImaginary && sc.inCfile) + else if (!e.type.isImaginary || !sc.inCfile) { - /* Convert to core.stdc.config.complex - */ - Type t = getComplexLibraryType(e.loc, sc, e.type.ty); - if (t.ty == Terror) - return setError(); + e.type = e.type.typeSemantic(e.loc, sc); + result = e; + return; + } - Type tf; - switch (e.type.ty) - { - case Timaginary32: tf = Type.tfloat32; break; - case Timaginary64: tf = Type.tfloat64; break; - case Timaginary80: tf = Type.tfloat80; break; - default: - assert(0); - } + /* Convert to core.stdc.config.complex + */ + Type t = getComplexLibraryType(e.loc, sc, e.type.ty); + if (t.ty == Terror) + return setError(); - /* Construct ts{re : 0.0, im : e} - */ - TypeStruct ts = t.isTypeStruct; - Expressions* elements = new Expressions(2); - (*elements)[0] = new RealExp(e.loc, CTFloat.zero, tf); - (*elements)[1] = new RealExp(e.loc, e.toImaginary(), tf); - Expression sle = new StructLiteralExp(e.loc, ts.sym, elements); - result = sle.expressionSemantic(sc); - return; + Type tf; + switch (e.type.ty) + { + case Timaginary32: tf = Type.tfloat32; break; + case Timaginary64: tf = Type.tfloat64; break; + case Timaginary80: tf = Type.tfloat80; break; + default: + assert(0); } - else - e.type = e.type.typeSemantic(e.loc, sc); - result = e; + + /* Construct ts{re : 0.0, im : e} + */ + TypeStruct ts = t.isTypeStruct; + Expressions* elements = new Expressions(2); + (*elements)[0] = new RealExp(e.loc, CTFloat.zero, tf); + (*elements)[1] = new RealExp(e.loc, e.toImaginary(), tf); + Expression sle = new StructLiteralExp(e.loc, ts.sym, elements); + result = sle.expressionSemantic(sc); } override void visit(ComplexExp e) @@ -4129,6 +4182,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor ClassDeclaration cd; Dsymbol s; + void err() + { + error(e.loc, "`super` is only allowed in non-static class member functions"); + result = ErrorExp.get(); + } /* Special case for typeof(this) and typeof(super) since both * should work even if they are not inside a non-static member function */ @@ -4143,22 +4201,22 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setError(); } cd = s.isClassDeclaration(); - if (cd) + if (!cd) + continue; + + cd = cd.baseClass; + if (!cd) { - cd = cd.baseClass; - if (!cd) - { - error(e.loc, "class `%s` has no `super`", s.toChars()); - return setError(); - } - e.type = cd.type; - result = e; - return; + error(e.loc, "class `%s` has no `super`", s.toChars()); + return setError(); } + e.type = cd.type; + result = e; + return; } } if (!fd) - goto Lerr; + return err(); e.var = fd.vthis; assert(e.var && e.var.parent); @@ -4170,7 +4228,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor cd = s.isClassDeclaration(); //printf("parent is %s %s\n", fd.toParent().kind(), fd.toParent().toChars()); if (!cd) - goto Lerr; + return err(); if (!cd.baseClass) { error(e.loc, "no base class for `%s`", cd.toChars()); @@ -4186,11 +4244,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setError(); result = e; - return; - - Lerr: - error(e.loc, "`super` is only allowed in non-static class member functions"); - result = ErrorExp.get(); } override void visit(NullExp e) @@ -5522,7 +5575,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } else if (auto f = e.var.isFuncDeclaration()) { - if (f.checkNestedReference(sc, e.loc)) + if (f.checkNestedFuncReference(sc, e.loc)) return setError(); } @@ -5576,10 +5629,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } else if (fd) { - // TODO: If fd isn't yet resolved its overload, the checkNestedReference + // TODO: If fd isn't yet resolved its overload, the checkNestedFuncReference // call would cause incorrect validation. // Maybe here should be moved in CallExp, or AddrExp for functions. - if (fd.checkNestedReference(sc, e.loc)) + if (fd.checkNestedFuncReference(sc, e.loc)) return setError(); } else if (auto od = e.var.isOverDeclaration()) @@ -5652,7 +5705,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } Expression e = exp; - uint olderrors; sc = sc.push(); // just create new scope sc.ctfe = false; // temporary stop CTFE @@ -5681,6 +5733,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } + void done() + { + sc = sc.pop(); + result = e; + } + //printf("td = %p, treq = %p\n", td, fd.treq); if (exp.td) { @@ -5696,10 +5754,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor else e = ErrorExp.get(); } - goto Ldone; + return done(); } - olderrors = global.errors; + const olderrors = global.errors; exp.fd.dsymbolSemantic(sc); if (olderrors == global.errors) { @@ -5712,7 +5770,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (exp.fd.type && exp.fd.type.ty == Tfunction && !exp.fd.type.nextOf()) (cast(TypeFunction)exp.fd.type).next = Type.terror; e = ErrorExp.get(); - goto Ldone; + return done(); } // Type is a "delegate to" or "pointer to" the function literal @@ -5725,7 +5783,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (exp.fd.type.isTypeError()) { e = ErrorExp.get(); - goto Ldone; + return done(); } exp.type = new TypeDelegate(exp.fd.type.isTypeFunction()); exp.type = exp.type.typeSemantic(exp.loc, sc); @@ -5754,10 +5812,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } exp.fd.tookAddressOf++; - - Ldone: - sc = sc.pop(); - result = e; + done(); } /** @@ -6279,47 +6334,46 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { if (tiargs && s.isFuncDeclaration()) continue; - if (auto f2 = resolveFuncCall(loc, sc, s, tiargs, tthis, argumentList, FuncResolveFlag.quiet)) + auto f2 = resolveFuncCall(loc, sc, s, tiargs, tthis, argumentList, FuncResolveFlag.quiet); + if (!f2) + continue; + if (f2.errors) + return null; + if (!f) { - if (f2.errors) - return null; - if (f) - { - /* Match in more than one overload set, - * even if one is a 'better' match than the other. - */ - if (f.isCsymbol() && f2.isCsymbol()) - { - /* C has global name space, so just pick one, such as f. - * If f and f2 are not compatible, that's how C rolls. - */ - } - else - ScopeDsymbol.multiplyDefined(loc, f, f2); // issue error - } - else - f = f2; + f = f2; + continue; } - } - if (!f) - { - .error(loc, "no overload matches for `%s`", exp.toChars()); - errorSupplemental(loc, "Candidates are:"); - foreach (s; os.a) + /* Match in more than one overload set, + * even if one is a 'better' match than the other. + */ + if (f.isCsymbol() && f2.isCsymbol()) { - overloadApply(s, (ds){ - if (auto fd = ds.isFuncDeclaration()) - .errorSupplemental(ds.loc, "%s%s", fd.toChars(), - fd.type.toTypeFunction().parameterList.parametersTypeToChars()); - else - .errorSupplemental(ds.loc, "%s", ds.toChars()); - return 0; - }); + /* C has global name space, so just pick one, such as f. + * If f and f2 are not compatible, that's how C rolls. + */ } + else + ScopeDsymbol.multiplyDefined(loc, f, f2); // issue error } - else if (f.errors) - f = null; - return f; + if (f && f.errors) + return null; + if (f) + return f; + .error(loc, "no overload matches for `%s`", exp.toChars()); + errorSupplemental(loc, "Candidates are:"); + foreach (s; os.a) + { + overloadApply(s, (ds){ + if (auto fd = ds.isFuncDeclaration()) + .errorSupplemental(ds.loc, "%s%s", fd.toChars(), + fd.type.toTypeFunction().parameterList.parametersTypeToChars()); + else + .errorSupplemental(ds.loc, "%s", ds.toChars()); + return 0; + }); + } + return f; } bool isSuper = false; @@ -6678,7 +6732,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.f.checkPurity(exp.loc, sc); exp.f.checkSafety(exp.loc, sc); exp.f.checkNogc(exp.loc, sc); - if (exp.f.checkNestedReference(sc, exp.loc)) + if (exp.f.checkNestedFuncReference(sc, exp.loc)) return setError(); } else if (sc.func && sc.intypeof != 1 && !(sc.ctfe || sc.debug_)) @@ -6761,7 +6815,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (exp.f.needThis()) { // Change the ancestor lambdas to delegate before hasThis(sc) call. - if (exp.f.checkNestedReference(sc, exp.loc)) + if (exp.f.checkNestedFuncReference(sc, exp.loc)) return setError(); auto memberFunc = hasThis(sc); @@ -6796,7 +6850,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor checkFunctionAttributes(exp, sc, exp.f); checkAccess(exp.loc, sc, null, exp.f); - if (exp.f.checkNestedReference(sc, exp.loc)) + if (exp.f.checkNestedFuncReference(sc, exp.loc)) return setError(); ethis = null; @@ -6915,7 +6969,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("DeclarationExp::semantic() %s\n", e.toChars()); } - uint olderrors = global.errors; + const olderrors = global.errors; /* This is here to support extern(linkage) declaration, * where the extern(linkage) winds up being an AttribDeclaration @@ -7144,17 +7198,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // Handle this in the glue layer Expression e = new TypeidExp(exp.loc, ta); - bool genObjCode = true; - - // https://issues.dlang.org/show_bug.cgi?id=23650 - // We generate object code for typeinfo, required - // by typeid, only if in non-speculative context - if (sc.traitsCompiles) - { - genObjCode = false; - } - - e.type = getTypeInfoType(exp.loc, ta, sc, genObjCode); + e.type = getTypeInfoType(exp.loc, ta, sc); semanticTypeInfo(sc, ta); if (ea) @@ -7440,39 +7484,37 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor //printf("targ = %s, %s\n", e.targ.toChars(), e.targ.deco); //printf("tspec = %s, %s\n", e.tspec.toChars(), e.tspec.deco); - if (e.tok == TOK.colon) + if (e.tok != TOK.colon) /* == */ { - // current scope is itself deprecated, or deprecations are not errors - const bool deprecationAllowed = sc.isDeprecated - || global.params.useDeprecated != DiagnosticReporting.error; - const bool preventAliasThis = e.targ.hasDeprecatedAliasThis && !deprecationAllowed; + if (e.targ.equals(e.tspec)) + return yes(); + else + return no(); + } - if (preventAliasThis && e.targ.ty == Tstruct) - { - if ((cast(TypeStruct) e.targ).implicitConvToWithoutAliasThis(e.tspec)) - return yes(); - else - return no(); - } - else if (preventAliasThis && e.targ.ty == Tclass) - { - if ((cast(TypeClass) e.targ).implicitConvToWithoutAliasThis(e.tspec)) - return yes(); - else - return no(); - } - else if (e.targ.implicitConvTo(e.tspec)) + // current scope is itself deprecated, or deprecations are not errors + const bool deprecationAllowed = sc.isDeprecated + || global.params.useDeprecated != DiagnosticReporting.error; + const bool preventAliasThis = e.targ.hasDeprecatedAliasThis && !deprecationAllowed; + + if (preventAliasThis && e.targ.ty == Tstruct) + { + if ((cast(TypeStruct) e.targ).implicitConvToWithoutAliasThis(e.tspec)) return yes(); else return no(); } - else /* == */ + else if (preventAliasThis && e.targ.ty == Tclass) { - if (e.targ.equals(e.tspec)) + if ((cast(TypeClass) e.targ).implicitConvToWithoutAliasThis(e.tspec)) return yes(); else return no(); } + else if (e.targ.implicitConvTo(e.tspec)) + return yes(); + else + return no(); } else if (e.tspec) { @@ -7656,16 +7698,15 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor private Expression compileIt(MixinExp exp, Scope *sc) { OutBuffer buf; - if (expressionsToString(buf, sc, exp.exps)) + if (expressionsToString(buf, sc, exp.exps, exp.loc, null, true)) return null; - uint errors = global.errors; + const errors = global.errors; const len = buf.length; const str = buf.extractChars()[0 .. len]; const bool doUnittests = global.params.parsingUnittestsRequired(); auto loc = adjustLocForMixin(str, exp.loc, global.params.mixinOut); scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); - p.transitionIn = global.params.v.vin; p.nextToken(); //printf("p.loc.linnum = %d\n", p.loc.linnum); @@ -8094,8 +8135,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor /* This is an `assert(0)` which means halt program execution */ FuncDeclaration fd = sc.parent.isFuncDeclaration(); - if (fd) - fd.hasReturnExp |= 4; sc.ctorflow.orCSX(CSX.halt); if (global.params.useAssert == CHECKENABLE.off) @@ -8192,7 +8231,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (e && isDotOpDispatch(e)) { auto ode = e; - uint errors = global.startGagging(); + const errors = global.startGagging(); e = resolvePropertiesX(sc, e); // Any error or if 'e' is not resolved, go to UFCS if (global.endGagging(errors) || e is ode) @@ -10555,7 +10594,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ auto ode = e; exp.e2 = exp.e2.expressionSemantic(sc); - uint errors = global.startGagging(); + const errors = global.startGagging(); e = resolvePropertiesX(sc, e, exp.e2); // Any error or if 'e' is not resolved, go to UFCS if (global.endGagging(errors) || e is ode) @@ -12206,7 +12245,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } if (tb1.ty == Tpointer && tb2.ty == Tpointer || - tb1.ty == Tnull && tb2.ty == Tnull) + tb1.ty == Tnull && tb2.ty == Tnull) { result = exp.incompatibleTypes(); return; @@ -14237,7 +14276,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Expression trySemantic(Expression exp, Scope* sc) { //printf("+trySemantic(%s)\n", exp.toChars()); - uint errors = global.startGagging(); + const errors = global.startGagging(); Expression e = expressionSemantic(exp, sc); if (global.endGagging(errors)) { @@ -14434,14 +14473,6 @@ private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc) return exp; } -private bool checkDisabled(Dsymbol s, ref Loc loc, Scope* sc) -{ - if (auto d = s.isDeclaration()) - return d.checkDisabled(loc, sc); - - return false; -} - /****************************** * Resolve properties, i.e. `e1.ident`, without seeing UFCS. * Params: @@ -14536,7 +14567,8 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) s = s.toAlias(); s.checkDeprecated(exp.loc, sc); - s.checkDisabled(exp.loc, sc); + if (auto d = s.isDeclaration()) + d.checkDisabled(exp.loc, sc); if (auto em = s.isEnumMember()) { @@ -14833,6 +14865,13 @@ Expression dotTemplateSemanticProp(DotTemplateInstanceExp exp, Scope* sc, bool g auto die = new DotIdExp(exp.loc, e1, exp.ti.name); Expression e = die.dotIdSemanticPropX(sc); + + Expression notTemplate() + { + error(exp.loc, "`%s` isn't a template", e.toChars()); + return errorExp(); + } + if (e == die) { exp.e1 = die.e1; // take back @@ -14877,7 +14916,7 @@ Expression dotTemplateSemanticProp(DotTemplateInstanceExp exp, Scope* sc, bool g exp.e1 = dve.e1; // pull semantic() result if (!exp.findTempDecl(sc)) - goto Lerr; + return notTemplate(); if (exp.ti.needsTypeInference(sc)) return exp; exp.ti.dsymbolSemantic(sc); @@ -14973,9 +15012,7 @@ Expression dotTemplateSemanticProp(DotTemplateInstanceExp exp, Scope* sc, bool g .expressionSemantic(sc); } -Lerr: - error(exp.loc, "`%s` isn't a template", e.toChars()); - return errorExp(); + return notTemplate(); } MATCH matchType(FuncExp funcExp, Type to, Scope* sc, FuncExp* presult, ErrorSink eSink) @@ -16280,7 +16317,7 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression * Returns: * `true` if ok, `false` for error */ -bool checkAddressVar(Scope* sc, Expression exp, VarDeclaration v) +private bool checkAddressVar(Scope* sc, Expression exp, VarDeclaration v) { //printf("checkAddressVar(exp: %s, v: %s)\n", exp.toChars(), v.toChars()); if (v is null) @@ -16464,7 +16501,7 @@ Expression getThisSkipNestedFuncs(const ref Loc loc, Scope* sc, Dsymbol s, Aggre * newly created variable such that a closure is made for the variable when * the address of `fd` is taken. */ -VarDeclaration makeThis2Argument(const ref Loc loc, Scope* sc, FuncDeclaration fd) +private VarDeclaration makeThis2Argument(const ref Loc loc, Scope* sc, FuncDeclaration fd) { Type tthis2 = Type.tvoidptr.sarrayOf(2); VarDeclaration vthis2 = new VarDeclaration(loc, tthis2, Identifier.generateId("__this"), null); @@ -16857,3 +16894,760 @@ bool evalStaticCondition(Scope* sc, Expression original, Expression e, out bool } return impl(e); } + +/************************************ + * Check to see the aggregate type is nested and its context pointer is + * accessible from the current scope. + * Returns true if error occurs. + */ +bool checkFrameAccess(Loc loc, Scope* sc, AggregateDeclaration ad, size_t iStart = 0) +{ + Dsymbol sparent = ad.toParentLocal(); + Dsymbol sparent2 = ad.toParent2(); + Dsymbol s = sc.func; + if (ad.isNested() && s) + { + //printf("ad = %p %s [%s], parent:%p\n", ad, ad.toChars(), ad.loc.toChars(), ad.parent); + //printf("sparent = %p %s [%s], parent: %s\n", sparent, sparent.toChars(), sparent.loc.toChars(), sparent.parent,toChars()); + //printf("sparent2 = %p %s [%s], parent: %s\n", sparent2, sparent2.toChars(), sparent2.loc.toChars(), sparent2.parent,toChars()); + if (!ensureStaticLinkTo(s, sparent) || sparent != sparent2 && !ensureStaticLinkTo(s, sparent2)) + { + error(loc, "cannot access frame pointer of `%s`", ad.toPrettyChars()); + return true; + } + } + + bool result = false; + for (size_t i = iStart; i < ad.fields.length; i++) + { + VarDeclaration vd = ad.fields[i]; + Type tb = vd.type.baseElemOf(); + if (tb.ty == Tstruct) + { + result |= checkFrameAccess(loc, sc, (cast(TypeStruct)tb).sym); + } + } + return result; +} + +/// Return value for `checkModifiable` +enum Modifiable +{ + /// Not modifiable + no, + /// Modifiable (the type is mutable) + yes, + /// Modifiable because it is initialization + initialization, +} + +/** + * Specifies how the checkModify deals with certain situations + */ +enum ModifyFlags +{ + /// Issue error messages on invalid modifications of the variable + none, + /// No errors are emitted for invalid modifications + noError = 0x1, + /// The modification occurs for a subfield of the current variable + fieldAssign = 0x2, +} + +/************************************* + * Check to see if declaration can be modified in this context (sc). + * Issue error if not. + * Params: + * loc = location for error messages + * e1 = `null` or `this` expression when this declaration is a field + * sc = context + * flag = if the first bit is set it means do not issue error message for + * invalid modification; if the second bit is set, it means that + this declaration is a field and a subfield of it is modified. + * Returns: + * Modifiable.yes or Modifiable.initialization + */ +private Modifiable checkModify(Declaration d, Loc loc, Scope* sc, Expression e1, ModifyFlags flag) +{ + VarDeclaration v = d.isVarDeclaration(); + if (v && v.canassign) + return Modifiable.initialization; + + if (d.isParameter() || d.isResult()) + { + for (Scope* scx = sc; scx; scx = scx.enclosing) + { + if (scx.func == d.parent && scx.contract != Contract.none) + { + const(char)* s = d.isParameter() && d.parent.ident != Id.ensure ? "parameter" : "result"; + if (!(flag & ModifyFlags.noError)) + error(loc, "%s `%s` cannot modify %s `%s` in contract", d.kind, d.toPrettyChars, s, d.toChars()); + return Modifiable.initialization; // do not report type related errors + } + } + } + + if (e1 && e1.op == EXP.this_ && d.isField()) + { + VarDeclaration vthis = e1.isThisExp().var; + for (Scope* scx = sc; scx; scx = scx.enclosing) + { + if (scx.func == vthis.parent && scx.contract != Contract.none) + { + if (!(flag & ModifyFlags.noError)) + error(loc, "%s `%s` cannot modify parameter `this` in contract", d.kind, d.toPrettyChars); + return Modifiable.initialization; // do not report type related errors + } + } + } + + if (v && (v.isCtorinit() || d.isField())) + { + // It's only modifiable if inside the right constructor + if ((d.storage_class & (STC.foreach_ | STC.ref_)) == (STC.foreach_ | STC.ref_)) + return Modifiable.initialization; + if (flag & ModifyFlags.fieldAssign) + return Modifiable.yes; + return modifyFieldVar(loc, sc, v, e1) ? Modifiable.initialization : Modifiable.yes; + } + return Modifiable.yes; +} + +/*********************************************** + * Mark variable v as modified if it is inside a constructor that var + * is a field in. + * Also used to allow immutable globals to be initialized inside a static constructor. + * Returns: + * true if it's an initialization of v + */ +private bool modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1) +{ + //printf("modifyFieldVar(var = %s)\n", var.toChars()); + Dsymbol s = sc.func; + while (1) + { + FuncDeclaration fd = null; + if (s) + fd = s.isFuncDeclaration(); + if (fd && + ((fd.isCtorDeclaration() && var.isField()) || + ((fd.isStaticCtorDeclaration() || fd.isCrtCtor) && !var.isField())) && + fd.toParentDecl() == var.toParent2() && + (!e1 || e1.op == EXP.this_)) + { + bool result = true; + + var.ctorinit = true; + //printf("setting ctorinit\n"); + + if (var.isField() && sc.ctorflow.fieldinit.length && !sc.intypeof) + { + assert(e1); + auto mustInit = ((var.storage_class & STC.nodefaultctor) != 0 || + var.type.needsNested()); + + const dim = sc.ctorflow.fieldinit.length; + auto ad = fd.isMemberDecl(); + assert(ad); + size_t i; + for (i = 0; i < dim; i++) // same as findFieldIndexByName in ctfeexp.c ? + { + if (ad.fields[i] == var) + break; + } + assert(i < dim); + auto fieldInit = &sc.ctorflow.fieldinit[i]; + const fi = fieldInit.csx; + + if (fi & CSX.this_ctor) + { + if (var.type.isMutable() && e1.type.isMutable()) + result = false; + else + { + const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod); + .error(loc, "%s field `%s` initialized multiple times", modStr, var.toChars()); + .errorSupplemental(fieldInit.loc, "Previous initialization is here."); + } + } + else if (sc.inLoop || (fi & CSX.label)) + { + if (!mustInit && var.type.isMutable() && e1.type.isMutable()) + result = false; + else + { + const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod); + .error(loc, "%s field `%s` initialization is not allowed in loops or after labels", modStr, var.toChars()); + } + } + + fieldInit.csx |= CSX.this_ctor; + fieldInit.loc = e1.loc; + if (var.overlapped) // https://issues.dlang.org/show_bug.cgi?id=15258 + { + foreach (j, v; ad.fields) + { + if (v is var || !var.isOverlappedWith(v)) + continue; + v.ctorinit = true; + sc.ctorflow.fieldinit[j].csx = CSX.this_ctor; + } + } + } + else if (fd != sc.func) + { + if (var.type.isMutable()) + result = false; + else if (sc.func.fes) + { + const(char)* p = var.isField() ? "field" : var.kind(); + .error(loc, "%s %s `%s` initialization is not allowed in foreach loop", + MODtoChars(var.type.mod), p, var.toChars()); + } + else + { + const(char)* p = var.isField() ? "field" : var.kind(); + .error(loc, "%s %s `%s` initialization is not allowed in nested function `%s`", + MODtoChars(var.type.mod), p, var.toChars(), sc.func.toChars()); + } + } + else if (fd.isStaticCtorDeclaration() && !fd.isSharedStaticCtorDeclaration() && + var.type.isImmutable()) + { + .error(loc, "%s %s `%s` initialization is not allowed in `static this`", + MODtoChars(var.type.mod), var.kind(), var.toChars()); + errorSupplemental(loc, "Use `shared static this` instead."); + } + else if (fd.isStaticCtorDeclaration() && !fd.isSharedStaticCtorDeclaration() && + var.type.isConst()) + { + // @@@DEPRECATED_2.116@@@ + // Turn this into an error, merging with the branch above + .deprecation(loc, "%s %s `%s` initialization is not allowed in `static this`", + MODtoChars(var.type.mod), var.kind(), var.toChars()); + deprecationSupplemental(loc, "Use `shared static this` instead."); + } + return result; + } + else + { + if (s) + { + s = s.toParentP(var.toParent2()); + continue; + } + } + break; + } + return false; +} + +/*************************************** + * Request additional semantic analysis for TypeInfo generation. + * Params: + * sc = context + * t = type that TypeInfo is being generated for + */ +void semanticTypeInfo(Scope* sc, Type t) +{ + if (sc) + { + if (sc.intypeof) + return; + if (!sc.needsCodegen()) + return; + } + + if (!t) + return; + + void visitVector(TypeVector t) + { + semanticTypeInfo(sc, t.basetype); + } + + void visitAArray(TypeAArray t) + { + semanticTypeInfo(sc, t.index); + semanticTypeInfo(sc, t.next); + } + + void visitStruct(TypeStruct t) + { + //printf("semanticTypeInfo.visit(TypeStruct = %s)\n", t.toChars()); + StructDeclaration sd = t.sym; + + /* Step 1: create TypeInfoDeclaration + */ + if (!sc) // inline may request TypeInfo. + { + Scope scx; + scx.eSink = global.errorSink; + scx._module = sd.getModule(); + getTypeInfoType(sd.loc, t, &scx); + sd.requestTypeInfo = true; + } + else if (!sc.minst) + { + // don't yet have to generate TypeInfo instance if + // the typeid(T) expression exists in speculative scope. + } + else + { + getTypeInfoType(sd.loc, t, sc); + sd.requestTypeInfo = true; + + // https://issues.dlang.org/show_bug.cgi?id=15149 + // if the typeid operand type comes from a + // result of auto function, it may be yet speculative. + // unSpeculative(sc, sd); + } + + /* Step 2: If the TypeInfo generation requires sd.semantic3, run it later. + * This should be done even if typeid(T) exists in speculative scope. + * Because it may appear later in non-speculative scope. + */ + if (!sd.members) + return; // opaque struct + if (!sd.xeq && !sd.xcmp && !sd.postblit && !sd.tidtor && !sd.xhash && !search_toString(sd)) + return; // none of TypeInfo-specific members + + // If the struct is in a non-root module, run semantic3 to get + // correct symbols for the member function. + if (sd.semanticRun >= PASS.semantic3) + { + // semantic3 is already done + } + else if (TemplateInstance ti = sd.isInstantiated()) + { + if (ti.minst && !ti.minst.isRoot()) + Module.addDeferredSemantic3(sd); + } + else + { + if (sd.inNonRoot()) + { + //printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd.toChars(), sd.inNonRoot()); + Module.addDeferredSemantic3(sd); + } + } + } + + void visitTuple(TypeTuple t) + { + if (t.arguments) + { + foreach (arg; *t.arguments) + { + semanticTypeInfo(sc, arg.type); + } + } + } + + /* Note structural similarity of this Type walker to that in isSpeculativeType() + */ + + Type tb = t.toBasetype(); + switch (tb.ty) + { + case Tvector: visitVector(tb.isTypeVector()); break; + case Taarray: visitAArray(tb.isTypeAArray()); break; + case Tstruct: visitStruct(tb.isTypeStruct()); break; + case Ttuple: visitTuple (tb.isTypeTuple()); break; + + case Tclass: + case Tenum: break; + + default: semanticTypeInfo(sc, tb.nextOf()); break; + } +} + +/** + * Issue an error if an attempt to call a disabled method is made + * + * If the declaration is disabled but inside a disabled function, + * returns `true` but do not issue an error message. + * + * Params: + * d = Declaration to check + * loc = Location information of the call + * sc = Scope in which the call occurs + * isAliasedDeclaration = if `true` searches overload set + * + * Returns: + * `true` if this `Declaration` is `@disable`d, `false` otherwise. + */ +bool checkDisabled(Declaration d, Loc loc, Scope* sc, bool isAliasedDeclaration = false) +{ + if (!(d.storage_class & STC.disable)) + return false; + + if (sc.func && sc.func.storage_class & STC.disable) + return true; + + if (auto p = d.toParent()) + { + if (auto postblit = d.isPostBlitDeclaration()) + { + /* https://issues.dlang.org/show_bug.cgi?id=21885 + * + * If the generated postblit is disabled, it + * means that one of the fields has a disabled + * postblit. Print the first field that has + * a disabled postblit. + */ + if (postblit.isGenerated()) + { + auto sd = p.isStructDeclaration(); + assert(sd); + for (size_t i = 0; i < sd.fields.length; i++) + { + auto structField = sd.fields[i]; + if (structField.overlapped) + continue; + Type tv = structField.type.baseElemOf(); + if (tv.ty != Tstruct) + continue; + auto sdv = (cast(TypeStruct)tv).sym; + if (!sdv.postblit) + continue; + if (sdv.postblit.isDisabled()) + { + .error(loc, "%s `%s` is not copyable because field `%s` is not copyable", p.kind, p.toPrettyChars, structField.toChars()); + return true; + } + } + } + .error(loc, "%s `%s` is not copyable because it has a disabled postblit", p.kind, p.toPrettyChars); + return true; + } + } + + // if the function is @disabled, maybe there + // is an overload in the overload set that isn't + if (isAliasedDeclaration) + { + if (FuncDeclaration fd = d.isFuncDeclaration()) + { + for (FuncDeclaration ovl = fd; ovl; ovl = cast(FuncDeclaration)ovl.overnext) + if (!(ovl.storage_class & STC.disable)) + return false; + } + } + + if (auto ctor = d.isCtorDeclaration()) + { + 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; + } + } + .error(loc, "%s `%s` cannot be used because it is annotated with `@disable`", d.kind, d.toPrettyChars); + return true; +} + +/******************************************* + * Helper function for the expansion of manifest constant. + */ +private Expression expandInitializer(VarDeclaration vd, Loc loc) +{ + assert((vd.storage_class & STC.manifest) && vd._init); + + auto e = vd.getConstInitializer(); + if (!e) + { + .error(loc, "cannot make expression out of initializer for `%s`", vd.toChars()); + return ErrorExp.get(); + } + + e = e.copy(); + e.loc = loc; // for better error message + return e; +} + +/***************************************************** + * Determine if template instance is really a template function, + * and that template function needs to infer types from the function + * arguments. + * + * Like findBestMatch, iterate possible template candidates, + * but just looks only the necessity of type inference. + */ +private bool needsTypeInference(TemplateInstance ti, Scope* sc, int flag = 0) +{ + //printf("TemplateInstance.needsTypeInference() %s\n", toChars()); + if (ti.semanticRun != PASS.initial) + return false; + + const olderrs = global.errors; + Objects dedtypes; + size_t count = 0; + + 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 any of the overloaded template declarations need inference, + * then return true + */ + if (!td.onemember) + return 0; + if (auto td2 = td.onemember.isTemplateDeclaration()) + { + if (!td2.onemember || !td2.onemember.isFuncDeclaration()) + return 0; + if (ti.tiargs.length >= td.parameters.length - (td.isVariadic() ? 1 : 0)) + return 0; + return 1; + } + auto fd = td.onemember.isFuncDeclaration(); + if (!fd || fd.type.ty != Tfunction) + return 0; + + foreach (tp; *td.parameters) + { + if (tp.isTemplateThisParameter()) + return 1; + } + + /* Determine if the instance arguments, tiargs, are all that is necessary + * to instantiate the template. + */ + //printf("tp = %p, td.parameters.length = %d, tiargs.length = %d\n", tp, td.parameters.length, tiargs.length); + auto tf = fd.type.isTypeFunction(); + if (tf.parameterList.length) + { + auto tp = td.isVariadic(); + if (tp && td.parameters.length > 1) + return 1; + + if (!tp && ti.tiargs.length < td.parameters.length) + { + // Can remain tiargs be filled by default arguments? + foreach (size_t i; ti.tiargs.length .. td.parameters.length) + { + if (!(*td.parameters)[i].hasDefaultArg()) + return 1; + } + } + + foreach (i, fparam; tf.parameterList) + { + // 'auto ref' needs inference. + if (fparam.storageClass & STC.auto_) + return 1; + } + } + + if (!flag) + { + /* Calculate the need for overload resolution. + * When only one template can match with tiargs, inference is not necessary. + */ + dedtypes.setDim(td.parameters.length); + dedtypes.zero(); + 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(ti.loc, "%s `%s` `%s` forward references template declaration `%s`", + ti.kind, ti.toPrettyChars, ti.toChars(), td.toChars()); + return 1; + } + } + MATCH m = matchWithInstance(sc, td, ti, dedtypes, ArgumentList(), 0); + if (m == MATCH.nomatch) + return 0; + } + + /* If there is more than one function template which matches, we may + * need type inference (see https://issues.dlang.org/show_bug.cgi?id=4430) + */ + return ++count > 1 ? 1 : 0; + }); + if (r) + return true; + } + + if (olderrs != global.errors) + { + if (!global.gag) + { + errorSupplemental(ti.loc, "while looking for match for `%s`", ti.toChars()); + ti.semanticRun = PASS.semanticdone; + ti.inst = ti; + } + ti.errors = true; + } + //printf("false\n"); + return false; +} + +/*************************************** + * Fill out remainder of elements[] with default initializers for fields[]. + * Params: + * sd = struct + * loc = location + * elements = explicit arguments which given to construct object. + * ctorinit = true if the elements will be used for default initialization. + * Returns: + * false if any errors occur. + * Otherwise, returns true and the missing arguments will be pushed in elements[]. + */ +bool fill(StructDeclaration sd, const ref Loc loc, ref Expressions elements, bool ctorinit) +{ + //printf("AggregateDeclaration::fill() %s\n", toChars()); + assert(sd.sizeok == Sizeok.done); + const nfields = sd.nonHiddenFields(); + bool errors = false; + + size_t dim = elements.length; + elements.setDim(nfields); + foreach (size_t i; dim .. nfields) + elements[i] = null; + + // Fill in missing any elements with default initializers + foreach (i; 0 .. nfields) + { + if (elements[i]) + continue; + + auto vd = sd.fields[i]; + auto vx = vd; + if (vd._init && vd._init.isVoidInitializer()) + vx = null; + + // Find overlapped fields with the hole [vd.offset .. vd.offset.size()]. + size_t fieldi = i; + foreach (j; 0 .. nfields) + { + if (i == j) + continue; + auto v2 = sd.fields[j]; + if (!vd.isOverlappedWith(v2)) + continue; + + if (elements[j]) + { + vx = null; + break; + } + if (v2._init && v2._init.isVoidInitializer()) + continue; + + version (all) + { + /* Prefer first found non-void-initialized field + * union U { int a; int b = 2; } + * U u; // Error: overlapping initialization for field a and b + */ + if (!vx) + { + vx = v2; + fieldi = j; + } + else if (v2._init) + { + .error(loc, "overlapping initialization for field `%s` and `%s`", v2.toChars(), vd.toChars()); + errors = true; + } + } + else + { + // fixes https://issues.dlang.org/show_bug.cgi?id=1432 by enabling this path always + + /* Prefer explicitly initialized field + * union U { int a; int b = 2; } + * U u; // OK (u.b == 2) + */ + if (!vx || !vx._init && v2._init) + { + vx = v2; + fieldi = j; + } + else if (vx != vd && !vx.isOverlappedWith(v2)) + { + // Both vx and v2 fills vd, but vx and v2 does not overlap + } + else if (vx._init && v2._init) + { + .error(loc, "overlapping default initialization for field `%s` and `%s`", + v2.toChars(), vd.toChars()); + errors = true; + } + else + assert(vx._init || !vx._init && !v2._init); + } + } + if (!vx) + continue; + + Expression e; + if (vx.type.size() == 0) + { + e = null; + } + else if (vx._init) + { + assert(!vx._init.isVoidInitializer()); + if (vx.inuse) // https://issues.dlang.org/show_bug.cgi?id=18057 + { + .error(loc, "%s `%s` recursive initialization of field", vx.kind(), vx.toPrettyChars()); + errors = true; + } + else + e = vx.getConstInitializer(false); + } + else + { + if ((vx.storage_class & STC.nodefaultctor) && !ctorinit) + { + .error(loc, "field `%s.%s` must be initialized because it has no default constructor", + sd.type.toChars(), vx.toChars()); + errors = true; + } + /* https://issues.dlang.org/show_bug.cgi?id=12509 + * Get the element of static array type. + */ + Type telem = vx.type; + if (telem.ty == Tsarray) + { + /* We cannot use Type::baseElemOf() here. + * If the bottom of the Tsarray is an enum type, baseElemOf() + * will return the base of the enum, and its default initializer + * would be different from the enum's. + */ + TypeSArray tsa; + while ((tsa = telem.toBasetype().isTypeSArray()) !is null) + telem = tsa.next; + if (telem.ty == Tvoid) + telem = Type.tuns8.addMod(telem.mod); + } + if (telem.needsNested() && ctorinit) + e = telem.defaultInit(loc); + else + e = telem.defaultInitLiteral(loc); + } + elements[fieldi] = e; + } + foreach (e; elements) + { + if (e && e.op == EXP.error) + return false; + } + + return !errors; +} diff --git a/gcc/d/dmd/func.d b/gcc/d/dmd/func.d index 9d59fc99104aa4287eb5adc191fc2b8d76af4a81..e9b9367fd32ef0971cdc4acf20bed75799167b78 100644 --- a/gcc/d/dmd/func.d +++ b/gcc/d/dmd/func.d @@ -29,15 +29,14 @@ import dmd.dcast; import dmd.dclass; import dmd.declaration; import dmd.delegatize; -import dmd.dinterpret; import dmd.dmodule; import dmd.dscope; import dmd.dstruct; import dmd.dsymbol; import dmd.dtemplate; -import dmd.errors; import dmd.escape; import dmd.expression; +import dmd.funcsem : overloadApply; import dmd.globals; import dmd.hdrgen; import dmd.id; @@ -50,8 +49,6 @@ import dmd.common.outbuffer; import dmd.rootobject; import dmd.root.string; import dmd.root.stringtable; -import dmd.semantic2; -import dmd.semantic3; import dmd.statement; import dmd.tokens; import dmd.visitor; @@ -140,6 +137,10 @@ private struct FUNCFLAG bool computedEscapingSiblings; /// `hasEscapingSiblings` has been computed bool dllImport; /// __declspec(dllimport) bool dllExport; /// __declspec(dllexport) + + bool hasReturnExp; /// Has return exp; statement + bool hasInlineAsm; /// Has asm{} statement + bool hasMultipleReturnExp; /// Has multiple return exp; statements } /*********************************************************** @@ -237,13 +238,6 @@ extern (C++) class FuncDeclaration : Declaration // Things that should really go into Scope - /// 1 if there's a return exp; statement - /// 2 if there's a throw statement - /// 4 if there's an assert(0) - /// 8 if there's inline asm - /// 16 if there are multiple return statements - int hasReturnExp; - VarDeclaration nrvo_var; /// variable to replace with shidden Symbol* shidden; /// hidden pointer passed to function @@ -685,72 +679,6 @@ extern (C++) class FuncDeclaration : Declaration return bitFields; } - final bool isSafe() - { - if (safetyInprocess) - setUnsafe(); - return type.toTypeFunction().trust == TRUST.safe; - } - - extern (D) final bool isSafeBypassingInference() - { - return !(safetyInprocess) && isSafe(); - } - - final bool isTrusted() - { - if (safetyInprocess) - setUnsafe(); - return type.toTypeFunction().trust == TRUST.trusted; - } - - /************************************** - * The function is doing something unsafe, so mark it as unsafe. - * - * Params: - * gag = surpress error message (used in escape.d) - * loc = location of error - * fmt = printf-style format string - * arg0 = (optional) argument for first %s format specifier - * arg1 = (optional) argument for second %s format specifier - * arg2 = (optional) argument for third %s format specifier - * Returns: whether there's a safe error - */ - extern (D) final bool setUnsafe( - bool gag = false, Loc loc = Loc.init, const(char)* fmt = null, - RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) - { - if (safetyInprocess) - { - safetyInprocess = false; - type.toTypeFunction().trust = TRUST.system; - if (fmt || arg0) - safetyViolation = new AttributeViolation(loc, fmt, arg0, arg1, arg2); - - if (fes) - fes.func.setUnsafe(); - } - else if (isSafe()) - { - if (!gag && fmt) - .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); - - return true; - } - return false; - } - - /************************************** - * The function is calling `@system` function `f`, so mark it as unsafe. - * - * Params: - * f = function being called (needed for diagnostic of inferred functions) - * Returns: whether there's a safe error - */ - extern (D) final bool setUnsafeCall(FuncDeclaration f) - { - return setUnsafe(false, f.loc, null, f, null); - } /************************************** * The function is doing something that may throw an exception, register that in case nothrow is being inferred @@ -1124,118 +1052,6 @@ extern (C++) class FuncDeclaration : Declaration } } -/*************************************************** - * Visit each overloaded function/template in turn, and call dg(s) on it. - * Exit when no more, or dg(s) returns nonzero. - * - * Params: - * fstart = symbol to start from - * dg = the delegate to be called on the overload - * sc = context used to check if symbol is accessible (and therefore visible), - * can be null - * - * Returns: - * ==0 continue - * !=0 done (and the return value from the last dg() call) - */ -extern (D) int overloadApply(Dsymbol fstart, scope int delegate(Dsymbol) dg, Scope* sc = null) -{ - Dsymbols visited; - - int overloadApplyRecurse(Dsymbol fstart, scope int delegate(Dsymbol) dg, Scope* sc) - { - // Detect cyclic calls. - if (visited.contains(fstart)) - return 0; - visited.push(fstart); - - Dsymbol next; - for (auto d = fstart; d; d = next) - { - import dmd.access : checkSymbolAccess; - if (auto od = d.isOverDeclaration()) - { - /* The scope is needed here to check whether a function in - an overload set was added by means of a private alias (or a - selective import). If the scope where the alias is created - is imported somewhere, the overload set is visible, but the private - alias is not. - */ - if (sc) - { - if (checkSymbolAccess(sc, od)) - { - if (int r = overloadApplyRecurse(od.aliassym, dg, sc)) - return r; - } - } - else if (int r = overloadApplyRecurse(od.aliassym, dg, sc)) - return r; - next = od.overnext; - } - else if (auto fa = d.isFuncAliasDeclaration()) - { - if (fa.hasOverloads) - { - if (int r = overloadApplyRecurse(fa.funcalias, dg, sc)) - return r; - } - else if (auto fd = fa.toAliasFunc()) - { - if (int r = dg(fd)) - return r; - } - else - { - .error(d.loc, "%s `%s` is aliased to a function", d.kind, d.toPrettyChars); - break; - } - next = fa.overnext; - } - else if (auto ad = d.isAliasDeclaration()) - { - if (sc) - { - if (checkSymbolAccess(sc, ad)) - next = ad.toAlias(); - } - else - next = ad.toAlias(); - if (next == ad) - break; - if (next == fstart) - break; - } - else if (auto td = d.isTemplateDeclaration()) - { - if (int r = dg(td)) - return r; - next = td.overnext; - } - else if (auto fd = d.isFuncDeclaration()) - { - if (int r = dg(fd)) - return r; - next = fd.overnext; - } - else if (auto os = d.isOverloadSet()) - { - foreach (ds; os.a) - if (int r = dg(ds)) - return r; - } - else - { - .error(d.loc, "%s `%s` is aliased to a function", d.kind, d.toPrettyChars); - break; - // BUG: should print error message? - } - } - return 0; - } - return overloadApplyRecurse(fstart, dg, sc); -} - /** Checks for mismatching modifiers between `lhsMod` and `rhsMod` and prints the mismatching modifiers to `buf`. @@ -2066,68 +1882,3 @@ struct AttributeViolation /// ditto RootObject arg2 = null; } - -/// Print the reason why `fd` was inferred `@system` as a supplemental error -/// Params: -/// fd = function to check -/// maxDepth = up to how many functions deep to report errors -/// deprecation = print deprecations instead of errors -/// stc = storage class of attribute to check -void errorSupplementalInferredAttr(FuncDeclaration fd, int maxDepth, bool deprecation, STC stc) -{ - auto errorFunc = deprecation ? &deprecationSupplemental : &errorSupplemental; - - AttributeViolation* s; - const(char)* attr; - if (stc & STC.safe) - { - s = fd.safetyViolation; - attr = "@safe"; - } - else if (stc & STC.pure_) - { - s = fd.pureViolation; - attr = "pure"; - } - else if (stc & STC.nothrow_) - { - s = fd.nothrowViolation; - attr = "nothrow"; - } - else if (stc & STC.nogc) - { - s = fd.nogcViolation; - attr = "@nogc"; - } - - if (!s) - return; - - if (s.fmtStr) - { - errorFunc(s.loc, deprecation ? - "which wouldn't be `%s` because of:" : - "which wasn't inferred `%s` because of:", attr); - if (stc == STC.nogc || stc == STC.pure_) - { - auto f = (cast(Dsymbol) s.arg0).isFuncDeclaration(); - errorFunc(s.loc, s.fmtStr, f.kind(), f.toPrettyChars(), s.arg1 ? s.arg1.toChars() : ""); - } - else - { - errorFunc(s.loc, s.fmtStr, - s.arg0 ? s.arg0.toChars() : "", s.arg1 ? s.arg1.toChars() : "", s.arg2 ? s.arg2.toChars() : ""); - } - } - else if (auto sa = s.arg0.isDsymbol()) - { - if (FuncDeclaration fd2 = sa.isFuncDeclaration()) - { - if (maxDepth > 0) - { - errorFunc(s.loc, "which calls `%s`", fd2.toPrettyChars()); - errorSupplementalInferredAttr(fd2, maxDepth - 1, deprecation, stc); - } - } - } -} diff --git a/gcc/d/dmd/funcsem.d b/gcc/d/dmd/funcsem.d index 2ffd3ad5abbcdb7736ec1b6d887cc19ff4e4352f..c142a16f065da237782a1b29e1a3f16cbf521e4b 100644 --- a/gcc/d/dmd/funcsem.d +++ b/gcc/d/dmd/funcsem.d @@ -54,6 +54,7 @@ import dmd.rootobject; import dmd.root.filename; import dmd.root.string; import dmd.root.stringtable; +import dmd.safe; import dmd.semantic2; import dmd.semantic3; import dmd.statement; @@ -243,7 +244,7 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl) funcdecl.visibility = sc.visibility; funcdecl.userAttribDecl = sc.userAttribDecl; - UserAttributeDeclaration.checkGNUABITag(funcdecl, funcdecl._linkage); + checkGNUABITag(funcdecl, funcdecl._linkage); checkMustUseReserved(funcdecl); if (!funcdecl.originalType) @@ -1166,8 +1167,8 @@ bool functionSemantic(FuncDeclaration fd) if (!fd.originalType) // semantic not yet run { TemplateInstance spec = fd.isSpeculative(); - uint olderrs = global.errors; - uint oldgag = global.gag; + const olderrs = global.errors; + const oldgag = global.gag; if (global.gag && !spec) global.gag = 0; dsymbolSemantic(fd, fd._scope); @@ -1222,8 +1223,8 @@ bool functionSemantic3(FuncDeclaration fd) * we need to temporarily ungag errors. */ TemplateInstance spec = fd.isSpeculative(); - uint olderrs = global.errors; - uint oldgag = global.gag; + const olderrs = global.errors; + const oldgag = global.gag; if (global.gag && !spec) global.gag = 0; semantic3(fd, fd._scope); @@ -1648,6 +1649,7 @@ FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, return null; } + import dmd.expressionsem : checkDisabled; // remove when deprecation period of class allocators and deallocators is over if (fd.isNewDeclaration() && fd.checkDisabled(loc, sc)) return null; @@ -2262,17 +2264,17 @@ bool canInferAttributes(FuncDeclaration fd, Scope* sc) } /********************************************* - * In the current function, we are calling 'this' function. - * 1. Check to see if the current function can call 'this' function, issue error if not. - * 2. If the current function is not the parent of 'this' function, then add - * the current function to the list of siblings of 'this' function. + * 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 checkNestedReference(FuncDeclaration fd, Scope* sc, const ref Loc loc) +bool checkNestedFuncReference(FuncDeclaration fd, Scope* sc, const ref Loc loc) { - //printf("FuncDeclaration::checkNestedReference() %s\n", toPrettyChars()); + //printf("FuncDeclaration::checkNestedFuncReference() %s\n", toPrettyChars()); if (auto fld = fd.isFuncLiteralDeclaration()) { if (fld.tok == TOK.reserved) @@ -2802,121 +2804,11 @@ void modifyReturns(FuncLiteralDeclaration fld, Scope* sc, Type tret) * Returns: `true` if the provided scope is the root * of the traits compiles list of scopes. */ -bool isRootTraitsCompilesScope(Scope* sc) +bool isRootTraitsCompilesScope(Scope* sc) @safe { return (sc.traitsCompiles) && !sc.func.skipCodegen; } -/************************************** - * A statement / expression in this scope is not `@safe`, - * so mark the enclosing function as `@system` - * - * Params: - * sc = scope that the unsafe statement / expression is in - * gag = surpress error message (used in escape.d) - * loc = location of error - * fmt = printf-style format string - * arg0 = (optional) argument for first %s format specifier - * arg1 = (optional) argument for second %s format specifier - * arg2 = (optional) argument for third %s format specifier - * Returns: whether there's a safe error - */ -bool setUnsafe(Scope* sc, - bool gag = false, Loc loc = Loc.init, const(char)* fmt = null, - RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) -{ - if (sc.intypeof) - return false; // typeof(cast(int*)0) is safe - - if (sc.debug_) // debug {} scopes are permissive - return false; - - if (!sc.func) - { - if (sc.varDecl) - { - if (sc.varDecl.storage_class & STC.safe) - { - .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); - return true; - } - else if (!(sc.varDecl.storage_class & STC.trusted)) - { - sc.varDecl.storage_class |= STC.system; - sc.varDecl.systemInferred = true; - } - } - return false; - } - - - if (isRootTraitsCompilesScope(sc)) // __traits(compiles, x) - { - if (sc.func.isSafeBypassingInference()) - { - // Message wil be gagged, but still call error() to update global.errors and for - // -verrors=spec - .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); - return true; - } - return false; - } - - return sc.func.setUnsafe(gag, loc, fmt, arg0, arg1, arg2); -} - -/*************************************** - * Like `setUnsafe`, but for safety errors still behind preview switches - * - * Given a `FeatureState fs`, for example dip1000 / dip25 / systemVariables, - * the behavior changes based on the setting: - * - * - In case of `-revert=fs`, it does nothing. - * - In case of `-preview=fs`, it's the same as `setUnsafe` - * - By default, print a deprecation in `@safe` functions, or store an attribute violation in inferred functions. - * - * Params: - * sc = used to find affected function/variable, and for checking whether we are in a deprecated / speculative scope - * fs = feature state from the preview flag - * gag = surpress error message - * loc = location of error - * msg = printf-style format string - * arg0 = (optional) argument for first %s format specifier - * arg1 = (optional) argument for second %s format specifier - * arg2 = (optional) argument for third %s format specifier - * Returns: whether an actual safe error (not deprecation) occured - */ -bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char)* msg, - RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) -{ - //printf("setUnsafePreview() fs:%d %s\n", fs, msg); - with (FeatureState) final switch (fs) - { - case disabled: - return false; - - case enabled: - return sc.setUnsafe(gag, loc, msg, arg0, arg1, arg2); - - case default_: - if (!sc.func) - return false; - if (sc.func.isSafeBypassingInference()) - { - if (!gag && !sc.isDeprecated()) - { - deprecation(loc, msg, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); - } - } - else if (!sc.func.safetyViolation) - { - import dmd.func : AttributeViolation; - sc.func.safetyViolation = new AttributeViolation(loc, msg, arg0, arg1, arg2); - } - return false; - } -} - /+ + Checks the parameter and return types iff this is a `main` function. + @@ -3448,3 +3340,176 @@ extern (D) bool isTypeIsolated(FuncDeclaration fd, Type t, ref StringTable!Type return true; } } + +/** + * Check signature of `pragma(printf)` function, print error if invalid. + * + * printf/scanf-like functions must be of the form: + * extern (C/C++) T printf([parameters...], const(char)* format, ...); + * or: + * extern (C/C++) T vprintf([parameters...], const(char)* format, va_list); + * + * Params: + * funcdecl = function to check + * f = function type + * sc = scope + */ +private void checkPrintfScanfSignature(FuncDeclaration funcdecl, TypeFunction f, Scope* sc) +{ + static bool isPointerToChar(Parameter p) + { + if (auto tptr = p.type.isTypePointer()) + { + return tptr.next.ty == Tchar; + } + return false; + } + + bool isVa_list(Parameter p) + { + return p.type.equals(target.va_listType(funcdecl.loc, sc)); + } + + const nparams = f.parameterList.length; + const p = (funcdecl.printf ? Id.printf : Id.scanf).toChars(); + if (!(f.linkage == LINK.c || f.linkage == LINK.cpp)) + { + .error(funcdecl.loc, "`pragma(%s)` function `%s` must have `extern(C)` or `extern(C++)` linkage," + ~" not `extern(%s)`", + p, funcdecl.toChars(), f.linkage.linkageToChars()); + } + if (f.parameterList.varargs == VarArg.variadic) + { + if (!(nparams >= 1 && isPointerToChar(f.parameterList[nparams - 1]))) + { + .error(funcdecl.loc, "`pragma(%s)` function `%s` must have" + ~ " signature `%s %s([parameters...], const(char)*, ...)` not `%s`", + p, funcdecl.toChars(), f.next.toChars(), funcdecl.toChars(), funcdecl.type.toChars()); + } + } + else if (f.parameterList.varargs == VarArg.none) + { + if(!(nparams >= 2 && isPointerToChar(f.parameterList[nparams - 2]) && + isVa_list(f.parameterList[nparams - 1]))) + .error(funcdecl.loc, "`pragma(%s)` function `%s` must have"~ + " signature `%s %s([parameters...], const(char)*, va_list)`", + p, funcdecl.toChars(), f.next.toChars(), funcdecl.toChars()); + } + else + { + .error(funcdecl.loc, "`pragma(%s)` function `%s` must have C-style variadic `...` or `va_list` parameter", + p, funcdecl.toChars()); + } +} + +/*************************************************** + * Visit each overloaded function/template in turn, and call dg(s) on it. + * Exit when no more, or dg(s) returns nonzero. + * + * Params: + * fstart = symbol to start from + * dg = the delegate to be called on the overload + * sc = context used to check if symbol is accessible (and therefore visible), + * can be null + * + * Returns: + * ==0 continue + * !=0 done (and the return value from the last dg() call) + */ +extern (D) int overloadApply(Dsymbol fstart, scope int delegate(Dsymbol) dg, Scope* sc = null) +{ + Dsymbols visited; + + int overloadApplyRecurse(Dsymbol fstart, scope int delegate(Dsymbol) dg, Scope* sc) + { + // Detect cyclic calls. + if (visited.contains(fstart)) + return 0; + visited.push(fstart); + + Dsymbol next; + for (auto d = fstart; d; d = next) + { + import dmd.access : checkSymbolAccess; + if (auto od = d.isOverDeclaration()) + { + /* The scope is needed here to check whether a function in + an overload set was added by means of a private alias (or a + selective import). If the scope where the alias is created + is imported somewhere, the overload set is visible, but the private + alias is not. + */ + if (sc) + { + if (checkSymbolAccess(sc, od)) + { + if (int r = overloadApplyRecurse(od.aliassym, dg, sc)) + return r; + } + } + else if (int r = overloadApplyRecurse(od.aliassym, dg, sc)) + return r; + next = od.overnext; + } + else if (auto fa = d.isFuncAliasDeclaration()) + { + if (fa.hasOverloads) + { + if (int r = overloadApplyRecurse(fa.funcalias, dg, sc)) + return r; + } + else if (auto fd = fa.toAliasFunc()) + { + if (int r = dg(fd)) + return r; + } + else + { + .error(d.loc, "%s `%s` is aliased to a function", d.kind, d.toPrettyChars); + break; + } + next = fa.overnext; + } + else if (auto ad = d.isAliasDeclaration()) + { + if (sc) + { + if (checkSymbolAccess(sc, ad)) + next = ad.toAlias(); + } + else + next = ad.toAlias(); + if (next == ad) + break; + if (next == fstart) + break; + } + else if (auto td = d.isTemplateDeclaration()) + { + if (int r = dg(td)) + return r; + next = td.overnext; + } + else if (auto fd = d.isFuncDeclaration()) + { + if (int r = dg(fd)) + return r; + next = fd.overnext; + } + else if (auto os = d.isOverloadSet()) + { + foreach (ds; os.a) + if (int r = dg(ds)) + return r; + } + else + { + .error(d.loc, "%s `%s` is aliased to a function", d.kind, d.toPrettyChars); + break; + // BUG: should print error message? + } + } + return 0; + } + return overloadApplyRecurse(fstart, dg, sc); +} diff --git a/gcc/d/dmd/globals.d b/gcc/d/dmd/globals.d index ea884455161070a75a1a11cd7152315e88ff4246..ccb63e330e9e5879b67bd65585e1c173461c9895 100644 --- a/gcc/d/dmd/globals.d +++ b/gcc/d/dmd/globals.d @@ -134,7 +134,7 @@ extern(C++) struct Verbose uint errorLimit = 20; uint errorSupplementLimit = 6; // Limit the number of supplemental messages for each error (0 means unlimited) - uint errorSupplementCount() + uint errorSupplementCount() @safe { if (verbose) return uint.max; @@ -157,7 +157,7 @@ extern (C++) struct Param bool useInline = false; // inline expand functions bool release; // build release version bool preservePaths; // true means don't strip path from source file - DiagnosticReporting warnings = DiagnosticReporting.off; // how compiler warnings are handled + DiagnosticReporting useWarnings = DiagnosticReporting.off; // how compiler warnings are handled bool cov; // generate code coverage data ubyte covPercent; // 0..100 code coverage percentage required bool ctfe_cov = false; // generate coverage data for ctfe @@ -256,7 +256,7 @@ extern (C++) struct Param const(char)* timeTraceFile; /// File path of output file /// - bool parsingUnittestsRequired() + bool parsingUnittestsRequired() @safe { return useUnitTests || ddoc.doOutput || dihdr.doOutput; } @@ -383,7 +383,15 @@ extern (C++) struct Global { compileEnv.vendor = "GNU D"; } - compileEnv.versionNumber = parseVersionNumber(_version); + else version (IN_LLVM) + { + compileEnv.vendor = "LDC"; + + import dmd.console : detectTerminal; + params.v.color = detectTerminal(); + } + + compileEnv.versionNumber = parseVersionNumber(versionString()); /* Initialize date, time, and timestamp */ diff --git a/gcc/d/dmd/globals.h b/gcc/d/dmd/globals.h index 45efab094752b82d14d0eb300878426943fc3f16..5348d614ebb3a64429042b805fd668bdef9e6136 100644 --- a/gcc/d/dmd/globals.h +++ b/gcc/d/dmd/globals.h @@ -35,7 +35,8 @@ enum enum class MessageStyle : unsigned char { digitalmars, // file(line,column): message - gnu // file:line:column: message + gnu, // file:line:column: message + sarif // JSON SARIF output, see https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html }; // The state of array bounds checking @@ -160,7 +161,7 @@ struct Param d_bool useInline; // inline expand functions d_bool release; // build release version d_bool preservePaths; // true means don't strip path from source file - Diagnostic warnings; + Diagnostic useWarnings; d_bool cov; // generate code coverage data unsigned char covPercent; // 0..100 code coverage percentage required d_bool ctfe_cov; // generate coverage data for ctfe @@ -291,6 +292,7 @@ struct CompileEnv DString vendor; DString timestamp; d_bool previewIn; + d_bool transitionIn; d_bool ddocOutput; d_bool masm; IdentifierCharLookup cCharLookupTable; diff --git a/gcc/d/dmd/hdrgen.d b/gcc/d/dmd/hdrgen.d index e3f1dc283f7039c12cd43b9ac44a0535818431ee..86131f2f11c96ed239a999fbe9b20c7afff008ab 100644 --- a/gcc/d/dmd/hdrgen.d +++ b/gcc/d/dmd/hdrgen.d @@ -1720,10 +1720,10 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) //printf("FuncDeclaration::toCBuffer() '%s'\n", f.toChars()); if (stcToBuffer(buf, f.storage_class)) buf.writeByte(' '); + typeToBuffer(f.type, f.ident, buf, hgs); auto tf = f.type.isTypeFunction(); - typeToBuffer(tf, f.ident, buf, hgs); - if (hgs.hdrgen) + if (hgs.hdrgen && tf) { // if the return type is missing (e.g. ref functions or auto) // https://issues.dlang.org/show_bug.cgi?id=20090 @@ -1858,9 +1858,9 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) if (stcToBuffer(buf, d.storage_class)) buf.writeByte(' '); buf.writestring("invariant"); - if(auto es = d.fbody.isExpStatement()) + auto es = d.fbody.isExpStatement(); + if (es && es.exp && es.exp.op == EXP.assert_) { - assert(es.exp && es.exp.op == EXP.assert_); buf.writestring(" ("); (cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs); buf.writestring(");"); @@ -3796,7 +3796,8 @@ private void tiargsToBuffer(TemplateInstance ti, ref OutBuffer buf, ref HdrGenSt } else if (Expression e = isExpression(oarg)) { - if (e.op == EXP.int64 || e.op == EXP.float64 || e.op == EXP.null_ || e.op == EXP.string_ || e.op == EXP.this_) + if (!(e.type && e.type.isTypeEnum()) && e.op == EXP.int64 || e.op == EXP.float64 || + e.op == EXP.null_ || e.op == EXP.string_ || e.op == EXP.this_) { toCBuffer(e, buf, hgs); return; diff --git a/gcc/d/dmd/iasm.d b/gcc/d/dmd/iasm.d index 1399ac27fae2b9b3719b3d56bfbdc652af803b83..7eb51d995ce1ee9cd5707f718bc34f95261dbb15 100644 --- a/gcc/d/dmd/iasm.d +++ b/gcc/d/dmd/iasm.d @@ -50,7 +50,7 @@ Statement asmSemantic(AsmStatement s, Scope *sc) return null; // Assume assembler code takes care of setting the return value - sc.func.hasReturnExp |= 8; + sc.func.hasInlineAsm = true; version (NoBackend) { diff --git a/gcc/d/dmd/iasmgcc.d b/gcc/d/dmd/iasmgcc.d index 4b1b2e78c69e9f399ad04cc5fafdf7b0f5afef3e..1410e0a2299173d055ce63260f0e4f318f7b9752 100644 --- a/gcc/d/dmd/iasmgcc.d +++ b/gcc/d/dmd/iasmgcc.d @@ -29,6 +29,117 @@ 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.scanloc = s.loc; + + // 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) + 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: /*********************************** @@ -292,117 +403,6 @@ Ldone: return s; } -/*********************************** - * Parse and run semantic analysis on a GccAsmStatement. - * Params: - * s = gcc asm statement being parsed - * sc = the scope where the asm statement is located - * Returns: - * the completed gcc asm statement, or null if errors occurred - */ -public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc) -{ - //printf("GccAsmStatement.semantic()\n"); - 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.scanloc = s.loc; - - // 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) - 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); -} - unittest { import dmd.mtype : TypeBasic; @@ -410,7 +410,7 @@ unittest if (!global.errorSink) global.errorSink = new ErrorSinkCompiler; - uint errors = global.startGagging(); + const errors = global.startGagging(); scope(exit) global.endGagging(errors); // If this check fails, then Type._init() was called before reaching here, diff --git a/gcc/d/dmd/initsem.d b/gcc/d/dmd/initsem.d index a72fd90ec983b8b48fb79c162daf18c02e388771..505a3e13636fb81fb3fccbdd29f80cd3fc93fb28 100644 --- a/gcc/d/dmd/initsem.d +++ b/gcc/d/dmd/initsem.d @@ -41,6 +41,7 @@ import dmd.location; import dmd.mtype; import dmd.opover; import dmd.optimize; +import dmd.safe : setUnsafe; import dmd.statement; import dmd.target; import dmd.tokens; diff --git a/gcc/d/dmd/json.d b/gcc/d/dmd/json.d index f20b3d4b38a3cec36a33e5061b3c2520b79d5df9..021387d6f4380eda4c3870b9ac6bd19a86385740 100644 --- a/gcc/d/dmd/json.d +++ b/gcc/d/dmd/json.d @@ -24,6 +24,7 @@ import dmd.denum; import dmd.dimport; import dmd.dmodule; import dmd.dsymbol; +import dmd.dsymbolsem : include; import dmd.dtemplate; import dmd.errors; import dmd.expression; diff --git a/gcc/d/dmd/lambdacomp.d b/gcc/d/dmd/lambdacomp.d index 0ef2dbd8e0ecb9437ed1b333ac41211ddde09e1a..90c794e240555e6f9512876012976b6964a9851e 100644 --- a/gcc/d/dmd/lambdacomp.d +++ b/gcc/d/dmd/lambdacomp.d @@ -26,8 +26,8 @@ import dmd.dsymbolsem; import dmd.dtemplate; import dmd.expression; import dmd.func; -import dmd.dmangle; import dmd.hdrgen; +import dmd.mangle; import dmd.mtype; import dmd.common.outbuffer; import dmd.root.rmem; diff --git a/gcc/d/dmd/lexer.d b/gcc/d/dmd/lexer.d index 2a11f30801799c3da7f5b33404f225ac92f86349..476a700bdeb7da13b58663130c87aff85ce1c751 100644 --- a/gcc/d/dmd/lexer.d +++ b/gcc/d/dmd/lexer.d @@ -53,6 +53,7 @@ struct CompileEnv const(char)[] timestamp; /// __TIMESTAMP__ bool previewIn; /// `in` means `[ref] scope const`, accepts rvalues + bool transitionIn; /// `-transition=in` is active, `in` parameters are listed bool ddocOutput; /// collect embedded documentation comments bool masm; /// use MASM inline asm syntax diff --git a/gcc/d/dmd/location.d b/gcc/d/dmd/location.d index ca6805ee0b68a12033120d8e56707416f39cf2a7..07870685910164cb105d33283f35fbe681616b0b 100644 --- a/gcc/d/dmd/location.d +++ b/gcc/d/dmd/location.d @@ -16,6 +16,7 @@ import core.stdc.stdio; import dmd.common.outbuffer; import dmd.root.array; import dmd.root.filename; +import dmd.root.string: toDString; version (DMDLIB) { @@ -27,8 +28,8 @@ enum MessageStyle : ubyte { digitalmars, /// filename.d(line): message gnu, /// filename.d:line: message, see https://www.gnu.org/prep/standards/html_node/Errors.html + sarif /// JSON SARIF output, see https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html } - /** A source code location @@ -126,35 +127,7 @@ nothrow: MessageStyle messageStyle = Loc.messageStyle) const nothrow { OutBuffer buf; - if (fileIndex) - { - buf.writestring(filename); - } - if (linnum) - { - final switch (messageStyle) - { - case MessageStyle.digitalmars: - buf.writeByte('('); - buf.print(linnum); - if (showColumns && charnum) - { - buf.writeByte(','); - buf.print(charnum); - } - buf.writeByte(')'); - break; - case MessageStyle.gnu: // https://www.gnu.org/prep/standards/html_node/Errors.html - buf.writeByte(':'); - buf.print(linnum); - if (showColumns && charnum) - { - buf.writeByte(':'); - buf.print(charnum); - } - break; - } - } + writeSourceLoc(buf, SourceLoc(this), showColumns, messageStyle); return buf.extractChars(); } @@ -210,3 +183,79 @@ nothrow: return fileIndex != 0; } } + +/** + * Format a source location for error messages + * + * Params: + * buf = buffer to write string into + * loc = source location to write + * showColumns = include column number in message + * messageStyle = select error message format + */ +void writeSourceLoc(ref OutBuffer buf, + SourceLoc loc, + bool showColumns, + MessageStyle messageStyle) nothrow +{ + buf.writestring(loc.filename); + if (loc.line == 0) + return; + + final switch (messageStyle) + { + case MessageStyle.digitalmars: + buf.writeByte('('); + buf.print(loc.line); + if (showColumns && loc.column) + { + buf.writeByte(','); + buf.print(loc.column); + } + buf.writeByte(')'); + break; + case MessageStyle.gnu: // https://www.gnu.org/prep/standards/html_node/Errors.html + buf.writeByte(':'); + buf.print(loc.line); + if (showColumns && loc.column) + { + buf.writeByte(':'); + buf.print(loc.column); + } + break; + case MessageStyle.sarif: // https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html + // No formatting needed here for SARIF + break; + } +} + +/** + * Describes a location in the source code as a file + line number + column number + * + * While `Loc` is a compact opaque location meant to be stored in the AST, + * this struct has simple modifiable fields and is used for printing. + */ +struct SourceLoc +{ + const(char)[] filename; /// name of source file + uint line; /// line number (starts at 1) + uint column; /// column number (starts at 1) + + // aliases for backwards compatibility + alias linnum = line; + alias charnum = column; + + this(const(char)[] filename, uint line, uint column) nothrow + { + this.filename = filename; + this.line = line; + this.column = column; + } + + this(Loc loc) nothrow + { + this.filename = loc.filename.toDString(); + this.line = loc.linnum; + this.column = loc.charnum; + } +} diff --git a/gcc/d/dmd/basicmangle.d b/gcc/d/dmd/mangle/basic.d similarity index 99% rename from gcc/d/dmd/basicmangle.d rename to gcc/d/dmd/mangle/basic.d index 52534fa3526bad4038b7c6b7560832b611f3fd46..eb5a49e9f15fc4e963a9d1f99712d39f3cdd65da 100644 --- a/gcc/d/dmd/basicmangle.d +++ b/gcc/d/dmd/mangle/basic.d @@ -7,7 +7,7 @@ * Documentation: https://dlang.org/phobos/dmd_basicmangle.html * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/basicmangle.d */ -module dmd.basicmangle; +module dmd.mangle.basic; import dmd.astenums; import dmd.common.outbuffer : OutBuffer; diff --git a/gcc/d/dmd/cppmangle.d b/gcc/d/dmd/mangle/cpp.d similarity index 99% rename from gcc/d/dmd/cppmangle.d rename to gcc/d/dmd/mangle/cpp.d index c5519217bcf9cae74f1b55939518512a2e899f29..9ce3f9c8ed038ae2989bb644ceb2dbf3fb0330af 100644 --- a/gcc/d/dmd/cppmangle.d +++ b/gcc/d/dmd/mangle/cpp.d @@ -21,7 +21,7 @@ * enter `C++, mangling` as the keywords. */ -module dmd.cppmangle; +module dmd.mangle.cpp; import core.stdc.stdio; @@ -30,6 +30,7 @@ import dmd.astenums; import dmd.attrib; import dmd.declaration; import dmd.dsymbol; +import dmd.dsymbolsem : isGNUABITag; import dmd.dtemplate; import dmd.errors; import dmd.expression; @@ -2433,7 +2434,7 @@ private struct ABITagContainer foreach (exp; *s.userAttribDecl.atts) { - if (UserAttributeDeclaration.isGNUABITag(exp)) + if (isGNUABITag(exp)) return (*exp.isStructLiteralExp().elements)[0] .isArrayLiteralExp(); } diff --git a/gcc/d/dmd/dmangle.d b/gcc/d/dmd/mangle/package.d similarity index 99% rename from gcc/d/dmd/dmangle.d rename to gcc/d/dmd/mangle/package.d index 21f0392a6fe3a3ca2922fabf6ff1d6752b978315..d184fe9f42c247c1a9eaa510fe77df530afc8714 100644 --- a/gcc/d/dmd/dmangle.d +++ b/gcc/d/dmd/mangle/package.d @@ -12,7 +12,7 @@ * References: https://dlang.org/blog/2017/12/20/ds-newfangled-name-mangling/ */ -module dmd.dmangle; +module dmd.mangle; /****************************************************************************** @@ -70,7 +70,7 @@ void mangleToBuffer(TemplateInstance ti, ref OutBuffer buf) } /// Returns: `true` if the given character is a valid mangled character -package bool isValidMangling(dchar c) nothrow +package(dmd) bool isValidMangling(dchar c) nothrow { import dmd.common.charactertables; @@ -139,7 +139,7 @@ import core.stdc.string; import dmd.aggregate; import dmd.arraytypes; import dmd.astenums; -import dmd.basicmangle; +import dmd.mangle.basic; import dmd.dclass; import dmd.declaration; import dmd.dinterpret; @@ -830,7 +830,7 @@ public: continue; } // Now that we know it is not an alias, we MUST obtain a value - uint olderr = global.errors; + const olderr = global.errors; ea = ea.ctfeInterpret(); if (ea.op == EXP.error || olderr != global.errors) continue; diff --git a/gcc/d/dmd/mtype.d b/gcc/d/dmd/mtype.d index aa4ca40d10801bfcd87b10378fecdd1237ec673d..be70b02dec90c4489f711e704396671330c5ec04 100644 --- a/gcc/d/dmd/mtype.d +++ b/gcc/d/dmd/mtype.d @@ -30,6 +30,7 @@ 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; @@ -515,7 +516,7 @@ extern (C++) abstract class Type : ASTNode static Type merge(Type t) { - import dmd.basicmangle : tyToDecoBuffer; + import dmd.mangle.basic : tyToDecoBuffer; OutBuffer buf; buf.reserve(3); @@ -1115,65 +1116,66 @@ extern (C++) abstract class Type : ASTNode Type t = this; if (t.isImmutable()) { + return t; } else if (stc & STC.immutable_) { t = t.makeImmutable(); + return t; } - else + + if ((stc & STC.shared_) && !t.isShared()) { - if ((stc & STC.shared_) && !t.isShared()) + if (t.isWild()) + { + if (t.isConst()) + t = t.makeSharedWildConst(); + else + t = t.makeSharedWild(); + } + else + { + if (t.isConst()) + t = t.makeSharedConst(); + else + t = t.makeShared(); + } + } + if ((stc & STC.const_) && !t.isConst()) + { + if (t.isShared()) { if (t.isWild()) - { - if (t.isConst()) - t = t.makeSharedWildConst(); - else - t = t.makeSharedWild(); - } + t = t.makeSharedWildConst(); else - { - if (t.isConst()) - t = t.makeSharedConst(); - else - t = t.makeShared(); - } + t = t.makeSharedConst(); } - if ((stc & STC.const_) && !t.isConst()) + else { - if (t.isShared()) - { - if (t.isWild()) - t = t.makeSharedWildConst(); - else - t = t.makeSharedConst(); - } + if (t.isWild()) + t = t.makeWildConst(); else - { - if (t.isWild()) - t = t.makeWildConst(); - else - t = t.makeConst(); - } + t = t.makeConst(); } - if ((stc & STC.wild) && !t.isWild()) + } + if ((stc & STC.wild) && !t.isWild()) + { + if (t.isShared()) { - if (t.isShared()) - { - if (t.isConst()) - t = t.makeSharedWildConst(); - else - t = t.makeSharedWild(); - } + if (t.isConst()) + t = t.makeSharedWildConst(); else - { - if (t.isConst()) - t = t.makeWildConst(); - else - t = t.makeWild(); - } + t = t.makeSharedWild(); + } + else + { + if (t.isConst()) + t = t.makeWildConst(); + else + t = t.makeWild(); } } + return t; } @@ -1398,33 +1400,6 @@ extern (C++) abstract class Type : ASTNode return t; } - /******************************************* - * Compute number of elements for a (possibly multidimensional) static array, - * or 1 for other types. - * Params: - * loc = for error message - * Returns: - * number of elements, uint.max on overflow - */ - final uint numberOfElems(const ref Loc loc) - { - //printf("Type::numberOfElems()\n"); - uinteger_t n = 1; - Type tb = this; - while ((tb = tb.toBasetype()).ty == Tsarray) - { - bool overflow = false; - n = mulu(n, (cast(TypeSArray)tb).dim.toUInteger(), overflow); - if (overflow || n >= uint.max) - { - error(loc, "static array `%s` size overflowed to %llu", toChars(), cast(ulong)n); - return uint.max; - } - tb = (cast(TypeSArray)tb).next; - } - return cast(uint)n; - } - /**************************************** * Return the mask that an integral type will * fit into. @@ -2724,36 +2699,6 @@ extern (C++) final class TypeFunction : TypeNext return newArgs; } - extern (D) bool checkRetType(const ref Loc loc) - { - Type tb = next.toBasetype(); - if (tb.ty == Tfunction) - { - error(loc, "functions cannot return a function"); - next = Type.terror; - } - if (tb.ty == Ttuple) - { - error(loc, "functions cannot return a sequence (use `std.typecons.Tuple`)"); - next = Type.terror; - } - if (!isRef && (tb.ty == Tstruct || tb.ty == Tsarray)) - { - if (auto ts = tb.baseElemOf().isTypeStruct()) - { - if (!ts.sym.members) - { - error(loc, "functions cannot return opaque type `%s` by value", tb.toChars()); - next = Type.terror; - } - } - } - if (tb.ty == Terror) - return true; - return false; - } - - /// Returns: `true` the function is `isInOutQual` or `isInOutParam` ,`false` otherwise. bool iswild() const pure nothrow @safe @nogc { diff --git a/gcc/d/dmd/objc.d b/gcc/d/dmd/objc.d index 6ba8d89b248a0047130a9a2bf86839a8a67d62eb..5cd928ca5f41f8ec28d1fefd8ae46f5f35637ecb 100644 --- a/gcc/d/dmd/objc.d +++ b/gcc/d/dmd/objc.d @@ -22,7 +22,6 @@ import dmd.cond; import dmd.dclass; import dmd.declaration; import dmd.denum; -import dmd.dmangle; import dmd.dmodule; import dmd.dscope; import dmd.dstruct; @@ -38,6 +37,7 @@ import dmd.hdrgen; import dmd.id; import dmd.identifier; import dmd.location; +import dmd.mangle; import dmd.mtype; import dmd.root.array; import dmd.common.outbuffer; diff --git a/gcc/d/dmd/optimize.d b/gcc/d/dmd/optimize.d index e409a7907e984c027bb87238c9239a05308d7c0e..282575db62cc1265d594b419c588e2133be84f33 100644 --- a/gcc/d/dmd/optimize.d +++ b/gcc/d/dmd/optimize.d @@ -183,31 +183,29 @@ private Expression fromConstInitializer(int result, Expression e1) { //printf("fromConstInitializer(result = %x, %s)\n", result, e1.toChars()); //static int xx; if (xx++ == 10) assert(0); + auto ve = e1.isVarExp(); + if (!ve) + return e1; + Expression e = e1; - if (auto ve = e1.isVarExp()) + VarDeclaration v = ve.var.isVarDeclaration(); + e = expandVar(result, v); + if (!e) + return e1; + + // If it is a comma expression involving a declaration, we mustn't + // perform a copy -- we'd get two declarations of the same variable. + // See https://issues.dlang.org/show_bug.cgi?id=4465. + if (e.op == EXP.comma && e.isCommaExp().e1.isDeclarationExp()) + e = e1; + else if (e.type != e1.type && e1.type && e1.type.ty != Tident) { - VarDeclaration v = ve.var.isVarDeclaration(); - e = expandVar(result, v); - if (e) - { - // If it is a comma expression involving a declaration, we mustn't - // perform a copy -- we'd get two declarations of the same variable. - // See https://issues.dlang.org/show_bug.cgi?id=4465. - if (e.op == EXP.comma && e.isCommaExp().e1.isDeclarationExp()) - e = e1; - else if (e.type != e1.type && e1.type && e1.type.ty != Tident) - { - // Type 'paint' operation - e = e.copy(); - e.type = e1.type; - } - e.loc = e1.loc; - } - else - { - e = e1; - } + // Type 'paint' operation + e = e.copy(); + e.type = e1.type; } + e.loc = e1.loc; + return e; } diff --git a/gcc/d/dmd/parse.d b/gcc/d/dmd/parse.d index bb2411825faeebd7baf7f866e2c7af22e975aa9d..e8324eb0710654db7f9984e2581f4094bf96fa39 100644 --- a/gcc/d/dmd/parse.d +++ b/gcc/d/dmd/parse.d @@ -49,8 +49,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer bool doUnittests; // parse unittest blocks } - bool transitionIn = false; /// `-transition=in` is active, `in` parameters are listed - /********************* * Use this constructor for string mixins. * Input: @@ -543,7 +541,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer break; case TOK.new_: - s = parseNew(pAttrs); + s = parseNewDeclaration(pAttrs); break; case TOK.colon: @@ -2493,7 +2491,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer AST.Expression constraint = tpl ? parseConstraint() : null; - AST.Type tf = new AST.TypeFunction(parameterList, null, linkage, stc); // RetrunType -> auto + AST.Type tf = new AST.TypeFunction(parameterList, null, linkage, stc); // ReturnType -> auto tf = tf.addSTC(stc); auto f = new AST.CtorDeclaration(loc, Loc.initial, stc, tf); @@ -2791,7 +2789,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * @disable new(); * Current token is 'new'. */ - private AST.Dsymbol parseNew(PrefixAttributes!AST* pAttrs) + private AST.Dsymbol parseNewDeclaration(PrefixAttributes!AST* pAttrs) { const loc = token.loc; StorageClass stc = getStorageClass!AST(pAttrs); @@ -2918,7 +2916,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer // Don't call nextToken again. } case TOK.in_: - if (transitionIn) + if (compileEnv.transitionIn) eSink.message(scanloc, "Usage of 'in' on parameter"); stc = STC.in_; if (compileEnv.previewIn) @@ -3501,7 +3499,12 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer name = _alias; _alias = null; } - s.addAlias(name, _alias); + if (s.isstatic) + error(loc, "static import `%s` cannot have an import bind list", s.toPrettyChars()); + if (!s.aliasId) + s.ident = null; // make it an anonymous import + s.names.push(name); + s.aliases.push(_alias); } while (token.value == TOK.comma); break; // no comma-separated imports of this form @@ -9499,6 +9502,15 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } /******************************************* + * Params: + * thisexp = If not null, it is the `this` reference for the creation + * of an inner class. + * https://dlang.org/spec/class.html#nested-explicit + * https://dlang.org/spec/expression.html#postfix_expressions + * If null, then it is a NewExpression. + * https://dlang.org/spec/expression.html#NewExpression + * Returns: + * NewExpression */ private AST.Expression parseNewExp(AST.Expression thisexp) { @@ -9769,6 +9781,7 @@ immutable PREC[EXP.max + 1] precedence = EXP.assign : PREC.assign, EXP.construct : PREC.assign, EXP.blit : PREC.assign, + EXP.loweredAssignExp : PREC.assign, EXP.addAssign : PREC.assign, EXP.minAssign : PREC.assign, EXP.concatenateAssign : PREC.assign, diff --git a/gcc/d/dmd/pragmasem.d b/gcc/d/dmd/pragmasem.d index 4dceb595e54fef5a20d2e54b0db44f8aa324883a..f2f748500a633cef34d2aa469cd2d080535ce801 100644 --- a/gcc/d/dmd/pragmasem.d +++ b/gcc/d/dmd/pragmasem.d @@ -21,6 +21,7 @@ import dmd.attrib; import dmd.dinterpret; import dmd.dscope; import dmd.dsymbol; +import dmd.dsymbolsem : include; import dmd.errors; import dmd.expression; import dmd.expressionsem; @@ -40,10 +41,10 @@ void pragmaDeclSemantic(PragmaDeclaration pd, Scope* sc) { import dmd.aggregate; import dmd.common.outbuffer; - import dmd.dmangle; import dmd.dmodule; import dmd.dsymbolsem; import dmd.identifier; + import dmd.mangle : isValidMangling; import dmd.root.rmem; import dmd.root.utf; import dmd.target; @@ -557,32 +558,19 @@ private uint setMangleOverride(Dsymbol s, const(char)[] sym) private bool pragmaMsgSemantic(Loc loc, Scope* sc, Expressions* args) { import dmd.tokens; + import dmd.common.outbuffer; if (!args) return true; - foreach (arg; *args) - { - sc = sc.startCTFE(); - auto e = arg.expressionSemantic(sc); - e = resolveProperties(sc, e); - sc = sc.endCTFE(); - // pragma(msg) is allowed to contain types as well as expressions - e = ctfeInterpretForPragmaMsg(e); - if (e.op == EXP.error) - { - errorSupplemental(loc, "while evaluating `pragma(msg, %s)`", arg.toChars()); - return false; - } - if (auto se = e.toStringExp()) - { - const slice = se.toUTF8(sc).peekString(); - fprintf(stderr, "%.*s", cast(int)slice.length, slice.ptr); - } - else - fprintf(stderr, "%s", e.toChars()); + OutBuffer buf; + if (expressionsToString(buf, sc, args, loc, "while evaluating `pragma(msg, %s)`", false)) + return false; + else + { + buf.writestring("\n"); + fprintf(stderr, buf.extractChars); } - fprintf(stderr, "\n"); return true; } diff --git a/gcc/d/dmd/root/filename.d b/gcc/d/dmd/root/filename.d index fb9a18b2892d05c3c66e6d774119523445f3abbb..a2f074dbfaec0167c19a52a091cfcb6cdf46ee07 100644 --- a/gcc/d/dmd/root/filename.d +++ b/gcc/d/dmd/root/filename.d @@ -277,7 +277,7 @@ nothrow: * Returns: * the slice */ - extern (D) static const(char)[] sansExt(const char[] filename) + extern (D) static const(char)[] sansExt(const char[] filename) @safe { auto e = ext(filename); size_t length = e.length; @@ -1101,7 +1101,7 @@ nothrow: return str.ptr; } - const(char)[] toString() const pure nothrow @nogc @trusted + const(char)[] toString() const pure nothrow @nogc @safe { return str; } diff --git a/gcc/d/dmd/root/string.d b/gcc/d/dmd/root/string.d index 4b4c3e1f6457fb99bee41afb1d4c3329509a86e2..383c59de01362fbfc8e65e9d28b3e0336052e265 100644 --- a/gcc/d/dmd/root/string.d +++ b/gcc/d/dmd/root/string.d @@ -20,6 +20,27 @@ inout(char)[] toDString (inout(char)* s) pure nothrow @nogc return s ? s[0 .. strlen(s)] : null; } +private struct FTuple(T...) +{ + T expand; +} + +/// Returns: a (length, ptr) tuple for passing a D string to `printf`-style functions with the format string `%.*s` +auto fTuple(const(char)[] str) +{ + return FTuple!(int, const(char)*)(cast(int) str.length, str.ptr); +} + +/// +unittest +{ + import core.stdc.stdio: snprintf; + char[6] buf = '.'; + const(char)[] str = "cutoff"[0..4]; + snprintf(buf.ptr, buf.length, "%.*s", str.fTuple.expand); + assert(buf[] == "cuto\0."); +} + /** Compare two slices for equality, in a case-insensitive way @@ -433,7 +454,7 @@ Returns: a `FindSplit` object that casts to `true` iff `needle` was found inside `str`. In that case, `split[1]` is the needle, and `split[0]`/`split[2]` are before/after the needle. */ -FindSplit findSplit(return scope const(char)[] str, scope const(char)[] needle) +FindSplit findSplit(return scope const(char)[] str, scope const(char)[] needle) @safe { if (needle.length > str.length) return FindSplit([str, null, null]); @@ -469,7 +490,7 @@ Params: Returns: substring of `str` inbetween `l` and `r` */ -const(char)[] findBetween(const(char)[] str, const(char)[] l, const(char)[] r) +const(char)[] findBetween(const(char)[] str, const(char)[] l, const(char)[] r) @safe { if (auto s0 = str.findSplit(l)) if (auto s1 = s0[2].findSplit(r)) diff --git a/gcc/d/dmd/safe.d b/gcc/d/dmd/safe.d index 34cab8008683bcac2c2cb2bc63c6977fcba47fc7..24d87458551655a2595f0b68f2a3117e49aa19c7 100644 --- a/gcc/d/dmd/safe.d +++ b/gcc/d/dmd/safe.d @@ -21,14 +21,20 @@ import dmd.dcast : implicitConvTo; import dmd.dclass; import dmd.declaration; import dmd.dscope; +import dmd.dsymbolsem : determineSize; +import dmd.errors; import dmd.expression; +import dmd.func; +import dmd.funcsem : isRootTraitsCompilesScope; +import dmd.globals : FeatureState; import dmd.id; import dmd.identifier; +import dmd.location; import dmd.mtype; +import dmd.rootobject; import dmd.target; import dmd.tokens; import dmd.typesem : hasPointers, arrayOf, size; -import dmd.funcsem : setUnsafe, setUnsafePreview; /************************************************************* * Check for unsafe access in @safe code: @@ -309,3 +315,183 @@ bool checkUnsafeDotExp(Scope* sc, Expression e, Identifier id, int flag) } return false; } + +bool isSafe(FuncDeclaration fd) +{ + if (fd.safetyInprocess) + fd.setUnsafe(); + return fd.type.toTypeFunction().trust == TRUST.safe; +} + +extern (D) bool isSafeBypassingInference(FuncDeclaration fd) +{ + return !(fd.safetyInprocess) && fd.isSafe(); +} + +bool isTrusted(FuncDeclaration fd) +{ + if (fd.safetyInprocess) + fd.setUnsafe(); + return fd.type.toTypeFunction().trust == TRUST.trusted; +} + +/************************************** + * The function is doing something unsafe, so mark it as unsafe. + * + * Params: + * fd = func declaration to set unsafe + * gag = surpress error message (used in escape.d) + * loc = location of error + * fmt = printf-style format string + * arg0 = (optional) argument for first %s format specifier + * arg1 = (optional) argument for second %s format specifier + * arg2 = (optional) argument for third %s format specifier + * Returns: whether there's a safe error + */ +extern (D) bool setUnsafe( + FuncDeclaration fd, + bool gag = false, Loc loc = Loc.init, const(char)* fmt = null, + RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) +{ + if (fd.safetyInprocess) + { + fd.safetyInprocess = false; + fd.type.toTypeFunction().trust = TRUST.system; + if (fmt || arg0) + fd.safetyViolation = new AttributeViolation(loc, fmt, arg0, arg1, arg2); + + if (fd.fes) + fd.fes.func.setUnsafe(); + } + else if (fd.isSafe()) + { + if (!gag && fmt) + .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); + + return true; + } + return false; +} + +/************************************** + * The function is calling `@system` function `f`, so mark it as unsafe. + * + * Params: + * fd = caller + * f = function being called (needed for diagnostic of inferred functions) + * Returns: whether there's a safe error + */ +extern (D) bool setUnsafeCall(FuncDeclaration fd, FuncDeclaration f) +{ + return fd.setUnsafe(false, f.loc, null, f, null); +} + +/************************************** + * A statement / expression in this scope is not `@safe`, + * so mark the enclosing function as `@system` + * + * Params: + * sc = scope that the unsafe statement / expression is in + * gag = surpress error message (used in escape.d) + * loc = location of error + * fmt = printf-style format string + * arg0 = (optional) argument for first %s format specifier + * arg1 = (optional) argument for second %s format specifier + * arg2 = (optional) argument for third %s format specifier + * Returns: whether there's a safe error + */ +bool setUnsafe(Scope* sc, + bool gag = false, Loc loc = Loc.init, const(char)* fmt = null, + RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) +{ + if (sc.intypeof) + return false; // typeof(cast(int*)0) is safe + + if (sc.debug_) // debug {} scopes are permissive + return false; + + if (!sc.func) + { + if (sc.varDecl) + { + if (sc.varDecl.storage_class & STC.safe) + { + .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); + return true; + } + else if (!(sc.varDecl.storage_class & STC.trusted)) + { + sc.varDecl.storage_class |= STC.system; + sc.varDecl.systemInferred = true; + } + } + return false; + } + + + if (isRootTraitsCompilesScope(sc)) // __traits(compiles, x) + { + if (sc.func.isSafeBypassingInference()) + { + // Message wil be gagged, but still call error() to update global.errors and for + // -verrors=spec + .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); + return true; + } + return false; + } + + return sc.func.setUnsafe(gag, loc, fmt, arg0, arg1, arg2); +} + +/*************************************** + * Like `setUnsafe`, but for safety errors still behind preview switches + * + * Given a `FeatureState fs`, for example dip1000 / dip25 / systemVariables, + * the behavior changes based on the setting: + * + * - In case of `-revert=fs`, it does nothing. + * - In case of `-preview=fs`, it's the same as `setUnsafe` + * - By default, print a deprecation in `@safe` functions, or store an attribute violation in inferred functions. + * + * Params: + * sc = used to find affected function/variable, and for checking whether we are in a deprecated / speculative scope + * fs = feature state from the preview flag + * gag = surpress error message + * loc = location of error + * msg = printf-style format string + * arg0 = (optional) argument for first %s format specifier + * arg1 = (optional) argument for second %s format specifier + * arg2 = (optional) argument for third %s format specifier + * Returns: whether an actual safe error (not deprecation) occured + */ +bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char)* msg, + RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) +{ + //printf("setUnsafePreview() fs:%d %s\n", fs, msg); + with (FeatureState) final switch (fs) + { + case disabled: + return false; + + case enabled: + return sc.setUnsafe(gag, loc, msg, arg0, arg1, arg2); + + case default_: + if (!sc.func) + return false; + if (sc.func.isSafeBypassingInference()) + { + if (!gag && !sc.isDeprecated()) + { + deprecation(loc, msg, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); + } + } + else if (!sc.func.safetyViolation) + { + import dmd.func : AttributeViolation; + sc.func.safetyViolation = new AttributeViolation(loc, msg, arg0, arg1, arg2); + } + return false; + } +} diff --git a/gcc/d/dmd/semantic2.d b/gcc/d/dmd/semantic2.d index fb7373c73f7bd7d2848a1f6b61046f2c35e54fd7..31fc418dbcc32b9e44dcfd257fab8bf7ee88fc28 100644 --- a/gcc/d/dmd/semantic2.d +++ b/gcc/d/dmd/semantic2.d @@ -146,11 +146,9 @@ private extern(C++) final class Semantic2Visitor : Visitor sc.tinst = tempinst; sc.minst = tempinst.minst; - int needGagging = (tempinst.gagged && !global.gag); - uint olderrors = global.errors; - int oldGaggedErrors = -1; // dead-store to prevent spurious warning - if (needGagging) - oldGaggedErrors = global.startGagging(); + const needGagging = (tempinst.gagged && !global.gag); + const olderrors = global.errors; + const oldGaggedErrors = needGagging ? global.startGagging() : -1; for (size_t i = 0; i < tempinst.members.length; i++) { @@ -230,7 +228,7 @@ private extern(C++) final class Semantic2Visitor : Visitor return; } - UserAttributeDeclaration.checkGNUABITag(vd, vd._linkage); + checkGNUABITag(vd, vd._linkage); if (vd._init && !vd.toParent().isFuncDeclaration()) { @@ -471,7 +469,7 @@ private extern(C++) final class Semantic2Visitor : Visitor return; TypeFunction f = cast(TypeFunction) fd.type; - UserAttributeDeclaration.checkGNUABITag(fd, fd._linkage); + checkGNUABITag(fd, fd._linkage); //semantic for parameters' UDAs foreach (i, param; f.parameterList) { @@ -505,7 +503,7 @@ private extern(C++) final class Semantic2Visitor : Visitor printf("+Nspace::semantic2('%s')\n", ns.toChars()); scope(exit) printf("-Nspace::semantic2('%s')\n", ns.toChars()); } - UserAttributeDeclaration.checkGNUABITag(ns, LINK.cpp); + checkGNUABITag(ns, LINK.cpp); if (!ns.members) return; @@ -563,7 +561,7 @@ private extern(C++) final class Semantic2Visitor : Visitor override void visit(CPPNamespaceDeclaration decl) { - UserAttributeDeclaration.checkGNUABITag(decl, LINK.cpp); + checkGNUABITag(decl, LINK.cpp); visit(cast(AttribDeclaration)decl); } @@ -590,7 +588,7 @@ private extern(C++) final class Semantic2Visitor : Visitor } // Handles compiler-recognized `core.attribute.gnuAbiTag` - if (UserAttributeDeclaration.isGNUABITag(e)) + if (isGNUABITag(e)) doGNUABITagSemantic(e, lastTag); } } @@ -612,8 +610,7 @@ private extern(C++) final class Semantic2Visitor : Visitor return; } - UserAttributeDeclaration.checkGNUABITag( - ad, ad.classKind == ClassKind.cpp ? LINK.cpp : LINK.d); + checkGNUABITag(ad, ad.classKind == ClassKind.cpp ? LINK.cpp : LINK.d); auto sc2 = ad.newScope(sc); @@ -720,8 +717,7 @@ private extern(C++) final class Semantic2Visitor : Visitor */ private void doGNUABITagSemantic(ref Expression e, ref Expression* lastTag) { - import dmd.dmangle; - + import dmd.mangle : isValidMangling; // When `@gnuAbiTag` is used, the type will be the UDA, not the struct literal if (e.op == EXP.type) { diff --git a/gcc/d/dmd/semantic3.d b/gcc/d/dmd/semantic3.d index 24bdd449d5eee68221638c79093934a2a78532f3..4f10982a2d1cfd524a87a1e79090eece335cde15 100644 --- a/gcc/d/dmd/semantic3.d +++ b/gcc/d/dmd/semantic3.d @@ -120,15 +120,13 @@ private extern(C++) final class Semantic3Visitor : Visitor sc.minst = tempinst.minst; bool needGagging = tempinst.gagged && !global.gag; - uint olderrors = global.errors; - int oldGaggedErrors = -1; // dead-store to prevent spurious warning + const olderrors = global.errors; + const oldGaggedErrors = needGagging ? global.startGagging() : -1; /* If this is a gagged instantiation, gag errors. * Future optimisation: If the results are actually needed, errors * would already be gagged, so we don't really need to run semantic * on the members. */ - if (needGagging) - oldGaggedErrors = global.startGagging(); for (size_t i = 0; i < tempinst.members.length; i++) { @@ -171,7 +169,7 @@ private extern(C++) final class Semantic3Visitor : Visitor sc = sc.push(tmix.argsym); sc = sc.push(tmix); - uint olderrors = global.errors; + const olderrors = global.errors; for (size_t i = 0; i < tmix.members.length; i++) { @@ -280,7 +278,7 @@ private extern(C++) final class Semantic3Visitor : Visitor * For generated opAssign function, any errors * from its body need to be gagged. */ - uint oldErrors = global.startGagging(); + const oldErrors = global.startGagging(); ++funcdecl.inuse; funcdecl.semantic3(sc); --funcdecl.inuse; @@ -313,7 +311,7 @@ private extern(C++) final class Semantic3Visitor : Visitor return; } - uint oldErrors = global.errors; + const oldErrors = global.errors; auto fds = FuncDeclSem3(funcdecl,sc); fds.checkInContractOverrides(); @@ -799,7 +797,8 @@ private extern(C++) final class Semantic3Visitor : Visitor Statement s = new ReturnStatement(funcdecl.loc, null); s = s.statementSemantic(sc2); funcdecl.fbody = new CompoundStatement(funcdecl.loc, funcdecl.fbody, s); - funcdecl.hasReturnExp |= (funcdecl.hasReturnExp & 1 ? 16 : 1); + funcdecl.hasMultipleReturnExp = funcdecl.hasReturnExp; + funcdecl.hasReturnExp = true; } } else if (funcdecl.fes) @@ -810,7 +809,8 @@ private extern(C++) final class Semantic3Visitor : Visitor Expression e = IntegerExp.literal!0; Statement s = new ReturnStatement(Loc.initial, e); funcdecl.fbody = new CompoundStatement(Loc.initial, funcdecl.fbody, s); - funcdecl.hasReturnExp |= (funcdecl.hasReturnExp & 1 ? 16 : 1); + funcdecl.hasMultipleReturnExp = funcdecl.hasReturnExp; + funcdecl.hasReturnExp = true; } assert(!funcdecl.returnLabel); } @@ -825,8 +825,7 @@ private extern(C++) final class Semantic3Visitor : Visitor } else { - const(bool) inlineAsm = (funcdecl.hasReturnExp & 8) != 0; - if ((blockexit & BE.fallthru) && f.next.ty != Tvoid && !inlineAsm && !sc.inCfile) + if ((blockexit & BE.fallthru) && f.next.ty != Tvoid && !funcdecl.hasInlineAsm && !sc.inCfile) { if (!funcdecl.hasReturnExp) .error(funcdecl.loc, "%s `%s` has no `return` statement, but is expected to return a value of type `%s`", funcdecl.kind, funcdecl.toPrettyChars, f.next.toChars()); @@ -1177,32 +1176,31 @@ private extern(C++) final class Semantic3Visitor : Visitor { if (v.isReference() || (v.storage_class & STC.lazy_)) continue; - if (v.needsScopeDtor()) - { - v.storage_class |= STC.nodtor; - if (!paramsNeedDtor) - continue; - - // same with ExpStatement.scopeCode() - Statement s = new DtorExpStatement(Loc.initial, v.edtor, v); + if (!v.needsScopeDtor()) + continue; + v.storage_class |= STC.nodtor; + if (!paramsNeedDtor) + continue; - s = s.statementSemantic(sc2); + // same with ExpStatement.scopeCode() + Statement s = new DtorExpStatement(Loc.initial, v.edtor, v); - const blockexit = s.blockExit(funcdecl, isNothrow ? global.errorSink : null); - if (blockexit & BE.throw_) - { - funcdecl.hasNoEH = false; - if (isNothrow) - error(funcdecl.loc, "%s `%s` may throw but is marked as `nothrow`", funcdecl.kind(), funcdecl.toPrettyChars()); - else if (funcdecl.nothrowInprocess) - f.isNothrow = false; - } + s = s.statementSemantic(sc2); - if (sbody.blockExit(funcdecl, f.isNothrow ? global.errorSink : null) == BE.fallthru) - sbody = new CompoundStatement(Loc.initial, sbody, s); - else - sbody = new TryFinallyStatement(Loc.initial, sbody, s); + const blockexit = s.blockExit(funcdecl, isNothrow ? global.errorSink : null); + if (blockexit & BE.throw_) + { + funcdecl.hasNoEH = false; + if (isNothrow) + error(funcdecl.loc, "%s `%s` may throw but is marked as `nothrow`", funcdecl.kind(), funcdecl.toPrettyChars()); + else if (funcdecl.nothrowInprocess) + f.isNothrow = false; } + + if (sbody.blockExit(funcdecl, f.isNothrow ? global.errorSink : null) == BE.fallthru) + sbody = new CompoundStatement(Loc.initial, sbody, s); + else + sbody = new TryFinallyStatement(Loc.initial, sbody, s); } } // from this point on all possible 'throwers' are checked @@ -1640,7 +1638,7 @@ void semanticTypeInfoMembers(StructDeclaration sd) sd.xeq._scope && sd.xeq.semanticRun < PASS.semantic3done) { - uint errors = global.startGagging(); + const errors = global.startGagging(); sd.xeq.semantic3(sd.xeq._scope); if (global.endGagging(errors)) sd.xeq = sd.xerreq; @@ -1650,7 +1648,7 @@ void semanticTypeInfoMembers(StructDeclaration sd) sd.xcmp._scope && sd.xcmp.semanticRun < PASS.semantic3done) { - uint errors = global.startGagging(); + const errors = global.startGagging(); sd.xcmp.semantic3(sd.xcmp._scope); if (global.endGagging(errors)) sd.xcmp = sd.xerrcmp; diff --git a/gcc/d/dmd/statement.d b/gcc/d/dmd/statement.d index b53c3d085b9d7aa091632cd92b6fd193ef707c30..5c759b088f5d6edc0fccef336ba0b93aa7ba9dea 100644 --- a/gcc/d/dmd/statement.d +++ b/gcc/d/dmd/statement.d @@ -19,7 +19,6 @@ import core.stdc.stdio; import dmd.arraytypes; import dmd.astenums; import dmd.ast_node; -import dmd.errors; import dmd.cond; import dmd.declaration; import dmd.dsymbol; diff --git a/gcc/d/dmd/statementsem.d b/gcc/d/dmd/statementsem.d index 9d988f08d12531cb89632f7be5095997e192c772..c584b846489060fa8287f9dfa167acb47043b982 100644 --- a/gcc/d/dmd/statementsem.d +++ b/gcc/d/dmd/statementsem.d @@ -57,6 +57,7 @@ import dmd.opover; import dmd.parse; import dmd.common.outbuffer; import dmd.root.string; +import dmd.safe : isSafe, setUnsafe; import dmd.semantic2; import dmd.sideeffect; import dmd.statement; @@ -2514,7 +2515,8 @@ Statement statementSemanticVisit(Statement s, Scope* sc) } else if (rs.exp) { - fd.hasReturnExp |= (fd.hasReturnExp & 1 ? 16 : 1); + fd.hasMultipleReturnExp = fd.hasReturnExp; + fd.hasReturnExp = true; FuncLiteralDeclaration fld = fd.isFuncLiteralDeclaration(); if (tret) @@ -3685,9 +3687,6 @@ public bool throwSemantic(const ref Loc loc, ref Expression exp, Scope* sc) return false; } - if (FuncDeclaration fd = sc.parent.isFuncDeclaration()) - fd.hasReturnExp |= 2; - if (auto ne = exp.isNewExp()) { ne.thrownew = true; @@ -4802,7 +4801,7 @@ private Statements* flatten(Statement statement, Scope* sc) OutBuffer buf; - if (expressionsToString(buf, sc, cs.exps)) + if (expressionsToString(buf, sc, cs.exps, cs.loc, null, true)) return errorStatements(); const errors = global.errors; @@ -4812,7 +4811,6 @@ private Statements* flatten(Statement statement, Scope* sc) const bool doUnittests = global.params.parsingUnittestsRequired(); auto loc = adjustLocForMixin(str, cs.loc, global.params.mixinOut); scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); - p.transitionIn = global.params.v.vin; p.nextToken(); auto a = new Statements(); diff --git a/gcc/d/dmd/target.d b/gcc/d/dmd/target.d index cc44b6c0c3b7e118fbc845cb661c16df58b2e486..77187105c516706b409f41d6f192683f9ff86ab4 100644 --- a/gcc/d/dmd/target.d +++ b/gcc/d/dmd/target.d @@ -301,6 +301,8 @@ extern (C++) struct Target */ struct TargetC { + import dmd.declaration : BitFieldDeclaration; + enum Runtime : ubyte { Unspecified, @@ -331,6 +333,14 @@ struct TargetC ubyte wchar_tsize; /// size of a C `wchar_t` type Runtime runtime; /// vendor of the C runtime to link against BitFieldStyle bitFieldStyle; /// different C compilers do it differently + + /** + * Indicates whether the specified bit-field contributes to the alignment + * of the containing aggregate. + * E.g., (not all) ARM ABIs do NOT ignore anonymous (incl. 0-length) + * bit-fields. + */ + extern (C++) bool contributesToAggregateAlignment(BitFieldDeclaration bfd); } //////////////////////////////////////////////////////////////////////////////// diff --git a/gcc/d/dmd/target.h b/gcc/d/dmd/target.h index 2284b8322e7bb0e8d5c530133a659cdadbe51f34..1a9837ff4c70cf24b531b530d642cc89c2c7a789 100644 --- a/gcc/d/dmd/target.h +++ b/gcc/d/dmd/target.h @@ -16,6 +16,7 @@ #include "globals.h" #include "tokens.h" +class BitFieldDeclaration; class ClassDeclaration; class Dsymbol; class Expression; @@ -77,6 +78,8 @@ struct TargetC uint8_t wchar_tsize; // size of a C 'wchar_t' type Runtime runtime; BitFieldStyle bitFieldStyle; // different C compilers do it differently + + bool contributesToAggregateAlignment(BitFieldDeclaration *bfd); }; struct TargetCPP diff --git a/gcc/d/dmd/template.h b/gcc/d/dmd/template.h index bccec1a68c128ff7222ef69677ff86735d7e7160..e751eadb5aeb7066c77b9045b732e19e0eaa171d 100644 --- a/gcc/d/dmd/template.h +++ b/gcc/d/dmd/template.h @@ -133,9 +133,6 @@ public: DYNCAST dyncast() const override { return DYNCAST_TEMPLATEPARAMETER; } - /* Create dummy argument based on parameter. - */ - virtual RootObject *dummyArg() = 0; void accept(Visitor *v) override { v->visit(this); } }; @@ -155,7 +152,6 @@ public: RootObject *specialization() override final; RootObject *defaultArg(const Loc &instLoc, Scope *sc) override final; bool hasDefaultArg() override final; - RootObject *dummyArg() override final; void accept(Visitor *v) override { v->visit(this); } }; @@ -187,7 +183,6 @@ public: RootObject *specialization() override; RootObject *defaultArg(const Loc &instLoc, Scope *sc) override; bool hasDefaultArg() override; - RootObject *dummyArg() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -208,7 +203,6 @@ public: RootObject *specialization() override; RootObject *defaultArg(const Loc &instLoc, Scope *sc) override; bool hasDefaultArg() override; - RootObject *dummyArg() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -225,7 +219,6 @@ public: RootObject *specialization() override; RootObject *defaultArg(const Loc &instLoc, Scope *sc) override; bool hasDefaultArg() override; - RootObject *dummyArg() override; void accept(Visitor *v) override { v->visit(this); } }; diff --git a/gcc/d/dmd/templatesem.d b/gcc/d/dmd/templatesem.d index 21b10bee7c94f44699dccf394062fdd98013bb74..bd3674a8cc8260b39c46ce0e7b54bc04ca3da070 100644 --- a/gcc/d/dmd/templatesem.d +++ b/gcc/d/dmd/templatesem.d @@ -23,7 +23,6 @@ import dmd.dcast; import dmd.dclass; import dmd.declaration; import dmd.dinterpret; -import dmd.dmangle; import dmd.dmodule; import dmd.dscope; import dmd.dsymbol; @@ -109,7 +108,7 @@ void templateDeclarationSemantic(Scope* sc, TemplateDeclaration tempdecl) tempdecl.isstatic = tempdecl.toParent().isModule() || (tempdecl._scope.stc & STC.static_); tempdecl.deprecated_ = !!(sc.stc & STC.deprecated_); - UserAttributeDeclaration.checkGNUABITag(tempdecl, sc.linkage); + checkGNUABITag(tempdecl, sc.linkage); if (!tempdecl.isstatic) { @@ -354,7 +353,7 @@ MATCH matchWithInstance(Scope* sc, TemplateDeclaration td, TemplateInstance ti, // Resolve parameter types and 'auto ref's. tf.inferenceArguments = argumentList; - uint olderrors = global.startGagging(); + const olderrors = global.startGagging(); fd.type = tf.typeSemantic(td.loc, paramscope); global.endGagging(olderrors); if (fd.type.ty != Tfunction) @@ -1422,7 +1421,7 @@ extern (D) MATCHpair deduceFunctionTemplateMatch(TemplateDeclaration td, Templat Dsymbol s; Scope *sco; - uint errors = global.startGagging(); + const errors = global.startGagging(); /* ref: https://issues.dlang.org/show_bug.cgi?id=11118 * The parameter isn't part of the template * ones, let's try to find it in the @@ -2429,3 +2428,70 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, m.last = MATCH.nomatch; } } +/* Create dummy argument based on parameter. + */ +private RootObject dummyArg(TemplateParameter tp) +{ + scope v = new DummyArgVisitor(); + tp.accept(v); + return v.result; +} +private extern(C++) class DummyArgVisitor : Visitor +{ + RootObject result; + + alias visit = typeof(super).visit; + override void visit(TemplateTypeParameter ttp) + { + Type t = ttp.specType; + if (t) + { + result = t; + return; + } + // Use this for alias-parameter's too (?) + if (!ttp.tdummy) + ttp.tdummy = new TypeIdentifier(ttp.loc, ttp.ident); + result = ttp.tdummy; + } + + override void visit(TemplateValueParameter tvp) + { + Expression e = tvp.specValue; + if (e) + { + result = e; + return; + } + + // Create a dummy value + auto pe = cast(void*)tvp.valType in tvp.edummies; + if (pe) + { + result = *pe; + return; + } + + e = tvp.valType.defaultInit(Loc.initial); + tvp.edummies[cast(void*)tvp.valType] = e; + result = e; + } + + override void visit(TemplateAliasParameter tap) + { + RootObject s = tap.specAlias; + if (s) + { + result = s; + return; + } + if (!tap.sdummy) + tap.sdummy = new Dsymbol(); + result = tap.sdummy; + } + + override void visit(TemplateTupleParameter tap) + { + result = null; + } +} diff --git a/gcc/d/dmd/traits.d b/gcc/d/dmd/traits.d index 1e7386147b1df8ad6e53799b97cd35f0c631d382..7b32209fd2ef7fecdb69de99968823113dd64119 100644 --- a/gcc/d/dmd/traits.d +++ b/gcc/d/dmd/traits.d @@ -26,7 +26,6 @@ import dmd.dclass; import dmd.declaration; import dmd.dimport; import dmd.dinterpret; -import dmd.dmangle; import dmd.dmodule; import dmd.dscope; import dmd.dsymbol; @@ -43,6 +42,7 @@ import dmd.hdrgen; import dmd.id; import dmd.identifier; import dmd.location; +import dmd.mangle : decoToType; import dmd.mtype; import dmd.nogc; import dmd.optimize; @@ -1038,7 +1038,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) e.ident == Id.getVirtualMethods || e.ident == Id.getOverloads) { - uint errors = global.errors; + const errors = global.errors; Expression eorig = ex; ex = ex.expressionSemantic(scx); if (errors < global.errors) @@ -1746,7 +1746,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) foreach (o; *e.args) { - uint errors = global.startGagging(); + const errors = global.startGagging(); Scope* sc2 = sc.push(); sc2.tinst = null; sc2.minst = null; // this is why code for these are not emitted to object file @@ -1991,9 +1991,9 @@ Expression semanticTraits(TraitsExp e, Scope* sc) return dimError(1); auto arg0 = (*e.args)[0]; Dsymbol s = getDsymbolWithoutExpCtx(arg0); - if (!s || !s.loc.isValid()) + if (!s || !s.loc.isValid() || s.isModule()) { - error(e.loc, "can only get the location of a symbol, not `%s`", arg0.toChars()); + error(e.loc, "can only get the location of a symbol, not `%s`", s ? s.toPrettyChars() : arg0.toChars()); return ErrorExp.get(); } diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d index a35a6eba73f713c7d1c0d9770ebee9bf1bcc856a..eebbf45573dc32c15ae3712d645569a0c5d6a4e0 100644 --- a/gcc/d/dmd/typesem.d +++ b/gcc/d/dmd/typesem.d @@ -28,7 +28,6 @@ import dmd.declaration; import dmd.denum; import dmd.dimport; import dmd.dinterpret; -import dmd.dmangle; import dmd.dmodule; import dmd.dscope; import dmd.dstruct; @@ -53,6 +52,7 @@ import dmd.initsem; import dmd.location; import dmd.visitor; import dmd.mtype; +import dmd.mangle; import dmd.nogc; import dmd.objc; import dmd.opover; @@ -230,7 +230,7 @@ private void resolveHelper(TypeQualified mt, const ref Loc loc, Scope* sc, Dsymb } Type t = s.getType(); // type symbol, type alias, or type tuple? - uint errorsave = global.errors; + const errorsave = global.errors; SearchOptFlags flags = t is null ? SearchOpt.localsOnly : SearchOpt.ignorePrivateImports; Dsymbol sm = s.searchX(loc, sc, id, flags); @@ -836,7 +836,9 @@ extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentLis L1: if (parameterList.varargs == VarArg.typesafe && u + 1 == nparams) // if last varargs param { - auto trailingArgs = args[u .. $]; + Expression[] trailingArgs; + if (args.length >= u) + trailingArgs = args[u .. $]; if (auto vmatch = matchTypeSafeVarArgs(tf, p, trailingArgs, pMessage)) return vmatch < match ? vmatch : match; // Error message was already generated in `matchTypeSafeVarArgs` @@ -905,25 +907,19 @@ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct, */ OutBuffer buf; auto callExp = e.isCallExp(); - void nocpctor() + + bool nocpctor() { buf.printf("`struct %s` does not define a copy constructor for `%s` to `%s` copies", argStruct.toChars(), arg.type.toChars(), tprm.toChars()); - } - auto f = callExp.f; - if (!f) - { - nocpctor(); *pMessage = buf.extractChars(); return false; } - char[] s; - if (!f.isPure && sc.func.setImpure()) - s ~= "pure "; - if (!f.isSafe() && !f.isTrusted() && sc.setUnsafe()) - s ~= "@safe "; - if (!f.isNogc && sc.func.setGC(arg.loc, null)) - s ~= "nogc "; + + auto f = callExp.f; + if (!f) + return nocpctor(); + if (f.isDisabled() && !f.isGenerated()) { /* https://issues.dlang.org/show_bug.cgi?id=24301 @@ -931,11 +927,21 @@ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct, */ buf.printf("`%s` copy constructor cannot be used because it is annotated with `@disable`", f.type.toChars()); + *pMessage = buf.extractChars(); + return false; } - else if (s) + + bool bpure = !f.isPure && sc.func.setImpure(); + bool bsafe = !f.isSafe() && !f.isTrusted() && sc.setUnsafe(); + bool bnogc = !f.isNogc && sc.func.setGC(arg.loc, null); + if (bpure | bsafe | bnogc) { - s[$-1] = '\0'; - buf.printf("`%s` copy constructor cannot be called from a `%s` context", f.type.toChars(), s.ptr); + const nullptr = "".ptr; + buf.printf("`%s` copy constructor cannot be called from a `%s%s%s` context", + f.type.toChars(), + bpure ? "pure " .ptr : nullptr, + bsafe ? "@safe ".ptr : nullptr, + bnogc ? "nogc" .ptr : nullptr); } else if (f.isGenerated() && f.isDisabled()) { @@ -951,7 +957,7 @@ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct, * i.e: `inout` constructor creates `const` object, not mutable. * Fallback to using the original generic error before https://issues.dlang.org/show_bug.cgi?id=22202. */ - nocpctor(); + return nocpctor(); } *pMessage = buf.extractChars(); @@ -2706,23 +2712,21 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) which isn't a particularly good error message (x is a variable?). */ Dsymbol varDecl = mtype.toDsymbol(sc); - const(Loc) varDeclLoc = varDecl.getLoc(); Module varDeclModule = varDecl.getModule(); //This can be null .error(loc, "variable `%s` is used as a type", mtype.toChars()); //Check for null to avoid https://issues.dlang.org/show_bug.cgi?id=22574 if ((varDeclModule !is null) && varDeclModule != sc._module) // variable is imported { - const(Loc) varDeclModuleImportLoc = varDeclModule.getLoc(); .errorSupplemental( - varDeclModuleImportLoc, + varDeclModule.loc, "variable `%s` is imported here from: `%s`", varDecl.toChars, varDeclModule.toPrettyChars, ); } - .errorSupplemental(varDeclLoc, "variable `%s` is declared here", varDecl.toChars); + .errorSupplemental(varDecl.loc, "variable `%s` is declared here", varDecl.toChars); } else .error(loc, "`%s` is used as a type", mtype.toChars()); @@ -4921,7 +4925,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag * e.g. * template opDispatch(name) if (isValid!name) { ... } */ - uint errors = gagError ? global.startGagging() : 0; + const errors = gagError ? global.startGagging() : 0; e = dti.dotTemplateSemanticProp(sc, DotExpFlag.none); if (gagError && global.endGagging(errors)) e = null; @@ -4939,7 +4943,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag */ auto die = new DotIdExp(e.loc, alias_e, ident); - auto errors = gagError ? 0 : global.startGagging(); + const errors = gagError ? 0 : global.startGagging(); auto exp = die.dotIdSemanticProp(sc, gagError); if (!gagError) { @@ -7479,6 +7483,62 @@ MATCH implicitConvToThroughAliasThis(TypeStruct from, Type to) return MATCH.nomatch; } +/******************************************* + * Compute number of elements for a (possibly multidimensional) static array, + * or 1 for other types. + * Params: + * t = static array type + * loc = for error message + * Returns: + * number of elements, uint.max on overflow + */ +uint numberOfElems(Type t, const ref Loc loc) +{ + //printf("Type::numberOfElems()\n"); + uinteger_t n = 1; + Type tb = t; + while ((tb = tb.toBasetype()).ty == Tsarray) + { + bool overflow = false; + n = mulu(n, (cast(TypeSArray)tb).dim.toUInteger(), overflow); + if (overflow || n >= uint.max) + { + error(loc, "static array `%s` size overflowed to %llu", t.toChars(), cast(ulong)n); + return uint.max; + } + tb = (cast(TypeSArray)tb).next; + } + return cast(uint)n; +} + +bool checkRetType(TypeFunction tf, const ref Loc loc) +{ + Type tb = tf.next.toBasetype(); + if (tb.ty == Tfunction) + { + error(loc, "functions cannot return a function"); + tf.next = Type.terror; + } + if (tb.ty == Ttuple) + { + error(loc, "functions cannot return a sequence (use `std.typecons.Tuple`)"); + tf.next = Type.terror; + } + if (!tf.isRef && (tb.ty == Tstruct || tb.ty == Tsarray)) + { + if (auto ts = tb.baseElemOf().isTypeStruct()) + { + if (!ts.sym.members) + { + error(loc, "functions cannot return opaque type `%s` by value", tb.toChars()); + tf.next = Type.terror; + } + } + } + if (tb.ty == Terror) + return true; + return false; +} /******************************* Private *****************************************/ @@ -7793,7 +7853,7 @@ Expression getMaxMinValue(EnumDeclaration ed, const ref Loc loc, Identifier id) RootObject compileTypeMixin(TypeMixin tm, ref const Loc loc, Scope* sc) { OutBuffer buf; - if (expressionsToString(buf, sc, tm.exps)) + if (expressionsToString(buf, sc, tm.exps, tm.loc, null, true)) return null; const errors = global.errors; @@ -7803,7 +7863,6 @@ RootObject compileTypeMixin(TypeMixin tm, ref const Loc loc, Scope* sc) const bool doUnittests = global.params.parsingUnittestsRequired(); auto locm = adjustLocForMixin(str, loc, global.params.mixinOut); scope p = new Parser!ASTCodegen(locm, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); - p.transitionIn = global.params.v.vin; p.nextToken(); //printf("p.loc.linnum = %d\n", p.loc.linnum); diff --git a/gcc/d/dmd/typinf.d b/gcc/d/dmd/typinf.d index 5d7154a39d99aa01b878c0e4e5326b8e079b020e..ceff41473e42862335b0e340cf5981141a272283 100644 --- a/gcc/d/dmd/typinf.d +++ b/gcc/d/dmd/typinf.d @@ -104,7 +104,7 @@ bool genTypeInfo(Expression e, const ref Loc loc, Type torig, Scope* sc) * Returns: * The type of the `TypeInfo` object associated with `t` */ -extern (C++) Type getTypeInfoType(const ref Loc loc, Type t, Scope* sc, bool genObjCode = true); +extern (C++) Type getTypeInfoType(const ref Loc loc, Type t, Scope* sc); private TypeInfoDeclaration getTypeInfoDeclaration(Type t) { diff --git a/gcc/d/dmd/typinf.h b/gcc/d/dmd/typinf.h index dd9572aab42f0afca8bf10a8d350e93157d42e78..6414ecdd36acf4b3effa8e480be1159009d0ecb0 100644 --- a/gcc/d/dmd/typinf.h +++ b/gcc/d/dmd/typinf.h @@ -22,4 +22,4 @@ namespace dmd bool isSpeculativeType(Type *t); bool builtinTypeInfo(Type *t); } -Type *getTypeInfoType(const Loc &loc, Type *t, Scope *sc, bool genObjCode = true); +Type *getTypeInfoType(const Loc &loc, Type *t, Scope *sc); diff --git a/gcc/d/dmd/utils.d b/gcc/d/dmd/utils.d index cde029d58674d3962b3f56272ee1f2702f90e235..8b418968dab67554b2e7bdc5b46cdbe4e2423734 100644 --- a/gcc/d/dmd/utils.d +++ b/gcc/d/dmd/utils.d @@ -237,7 +237,7 @@ bool parseDigits(T)(ref T val, const(char)[] p, const T max = T.max) * size = 1 for ubyte[], 2 for ushort[], 4 for uint[], 8 for ulong[] * Returns: copy of `data`, with bytes shuffled if compiled for `version(LittleEndian)` */ -ubyte[] arrayCastBigEndian(const ubyte[] data, size_t size) +ubyte[] arrayCastBigEndian(const ubyte[] data, size_t size) @safe { ubyte[] impl(T)() { diff --git a/gcc/d/dmd/visitor/foreachvar.d b/gcc/d/dmd/visitor/foreachvar.d index 229ade58d762a35ec55761d1f2fef3f68ccc1b74..4f1f64fb0d7c0fdd115cf9f16712cee6948721a8 100644 --- a/gcc/d/dmd/visitor/foreachvar.d +++ b/gcc/d/dmd/visitor/foreachvar.d @@ -24,7 +24,6 @@ import dmd.dstruct; import dmd.dsymbol; import dmd.dsymbolsem; import dmd.dtemplate; -import dmd.errors; import dmd.expression; import dmd.func; import dmd.id; diff --git a/gcc/d/typeinfo.cc b/gcc/d/typeinfo.cc index c0a496af2fe5bf6882d90bf400ba8714ad15dfdd..66cfa2c592962ce3d9e940bf05556c79c6511f54 100644 --- a/gcc/d/typeinfo.cc +++ b/gcc/d/typeinfo.cc @@ -1540,12 +1540,10 @@ get_cpp_typeinfo_decl (ClassDeclaration *decl) return decl->cpp_type_info_ptr_sym; } -/* Get the exact TypeInfo for TYPE, if it doesn't exist, create it. - When GENERATE is true, push the TypeInfo as a member of MOD so that it will - get code generation. */ +/* Get the exact TypeInfo for TYPE, if it doesn't exist, create it. */ void -create_typeinfo (Type *type, Module *mod, bool generate) +create_typeinfo (Type *type, Module *mod) { if (!Type::dtypeinfo) create_frontend_tinfo_types (); @@ -1703,7 +1701,7 @@ create_typeinfo (Type *type, Module *mod, bool generate) /* If this has a custom implementation in rt/typeinfo, then do not generate a COMDAT for it. */ - if (generate && !builtin_typeinfo_p (t)) + if (!builtin_typeinfo_p (t)) { /* Find module that will go all the way to an object file. */ if (mod) diff --git a/gcc/d/types.cc b/gcc/d/types.cc index 4815fbbef4298fa3666dc657a463d68e966810a3..47ef66c258074d2283f3c1fbbfeb3510f0ffcecf 100644 --- a/gcc/d/types.cc +++ b/gcc/d/types.cc @@ -481,7 +481,7 @@ layout_aggregate_members (Dsymbols *members, tree context, bool inherited_p) AttribDeclaration *attrib = sym->isAttribDeclaration (); if (attrib != NULL) { - Dsymbols *decls = attrib->include (NULL); + Dsymbols *decls = dmd::include (attrib, NULL); if (decls != NULL) { fields += layout_aggregate_members (decls, context, inherited_p); diff --git a/gcc/testsuite/gdc.test/compilable/compile1.d b/gcc/testsuite/gdc.test/compilable/compile1.d index 4678eb533864e2f7ade6dc00c8b50fb0a3b2cc81..676108f68cfa51b6e2fc2318f16875211ab098b4 100644 --- a/gcc/testsuite/gdc.test/compilable/compile1.d +++ b/gcc/testsuite/gdc.test/compilable/compile1.d @@ -623,6 +623,11 @@ static assert (__traits(compiles, false && error) == false); int f11042a3()() if (__traits(compiles, true || error) == false) { return 0; } enum x11042a3 = f11042a3(); int f11042b3()() if (__traits(compiles, false && error) == false) { return 0; } enum x11042b3 = f11042b3(); +// https://issues.dlang.org/show_bug.cgi?id=24699 +enum T24699(bool cond) = cond; +enum b24699a = T24699!(true || error); +enum b24699b = T24699!(false && error); + /***************************************************/ // https://issues.dlang.org/show_bug.cgi?id=11554 diff --git a/gcc/testsuite/gdc.test/compilable/deprecationlimit.d b/gcc/testsuite/gdc.test/compilable/deprecationlimit.d index dcdc9e118ca56b56f10dc71faaaa154539d54f2d..8ee7ab650fbf8daa885228cdd932351c82a96163 100644 --- a/gcc/testsuite/gdc.test/compilable/deprecationlimit.d +++ b/gcc/testsuite/gdc.test/compilable/deprecationlimit.d @@ -18,5 +18,5 @@ void main() f(); f(); f(); - f(); + static assert("1"); // also surpress deprecationSupplemental } diff --git a/gcc/testsuite/gdc.test/compilable/import_exp.d b/gcc/testsuite/gdc.test/compilable/import_exp.d index 014d0f65f6f9ac626f60b8a32c528cb9d4a0ed8a..d29fc67ed66ec9298cb7e044697ee45fa313f75e 100644 --- a/gcc/testsuite/gdc.test/compilable/import_exp.d +++ b/gcc/testsuite/gdc.test/compilable/import_exp.d @@ -34,3 +34,11 @@ enum expectedStart = "module imports.imp16088;"; immutable ubyte[] s0 = import("imp16088.d"); static assert(s0[0 .. expectedStart.length] == "module imports.imp16088;"); + +// https://issues.dlang.org/show_bug.cgi?id=24687 + +void foo(string path); +void foo(const(ubyte[]) data); + +void bar1() { foo(import("imp16088.d")); } // matches both +void bar2() { foo(cast(const(ubyte[])) import("imp16088.d")); } // matches both! diff --git a/gcc/testsuite/gdc.test/compilable/imports/vcg_ast_import.d b/gcc/testsuite/gdc.test/compilable/imports/vcg_ast_import.d new file mode 100644 index 0000000000000000000000000000000000000000..a2064c0d175bfee1636708e22bdf2ccade3edec6 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/imports/vcg_ast_import.d @@ -0,0 +1,4 @@ +struct O +{ + invariant() {} +} diff --git a/gcc/testsuite/gdc.test/compilable/isZeroInit.d b/gcc/testsuite/gdc.test/compilable/isZeroInit.d index a64420febc0fbfa85329106ab956a9c3c31045b8..342dc07bf0f2b4b0eac7f645af8b2f11ef2ad4ba 100644 --- a/gcc/testsuite/gdc.test/compilable/isZeroInit.d +++ b/gcc/testsuite/gdc.test/compilable/isZeroInit.d @@ -78,3 +78,45 @@ static if (is(Vector!(int[4]))) static assert(__traits(isZeroInit, Holder!(Vector!(int[4]), 0))); static assert(!__traits(isZeroInit, Holder!(Vector!(int[4]), 1))); } + +// https://issues.dlang.org/show_bug.cgi?id=24776 +struct S6 { + union { + int i1; + float f1; + } +} +static assert(__traits(isZeroInit, S6)); + +struct S7 +{ + union { + float f2; + int i2; + } +} +static assert(!__traits(isZeroInit, S7)); + +// https://issues.dlang.org/show_bug.cgi?id=23841 +union U +{ + float x = 0; + float y; +} +static assert(__traits(isZeroInit, U)); + +union U2 +{ + float x; + int y; +} +static assert(!__traits(isZeroInit, U2)); + +struct S8 { + int[0] dummy; // same offset as anon union, but doesn't overlap; should be ignored anyway for zero-init check + union { + float f; // is the first member of the anon union and must be checked + int i; + } +} +static assert(!__traits(isZeroInit, S8)); diff --git a/gcc/testsuite/gdc.test/compilable/staticforeach.d b/gcc/testsuite/gdc.test/compilable/staticforeach.d index ce9eb74a1ef0af220973be45ee05de69bd3c4fa2..5b23446f89b8a4885805eb8b990c34db9c3a1e95 100644 --- a/gcc/testsuite/gdc.test/compilable/staticforeach.d +++ b/gcc/testsuite/gdc.test/compilable/staticforeach.d @@ -117,8 +117,8 @@ foo2 T2 TestStaticForeach2 issue22007 -1 2 '3' -2 3 '4' +1 2 3 +2 3 4 0 1 1 2 2 3 diff --git a/gcc/testsuite/gdc.test/compilable/test24337.d b/gcc/testsuite/gdc.test/compilable/test24337.d new file mode 100644 index 0000000000000000000000000000000000000000..a31f665f35271ba02ed1a6a0190587f8a71fd95c --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test24337.d @@ -0,0 +1,11 @@ +/* +TEST_OUTPUT: +--- +"ab"w x"11223344556677" +--- +*/ +// https://issues.dlang.org/show_bug.cgi?id=24337 + +immutable ushort[] y = cast(immutable ushort[]) "ab"w; +immutable ulong[] z = x"00 11 22 33 44 55 66 77"; +pragma(msg, y, " ", z); diff --git a/gcc/testsuite/gdc.test/compilable/test24760.d b/gcc/testsuite/gdc.test/compilable/test24760.d new file mode 100644 index 0000000000000000000000000000000000000000..7c84848a6fe939d3b02e3be5b2f1dad451c72856 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test24760.d @@ -0,0 +1,4 @@ +// https://issues.dlang.org/show_bug.cgi?id=24760 + +long f(int e = 0, uint[] optional...) => optional.length; +long f0() => f(); // compiler segfaults diff --git a/gcc/testsuite/gdc.test/compilable/vcg-ast.d b/gcc/testsuite/gdc.test/compilable/vcg-ast.d index 4a7b8bc33c489e3ef9d88a451ef83500caead81c..9197441affea872f75b1560d4270115e5d8b3813 100644 --- a/gcc/testsuite/gdc.test/compilable/vcg-ast.d +++ b/gcc/testsuite/gdc.test/compilable/vcg-ast.d @@ -2,6 +2,7 @@ REQUIRED_ARGS: -vcg-ast -o- 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 */ @@ -63,3 +64,14 @@ void main() { values!wchar_t; } + +// https://issues.dlang.org/show_bug.cgi?id=24764 + +import imports.vcg_ast_import; + +template imported() +{ + import imported = imports.vcg_ast_import; +} + +alias myImport = imported!(); diff --git a/gcc/testsuite/gdc.test/compilable/vcg_ast_compilable.d b/gcc/testsuite/gdc.test/compilable/vcg_ast_compilable.d new file mode 100644 index 0000000000000000000000000000000000000000..e84846dfbe18ee726b195259f1c6304f39418a42 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/vcg_ast_compilable.d @@ -0,0 +1,69 @@ +/* +REQUIRED_ARGS: -vcg-ast -o- +OUTPUT_FILES: compilable/vcg_ast_compilable.d.cg +TEST_OUTPUT: +--- +=== compilable/vcg_ast_compilable.d.cg +import object; +auto binaryFun(E)(E b) +{ + return 'a' == b; +} +void find(Element)(Element needle) if (is(typeof(binaryFun(needle)))) +{ +} +void find()(string needle) +{ +} +void splitter() +{ + find(3); + find(""); +} +binaryFun!int +{ + auto pure nothrow @nogc @safe bool binaryFun(int b) + { + return 97 == b; + } + +} +find!int +{ + pure nothrow @nogc @safe void find(int needle) + { + } + +} +binaryFun!string +{ + auto _error_ binaryFun + { + __error__ + } + +} +find!() +{ + pure nothrow @nogc @safe void find(string needle) + { + } + +} +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=24431 +auto binaryFun(E)(E b) +{ + return 'a' == b; +} + +void find(Element)(Element needle) if (is(typeof(binaryFun(needle)))) { } +void find()(string needle) { } + +void splitter() +{ + find!int(3); + find!()(""); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/failCopyCtor.d b/gcc/testsuite/gdc.test/fail_compilation/failCopyCtor.d deleted file mode 100644 index f341675f32814a2713335e27e320e14f0cf3e981..0000000000000000000000000000000000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/failCopyCtor.d +++ /dev/null @@ -1,15 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/failCopyCtor.d(10): Error: `struct A` may not define both a rvalue constructor and a copy constructor -fail_compilation/failCopyCtor.d(12): rvalue constructor defined here -fail_compilation/failCopyCtor.d(13): copy constructor defined here ---- -*/ - -struct A -{ - this(immutable A a) {} - this(ref shared A a) immutable {} - this(ref A a) {} -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/template_enum_param.d b/gcc/testsuite/gdc.test/fail_compilation/template_enum_param.d new file mode 100644 index 0000000000000000000000000000000000000000..79fb5e4d8f8547ea83123085e6fd59663d8758d6 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/template_enum_param.d @@ -0,0 +1,17 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/template_enum_param.d(15): Error: static assert: `false` is false +fail_compilation/template_enum_param.d(17): instantiated from here: `X!(E.a)` +--- +*/ + +enum E +{ + a,b,c +} +template X(E e) +{ + static assert(false); +} +alias Y = X!(E.a); diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21995.d b/gcc/testsuite/gdc.test/fail_compilation/test21995.d new file mode 100644 index 0000000000000000000000000000000000000000..a1e0b8319194f46a63ef38dced5d7b966c9552a8 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test21995.d @@ -0,0 +1,11 @@ +/* TEST_OUTPUT: +--- +fail_compilation/test21995.d(10): Error: max object size 4294967295 exceeded from adding field size 3 + alignment adjustment 1 + field offset 4294967292 when placing field in aggregate +--- +*/ +struct S +{ + ubyte[0x7ffffffe] a; + ubyte[0x7ffffffe] b; + ubyte[3] c; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/trait_loc_err.d b/gcc/testsuite/gdc.test/fail_compilation/trait_loc_err.d index c5d0579f4a055367b0f07a1b8feeef972908303d..29162144110b01f7f0e120dc5a8654b900260c84 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/trait_loc_err.d +++ b/gcc/testsuite/gdc.test/fail_compilation/trait_loc_err.d @@ -1,8 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/trait_loc_err.d(13): Error: can only get the location of a symbol, not `trait_loc_err` -fail_compilation/trait_loc_err.d(14): Error: can only get the location of a symbol, not `stdc` +fail_compilation/trait_loc_err.d(14): Error: can only get the location of a symbol, not `trait_loc_err` +fail_compilation/trait_loc_err.d(15): Error: can only get the location of a symbol, not `core.stdc` +fail_compilation/trait_loc_err.d(16): Error: can only get the location of a symbol, not `core.stdc.stdio` --- */ module trait_loc_err; @@ -12,4 +13,5 @@ void main() { __traits(getLocation, __traits(parent, main)); __traits(getLocation, __traits(parent, core.stdc.stdio)); + __traits(getLocation, core.stdc.stdio); } diff --git a/gcc/testsuite/gdc.test/runnable/ifti.d b/gcc/testsuite/gdc.test/runnable/ifti.d index 0c94946c8b36861f308247ffe14abe080cf4140a..293b8198d7fb7ecb8dced743619f30a8e3f9d396 100644 --- a/gcc/testsuite/gdc.test/runnable/ifti.d +++ b/gcc/testsuite/gdc.test/runnable/ifti.d @@ -72,6 +72,20 @@ class Tst(TST, int v = 2) { class Y : Tst!(float) {} +// https://issues.dlang.org/show_bug.cgi?id=24731 +void test24731() +{ + static int solve(size_t N)(ref double[N+1][N]) + { + return N; + } + + double[3][2] m; + assert(solve(m) == 2); + assert(solve!2(m) == 2); +} + + void main() { Tst!(int) t = new Tst!(int); Y u = new Y; @@ -113,4 +127,5 @@ void main() { printf("%g\n", i); } + test24731(); } diff --git a/gcc/testsuite/gdc.test/runnable/test13613.d b/gcc/testsuite/gdc.test/runnable/test13613.d index ea57df4e9159d5be12ea33844158fdedeea6fae3..1382f00a1780db2b693a1fb795eb250bb829f4c5 100644 --- a/gcc/testsuite/gdc.test/runnable/test13613.d +++ b/gcc/testsuite/gdc.test/runnable/test13613.d @@ -4,9 +4,9 @@ /* TEST_OUTPUT: --- -CT x.offsetof = < CT y.offsetof = < 0 > y +CT x.offsetof = < 0 > x --- */ diff --git a/gcc/testsuite/gdc.test/runnable/test23650.d b/gcc/testsuite/gdc.test/runnable/test23650.d deleted file mode 100644 index 3ce8f5f0759ba6b415aaca8a4472abaf17718a8e..0000000000000000000000000000000000000000 --- a/gcc/testsuite/gdc.test/runnable/test23650.d +++ /dev/null @@ -1,13 +0,0 @@ -// https://issues.dlang.org/show_bug.cgi?id=23650 - -__gshared int x; - -void main() -{ - - static assert(__traits(compiles, - { - struct S { int *p = &x; } - auto t = typeid(S); - })); -} diff --git a/gcc/testsuite/gdc.test/runnable/test24599.d b/gcc/testsuite/gdc.test/runnable/test24599.d new file mode 100644 index 0000000000000000000000000000000000000000..257773846ebf070793682ffa284ae7a2c6641445 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/test24599.d @@ -0,0 +1,24 @@ +module mod; + +struct Variable +{ + size_t toHash() const { return 0; } +} + +enum hasInoutConstruction(T) = __traits(compiles, { struct S { T a; } }); + +struct Algebraic(T) +{ + static if (hasInoutConstruction!T) + { + } +} + +Algebraic!Variable foo(); + +struct S +{ + Variable[] symbols; +} + +void main() {} diff --git a/gcc/testsuite/gdc.test/runnable/test24819.d b/gcc/testsuite/gdc.test/runnable/test24819.d new file mode 100644 index 0000000000000000000000000000000000000000..3ba374bed368eddac00110a65ea25dbefdaabd76 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/test24819.d @@ -0,0 +1,18 @@ +import core.stdc.stdio; + +pragma(inline, true) +double sqrt(double x) +{ + static import core.math; + return core.math.sqrt(x); +} + +int main() +{ + double q = -5.0; + double r = q + 1.0; + double result = sqrt(-r); + //printf("%f\n", result); + assert(result == 2); + return 0; +} diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE index acb7d98123f83f1bc90324fe0616009d2eaffb88..f660884c3b9590a9f318b1d3025a5c9b36456315 100644 --- a/libphobos/libdruntime/MERGE +++ b/libphobos/libdruntime/MERGE @@ -1,4 +1,4 @@ -4ccb01fde535c7ad6ad4bdae2516c99420751814 +2b89c2909de239bd603d6f36379658fe902667db 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/core/sys/windows/w32api.d b/libphobos/libdruntime/core/sys/windows/w32api.d index 5a8a59bad463f99c45fa1814f3eaefa93d694dca..a5aa975b20806501bd57b73194be90aa3bfbee00 100644 --- a/libphobos/libdruntime/core/sys/windows/w32api.d +++ b/libphobos/libdruntime/core/sys/windows/w32api.d @@ -42,7 +42,7 @@ enum __W32API_MINOR_VERSION = 17; // but third-party compilers could use this enum uint _WIN32_WINNT = 0x500; } else { - enum uint _WIN32_WINNT = 0x501; + enum uint _WIN32_WINNT = 0x601; } version (IE11) { diff --git a/libphobos/libdruntime/core/sys/windows/winbase.d b/libphobos/libdruntime/core/sys/windows/winbase.d index 2e32ba25abf69d2d53c4cd14461a0447b8b7e1cd..a30035c0accc66786f9e12695d72fca48ab24c19 100644 --- a/libphobos/libdruntime/core/sys/windows/winbase.d +++ b/libphobos/libdruntime/core/sys/windows/winbase.d @@ -345,6 +345,14 @@ enum DWORD SECURITY_SQOS_PRESENT = 0x00100000, SECURITY_VALID_SQOS_FLAGS = 0x001F0000; +// for GetFinalPathNameByHandle() +enum DWORD + VOLUME_NAME_DOS = 0x0, + VOLUME_NAME_GUID = 0x1, + VOLUME_NAME_NT = 0x2, + VOLUME_NAME_NONE = 0x4, + FILE_NAME_NORMALIZED = 0x0, + FILE_NAME_OPENED = 0x8; // Thread exit code enum DWORD STILL_ACTIVE = 0x103; @@ -1333,6 +1341,51 @@ enum GET_FILEEX_INFO_LEVELS { GetFileExMaxInfoLevel } +import core.sys.windows.sdkddkver : NTDDI_VERSION, NTDDI_LONGHORN; + +static if (NTDDI_VERSION >= NTDDI_LONGHORN) +{ + enum FILE_INFO_BY_HANDLE_CLASS + { + FileBasicInfo, + FileStandardInfo, + FileNameInfo, + FileRenameInfo, + FileDispositionInfo, + FileAllocationInfo, + FileEndOfFileInfo, + FileStreamInfo, + FileCompressionInfo, + FileAttributeTagInfo, + FileIdBothDirectoryInfo, + FileIdBothDirectoryRestartInfo, + FileIoPriorityHintInfo, + FileRemoteProtocolInfo, + FileFullDirectoryInfo, + FileFullDirectoryRestartInfo, +// static if (NTDDI_VERSION >= NTDDI_WIN8) +// { + FileStorageInfo, + FileAlignmentInfo, + FileIdInfo, + FileIdExtdDirectoryInfo, + FileIdExtdDirectoryRestartInfo, +// } +// static if (NTDDI_VERSION >= NTDDI_WIN10_RS1) +// { + FileDispositionInfoEx, + FileRenameInfoEx, +// } +// static if (NTDDI_VERSION >= NTDDI_WIN10_19H1) +// { + FileCaseSensitiveInfo, + FileNormalizedNameInfo, +// } + MaximumFileInfoByHandleClass + } + alias PFILE_INFO_BY_HANDLE_CLASS = FILE_INFO_BY_HANDLE_CLASS*; +} + struct SYSTEM_INFO { union { DWORD dwOemId; @@ -1590,12 +1643,32 @@ static if (_WIN32_WINNT >= 0x410) { alias DWORD EXECUTION_STATE; } -// CreateSymbolicLink +// CreateSymbolicLink, GetFileInformationByHandleEx static if (_WIN32_WINNT >= 0x600) { enum { SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1, SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2 } + + struct FILE_BASIC_INFO + { + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + DWORD FileAttributes; + } + alias PFILE_BASIC_INFO = FILE_BASIC_INFO*; + + struct FILE_STANDARD_INFO + { + LARGE_INTEGER AllocationSize; + LARGE_INTEGER EndOfFile; + DWORD NumberOfLinks; + BOOLEAN DeletePending; + BOOLEAN Directory; + } + alias PFILE_STANDARD_INFO = FILE_STANDARD_INFO*; } // Callbacks @@ -1844,6 +1917,8 @@ WINBASEAPI DWORD WINAPI GetCurrentThreadId(void); DWORD GetFileSize(HANDLE, PDWORD); BOOL GetFileTime(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME); DWORD GetFileType(HANDLE); + DWORD GetFinalPathNameByHandleA(HANDLE, LPSTR, DWORD, DWORD); + DWORD GetFinalPathNameByHandleW(HANDLE, LPWSTR, DWORD, DWORD); DWORD GetFullPathNameA(LPCSTR, DWORD, LPSTR, LPSTR*); DWORD GetFullPathNameW(LPCWSTR, DWORD, LPWSTR, LPWSTR*); DWORD GetLastError() @trusted; @@ -2485,6 +2560,7 @@ WINBASEAPI BOOL WINAPI SetEvent(HANDLE); static if (_WIN32_WINNT >= 0x600) { BOOL CreateSymbolicLinkA(LPCSTR, LPCSTR, DWORD); BOOL CreateSymbolicLinkW(LPCWSTR, LPCWSTR, DWORD); + BOOL GetFileInformationByHandleEx(HANDLE, FILE_INFO_BY_HANDLE_CLASS, LPVOID, DWORD); } } diff --git a/libphobos/libdruntime/rt/lifetime.d b/libphobos/libdruntime/rt/lifetime.d index a8d084060cf0ade6b1cb44ee51a44056b5dfcc02..1de993c0bff97501cb5efd86d669889ce5eae641 100644 --- a/libphobos/libdruntime/rt/lifetime.d +++ b/libphobos/libdruntime/rt/lifetime.d @@ -1758,85 +1758,65 @@ do Given an array of length `size` that needs to be expanded to `newlength`, compute a new capacity. -Better version by Dave Fladebo: +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. -- Arrays smaller than PAGESIZE bytes are left as-is, so for the most -common cases, memory allocation is 1 to 1. The small overhead added -doesn't affect small array perf. (it's virtually the same as -current). -- Larger arrays have some space pre-allocated. +- 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. -- The logorithmic algorithm allocates relatively more space for -mid-size arrays, making it very fast for medium arrays (for -mid-to-large arrays, this turns out to be quite a bit faster than the -equivalent realloc() code in C, on Linux at least. Small arrays are -just as fast as GCC). - 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` - size = old `.length` + elemsize = size of the element in the new array Returns: new capacity for array */ -size_t newCapacity(size_t newlength, size_t size) +size_t newCapacity(size_t newlength, size_t elemsize) { - version (none) - { - size_t newcap = newlength * size; - } - else - { - size_t newcap = newlength * size; - size_t newext = 0; + size_t newcap = newlength * elemsize; - if (newcap > PAGESIZE) + /* + * 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) { - //double mult2 = 1.0 + (size / log10(pow(newcap * 2.0,2.0))); - - // redo above line using only integer math + auto factor = 128 + 1280 / (i + 1); + result[i] = factor > 234 ? 234 : factor; + } + return result; + }(); - /*static int log2plus1(size_t c) - { int i; + auto mult = multTable[bsr(newcap)]; - if (c == 0) - i = -1; - else - for (i = 1; c >>= 1; i++) - { - } - return i; - }*/ - - /* The following setting for mult sets how much bigger - * the new size will be over what is actually needed. - * 100 means the same size, more means proportionally more. - * More means faster but more memory consumption. - */ - //long mult = 100 + (1000L * size) / (6 * log2plus1(newcap)); - //long mult = 100 + (1000L * size) / log2plus1(newcap); - import core.bitop; - long mult = 100 + (1000L) / (bsr(newcap) + 1); - - // testing shows 1.02 for large arrays is about the point of diminishing return - // - // Commented out because the multipler will never be < 102. In order for it to be < 2, - // then 1000L / (bsr(x) + 1) must be > 2. The highest bsr(x) + 1 - // could be is 65 (64th bit set), and 1000L / 64 is much larger - // than 2. We need 500 bit integers for 101 to be achieved :) - /*if (mult < 102) - mult = 102;*/ - /*newext = cast(size_t)((newcap * mult) / 100); - newext -= newext % size;*/ - // This version rounds up to the next element, and avoids using - // mod. - newext = cast(size_t)((newlength * mult + 99) / 100) * size; - debug(PRINTF) printf("mult: %2.2f, alloc: %2.2f\n",mult/100.0,newext / cast(double)size); - } - newcap = newext > newcap ? newext : newcap; - debug(PRINTF) printf("newcap = %d, newlength = %d, size = %d\n", newcap, newlength, size); - } + // 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 = %d, newlength = %d, elemsize = %d\n", newcap, newlength, elemsize); return newcap; } diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE index 8daeda505f4f3d7f4c63b05553f9b5aadc8d35b7..a3a6f85c1a4696a405a5292cb7bcc09c262957f6 100644 --- a/libphobos/src/MERGE +++ b/libphobos/src/MERGE @@ -1,4 +1,4 @@ -eab6595ade1dab9a757bc0efe2f4e92f39cab0f7 +bdedad3bf8090808e41726e7b9f8e928f6cb10bd The first line of this file holds the git revision number of the last merge done from the dlang/phobos repository. diff --git a/libphobos/src/std/algorithm/comparison.d b/libphobos/src/std/algorithm/comparison.d index 5c70960a711fc15c36c03f208c634a832c29ffd7..60fd114da2d7bbc42a6314593b3096a55462d4dc 100644 --- a/libphobos/src/std/algorithm/comparison.d +++ b/libphobos/src/std/algorithm/comparison.d @@ -102,7 +102,7 @@ template among(values...) if (isExpressionTuple!values) { uint among(Value)(Value value) - if (!is(CommonType!(Value, values) == void)) + if (!is(CommonType!(Value, values) == void)) { switch (value) { diff --git a/libphobos/src/std/algorithm/iteration.d b/libphobos/src/std/algorithm/iteration.d index 1453d2b64b948fcb7832fe6acba0516ae85aa868..8a3add3b73339f7c9adf31edc8efe8cad3712558 100644 --- a/libphobos/src/std/algorithm/iteration.d +++ b/libphobos/src/std/algorithm/iteration.d @@ -443,7 +443,8 @@ if (fun.length >= 1) A range with each fun applied to all the elements. If there is more than one fun, the element type will be `Tuple` containing one element for each fun. */ - auto map(Range)(Range r) if (isInputRange!(Unqual!Range)) + auto map(Range)(Range r) + if (isInputRange!(Unqual!Range)) { import std.meta : AliasSeq, staticMap; @@ -1308,7 +1309,8 @@ if (is(typeof(unaryFun!predicate))) A range containing only elements `x` in `range` for which `predicate(x)` returns `true`. */ - auto filter(Range)(Range range) if (isInputRange!(Unqual!Range)) + auto filter(Range)(Range range) + if (isInputRange!(Unqual!Range)) { return FilterResult!(unaryFun!predicate, Range)(range); } @@ -1545,7 +1547,8 @@ template filterBidirectional(alias pred) Returns: A range containing only the elements in `r` for which `pred` returns `true`. */ - auto filterBidirectional(Range)(Range r) if (isBidirectionalRange!(Unqual!Range)) + auto filterBidirectional(Range)(Range r) + if (isBidirectionalRange!(Unqual!Range)) { return FilterBidiResult!(unaryFun!pred, Range)(r); } diff --git a/libphobos/src/std/algorithm/mutation.d b/libphobos/src/std/algorithm/mutation.d index e434d248edbfb1ec59790445fe866881470876a8..ea1a1b290dc23e42563275cdd87ea02fbd8cd470 100644 --- a/libphobos/src/std/algorithm/mutation.d +++ b/libphobos/src/std/algorithm/mutation.d @@ -2860,6 +2860,13 @@ Params: lhs = Data to be swapped with `rhs`. rhs = Data to be swapped with `lhs`. */ +void swap(T)(ref T lhs, ref T rhs) +if (is(typeof(lhs.proxySwap(rhs)))) +{ + lhs.proxySwap(rhs); +} + +/// ditto void swap(T)(ref T lhs, ref T rhs) @trusted pure nothrow @nogc if (isBlitAssignable!T && !is(typeof(lhs.proxySwap(rhs)))) { @@ -3121,13 +3128,6 @@ if (isBlitAssignable!T && !is(typeof(lhs.proxySwap(rhs)))) swap(a3, a4); } -/// ditto -void swap(T)(ref T lhs, ref T rhs) -if (is(typeof(lhs.proxySwap(rhs)))) -{ - lhs.proxySwap(rhs); -} - /** Swaps two elements in-place of a range `r`, specified by their indices `i1` and `i2`. diff --git a/libphobos/src/std/algorithm/sorting.d b/libphobos/src/std/algorithm/sorting.d index cb4715328722beea0341a37d8bdddcee35778897..29839d18091aa07c6e0bc11db5f44a12467cf0e6 100644 --- a/libphobos/src/std/algorithm/sorting.d +++ b/libphobos/src/std/algorithm/sorting.d @@ -2625,11 +2625,21 @@ private template TimSortImpl(alias pred, R) // can't use `temp.length` if there's no default constructor static if (__traits(compiles, { T defaultConstructed; cast(void) defaultConstructed; })) { - if (__ctfe) temp.length = newSize; - else temp = () @trusted { return uninitializedArray!(T[])(newSize); }(); + + static if (hasElaborateAssign!T) + temp.length = newSize; + else + { + if (__ctfe) temp.length = newSize; + else temp = () @trusted { return uninitializedArray!(T[])(newSize); }(); + } } else { + static assert(!hasElaborateAssign!T, + "Structs which have opAssign but cannot be default-initialized " ~ + "do not currently work with stable sort: " ~ + "https://issues.dlang.org/show_bug.cgi?id=24810"); temp = () @trusted { return uninitializedArray!(T[])(newSize); }(); } } @@ -3093,6 +3103,65 @@ private template TimSortImpl(alias pred, R) array.sort!((a, b) => false, SwapStrategy.stable); } +// https://issues.dlang.org/show_bug.cgi?id=24809 +@safe unittest +{ + static struct E + { + int value; + int valid = 42; + + ~this() + { + assert(valid == 42); + } + } + + import std.array : array; + import std.range : chain, only, repeat; + auto arr = chain(repeat(E(41), 18), + only(E(39)), + repeat(E(41), 16), + only(E(1)), + repeat(E(42), 33), + only(E(33)), + repeat(E(42), 16), + repeat(E(43), 27), + only(E(33)), + repeat(E(43), 34), + only(E(34)), + only(E(43)), + only(E(63)), + repeat(E(44), 42), + only(E(27)), + repeat(E(44), 11), + repeat(E(45), 64), + repeat(E(46), 3), + only(E(11)), + repeat(E(46), 7), + only(E(4)), + repeat(E(46), 34), + only(E(36)), + repeat(E(46), 17), + repeat(E(47), 36), + only(E(39)), + repeat(E(47), 26), + repeat(E(48), 17), + only(E(21)), + repeat(E(48), 5), + only(E(39)), + repeat(E(48), 14), + only(E(58)), + repeat(E(48), 24), + repeat(E(49), 13), + only(E(40)), + repeat(E(49), 38), + only(E(18)), + repeat(E(49), 11), + repeat(E(50), 6)).array(); + + arr.sort!((a, b) => a.value < b.value, SwapStrategy.stable)(); +} // schwartzSort /** diff --git a/libphobos/src/std/array.d b/libphobos/src/std/array.d index 27d216740aa7cd9b84ba404d89bc999397907a3e..acd5311c4d22cbe71268a166e7f9f3354a9b3b1f 100644 --- a/libphobos/src/std/array.d +++ b/libphobos/src/std/array.d @@ -3701,7 +3701,8 @@ if (isDynamicArray!A) * Params: * item = the single item to append */ - void put(U)(U item) if (canPutItem!U) + void put(U)(U item) + if (canPutItem!U) { static if (isSomeChar!T && isSomeChar!U && T.sizeof < U.sizeof) { @@ -3730,7 +3731,8 @@ if (isDynamicArray!A) } // Const fixing hack. - void put(Range)(Range items) if (canPutConstRange!Range) + void put(Range)(Range items) + if (canPutConstRange!Range) { alias p = put!(Unqual!Range); p(items); @@ -3743,7 +3745,8 @@ if (isDynamicArray!A) * Params: * items = the range of items to append */ - void put(Range)(Range items) if (canPutRange!Range) + void put(Range)(Range items) + if (canPutRange!Range) { // note, we disable this branch for appending one type of char to // another because we can't trust the length portion. diff --git a/libphobos/src/std/base64.d b/libphobos/src/std/base64.d index 0fc92ac2b016293ea20cb1e55c8204ccb1194289..0ce81b520efe8e9b4f95bcfba746bfa3d70e0092 100644 --- a/libphobos/src/std/base64.d +++ b/libphobos/src/std/base64.d @@ -299,9 +299,10 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** * ditto */ - char[] encode(R1, R2)(R1 source, R2 buffer) if (!isArray!R1 && isInputRange!R1 && - is(ElementType!R1 : ubyte) && hasLength!R1 && - is(R2 == char[])) + char[] encode(R1, R2)(R1 source, R2 buffer) + if (!isArray!R1 && isInputRange!R1 && + is(ElementType!R1 : ubyte) && hasLength!R1 && + is(R2 == char[])) in { assert(buffer.length >= encodeLength(source.length), "Insufficient buffer for encoding"); @@ -474,8 +475,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * ditto */ size_t encode(R1, R2)(R1 source, auto ref R2 range) - if (!isArray!R1 && isInputRange!R1 && is(ElementType!R1 : ubyte) && - hasLength!R1 && !is(R2 == char[]) && isOutputRange!(R2, char)) + if (!isArray!R1 && isInputRange!R1 && is(ElementType!R1 : ubyte) && + hasLength!R1 && !is(R2 == char[]) && isOutputRange!(R2, char)) { immutable srcLen = source.length; if (srcLen == 0) @@ -559,7 +560,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * A newly-allocated `char[]` buffer containing the encoded string. */ @safe - pure char[] encode(Range)(Range source) if (isArray!Range && is(ElementType!Range : ubyte)) + pure char[] encode(Range)(Range source) + if (isArray!Range && is(ElementType!Range : ubyte)) { return encode(source, new char[encodeLength(source.length)]); } @@ -575,8 +577,9 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** * ditto */ - char[] encode(Range)(Range source) if (!isArray!Range && isInputRange!Range && - is(ElementType!Range : ubyte) && hasLength!Range) + char[] encode(Range)(Range source) + if (!isArray!Range && isInputRange!Range && + is(ElementType!Range : ubyte) && hasLength!Range) { return encode(source, new char[encodeLength(source.length)]); } @@ -592,8 +595,9 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * Note: This struct is not intended to be created in user code directly; * use the $(LREF encoder) function instead. */ - struct Encoder(Range) if (isInputRange!Range && (is(ElementType!Range : const(ubyte)[]) || - is(ElementType!Range : const(char)[]))) + struct Encoder(Range) + if (isInputRange!Range && (is(ElementType!Range : const(ubyte)[]) || + is(ElementType!Range : const(char)[]))) { private: Range range_; @@ -702,7 +706,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * Note: This struct is not intended to be created in user code directly; * use the $(LREF encoder) function instead. */ - struct Encoder(Range) if (isInputRange!Range && is(ElementType!Range : ubyte)) + struct Encoder(Range) + if (isInputRange!Range && is(ElementType!Range : ubyte)) { private: Range range_; @@ -884,7 +889,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * } * ----- */ - Encoder!(Range) encoder(Range)(Range range) if (isInputRange!Range) + Encoder!(Range) encoder(Range)(Range range) + if (isInputRange!Range) { return typeof(return)(range); } @@ -981,8 +987,9 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * base alphabet of the current Base64 encoding scheme. */ @trusted - pure ubyte[] decode(R1, R2)(in R1 source, return scope R2 buffer) if (isArray!R1 && is(ElementType!R1 : dchar) && - is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) + pure ubyte[] decode(R1, R2)(in R1 source, return scope R2 buffer) + if (isArray!R1 && is(ElementType!R1 : dchar) && + is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) in { assert(buffer.length >= realDecodeLength(source), "Insufficient buffer for decoding"); @@ -1065,9 +1072,10 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** * ditto */ - ubyte[] decode(R1, R2)(R1 source, R2 buffer) if (!isArray!R1 && isInputRange!R1 && - is(ElementType!R1 : dchar) && hasLength!R1 && - is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) + ubyte[] decode(R1, R2)(R1 source, R2 buffer) + if (!isArray!R1 && isInputRange!R1 && + is(ElementType!R1 : dchar) && hasLength!R1 && + is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) in { assert(buffer.length >= decodeLength(source.length), "Insufficient buffer for decoding"); @@ -1156,8 +1164,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * base alphabet of the current Base64 encoding scheme. */ size_t decode(R1, R2)(in R1 source, auto ref R2 range) - if (isArray!R1 && is(ElementType!R1 : dchar) && - !is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) + if (isArray!R1 && is(ElementType!R1 : dchar) && + !is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) out(result) { immutable expect = realDecodeLength(source); @@ -1244,8 +1252,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * ditto */ size_t decode(R1, R2)(R1 source, auto ref R2 range) - if (!isArray!R1 && isInputRange!R1 && is(ElementType!R1 : dchar) && - hasLength!R1 && !is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) + if (!isArray!R1 && isInputRange!R1 && is(ElementType!R1 : dchar) && + hasLength!R1 && !is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) out(result) { // @@@BUG@@@ Workaround for DbC problem. @@ -1334,7 +1342,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * A newly-allocated `ubyte[]` buffer containing the decoded string. */ @safe - pure ubyte[] decode(Range)(Range source) if (isArray!Range && is(ElementType!Range : dchar)) + pure ubyte[] decode(Range)(Range source) + if (isArray!Range && is(ElementType!Range : dchar)) { return decode(source, new ubyte[decodeLength(source.length)]); } @@ -1350,8 +1359,9 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** * ditto */ - ubyte[] decode(Range)(Range source) if (!isArray!Range && isInputRange!Range && - is(ElementType!Range : dchar) && hasLength!Range) + ubyte[] decode(Range)(Range source) + if (!isArray!Range && isInputRange!Range && + is(ElementType!Range : dchar) && hasLength!Range) { return decode(source, new ubyte[decodeLength(source.length)]); } @@ -1367,8 +1377,9 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * Note: This struct is not intended to be created in user code directly; * use the $(LREF decoder) function instead. */ - struct Decoder(Range) if (isInputRange!Range && (is(ElementType!Range : const(char)[]) || - is(ElementType!Range : const(ubyte)[]))) + struct Decoder(Range) + if (isInputRange!Range && (is(ElementType!Range : const(char)[]) || + is(ElementType!Range : const(ubyte)[]))) { private: Range range_; @@ -1492,7 +1503,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * Note: This struct is not intended to be created in user code directly; * use the $(LREF decoder) function instead. */ - struct Decoder(Range) if (isInputRange!Range && is(ElementType!Range : char)) + struct Decoder(Range) + if (isInputRange!Range && is(ElementType!Range : char)) { private: Range range_; @@ -1683,7 +1695,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * } * ----- */ - Decoder!(Range) decoder(Range)(Range range) if (isInputRange!Range) + Decoder!(Range) decoder(Range)(Range range) + if (isInputRange!Range) { return typeof(return)(range); } diff --git a/libphobos/src/std/bigint.d b/libphobos/src/std/bigint.d index 0240ea1d179a01a2b22dbc927224aef98be51b81..a8b389795976cc75d387827a3cc74b3a441527d2 100644 --- a/libphobos/src/std/bigint.d +++ b/libphobos/src/std/bigint.d @@ -63,8 +63,8 @@ public: * Throws: * $(REF ConvException, std,conv) if the string doesn't represent a valid number */ - this(Range)(Range s) if ( - isBidirectionalRange!Range && + this(Range)(Range s) + if (isBidirectionalRange!Range && isSomeChar!(ElementType!Range) && !isInfinite!Range && !isNarrowString!Range) @@ -160,8 +160,8 @@ public: * (ignored when magnitude is zero) * magnitude = a finite range of unsigned integers */ - this(Range)(bool isNegative, Range magnitude) if ( - isInputRange!Range && + this(Range)(bool isNegative, Range magnitude) + if (isInputRange!Range && isUnsigned!(ElementType!Range) && (hasLength!Range || isForwardRange!Range) && !isInfinite!Range) @@ -181,7 +181,8 @@ public: } /// Construct a `BigInt` from a built-in integral type. - this(T)(T x) pure nothrow @safe if (isIntegral!T) + this(T)(T x) pure nothrow @safe + if (isIntegral!T) { data = data.init; // @@@: Workaround for compiler bug opAssign(x); @@ -196,7 +197,8 @@ public: } /// Construct a `BigInt` from another `BigInt`. - this(T)(T x) pure nothrow @safe if (is(immutable T == immutable BigInt)) + this(T)(T x) pure nothrow @safe + if (is(immutable T == immutable BigInt)) { opAssign(x); } @@ -210,7 +212,8 @@ public: } /// Assignment from built-in integer types. - BigInt opAssign(T)(T x) pure nothrow @safe if (isIntegral!T) + BigInt opAssign(T)(T x) pure nothrow @safe + if (isIntegral!T) { data = cast(ulong) absUnsign(x); sign = (x < 0); @@ -247,8 +250,8 @@ public: * `BigInt op= integer`. */ BigInt opOpAssign(string op, T)(T y) pure nothrow @safe return scope - if ((op=="+" || op=="-" || op=="*" || op=="/" || op=="%" - || op==">>" || op=="<<" || op=="^^" || op=="|" || op=="&" || op=="^") && isIntegral!T) + if ((op=="+" || op=="-" || op=="*" || op=="/" || op=="%" + || op==">>" || op=="<<" || op=="^^" || op=="|" || op=="&" || op=="^") && isIntegral!T) { ulong u = absUnsign(y); @@ -436,8 +439,7 @@ public: * Implements assignment operators of the form `BigInt op= BigInt`. */ BigInt opOpAssign(string op, T)(T y) pure nothrow @safe return scope - if ((op=="+" || op== "-" || op=="*" || op=="|" || op=="&" || op=="^" || op=="/" || op=="%") - && is (T: BigInt)) + if ((op=="+" || op== "-" || op=="*" || op=="|" || op=="&" || op=="^" || op=="/" || op=="%") && is (T: BigInt)) { static if (op == "+") { @@ -494,9 +496,8 @@ public: * Implements binary operators between `BigInt`s. */ BigInt opBinary(string op, T)(T y) pure nothrow @safe const return scope - if ((op=="+" || op == "*" || op=="-" || op=="|" || op=="&" || op=="^" || - op=="/" || op=="%") - && is (T: BigInt)) + if ((op=="+" || op == "*" || op=="-" || op=="|" || op=="&" || op=="^" || + op=="/" || op=="%") && is (T: BigInt)) { BigInt r = this; return r.opOpAssign!(op)(y); @@ -515,9 +516,9 @@ public: * Implements binary operators between `BigInt`'s and built-in integers. */ BigInt opBinary(string op, T)(T y) pure nothrow @safe const return scope - if ((op=="+" || op == "*" || op=="-" || op=="/" || op=="|" || op=="&" || - op=="^"|| op==">>" || op=="<<" || op=="^^") - && isIntegral!T) + if ((op=="+" || op == "*" || op=="-" || op=="/" || op=="|" || op=="&" || + op=="^"|| op==">>" || op=="<<" || op=="^^") + && isIntegral!T) { BigInt r = this; r.opOpAssign!(op)(y); @@ -546,7 +547,7 @@ public: ) */ auto opBinary(string op, T)(T y) pure nothrow @safe const - if (op == "%" && isIntegral!T) + if (op == "%" && isIntegral!T) { assert(y != 0, "% 0 not allowed"); @@ -602,7 +603,7 @@ public: `BigInt` on the right-hand side. */ BigInt opBinaryRight(string op, T)(T y) pure nothrow @safe const - if ((op=="+" || op=="*" || op=="|" || op=="&" || op=="^") && isIntegral!T) + if ((op=="+" || op=="*" || op=="|" || op=="&" || op=="^") && isIntegral!T) { return opBinary!(op)(y); } @@ -627,7 +628,7 @@ public: // BigInt = integer op BigInt /// ditto BigInt opBinaryRight(string op, T)(T y) pure nothrow @safe const - if (op == "-" && isIntegral!T) + if (op == "-" && isIntegral!T) { ulong u = absUnsign(y); BigInt r; @@ -643,7 +644,7 @@ public: // integer = integer op BigInt /// ditto T opBinaryRight(string op, T)(T x) pure nothrow @safe const - if ((op=="%" || op=="/") && isIntegral!T) + if ((op=="%" || op=="/") && isIntegral!T) { checkDivByZero(); @@ -669,7 +670,8 @@ public: /** Implements `BigInt` unary operators. */ - BigInt opUnary(string op)() pure nothrow @safe const if (op=="+" || op=="-" || op=="~") + BigInt opUnary(string op)() pure nothrow @safe const + if (op=="+" || op=="-" || op=="~") { static if (op=="-") { @@ -687,7 +689,8 @@ public: // non-const unary operations /// ditto - BigInt opUnary(string op)() pure nothrow @safe if (op=="++" || op=="--") + BigInt opUnary(string op)() pure nothrow @safe + if (op=="++" || op=="--") { static if (op=="++") { @@ -721,7 +724,8 @@ public: } /// ditto - bool opEquals(T)(const T y) const pure nothrow @nogc @safe if (isIntegral!T) + bool opEquals(T)(const T y) const pure nothrow @nogc @safe + if (isIntegral!T) { if (sign != (y<0)) return 0; @@ -729,7 +733,8 @@ public: } /// ditto - bool opEquals(T)(const T y) const pure nothrow @nogc if (isFloatingPoint!T) + bool opEquals(T)(const T y) const pure nothrow @nogc + if (isFloatingPoint!T) { return 0 == opCmp(y); } @@ -896,7 +901,8 @@ public: /** Implements casting to floating point types. */ - T opCast(T)() @safe nothrow @nogc const if (isFloatingPoint!T) + T opCast(T)() @safe nothrow @nogc const + if (isFloatingPoint!T) { return toFloat!(T, "nearest"); } @@ -1090,7 +1096,8 @@ public: } /// ditto - int opCmp(T)(const T y) pure nothrow @nogc @safe const if (isIntegral!T) + int opCmp(T)(const T y) pure nothrow @nogc @safe const + if (isIntegral!T) { if (sign != (y<0) ) return sign ? -1 : 1; @@ -1098,7 +1105,8 @@ public: return sign? -cmp: cmp; } /// ditto - int opCmp(T)(const T y) nothrow @nogc @safe const if (isFloatingPoint!T) + int opCmp(T)(const T y) nothrow @nogc @safe const + if (isFloatingPoint!T) { import core.bitop : bsr; import std.math.operations : cmp; diff --git a/libphobos/src/std/bitmanip.d b/libphobos/src/std/bitmanip.d index 0993d34843fcf58b739372b86381cd96b1eff68f..15211a3a98adab5ab2952bf801a350a5db76055c 100644 --- a/libphobos/src/std/bitmanip.d +++ b/libphobos/src/std/bitmanip.d @@ -1925,7 +1925,7 @@ public: * Support for unary operator ~ for `BitArray`. */ BitArray opUnary(string op)() const pure nothrow - if (op == "~") + if (op == "~") { auto dim = this.dim; @@ -1962,7 +1962,7 @@ public: * Support for binary bitwise operators for `BitArray`. */ BitArray opBinary(string op)(const BitArray e2) const pure nothrow - if (op == "-" || op == "&" || op == "|" || op == "^") + if (op == "-" || op == "&" || op == "|" || op == "^") in { assert(e2.length == _len, "e2 must have the same length as this"); @@ -2064,7 +2064,7 @@ public: * Support for operator op= for `BitArray`. */ BitArray opOpAssign(string op)(const BitArray e2) @nogc pure nothrow return scope - if (op == "-" || op == "&" || op == "|" || op == "^") + if (op == "-" || op == "&" || op == "|" || op == "^") in { assert(e2.length == _len, "e2 must have the same length as this"); @@ -2185,7 +2185,7 @@ public: * concatenation semantics are not followed) */ BitArray opOpAssign(string op)(bool b) pure nothrow return scope - if (op == "~") + if (op == "~") { length = _len + 1; this[_len - 1] = b; @@ -2215,7 +2215,7 @@ public: * ditto */ BitArray opOpAssign(string op)(BitArray b) pure nothrow return scope - if (op == "~") + if (op == "~") { auto istart = _len; length = _len + b.length; @@ -2249,7 +2249,7 @@ public: * Support for binary operator ~ for `BitArray`. */ BitArray opBinary(string op)(bool b) const pure nothrow - if (op == "~") + if (op == "~") { BitArray r; @@ -2261,7 +2261,7 @@ public: /** ditto */ BitArray opBinaryRight(string op)(bool b) const pure nothrow - if (op == "~") + if (op == "~") { BitArray r; @@ -2274,7 +2274,7 @@ public: /** ditto */ BitArray opBinary(string op)(BitArray b) const pure nothrow - if (op == "~") + if (op == "~") { BitArray r; @@ -2398,7 +2398,7 @@ public: * preserve bits past the end of the array.) */ void opOpAssign(string op)(size_t nbits) @nogc pure nothrow - if (op == "<<") + if (op == "<<") { size_t wordsToShift = nbits / bitsPerSizeT; size_t bitsToShift = nbits % bitsPerSizeT; @@ -2432,7 +2432,7 @@ public: * preserve bits past the end of the array.) */ void opOpAssign(string op)(size_t nbits) @nogc pure nothrow - if (op == ">>") + if (op == ">>") { size_t wordsToShift = nbits / bitsPerSizeT; size_t bitsToShift = nbits % bitsPerSizeT; @@ -2945,58 +2945,6 @@ if (isIntegral!T || isSomeChar!T || isBoolean!T) } -private union EndianSwapper(T) -if (canSwapEndianness!T) -{ - T value; - ubyte[T.sizeof] array; - - static if (is(immutable FloatingPointTypeOf!(T) == immutable float)) - uint intValue; - else static if (is(immutable FloatingPointTypeOf!(T) == immutable double)) - ulong intValue; - -} - -// Can't use EndianSwapper union during CTFE. -private auto ctfeRead(T)(const ubyte[T.sizeof] array) -if (__traits(isIntegral, T)) -{ - Unqual!T result; - version (LittleEndian) - foreach_reverse (b; array) - result = cast() cast(T) ((result << 8) | b); - else - foreach (b; array) - result = cast() cast(T) ((result << 8) | b); - return cast(T) result; -} - -// Can't use EndianSwapper union during CTFE. -private auto ctfeBytes(T)(const T value) -if (__traits(isIntegral, T)) -{ - ubyte[T.sizeof] result; - Unqual!T tmp = value; - version (LittleEndian) - { - foreach (i; 0 .. T.sizeof) - { - result[i] = cast(ubyte) tmp; - tmp = cast() cast(T) (tmp >>> 8); - } - } - else - { - foreach_reverse (i; 0 .. T.sizeof) - { - result[i] = cast(ubyte) tmp; - tmp = cast()(T) (tmp >>> 8); - } - } - return result; -} - /++ Converts the given value from the native endianness to big endian and returns it as a `ubyte[n]` where `n` is the size of the given type. @@ -3010,13 +2958,21 @@ if (__traits(isIntegral, T)) and therefore could vary from machine to machine (which could make it unusable if you tried to transfer it to another machine). +/ -auto nativeToBigEndian(T)(const T val) @safe pure nothrow @nogc +auto nativeToBigEndian(T)(const T val) @trusted pure nothrow @nogc if (canSwapEndianness!T) { - version (LittleEndian) - return nativeToEndianImpl!true(val); + static if (isFloatOrDouble!T) + return nativeToBigEndian(*cast(const UnsignedOfSize!(T.sizeof)*) &val); else - return nativeToEndianImpl!false(val); + { + enum len = T.sizeof; + ubyte[len] retval; + + static foreach (i; 0 .. len) + retval[i] = cast(ubyte)(val >> (len - i - 1) * 8); + + return retval; + } } /// @@ -3043,26 +2999,6 @@ if (canSwapEndianness!T) assert(cd == bigEndianToNative!double(swappedCD)); } -private auto nativeToEndianImpl(bool swap, T)(const T val) @safe pure nothrow @nogc -if (__traits(isIntegral, T)) -{ - if (!__ctfe) - { - static if (swap) - return EndianSwapper!T(swapEndian(val)).array; - else - return EndianSwapper!T(val).array; - } - else - { - // Can't use EndianSwapper in CTFE. - static if (swap) - return ctfeBytes(swapEndian(val)); - else - return ctfeBytes(val); - } -} - @safe unittest { import std.meta; @@ -3149,13 +3085,25 @@ if (__traits(isIntegral, T)) because the FPU will mess up any swapped floating point values. So, you can't actually have swapped floating point values as floating point values). +/ -T bigEndianToNative(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc +T bigEndianToNative(T, size_t n)(ubyte[n] val) @trusted pure nothrow @nogc if (canSwapEndianness!T && n == T.sizeof) { - version (LittleEndian) - return endianToNativeImpl!(true, T, n)(val); + static if (isFloatOrDouble!T) + { + auto retval = bigEndianToNative!(UnsignedOfSize!(T.sizeof))(val); + return *cast(const T*) &retval; + } else - return endianToNativeImpl!(false, T, n)(val); + { + enum len = T.sizeof; + alias U = UnsignedOfSize!len; + U retval; + + static foreach (i; 0 .. len) + retval |= (cast(U) val[i]) << (len - i - 1) * 8; + + return cast(T) retval; + } } /// @@ -3179,13 +3127,21 @@ if (canSwapEndianness!T && n == T.sizeof) because the FPU will mess up any swapped floating point values. So, you can't actually have swapped floating point values as floating point values). +/ -auto nativeToLittleEndian(T)(const T val) @safe pure nothrow @nogc +auto nativeToLittleEndian(T)(const T val) @trusted pure nothrow @nogc if (canSwapEndianness!T) { - version (BigEndian) - return nativeToEndianImpl!true(val); + static if (isFloatOrDouble!T) + return nativeToLittleEndian(*cast(const UnsignedOfSize!(T.sizeof)*) &val); else - return nativeToEndianImpl!false(val); + { + enum len = T.sizeof; + ubyte[len] retval; + + static foreach (i; 0 .. len) + retval[i] = cast(ubyte)(val >> i * 8); + + return retval; + } } /// @@ -3195,9 +3151,21 @@ if (canSwapEndianness!T) ubyte[4] swappedI = nativeToLittleEndian(i); assert(i == littleEndianToNative!int(swappedI)); + float f = 123.45f; + ubyte[4] swappedF = nativeToLittleEndian(f); + assert(f == littleEndianToNative!float(swappedF)); + + const float cf = 123.45f; + ubyte[4] swappedCF = nativeToLittleEndian(cf); + assert(cf == littleEndianToNative!float(swappedCF)); + double d = 123.45; ubyte[8] swappedD = nativeToLittleEndian(d); assert(d == littleEndianToNative!double(swappedD)); + + const double cd = 123.45; + ubyte[8] swappedCD = nativeToLittleEndian(cd); + assert(cd == littleEndianToNative!double(swappedCD)); } @safe unittest @@ -3259,13 +3227,25 @@ if (canSwapEndianness!T) and therefore could vary from machine to machine (which could make it unusable if you tried to transfer it to another machine). +/ -T littleEndianToNative(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc +T littleEndianToNative(T, size_t n)(ubyte[n] val) @trusted pure nothrow @nogc if (canSwapEndianness!T && n == T.sizeof) { - version (BigEndian) - return endianToNativeImpl!(true, T, n)(val); + static if (isFloatOrDouble!T) + { + auto retval = littleEndianToNative!(UnsignedOfSize!(T.sizeof))(val); + return *cast(const T*) &retval; + } else - return endianToNativeImpl!(false, T, n)(val); + { + enum len = T.sizeof; + alias U = UnsignedOfSize!len; + U retval; + + static foreach (i; 0 .. len) + retval |= (cast(U) val[i]) << i * 8; + + return cast(T) retval; + } } /// @@ -3280,70 +3260,6 @@ if (canSwapEndianness!T && n == T.sizeof) assert(c == littleEndianToNative!dchar(swappedC)); } -private T endianToNativeImpl(bool swap, T, size_t n)(ubyte[n] val) @nogc nothrow pure @trusted -if (__traits(isIntegral, T) && n == T.sizeof) -{ - if (!__ctfe) - { - EndianSwapper!T es = { array: val }; - static if (swap) - return swapEndian(es.value); - else - return es.value; - } - else - { - static if (swap) - return swapEndian(ctfeRead!T(val)); - else - return ctfeRead!T(val); - } -} - -private auto nativeToEndianImpl(bool swap, T)(const T val) @trusted pure nothrow @nogc -if (isFloatOrDouble!T) -{ - if (!__ctfe) - { - EndianSwapper!T es = EndianSwapper!T(val); - static if (swap) - es.intValue = swapEndian(es.intValue); - return es.array; - } - else - { - static if (T.sizeof == 4) - uint intValue = *cast(const uint*) &val; - else static if (T.sizeof == 8) - ulong intValue = *cast(const ulong*) & val; - static if (swap) - intValue = swapEndian(intValue); - return ctfeBytes(intValue); - } -} - -private auto endianToNativeImpl(bool swap, T, size_t n)(ubyte[n] val) @trusted pure nothrow @nogc -if (isFloatOrDouble!T && n == T.sizeof) -{ - if (!__ctfe) - { - EndianSwapper!T es = { array: val }; - static if (swap) - es.intValue = swapEndian(es.intValue); - return es.value; - } - else - { - static if (n == 4) - uint x = ctfeRead!uint(val); - else static if (n == 8) - ulong x = ctfeRead!ulong(val); - static if (swap) - x = swapEndian(x); - return *cast(T*) &x; - } -} - private template isFloatOrDouble(T) { enum isFloatOrDouble = isFloatingPoint!T && @@ -3405,6 +3321,42 @@ private template canSwapEndianness(T) } } +private template UnsignedOfSize(size_t n) +{ + static if (n == 8) + alias UnsignedOfSize = ulong; + else static if (n == 4) + alias UnsignedOfSize = uint; + else static if (n == 2) + alias UnsignedOfSize = ushort; + else static if (n == 1) + alias UnsignedOfSize = ubyte; + else + alias UnsignedOfSize = void; +} + +@safe unittest +{ + static assert(is(UnsignedOfSize!(byte.sizeof) == ubyte)); + static assert(is(UnsignedOfSize!(ubyte.sizeof) == ubyte)); + static assert(is(UnsignedOfSize!(short.sizeof) == ushort)); + static assert(is(UnsignedOfSize!(ushort.sizeof) == ushort)); + static assert(is(UnsignedOfSize!(int.sizeof) == uint)); + static assert(is(UnsignedOfSize!(uint.sizeof) == uint)); + static assert(is(UnsignedOfSize!(long.sizeof) == ulong)); + static assert(is(UnsignedOfSize!(ulong.sizeof) == ulong)); + + static assert(is(UnsignedOfSize!(bool.sizeof) == ubyte)); + static assert(is(UnsignedOfSize!(char.sizeof) == ubyte)); + static assert(is(UnsignedOfSize!(wchar.sizeof) == ushort)); + static assert(is(UnsignedOfSize!(dchar.sizeof) == uint)); + + static assert(is(UnsignedOfSize!(float.sizeof) == uint)); + static assert(is(UnsignedOfSize!(double.sizeof) == ulong)); + + static assert(is(UnsignedOfSize!10 == void)); +} + /++ Takes a range of `ubyte`s and converts the first `T.sizeof` bytes to `T`. The value returned is converted from the given endianness to the diff --git a/libphobos/src/std/checkedint.d b/libphobos/src/std/checkedint.d index cec1dc1a2dbbaca26c360a5b6c98b54286c752df..630ae4153a8ce4b3fac36d00e537856318350fcc 100644 --- a/libphobos/src/std/checkedint.d +++ b/libphobos/src/std/checkedint.d @@ -3362,12 +3362,14 @@ version (StdUnittest) private struct CountOverflows static struct Hook1 { uint calls; - auto hookOpUnary(string op, T)(T value) if (op == "-") + auto hookOpUnary(string op, T)(T value) + if (op == "-") { ++calls; return T(42); } - auto hookOpUnary(string op, T)(T value) if (op == "~") + auto hookOpUnary(string op, T)(T value) + if (op == "~") { ++calls; return T(43); @@ -3383,12 +3385,14 @@ version (StdUnittest) private struct CountOverflows static struct Hook2 { uint calls; - void hookOpUnary(string op, T)(ref T value) if (op == "++") + void hookOpUnary(string op, T)(ref T value) + if (op == "++") { ++calls; --value; } - void hookOpUnary(string op, T)(ref T value) if (op == "--") + void hookOpUnary(string op, T)(ref T value) + if (op == "--") { ++calls; ++value; diff --git a/libphobos/src/std/complex.d b/libphobos/src/std/complex.d index 60746f98ef7f68b3328d92579d2af3e795993a12..01e8dd2c1dac946d7f88012e3f7874a4f6a92847 100644 --- a/libphobos/src/std/complex.d +++ b/libphobos/src/std/complex.d @@ -156,7 +156,7 @@ if (isFloatingPoint!T) /// ditto void toString(Writer, Char)(scope Writer w, scope const ref FormatSpec!Char formatSpec) const - if (isOutputRange!(Writer, const(Char)[])) + if (isOutputRange!(Writer, const(Char)[])) { import std.format.write : formatValue; import std.math.traits : signbit; @@ -231,14 +231,14 @@ if (isFloatingPoint!T) // +complex Complex opUnary(string op)() const - if (op == "+") + if (op == "+") { return this; } // -complex Complex opUnary(string op)() const - if (op == "-") + if (op == "-") { return Complex(-re, -im); } @@ -255,7 +255,7 @@ if (isFloatingPoint!T) // complex op numeric Complex!(CommonType!(T,R)) opBinary(string op, R)(const R r) const - if (isNumeric!R) + if (isNumeric!R) { alias C = typeof(return); auto w = C(this.re, this.im); @@ -264,21 +264,21 @@ if (isFloatingPoint!T) // numeric + complex, numeric * complex Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(const R r) const - if ((op == "+" || op == "*") && (isNumeric!R)) + if ((op == "+" || op == "*") && (isNumeric!R)) { return opBinary!(op)(r); } // numeric - complex Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(const R r) const - if (op == "-" && isNumeric!R) + if (op == "-" && isNumeric!R) { return Complex(r - re, -im); } // numeric / complex Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(const R r) const - if (op == "/" && isNumeric!R) + if (op == "/" && isNumeric!R) { version (FastMath) { @@ -320,7 +320,7 @@ if (isFloatingPoint!T) // numeric ^^ complex Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(const R lhs) const - if (op == "^^" && isNumeric!R) + if (op == "^^" && isNumeric!R) { import core.math : cos, sin; import std.math.exponential : exp, log; @@ -349,7 +349,7 @@ if (isFloatingPoint!T) // complex += complex, complex -= complex ref Complex opOpAssign(string op, C)(const C z) - if ((op == "+" || op == "-") && is(C R == Complex!R)) + if ((op == "+" || op == "-") && is(C R == Complex!R)) { mixin ("re "~op~"= z.re;"); mixin ("im "~op~"= z.im;"); @@ -358,7 +358,7 @@ if (isFloatingPoint!T) // complex *= complex ref Complex opOpAssign(string op, C)(const C z) - if (op == "*" && is(C R == Complex!R)) + if (op == "*" && is(C R == Complex!R)) { auto temp = re*z.re - im*z.im; im = im*z.re + re*z.im; @@ -368,7 +368,7 @@ if (isFloatingPoint!T) // complex /= complex ref Complex opOpAssign(string op, C)(const C z) - if (op == "/" && is(C R == Complex!R)) + if (op == "/" && is(C R == Complex!R)) { version (FastMath) { @@ -409,7 +409,7 @@ if (isFloatingPoint!T) // complex ^^= complex ref Complex opOpAssign(string op, C)(const C z) - if (op == "^^" && is(C R == Complex!R)) + if (op == "^^" && is(C R == Complex!R)) { import core.math : cos, sin; import std.math.exponential : exp, log; @@ -425,7 +425,7 @@ if (isFloatingPoint!T) // complex += numeric, complex -= numeric ref Complex opOpAssign(string op, U : T)(const U a) - if (op == "+" || op == "-") + if (op == "+" || op == "-") { mixin ("re "~op~"= a;"); return this; @@ -433,7 +433,7 @@ if (isFloatingPoint!T) // complex *= numeric, complex /= numeric ref Complex opOpAssign(string op, U : T)(const U a) - if (op == "*" || op == "/") + if (op == "*" || op == "/") { mixin ("re "~op~"= a;"); mixin ("im "~op~"= a;"); @@ -442,7 +442,7 @@ if (isFloatingPoint!T) // complex ^^= real ref Complex opOpAssign(string op, R)(const R r) - if (op == "^^" && isFloatingPoint!R) + if (op == "^^" && isFloatingPoint!R) { import core.math : cos, sin; immutable ab = abs(this)^^r; @@ -454,7 +454,7 @@ if (isFloatingPoint!T) // complex ^^= int ref Complex opOpAssign(string op, U)(const U i) - if (op == "^^" && isIntegral!U) + if (op == "^^" && isIntegral!U) { switch (i) { diff --git a/libphobos/src/std/concurrency.d b/libphobos/src/std/concurrency.d index 267f682b3e964f3146ecc59efea26531b3099ba1..94265a2b7797e85384c9f1631ae8d854cb085bde 100644 --- a/libphobos/src/std/concurrency.d +++ b/libphobos/src/std/concurrency.d @@ -163,7 +163,8 @@ private MsgType type; Variant data; - this(T...)(MsgType t, T vals) if (T.length > 0) + this(T...)(MsgType t, T vals) + if (T.length > 0) { static if (T.length == 1) { diff --git a/libphobos/src/std/container/dlist.d b/libphobos/src/std/container/dlist.d index 4fdf13d486f107777d2db9145dae3e7344f6e6ba..728aacde5c086dfcf14eb7df1ced86d751d75f63 100644 --- a/libphobos/src/std/container/dlist.d +++ b/libphobos/src/std/container/dlist.d @@ -245,7 +245,8 @@ struct DList(T) /** Constructor taking a number of nodes */ - this(U)(U[] values...) if (isImplicitlyConvertible!(U, T)) + this(U)(U[] values...) + if (isImplicitlyConvertible!(U, T)) { insertBack(values); } diff --git a/libphobos/src/std/container/package.d b/libphobos/src/std/container/package.d index 763da8b52b0bcf3d2a998b8e47b460c54c31d006..fc0495057d0c5cfcc4c0d6298f5237dd0fab09bd 100644 --- a/libphobos/src/std/container/package.d +++ b/libphobos/src/std/container/package.d @@ -801,7 +801,8 @@ Indexing operators yield or modify the value at a specified index. /** $(D k in container) returns true if the given key is in the container. */ - bool opBinaryRight(string op)(KeyType k) if (op == "in") + bool opBinaryRight(string op)(KeyType k) + if (op == "in") { assert(0, "Not implemented"); } @@ -843,13 +844,15 @@ define `opBinary`. Complexity: $(BIGOH n + m), where m is the number of elements in $(D stuff) */ - TotalContainer opBinary(string op)(Stuff rhs) if (op == "~") + TotalContainer opBinary(string op)(Stuff rhs) + if (op == "~") { assert(0, "Not implemented"); } /// ditto - TotalContainer opBinaryRight(string op)(Stuff lhs) if (op == "~") + TotalContainer opBinaryRight(string op)(Stuff lhs) + if (op == "~") { assert(0, "Not implemented"); } @@ -857,7 +860,8 @@ stuff) /** Forwards to $(D insertAfter(this[], stuff)). */ - void opOpAssign(string op)(Stuff stuff) if (op == "~") + void opOpAssign(string op)(Stuff stuff) + if (op == "~") { assert(0, "Not implemented"); } diff --git a/libphobos/src/std/container/rbtree.d b/libphobos/src/std/container/rbtree.d index 9bd8d27c0656b0a36e1697db32f47286dcc87eeb..53697029c970f203109eb3327ce988a8b5ed7377 100644 --- a/libphobos/src/std/container/rbtree.d +++ b/libphobos/src/std/container/rbtree.d @@ -1057,7 +1057,8 @@ if (is(typeof(binaryFun!less(T.init, T.init)))) Complexity: $(BIGOH log(n)) +/ - bool opBinaryRight(string op)(Elem e) const if (op == "in") + bool opBinaryRight(string op)(Elem e) const + if (op == "in") { return _find(e) !is null; } @@ -1261,7 +1262,8 @@ if (is(typeof(binaryFun!less(T.init, T.init)))) * * Complexity: $(BIGOH log(n)) */ - size_t stableInsert(Stuff)(Stuff stuff) if (isImplicitlyConvertible!(Stuff, Elem)) + size_t stableInsert(Stuff)(Stuff stuff) + if (isImplicitlyConvertible!(Stuff, Elem)) { static if (allowDuplicates) { @@ -1283,8 +1285,8 @@ if (is(typeof(binaryFun!less(T.init, T.init)))) * Complexity: $(BIGOH m * log(n)) */ size_t stableInsert(Stuff)(scope Stuff stuff) - if (isInputRange!Stuff && - isImplicitlyConvertible!(ElementType!Stuff, Elem)) + if (isInputRange!Stuff && + isImplicitlyConvertible!(ElementType!Stuff, Elem)) { size_t result = 0; static if (allowDuplicates) @@ -1534,7 +1536,7 @@ assert(equal(rbt[], [5])); -------------------- +/ size_t removeKey(U...)(U elems) - if (allSatisfy!(isImplicitlyConvertibleToElem, U)) + if (allSatisfy!(isImplicitlyConvertibleToElem, U)) { Elem[U.length] toRemove = [elems]; return removeKey(toRemove[]); @@ -1542,7 +1544,7 @@ assert(equal(rbt[], [5])); /++ Ditto +/ size_t removeKey(U)(scope U[] elems) - if (isImplicitlyConvertible!(U, Elem)) + if (isImplicitlyConvertible!(U, Elem)) { immutable lenBefore = length; @@ -1564,9 +1566,9 @@ assert(equal(rbt[], [5])); /++ Ditto +/ size_t removeKey(Stuff)(Stuff stuff) - if (isInputRange!Stuff && - isImplicitlyConvertible!(ElementType!Stuff, Elem) && - !isDynamicArray!Stuff) + if (isInputRange!Stuff && + isImplicitlyConvertible!(ElementType!Stuff, Elem) && + !isDynamicArray!Stuff) { import std.array : array; //We use array in case stuff is a Range from this RedBlackTree - either @@ -1873,7 +1875,8 @@ assert(equal(rbt[], [5])); /** * Constructor. Pass in a range of elements to initialize the tree with. */ - this(Stuff)(Stuff stuff) if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, Elem)) + this(Stuff)(Stuff stuff) + if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, Elem)) { _setup(); stableInsert(stuff); diff --git a/libphobos/src/std/container/slist.d b/libphobos/src/std/container/slist.d index 0b504b474a803c851caed34ed83b8cde6d164730..ef6fbf060f1681ba7f2daf97bff3aec4c955fc8d 100644 --- a/libphobos/src/std/container/slist.d +++ b/libphobos/src/std/container/slist.d @@ -182,7 +182,8 @@ if (!is(T == shared)) /** Constructor taking a number of nodes */ - this(U)(U[] values...) if (isImplicitlyConvertible!(U, T)) + this(U)(U[] values...) + if (isImplicitlyConvertible!(U, T)) { insertFront(values); } diff --git a/libphobos/src/std/container/util.d b/libphobos/src/std/container/util.d index cc273a24b718d45222a3677a1f8cfd1bf17e7470..0b883d06421b37877115b23926b45d1843ebf586 100644 --- a/libphobos/src/std/container/util.d +++ b/libphobos/src/std/container/util.d @@ -109,14 +109,14 @@ if (!is(Container)) import std.traits : isDynamicArray; auto make(Range)(Range range) - if (!isDynamicArray!Range && isInputRange!Range && !isInfinite!Range) + if (!isDynamicArray!Range && isInputRange!Range && !isInfinite!Range) { import std.range : ElementType; return .make!(Container!(ElementType!Range, Args))(range); } auto make(T)(T[] items...) - if (!isInfinite!T) + if (!isInfinite!T) { return .make!(Container!(T, Args))(items); } diff --git a/libphobos/src/std/conv.d b/libphobos/src/std/conv.d index 3aa73c68bf1b1a6da71356f21f63346b77fa9a52..9c9d8db236a4e05c990931887d748d4dbe6031ea 100644 --- a/libphobos/src/std/conv.d +++ b/libphobos/src/std/conv.d @@ -205,21 +205,21 @@ $(PRE $(I UnsignedInteger): template to(T) { T to(A...)(A args) - if (A.length > 0) + if (A.length > 0) { return toImpl!T(args); } // Fix https://issues.dlang.org/show_bug.cgi?id=6175 T to(S)(ref S arg) - if (isStaticArray!S) + if (isStaticArray!S) { return toImpl!T(arg); } // Fix https://issues.dlang.org/show_bug.cgi?id=16108 T to(S)(ref S arg) - if (isAggregateType!S && !isCopyable!S) + if (isAggregateType!S && !isCopyable!S) { return toImpl!T(arg); } @@ -917,9 +917,22 @@ if (!is(S : T) && auto result = ()@trusted{ return cast(T) value; }(); if (!result && value) { - throw new ConvException("Cannot convert object of static type " - ~S.classinfo.name~" and dynamic type "~value.classinfo.name - ~" to type "~T.classinfo.name); + string name(TypeInfo ti) @trusted + { + while (auto tc = (cast(TypeInfo_Const) ti)) + { + ti = tc.base; + } + if (auto tinf = cast(TypeInfo_Interface) ti) + { + ti = tinf.info; + } + TypeInfo_Class tc = cast(TypeInfo_Class) ti; + assert(tc); + return tc.name; + } + throw new ConvException("Cannot convert object of static type " ~ + name(typeid(S)) ~ " and dynamic type " ~ name(typeid(value)) ~ " to type " ~ name(typeid(T))); } return result; } diff --git a/libphobos/src/std/datetime/date.d b/libphobos/src/std/datetime/date.d index c757b1e288096988cf0900b4d4e21fc2ebbf3910..0f417b15081e84bf3dc1c494549c7938a2b351f7 100644 --- a/libphobos/src/std/datetime/date.d +++ b/libphobos/src/std/datetime/date.d @@ -1075,7 +1075,7 @@ public: +/ ref DateTime add(string units) (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow @nogc - if (units == "years" || units == "months") + if (units == "years" || units == "months") { _date.add!units(value, allowOverflow); return this; @@ -1140,7 +1140,7 @@ public: +/ ref DateTime roll(string units) (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow @nogc - if (units == "years" || units == "months") + if (units == "years" || units == "months") { _date.roll!units(value, allowOverflow); return this; @@ -1209,7 +1209,7 @@ public: A reference to the `DateTime` (`this`). +/ ref DateTime roll(string units)(long value) @safe pure nothrow @nogc - if (units == "days") + if (units == "days") { _date.roll!"days"(value); return this; @@ -1250,9 +1250,9 @@ public: /// ditto ref DateTime roll(string units)(long value) @safe pure nothrow @nogc - if (units == "hours" || - units == "minutes" || - units == "seconds") + if (units == "hours" || + units == "minutes" || + units == "seconds") { _tod.roll!units(value); return this; @@ -2138,7 +2138,7 @@ public: this $(LREF DateTime). +/ DateTime opBinary(string op)(Duration duration) const @safe pure nothrow @nogc - if (op == "+" || op == "-") + if (op == "+" || op == "-") { DateTime retval = this; immutable seconds = duration.total!"seconds"; @@ -2233,7 +2233,7 @@ public: $(LREF DateTime). +/ ref DateTime opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc - if (op == "+" || op == "-") + if (op == "+" || op == "-") { import core.time : convert; import std.format : format; @@ -2339,7 +2339,7 @@ public: ) +/ Duration opBinary(string op)(DateTime rhs) const @safe pure nothrow @nogc - if (op == "-") + if (op == "-") { immutable dateResult = _date - rhs.date; immutable todResult = _tod - rhs._tod; @@ -3151,7 +3151,7 @@ public: be valid. +/ static DateTime fromISOString(S)(scope const S isoString) @safe pure - if (isSomeString!S) + if (isSomeString!S) { import std.algorithm.searching : countUntil; import std.exception : enforce; @@ -3252,7 +3252,7 @@ public: would not be valid. +/ static DateTime fromISOExtString(S)(scope const S isoExtString) @safe pure - if (isSomeString!(S)) + if (isSomeString!(S)) { import std.algorithm.searching : countUntil; import std.exception : enforce; @@ -3353,7 +3353,7 @@ public: would not be valid. +/ static DateTime fromSimpleString(S)(scope const S simpleString) @safe pure - if (isSomeString!(S)) + if (isSomeString!(S)) { import std.algorithm.searching : countUntil; import std.exception : enforce; @@ -4483,7 +4483,7 @@ public: +/ @safe pure nothrow @nogc ref Date add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (units == "years") + if (units == "years") { _year += value; @@ -4724,7 +4724,7 @@ public: // Shares documentation with "years" version. @safe pure nothrow @nogc ref Date add(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (units == "months") + if (units == "months") { auto years = months / 12; months %= 12; @@ -5268,7 +5268,7 @@ public: +/ @safe pure nothrow @nogc ref Date roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (units == "years") + if (units == "years") { return add!"years"(value, allowOverflow); } @@ -5313,7 +5313,7 @@ public: // Shares documentation with "years" version. @safe pure nothrow @nogc ref Date roll(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (units == "months") + if (units == "months") { months %= 12; auto newMonth = _month + months; @@ -5910,7 +5910,7 @@ public: A reference to the `Date` (`this`). +/ ref Date roll(string units)(long days) @safe pure nothrow @nogc - if (units == "days") + if (units == "days") { immutable limit = maxDay(_year, _month); days %= limit; @@ -6148,7 +6148,7 @@ public: this $(LREF Date). +/ Date opBinary(string op)(Duration duration) const @safe pure nothrow @nogc - if (op == "+" || op == "-") + if (op == "+" || op == "-") { Date retval = this; immutable days = duration.total!"days"; @@ -6238,7 +6238,7 @@ public: this $(LREF Date). +/ ref Date opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc - if (op == "+" || op == "-") + if (op == "+" || op == "-") { immutable days = duration.total!"days"; mixin("return _addDays(" ~ op ~ "days);"); @@ -6313,7 +6313,7 @@ public: ) +/ Duration opBinary(string op)(Date rhs) const @safe pure nothrow @nogc - if (op == "-") + if (op == "-") { import core.time : dur; return dur!"days"(this.dayOfGregorianCal - rhs.dayOfGregorianCal); @@ -7621,7 +7621,7 @@ public: valid. +/ static Date fromISOString(S)(scope const S isoString) @safe pure - if (isSomeString!S) + if (isSomeString!S) { import std.algorithm.searching : startsWith; import std.conv : to, text, ConvException; @@ -7764,7 +7764,7 @@ public: would not be valid. +/ static Date fromISOExtString(S)(scope const S isoExtString) @safe pure - if (isSomeString!(S)) + if (isSomeString!(S)) { import std.algorithm.searching : startsWith; import std.conv : to, ConvException; @@ -7902,7 +7902,7 @@ public: be valid. +/ static Date fromSimpleString(S)(scope const S simpleString) @safe pure - if (isSomeString!(S)) + if (isSomeString!(S)) { import std.algorithm.searching : startsWith; import std.conv : to, ConvException; @@ -8606,7 +8606,7 @@ public: A reference to the `TimeOfDay` (`this`). +/ ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc - if (units == "hours") + if (units == "hours") { import core.time : dur; return this += dur!"hours"(value); @@ -8655,7 +8655,7 @@ public: /// ditto ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc - if (units == "minutes" || units == "seconds") + if (units == "minutes" || units == "seconds") { import std.format : format; @@ -8851,7 +8851,7 @@ public: this $(LREF TimeOfDay). +/ TimeOfDay opBinary(string op)(Duration duration) const @safe pure nothrow @nogc - if (op == "+" || op == "-") + if (op == "+" || op == "-") { TimeOfDay retval = this; immutable seconds = duration.total!"seconds"; @@ -8938,7 +8938,7 @@ public: this $(LREF TimeOfDay). +/ ref TimeOfDay opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc - if (op == "+" || op == "-") + if (op == "+" || op == "-") { immutable seconds = duration.total!"seconds"; mixin("return _addSeconds(" ~ op ~ "seconds);"); @@ -9004,7 +9004,7 @@ public: rhs = The $(LREF TimeOfDay) to subtract from this one. +/ Duration opBinary(string op)(TimeOfDay rhs) const @safe pure nothrow @nogc - if (op == "-") + if (op == "-") { immutable lhsSec = _hour * 3600 + _minute * 60 + _second; immutable rhsSec = rhs._hour * 3600 + rhs._minute * 60 + rhs._second; @@ -9201,7 +9201,7 @@ public: not be valid. +/ static TimeOfDay fromISOString(S)(scope const S isoString) @safe pure - if (isSomeString!S) + if (isSomeString!S) { import std.conv : to, text, ConvException; import std.exception : enforce; @@ -9326,7 +9326,7 @@ public: would not be valid. +/ static TimeOfDay fromISOExtString(S)(scope const S isoExtString) @safe pure - if (isSomeString!S) + if (isSomeString!S) { import std.conv : ConvException, text, to; import std.string : strip; diff --git a/libphobos/src/std/datetime/interval.d b/libphobos/src/std/datetime/interval.d index d787e3a8080cc5e76c77b6e632b69657aa85cd10..832a2cb998a8de19c3676c89ae632cd4729cf0a4 100644 --- a/libphobos/src/std/datetime/interval.d +++ b/libphobos/src/std/datetime/interval.d @@ -137,7 +137,7 @@ public: -------------------- +/ this(U)(scope const TP begin, scope const U end) pure - if (is(immutable TP == immutable U)) + if (is(immutable TP == immutable U)) { if (!_valid(begin, end)) throw new DateTimeException("Arguments would result in an invalid Interval."); @@ -162,7 +162,7 @@ public: -------------------- +/ this(D)(scope const TP begin, scope const D duration) pure - if (__traits(compiles, begin + duration)) + if (__traits(compiles, begin + duration)) { _begin = cast(TP) begin; _end = begin + duration; @@ -1118,7 +1118,7 @@ public: -------------------- +/ void shift(D)(D duration) pure - if (__traits(compiles, begin + duration)) + if (__traits(compiles, begin + duration)) { _enforceNotEmpty(); @@ -1168,7 +1168,7 @@ public: -------------------- +/ void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (isIntegral!T) + if (isIntegral!T) { _enforceNotEmpty(); @@ -1215,7 +1215,7 @@ public: -------------------- +/ void expand(D)(D duration, Direction dir = Direction.both) pure - if (__traits(compiles, begin + duration)) + if (__traits(compiles, begin + duration)) { _enforceNotEmpty(); @@ -3919,7 +3919,7 @@ assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); -------------------- +/ void shift(D)(D duration) pure nothrow - if (__traits(compiles, begin + duration)) + if (__traits(compiles, begin + duration)) { _begin += duration; } @@ -3960,7 +3960,7 @@ assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); -------------------- +/ void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (isIntegral!T) + if (isIntegral!T) { auto begin = _begin; @@ -3992,7 +3992,7 @@ assert(interval2 == PosInfInterval!Date(Date(1996, 1, 4))); -------------------- +/ void expand(D)(D duration) pure nothrow - if (__traits(compiles, begin + duration)) + if (__traits(compiles, begin + duration)) { _begin -= duration; } @@ -4028,7 +4028,7 @@ assert(interval2 == PosInfInterval!Date(Date(1998, 1, 2))); -------------------- +/ void expand(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (isIntegral!T) + if (isIntegral!T) { auto begin = _begin; @@ -6145,7 +6145,7 @@ assert(interval2 == NegInfInterval!Date( Date(2012, 2, 15))); -------------------- +/ void shift(D)(D duration) pure nothrow - if (__traits(compiles, end + duration)) + if (__traits(compiles, end + duration)) { _end += duration; } @@ -6185,7 +6185,7 @@ assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); -------------------- +/ void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (isIntegral!T) + if (isIntegral!T) { auto end = _end; @@ -6217,7 +6217,7 @@ assert(interval2 == NegInfInterval!Date(Date(2012, 2, 28))); -------------------- +/ void expand(D)(D duration) pure nothrow - if (__traits(compiles, end + duration)) + if (__traits(compiles, end + duration)) { _end += duration; } @@ -6253,7 +6253,7 @@ assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); -------------------- +/ void expand(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (isIntegral!T) + if (isIntegral!T) { auto end = _end; diff --git a/libphobos/src/std/datetime/systime.d b/libphobos/src/std/datetime/systime.d index a2e52aebac5db537d6e4ffd01077e049688e42db..fd2a9e192e4278d05b9fa02931c4ee97fb1fa65a 100644 --- a/libphobos/src/std/datetime/systime.d +++ b/libphobos/src/std/datetime/systime.d @@ -2444,7 +2444,7 @@ public: this SysTime. +/ T toUnixTime(T = time_t)() @safe const pure nothrow scope - if (is(T == int) || is(T == long)) + if (is(T == int) || is(T == long)) { return stdTimeToUnixTime!T(_stdTime); } @@ -2792,7 +2792,7 @@ public: causing the month to increment. +/ ref SysTime add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow scope - if (units == "years" || units == "months") + if (units == "years" || units == "months") { auto hnsecs = adjTime; auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; @@ -4830,7 +4830,7 @@ public: $(LREF SysTime). +/ ref SysTime roll(string units)(long value) @safe nothrow scope - if (units == "days") + if (units == "days") { auto hnsecs = adjTime; auto gdays = splitUnitsFromHNSecs!"days"(hnsecs) + 1; @@ -5195,7 +5195,7 @@ public: // Shares documentation with "days" version. ref SysTime roll(string units)(long value) @safe nothrow scope - if (units == "hours" || units == "minutes" || units == "seconds") + if (units == "hours" || units == "minutes" || units == "seconds") { try { @@ -5871,7 +5871,7 @@ public: // Shares documentation with "days" version. ref SysTime roll(string units)(long value) @safe nothrow scope - if (units == "msecs" || units == "usecs" || units == "hnsecs") + if (units == "msecs" || units == "usecs" || units == "hnsecs") { auto hnsecs = adjTime; immutable days = splitUnitsFromHNSecs!"days"(hnsecs); @@ -6308,7 +6308,7 @@ public: this $(LREF SysTime). +/ SysTime opBinary(string op)(Duration duration) @safe const pure nothrow return scope - if (op == "+" || op == "-") + if (op == "+" || op == "-") { SysTime retval = SysTime(this._stdTime, this._timezone); immutable hnsecs = duration.total!"hnsecs"; @@ -6528,7 +6528,7 @@ public: this $(LREF SysTime). +/ ref SysTime opOpAssign(string op)(Duration duration) @safe pure nothrow scope - if (op == "+" || op == "-") + if (op == "+" || op == "-") { immutable hnsecs = duration.total!"hnsecs"; mixin("_stdTime " ~ op ~ "= hnsecs;"); @@ -6732,7 +6732,7 @@ public: ) +/ Duration opBinary(string op)(SysTime rhs) @safe const pure nothrow scope - if (op == "-") + if (op == "-") { return dur!"hnsecs"(_stdTime - rhs._stdTime); } @@ -7992,7 +7992,7 @@ public: Returns a $(REF Date,std,datetime,date) equivalent to this $(LREF SysTime). +/ Date opCast(T)() @safe const nothrow scope - if (is(immutable T == immutable Date)) + if (is(immutable T == immutable Date)) { return Date(dayOfGregorianCal); } @@ -8033,7 +8033,7 @@ public: $(LREF SysTime). +/ DateTime opCast(T)() @safe const nothrow scope - if (is(immutable T == immutable DateTime)) + if (is(immutable T == immutable DateTime)) { try { @@ -8099,7 +8099,7 @@ public: $(LREF SysTime). +/ TimeOfDay opCast(T)() @safe const nothrow scope - if (is(immutable T == immutable TimeOfDay)) + if (is(immutable T == immutable TimeOfDay)) { try { @@ -8156,7 +8156,7 @@ public: // should be allowed, and it doesn't work without this opCast() since opCast() // has already been defined for other types. SysTime opCast(T)() @safe const pure nothrow scope - if (is(immutable T == immutable SysTime)) + if (is(immutable T == immutable SysTime)) { return SysTime(_stdTime, _timezone); } @@ -8799,7 +8799,7 @@ public: be valid. +/ static SysTime fromISOString(S)(scope const S isoString, immutable TimeZone tz = null) @safe - if (isSomeString!S) + if (isSomeString!S) { import std.algorithm.searching : startsWith, find; import std.conv : to; @@ -9100,7 +9100,7 @@ public: be valid. +/ static SysTime fromISOExtString(S)(scope const S isoExtString, immutable TimeZone tz = null) @safe - if (isSomeString!(S)) + if (isSomeString!(S)) { import std.algorithm.searching : countUntil, find; import std.conv : to; @@ -9351,7 +9351,7 @@ public: be valid. +/ static SysTime fromSimpleString(S)(scope const S simpleString, immutable TimeZone tz = null) @safe - if (isSomeString!(S)) + if (isSomeString!(S)) { import std.algorithm.searching : find; import std.conv : to; diff --git a/libphobos/src/std/datetime/timezone.d b/libphobos/src/std/datetime/timezone.d index 4b6f27d066d5517bead165f353bd7f5cd91a63b6..6a1898b0ef50c90b6acda4b8ecd5447dcd3c3abd 100644 --- a/libphobos/src/std/datetime/timezone.d +++ b/libphobos/src/std/datetime/timezone.d @@ -1550,7 +1550,7 @@ package: isoString = A string which represents a time zone in the ISO format. +/ static immutable(SimpleTimeZone) fromISOString(S)(S isoString) @safe pure - if (isSomeString!S) + if (isSomeString!S) { import std.algorithm.searching : startsWith; import std.conv : text, to, ConvException; @@ -1704,7 +1704,7 @@ package: isoExtString = A string which represents a time zone in the ISO format. +/ static immutable(SimpleTimeZone) fromISOExtString(S)(scope S isoExtString) @safe pure - if (isSomeString!S) + if (isSomeString!S) { import std.algorithm.searching : startsWith; import std.conv : ConvException, to; @@ -2642,7 +2642,7 @@ private: Reads an int from a TZ file. +/ static T readVal(T)(ref File tzFile) @trusted - if ((isIntegral!T || isSomeChar!T) || is(immutable T == immutable bool)) + if ((isIntegral!T || isSomeChar!T) || is(immutable T == immutable bool)) { import std.bitmanip : bigEndianToNative; T[1] buff; @@ -2657,7 +2657,7 @@ private: Reads an array of values from a TZ file. +/ static T readVal(T)(ref File tzFile, size_t length) @trusted - if (isArray!T) + if (isArray!T) { auto buff = new T(length); @@ -2672,7 +2672,7 @@ private: Reads a `TempTTInfo` from a TZ file. +/ static T readVal(T)(ref File tzFile) @safe - if (is(T == TempTTInfo)) + if (is(T == TempTTInfo)) { return TempTTInfo(readVal!int(tzFile), readVal!bool(tzFile), diff --git a/libphobos/src/std/exception.d b/libphobos/src/std/exception.d index c3024d799686d35c5edb54cc83f3270493ec6d30..9b1a7c8a62cedbd7c9835bd4be15b2114f615157 100644 --- a/libphobos/src/std/exception.d +++ b/libphobos/src/std/exception.d @@ -1801,7 +1801,7 @@ expression. @system unittest { import std.format : format; - assert("%s".format.ifThrown!Exception(e => e.classinfo.name) == "std.format.FormatException"); + assert("%s".format.ifThrown!Exception(e => typeid(e).name) == "std.format.FormatException"); } //Verify Examples @@ -1834,7 +1834,7 @@ expression. static assert(!__traits(compiles, (new Object()).ifThrown(1))); //Use a lambda to get the thrown object. - assert("%s".format().ifThrown(e => e.classinfo.name) == "std.format.FormatException"); + assert("%s".format().ifThrown(e => typeid(e).name) == "std.format.FormatException"); } @system unittest diff --git a/libphobos/src/std/experimental/allocator/common.d b/libphobos/src/std/experimental/allocator/common.d index d2efe338d7b79b128367124244ea7e448464e0c0..b06fb627d71fa0905789c66c2b7684d2eac6eed1 100644 --- a/libphobos/src/std/experimental/allocator/common.d +++ b/libphobos/src/std/experimental/allocator/common.d @@ -66,6 +66,39 @@ unittest static assert(stateSize!C3 == 2 * size_t.sizeof + char.sizeof); } +/** +State of an allocator `A`. + +`AllocatorState!(A).sizeof` is zero for `A` being `NullAllocator`, `Mallocator`, +`GCAllocator`, and `MMapAllocator` and typically non-zero for the other. + */ +mixin template AllocatorState(A) +if (isAllocator!A) +{ + static if (stateSize!A == 0) + alias allocator = A.instance; + else + A allocator; +} + +/// +@safe @nogc nothrow pure +unittest +{ + import std.experimental.allocator.building_blocks.null_allocator : NullAllocator; + import std.experimental.allocator.mallocator : Mallocator; + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.mmap_allocator : MmapAllocator; + struct S + { + mixin AllocatorState!NullAllocator n; + mixin AllocatorState!GCAllocator g; + mixin AllocatorState!Mallocator m; + mixin AllocatorState!MmapAllocator p; + } + static assert(S.sizeof == 1); +} + /** Returns `true` if the `Allocator` has the alignment known at compile time; otherwise it returns `false`. diff --git a/libphobos/src/std/experimental/allocator/mallocator.d b/libphobos/src/std/experimental/allocator/mallocator.d index 087dbec449a56eb49edbee7c59cd9f7cade663e9..fbe936644f1d0bbbbf224a15a7c3831f1b1434d7 100644 --- a/libphobos/src/std/experimental/allocator/mallocator.d +++ b/libphobos/src/std/experimental/allocator/mallocator.d @@ -118,9 +118,9 @@ struct Mallocator version (CRuntime_Microsoft) { - @nogc nothrow private extern(C) void* _aligned_malloc(size_t, size_t); - @nogc nothrow private extern(C) void _aligned_free(void *memblock); - @nogc nothrow private extern(C) void* _aligned_realloc(void *, size_t, size_t); + @nogc nothrow pure private extern(C) void* _aligned_malloc(size_t, size_t); + @nogc nothrow pure private extern(C) void _aligned_free(void *memblock); + @nogc nothrow pure private extern(C) void* _aligned_realloc(void *, size_t, size_t); } /** @@ -138,7 +138,7 @@ struct AlignedMallocator /** Forwards to $(D alignedAllocate(bytes, platformAlignment)). */ - @trusted @nogc nothrow + @trusted @nogc nothrow pure void[] allocate(size_t bytes) shared { if (!bytes) return null; @@ -152,7 +152,7 @@ struct AlignedMallocator `__aligned_malloc`) on Windows. */ version (Posix) - @trusted @nogc nothrow + @trusted @nogc nothrow pure void[] alignedAllocate(size_t bytes, uint a) shared { import core.stdc.errno : ENOMEM, EINVAL; @@ -184,7 +184,7 @@ version (LDC_AddressSanitizer) return result[0 .. bytes]; } else version (Windows) - @trusted @nogc nothrow + @trusted @nogc nothrow pure void[] alignedAllocate(size_t bytes, uint a) shared { auto result = _aligned_malloc(bytes, a); @@ -198,15 +198,15 @@ version (LDC_AddressSanitizer) `__aligned_free(b.ptr)`) on Windows. */ version (Posix) - @system @nogc nothrow + @system @nogc nothrow pure bool deallocate(void[] b) shared { - import core.stdc.stdlib : free; - free(b.ptr); + import core.memory : pureFree; + pureFree(b.ptr); return true; } else version (Windows) - @system @nogc nothrow + @system @nogc nothrow pure bool deallocate(void[] b) shared { _aligned_free(b.ptr); @@ -219,7 +219,7 @@ version (LDC_AddressSanitizer) Should be used with blocks obtained with `allocate` otherwise the custom alignment passed with `alignedAllocate` can be lost. */ - @system @nogc nothrow + @system @nogc nothrow pure bool reallocate(ref void[] b, size_t newSize) shared { return alignedReallocate(b, newSize, alignment); @@ -233,7 +233,7 @@ version (LDC_AddressSanitizer) $(D __aligned_realloc(b.ptr, newSize, a))). */ version (Windows) - @system @nogc nothrow + @system @nogc nothrow pure bool alignedReallocate(ref void[] b, size_t s, uint a) shared { if (!s) @@ -250,7 +250,7 @@ version (LDC_AddressSanitizer) /// ditto version (Posix) - @system @nogc nothrow + @system @nogc nothrow pure bool alignedReallocate(ref void[] b, size_t s, uint a) shared { if (!s) @@ -281,7 +281,7 @@ version (LDC_AddressSanitizer) } /// -@nogc @system nothrow unittest +pure @nogc @system nothrow unittest { auto buffer = AlignedMallocator.instance.alignedAllocate(1024 * 1024 * 4, 128); @@ -290,7 +290,7 @@ version (LDC_AddressSanitizer) } version (Posix) -@nogc @system nothrow unittest +pure @nogc @system nothrow unittest { // https://issues.dlang.org/show_bug.cgi?id=16398 // test the "pseudo" alignedReallocate for Posix diff --git a/libphobos/src/std/file.d b/libphobos/src/std/file.d index 2a0d1392c0884ac017afefc7874d538bf80da8f0..c3466acc05539f94a2d3a586bd923ad984c2244f 100644 --- a/libphobos/src/std/file.d +++ b/libphobos/src/std/file.d @@ -4794,7 +4794,7 @@ private struct DirIteratorImpl } this(R)(R pathname, SpanMode mode, bool followSymlink) - if (isSomeFiniteCharInputRange!R) + if (isSomeFiniteCharInputRange!R) { _mode = mode; _followSymlink = followSymlink; diff --git a/libphobos/src/std/format/internal/write.d b/libphobos/src/std/format/internal/write.d index 16c7a51d8c6e114e6ec89e5bac81333b8f8927af..8b60565d8a17177b2bfd83da430d98dcb793e11f 100644 --- a/libphobos/src/std/format/internal/write.d +++ b/libphobos/src/std/format/internal/write.d @@ -1935,7 +1935,8 @@ template hasToString(T, Char) static struct G { string toString() {return "";} - void toString(Writer)(ref Writer w) if (isOutputRange!(Writer, string)) {} + void toString(Writer)(ref Writer w) + if (isOutputRange!(Writer, string)) {} } static struct H { @@ -1946,7 +1947,8 @@ template hasToString(T, Char) } static struct I { - void toString(Writer)(ref Writer w) if (isOutputRange!(Writer, string)) {} + void toString(Writer)(ref Writer w) + if (isOutputRange!(Writer, string)) {} void toString(Writer)(ref Writer w, scope const ref FormatSpec!char fmt) if (isOutputRange!(Writer, string)) {} @@ -2042,7 +2044,8 @@ template hasToString(T, Char) static struct G { string toString() const {return "";} - void toString(Writer)(ref Writer w) const if (isOutputRange!(Writer, string)) {} + void toString(Writer)(ref Writer w) const + if (isOutputRange!(Writer, string)) {} } static struct H { @@ -2053,7 +2056,8 @@ template hasToString(T, Char) } static struct I { - void toString(Writer)(ref Writer w) const if (isOutputRange!(Writer, string)) {} + void toString(Writer)(ref Writer w) const + if (isOutputRange!(Writer, string)) {} void toString(Writer)(ref Writer w, scope const ref FormatSpec!char fmt) const if (isOutputRange!(Writer, string)) {} @@ -2603,7 +2607,8 @@ if ((is(T == struct) || is(T == union)) && (hasToString!(T, Char) || !is(Builtin { int n = 0; alias n this; - T opCast(T) () if (is(T == Frop)) + T opCast(T) () + if (is(T == Frop)) { return Frop(); } diff --git a/libphobos/src/std/functional.d b/libphobos/src/std/functional.d index 588a9c8a54785e046efbb040fe7bc65cabb7cb9e..5022702ace90656b830174622edce17c8af8fbce 100644 --- a/libphobos/src/std/functional.d +++ b/libphobos/src/std/functional.d @@ -445,7 +445,7 @@ if (S=="<"||S==">"||S=="<="||S==">="||S=="=="||S=="!=") { import std.traits : isIntegral; private bool unsafeOp(ElementType1, ElementType2)(ElementType1 a, ElementType2 b) pure - if (isIntegral!ElementType1 && isIntegral!ElementType2) + if (isIntegral!ElementType1 && isIntegral!ElementType2) { import std.traits : CommonType; alias T = CommonType!(ElementType1, ElementType2); diff --git a/libphobos/src/std/int128.d b/libphobos/src/std/int128.d index 92895445aedcaaec58a7f937387dbe32faaa4bbc..c5569152bbdd55a7c00ef4284cdbe048c8323ce1 100644 --- a/libphobos/src/std/int128.d +++ b/libphobos/src/std/int128.d @@ -111,7 +111,7 @@ public struct Int128 * Returns: lvalue of result */ Int128 opUnary(string op)() const - if (op == "+") + if (op == "+") { return this; } @@ -121,7 +121,7 @@ public struct Int128 * Returns: lvalue of result */ Int128 opUnary(string op)() const - if (op == "-" || op == "~") + if (op == "-" || op == "~") { static if (op == "-") return Int128(neg(this.data)); @@ -134,7 +134,7 @@ public struct Int128 * Returns: lvalue of result */ Int128 opUnary(string op)() - if (op == "++" || op == "--") + if (op == "++" || op == "--") { static if (op == "++") this.data = inc(this.data); @@ -206,9 +206,9 @@ public struct Int128 * Returns: value after the operation is applied */ Int128 opBinary(string op)(Int128 op2) const - if (op == "+" || op == "-" || - op == "*" || op == "/" || op == "%" || - op == "&" || op == "|" || op == "^") + if (op == "+" || op == "-" || + op == "*" || op == "/" || op == "%" || + op == "&" || op == "|" || op == "^") { static if (op == "+") return Int128(add(this.data, op2.data)); @@ -236,10 +236,10 @@ public struct Int128 /// ditto Int128 opBinary(string op, Int)(const Int op2) const - if ((op == "+" || op == "-" || - op == "*" || op == "/" || op == "%" || - op == "&" || op == "|" || op == "^") && - is(Int : long) && __traits(isIntegral, Int)) + if ((op == "+" || op == "-" || + op == "*" || op == "/" || op == "%" || + op == "&" || op == "|" || op == "^") && + is(Int : long) && __traits(isIntegral, Int)) { static if (__traits(isUnsigned, Int)) return mixin("this " ~ op ~ " Int128(0, op2)"); @@ -249,20 +249,20 @@ public struct Int128 /// ditto Int128 opBinary(string op, IntLike)(auto ref IntLike op2) const - if ((op == "+" || op == "-" || - op == "*" || op == "/" || op == "%" || - op == "&" || op == "|" || op == "^") && - is(IntLike : long) && !__traits(isIntegral, IntLike)) + if ((op == "+" || op == "-" || + op == "*" || op == "/" || op == "%" || + op == "&" || op == "|" || op == "^") && + is(IntLike : long) && !__traits(isIntegral, IntLike)) { return opBinary!(op)(__traits(getMember, op2, __traits(getAliasThis, IntLike)[0])); } /// ditto Int128 opBinaryRight(string op, Int)(const Int op2) const - if ((op == "+" || op == "-" || - op == "*" || op == "/" || op == "%" || - op == "&" || op == "|" || op == "^") && - is(Int : long) && __traits(isIntegral, Int)) + if ((op == "+" || op == "-" || + op == "*" || op == "/" || op == "%" || + op == "&" || op == "|" || op == "^") && + is(Int : long) && __traits(isIntegral, Int)) { static if (__traits(isUnsigned, Int)) mixin("return Int128(0, op2) " ~ op ~ " this;"); @@ -272,31 +272,31 @@ public struct Int128 /// ditto Int128 opBinaryRight(string op, IntLike)(auto ref IntLike op2) const - if ((op == "+" || op == "-" || - op == "*" || op == "/" || op == "%" || - op == "&" || op == "|" || op == "^") && - is(IntLike : long) && !__traits(isIntegral, IntLike)) + if ((op == "+" || op == "-" || + op == "*" || op == "/" || op == "%" || + op == "&" || op == "|" || op == "^") && + is(IntLike : long) && !__traits(isIntegral, IntLike)) { return opBinaryRight!(op)(__traits(getMember, op2, __traits(getAliasThis, IntLike)[0])); } /// ditto Int128 opBinary(string op)(long op2) const - if (op == "<<") + if (op == "<<") { return Int128(shl(this.data, cast(uint) op2)); } /// ditto Int128 opBinary(string op)(long op2) const - if (op == ">>") + if (op == ">>") { return Int128(sar(this.data, cast(uint) op2)); } /// ditto Int128 opBinary(string op)(long op2) const - if (op == ">>>") + if (op == ">>>") { return Int128(shr(this.data, cast(uint) op2)); } @@ -307,10 +307,10 @@ public struct Int128 * Returns: lvalue of updated left operand */ ref Int128 opOpAssign(string op)(Int128 op2) - if (op == "+" || op == "-" || - op == "*" || op == "/" || op == "%" || - op == "&" || op == "|" || op == "^" || - op == "<<" || op == ">>" || op == ">>>") + if (op == "+" || op == "-" || + op == "*" || op == "/" || op == "%" || + op == "&" || op == "|" || op == "^" || + op == "<<" || op == ">>" || op == ">>>") { mixin("this = this " ~ op ~ " op2;"); return this; @@ -318,11 +318,11 @@ public struct Int128 /// ditto ref Int128 opOpAssign(string op, Int)(auto ref Int op2) - if ((op == "+" || op == "-" || - op == "*" || op == "/" || op == "%" || - op == "&" || op == "|" || op == "^" || - op == "<<" || op == ">>" || op == ">>>") - && is(Int : long)) + if ((op == "+" || op == "-" || + op == "*" || op == "/" || op == "%" || + op == "&" || op == "|" || op == "^" || + op == "<<" || op == ">>" || op == ">>>") + && is(Int : long)) { mixin("this = this " ~ op ~ " op2;"); return this; diff --git a/libphobos/src/std/internal/math/biguintcore.d b/libphobos/src/std/internal/math/biguintcore.d index 9df6bb22274ff06585b90120a025bd39c5bac76d..9c794b60de1220ac52ec20bad22efaa563ddb405 100644 --- a/libphobos/src/std/internal/math/biguintcore.d +++ b/libphobos/src/std/internal/math/biguintcore.d @@ -250,7 +250,8 @@ private: data = x; } package(std) // used from: std.bigint - this(T)(T x) pure nothrow @safe scope if (isIntegral!T) + this(T)(T x) pure nothrow @safe scope + if (isIntegral!T) { opAssign(x); } @@ -312,7 +313,8 @@ public: } /// - void opAssign(Tulong)(Tulong u) pure nothrow @safe scope if (is (Tulong == ulong)) + void opAssign(Tulong)(Tulong u) pure nothrow @safe scope + if (is (Tulong == ulong)) { if (u == 0) data = ZERO; else if (u == 1) data = ONE; @@ -356,7 +358,8 @@ public: } /// - int opCmp(Tulong)(Tulong y) pure nothrow @nogc const @safe scope if (is (Tulong == ulong)) + int opCmp(Tulong)(Tulong y) pure nothrow @nogc const @safe scope + if (is (Tulong == ulong)) { if (data.length > maxBigDigits!Tulong) return 1; @@ -501,8 +504,8 @@ public: } // return false if invalid character found - bool fromHexString(Range)(Range s) scope if ( - isBidirectionalRange!Range && isSomeChar!(ElementType!Range)) + bool fromHexString(Range)(Range s) scope + if (isBidirectionalRange!Range && isSomeChar!(ElementType!Range)) { import std.range : walkLength; @@ -570,8 +573,8 @@ public: } // return true if OK; false if erroneous characters found - bool fromDecimalString(Range)(Range s) scope if ( - isForwardRange!Range && isSomeChar!(ElementType!Range)) + bool fromDecimalString(Range)(Range s) scope + if (isForwardRange!Range && isSomeChar!(ElementType!Range)) { import std.range : walkLength; @@ -596,9 +599,9 @@ public: } void fromMagnitude(Range)(Range magnitude) scope - if (isInputRange!Range - && (isForwardRange!Range || hasLength!Range) - && isUnsigned!(ElementType!Range)) + if (isInputRange!Range + && (isForwardRange!Range || hasLength!Range) + && isUnsigned!(ElementType!Range)) { while (!magnitude.empty && magnitude.front == 0) magnitude.popFront; @@ -711,7 +714,7 @@ public: // return x >> y BigUint opBinary(string op, Tulong)(Tulong y) pure nothrow @safe const return scope - if (op == ">>" && is (Tulong == ulong)) + if (op == ">>" && is (Tulong == ulong)) { assert(y > 0, "Can not right shift BigUint by 0"); uint bits = cast(uint) y & BIGDIGITSHIFTMASK; @@ -735,7 +738,7 @@ public: // return x << y BigUint opBinary(string op, Tulong)(Tulong y) pure nothrow @safe const scope - if (op == "<<" && is (Tulong == ulong)) + if (op == "<<" && is (Tulong == ulong)) { assert(y > 0, "Can not left shift BigUint by 0"); if (isZero()) return this; @@ -761,8 +764,8 @@ public: // If wantSub is false, return x + y, leaving sign unchanged // If wantSub is true, return abs(x - y), negating sign if x < y - static BigUint addOrSubInt(Tulong)(const scope BigUint x, Tulong y, - bool wantSub, ref bool sign) pure nothrow @safe if (is(Tulong == ulong)) + static BigUint addOrSubInt(Tulong)(const scope BigUint x, Tulong y, bool wantSub, ref bool sign) pure nothrow @safe + if (is(Tulong == ulong)) { BigUint r; if (wantSub) @@ -921,7 +924,8 @@ public: } // return x % y - static uint modInt(T)(scope BigUint x, T y_) pure if ( is(immutable T == immutable uint) ) + static uint modInt(T)(scope BigUint x, T y_) pure + if ( is(immutable T == immutable uint) ) { import core.memory : GC; uint y = y_; @@ -994,7 +998,8 @@ public: // return x op y static BigUint bitwiseOp(string op)(scope BigUint x, scope BigUint y, bool xSign, bool ySign, ref bool resultSign) - pure nothrow @safe if (op == "|" || op == "^" || op == "&") + pure nothrow @safe + if (op == "|" || op == "^" || op == "&") { auto d1 = includeSign(x.data, y.uintLength, xSign); auto d2 = includeSign(y.data, x.uintLength, ySign); diff --git a/libphobos/src/std/internal/test/dummyrange.d b/libphobos/src/std/internal/test/dummyrange.d index e07e27510214f82cd051fb4e1010c8b84e316918..42dc2646d872f73a30010d1051836b65edc554d1 100644 --- a/libphobos/src/std/internal/test/dummyrange.d +++ b/libphobos/src/std/internal/test/dummyrange.d @@ -255,10 +255,11 @@ class ReferenceInputRange(T) { import std.array : array; - this(Range)(Range r) if (isInputRange!Range) {_payload = array(r);} - final @property ref T front(){return _payload.front;} - final void popFront(){_payload.popFront();} - final @property bool empty(){return _payload.empty;} + this(Range)(Range r) + if (isInputRange!Range) {_payload = array(r);} + final @property ref T front() {return _payload.front;} + final void popFront() {_payload.popFront();} + final @property bool empty() {return _payload.empty;} protected T[] _payload; } @@ -268,8 +269,8 @@ Infinite input range class ReferenceInfiniteInputRange(T) { this(T first = T.init) {_val = first;} - final @property T front(){return _val;} - final void popFront(){++_val;} + final @property T front() {return _val;} + final void popFront() {++_val;} enum bool empty = false; protected T _val; } @@ -279,7 +280,8 @@ Reference forward range */ class ReferenceForwardRange(T) : ReferenceInputRange!T { - this(Range)(Range r) if (isInputRange!Range) {super(r);} + this(Range)(Range r) + if (isInputRange!Range) {super(r);} final @property auto save(this This)() {return new This( _payload);} } @@ -298,9 +300,10 @@ Reference bidirectional range */ class ReferenceBidirectionalRange(T) : ReferenceForwardRange!T { - this(Range)(Range r) if (isInputRange!Range) {super(r);} - final @property ref T back(){return _payload.back;} - final void popBack(){_payload.popBack();} + this(Range)(Range r) + if (isInputRange!Range) {super(r);} + final @property ref T back() {return _payload.back;} + final void popBack() {_payload.popBack();} } @safe unittest diff --git a/libphobos/src/std/json.d b/libphobos/src/std/json.d index 9dcec89b2cea71ae359e2bcbed273646ad71ae46..7182f6ee80720f676d759f4ba47c9e987508a6a8 100644 --- a/libphobos/src/std/json.d +++ b/libphobos/src/std/json.d @@ -85,6 +85,7 @@ enum JSONOptions escapeNonAsciiChars = 0x2, /// Encode non-ASCII characters with a Unicode escape sequence doNotEscapeSlashes = 0x4, /// Do not escape slashes ('/') strictParsing = 0x8, /// Strictly follow RFC-8259 grammar when parsing + preserveObjectOrder = 0x16, /// Preserve order of object keys when parsing } /** @@ -126,13 +127,30 @@ struct JSONValue { import std.exception : enforce; + import std.typecons : Tuple; + + alias OrderedObjectMember = Tuple!( + string, "key", + JSONValue, "value", + ); + union Store { + struct Object + { + bool isOrdered; + union + { + JSONValue[string] unordered; + OrderedObjectMember[] ordered; + } + } + string str; long integer; ulong uinteger; double floating; - JSONValue[string] object; + Object object; JSONValue[] array; } private Store store; @@ -272,9 +290,9 @@ struct JSONValue } /*** - * Value getter/setter for `JSONType.object`. + * Value getter/setter for unordered `JSONType.object`. * Throws: `JSONException` for read access if `type` is not - * `JSONType.object`. + * `JSONType.object` or the object is ordered. * Note: This is @system because of the following pattern: --- auto a = &(json.object()); @@ -286,7 +304,9 @@ struct JSONValue { enforce!JSONException(type == JSONType.object, "JSONValue is not an object"); - return store.object; + enforce!JSONException(!store.object.isOrdered, + "JSONValue object is ordered, cannot return by ref"); + return store.object.unordered; } /// ditto @property JSONValue[string] object(return scope JSONValue[string] v) pure nothrow @nogc @trusted // TODO make @safe @@ -296,7 +316,7 @@ struct JSONValue } /*** - * Value getter for `JSONType.object`. + * Value getter for unordered `JSONType.object`. * Unlike `object`, this retrieves the object by value * and can be used in @safe code. * @@ -316,7 +336,71 @@ struct JSONValue { enforce!JSONException(type == JSONType.object, "JSONValue is not an object"); - return store.object; + if (store.object.isOrdered) + { + // Convert to unordered + JSONValue[string] result; + foreach (pair; store.object.ordered) + result[pair.key] = pair.value; + return cast(inout) result; + } + else + return store.object.unordered; + } + + /*** + * Value getter/setter for ordered `JSONType.object`. + * Throws: `JSONException` for read access if `type` is not + * `JSONType.object` or the object is unordered. + * Note: This is @system because of the following pattern: + --- + auto a = &(json.orderedObject()); + json.uinteger = 0; // overwrite AA pointer + (*a)["hello"] = "world"; // segmentation fault + --- + */ + @property ref inout(OrderedObjectMember[]) orderedObject() inout pure @system return + { + enforce!JSONException(type == JSONType.object, + "JSONValue is not an object"); + enforce!JSONException(store.object.isOrdered, + "JSONValue object is unordered, cannot return by ref"); + return store.object.ordered; + } + /// ditto + @property OrderedObjectMember[] orderedObject(return scope OrderedObjectMember[] v) pure nothrow @nogc @trusted // TODO make @safe + { + assign(v); + return v; + } + + /*** + * Value getter for ordered `JSONType.object`. + * Unlike `orderedObject`, this retrieves the object by value + * and can be used in @safe code. + */ + @property inout(OrderedObjectMember[]) orderedObjectNoRef() inout pure @trusted + { + enforce!JSONException(type == JSONType.object, + "JSONValue is not an object"); + if (store.object.isOrdered) + return store.object.ordered; + else + { + // Convert to ordered + OrderedObjectMember[] result; + foreach (key, value; store.object.unordered) + result ~= OrderedObjectMember(key, value); + return cast(inout) result; + } + } + + /// Returns `true` if the order of keys of the represented object is being preserved. + @property bool isOrdered() const pure @trusted + { + enforce!JSONException(type == JSONType.object, + "JSONValue is not an object"); + return store.object.isOrdered; } /*** @@ -517,16 +601,30 @@ struct JSONValue static if (is(Value : JSONValue)) { JSONValue[string] t = arg; - () @trusted { store.object = t; }(); + () @trusted { + store.object.isOrdered = false; + store.object.unordered = t; + }(); } else { JSONValue[string] aa; foreach (key, value; arg) aa[key] = JSONValue(value); - () @trusted { store.object = aa; }(); + () @trusted { + store.object.isOrdered = false; + store.object.unordered = aa; + }(); } } + else static if (is(T : OrderedObjectMember[])) + { + type_tag = JSONType.object; + () @trusted { + store.object.isOrdered = true; + store.object.ordered = arg; + }(); + } else static if (isArray!T) { type_tag = JSONType.array; @@ -554,7 +652,8 @@ struct JSONValue } } - private void assignRef(T)(ref T arg) if (isStaticArray!T) + private void assignRef(T)(ref T arg) + if (isStaticArray!T) { type_tag = JSONType.array; static if (is(ElementEncodingType!T : JSONValue)) @@ -582,12 +681,14 @@ struct JSONValue * and `K` i.e. a JSON object, any array or `bool`. The type will * be set accordingly. */ - this(T)(T arg) if (!isStaticArray!T) + this(T)(T arg) + if (!isStaticArray!T) { assign(arg); } /// Ditto - this(T)(ref T arg) if (isStaticArray!T) + this(T)(ref T arg) + if (isStaticArray!T) { assignRef(arg); } @@ -629,6 +730,33 @@ struct JSONValue assert(obj1 != obj2); } + /** + * An enum value that can be used to obtain a `JSONValue` representing + * an empty JSON object. + * Unlike `emptyObject`, the order of inserted keys is preserved. + */ + enum emptyOrderedObject = { + JSONValue v = void; + v.orderedObject = null; + return v; + }(); + /// + @system unittest + { + JSONValue obj = JSONValue.emptyOrderedObject; + assert(obj.type == JSONType.object); + assert(obj.isOrdered); + obj["b"] = JSONValue(2); + obj["a"] = JSONValue(1); + assert(obj["a"] == JSONValue(1)); + assert(obj["b"] == JSONValue(2)); + + string[] keys; + foreach (string k, JSONValue v; obj) + keys ~= k; + assert(keys == ["b", "a"]); + } + /** * An enum value that can be used to obtain a `JSONValue` representing * an empty JSON array. @@ -649,12 +777,14 @@ struct JSONValue assert(arr1 != arr2); } - void opAssign(T)(T arg) if (!isStaticArray!T && !is(T : JSONValue)) + void opAssign(T)(T arg) + if (!isStaticArray!T && !is(T : JSONValue)) { assign(arg); } - void opAssign(T)(ref T arg) if (isStaticArray!T) + void opAssign(T)(ref T arg) + if (isStaticArray!T) { assignRef(arg); } @@ -708,16 +838,33 @@ struct JSONValue */ void opIndexAssign(T)(auto ref T value, string key) { - enforce!JSONException(type == JSONType.object || type == JSONType.null_, - "JSONValue must be object or null"); - JSONValue[string] aa = null; - if (type == JSONType.object) + enforce!JSONException( + type == JSONType.object || + type == JSONType.null_, + "JSONValue must be object or null"); + if (type == JSONType.object && isOrdered) { - aa = this.objectNoRef; + auto arr = this.orderedObjectNoRef; + foreach (ref pair; arr) + if (pair.key == key) + { + pair.value = value; + return; + } + arr ~= OrderedObjectMember(key, JSONValue(value)); + this.orderedObject = arr; } + else + { + JSONValue[string] aa = null; + if (type == JSONType.object) + { + aa = this.objectNoRef; + } - aa[key] = value; - this.object = aa; + aa[key] = value; + this.object = aa; + } } /// @safe unittest @@ -828,6 +975,8 @@ struct JSONValue /// ditto bool opEquals(ref const JSONValue rhs) const @nogc nothrow pure @trusted { + import std.algorithm.searching : canFind; + // Default doesn't work well since store is a union. Compare only // what should be in store. // This is @trusted to remain nogc, nothrow, fast, and usable from @safe code. @@ -873,7 +1022,45 @@ struct JSONValue case JSONType.string: return type_tag == rhs.type_tag && store.str == rhs.store.str; case JSONType.object: - return type_tag == rhs.type_tag && store.object == rhs.store.object; + if (rhs.type_tag != JSONType.object) + return false; + if (store.object.isOrdered) + { + if (rhs.store.object.isOrdered) + { + if (store.object.ordered.length != rhs.store.object.ordered.length) + return false; + foreach (ref pair; store.object.ordered) + if (!rhs.store.object.ordered.canFind(pair)) + return false; + return true; + } + else + { + if (store.object.ordered.length != rhs.store.object.unordered.length) + return false; + foreach (ref pair; store.object.ordered) + if (pair.key !in rhs.store.object.unordered || + rhs.store.object.unordered[pair.key] != pair.value) + return false; + return true; + } + } + else + { + if (rhs.store.object.isOrdered) + { + if (store.object.unordered.length != rhs.store.object.ordered.length) + return false; + foreach (ref pair; rhs.store.object.ordered) + if (pair.key !in store.object.unordered || + store.object.unordered[pair.key] != pair.value) + return false; + return true; + } + else + return store.object.unordered == rhs.store.object.unordered; + } case JSONType.array: return type_tag == rhs.type_tag && store.array == rhs.store.array; case JSONType.true_: @@ -914,14 +1101,27 @@ struct JSONValue int opApply(scope int delegate(string key, ref JSONValue) dg) @system { enforce!JSONException(type == JSONType.object, - "JSONValue is not an object"); + "JSONValue is not an object"); + int result; - foreach (string key, ref value; object) + if (isOrdered) { - result = dg(key, value); - if (result) - break; + foreach (ref pair; orderedObject) + { + result = dg(pair.key, pair.value); + if (result) + break; + } + } + else + { + foreach (string key, ref value; object) + { + result = dg(key, value); + if (result) + break; + } } return result; @@ -1018,6 +1218,7 @@ if (isSomeFiniteCharInputRange!T) Nullable!Char next; int line = 1, pos = 0; immutable bool strict = (options & JSONOptions.strictParsing) != 0; + immutable bool ordered = (options & JSONOptions.preserveObjectOrder) != 0; void error(string msg) { @@ -1258,31 +1459,62 @@ if (isSomeFiniteCharInputRange!T) switch (c) { case '{': - if (testChar('}')) + if (ordered) { - value.object = null; - break; - } + if (testChar('}')) + { + value.orderedObject = null; + break; + } - JSONValue[string] obj; - do + JSONValue.OrderedObjectMember[] obj; + do + { + skipWhitespace(); + if (!strict && peekChar() == '}') + { + break; + } + checkChar('"'); + string name = parseString(); + checkChar(':'); + JSONValue member; + parseValue(member); + obj ~= JSONValue.OrderedObjectMember(name, member); + } + while (testChar(',')); + value.orderedObject = obj; + + checkChar('}'); + } + else { - skipWhitespace(); - if (!strict && peekChar() == '}') + if (testChar('}')) { + value.object = null; break; } - checkChar('"'); - string name = parseString(); - checkChar(':'); - JSONValue member; - parseValue(member); - obj[name] = member; - } - while (testChar(',')); - value.object = obj; - checkChar('}'); + JSONValue[string] obj; + do + { + skipWhitespace(); + if (!strict && peekChar() == '}') + { + break; + } + checkChar('"'); + string name = parseString(); + checkChar(':'); + JSONValue member; + parseValue(member); + obj[name] = member; + } + while (testChar(',')); + value.object = obj; + + checkChar('}'); + } break; case '[': @@ -1638,49 +1870,82 @@ if (isOutputRange!(Out,char)) final switch (value.type) { case JSONType.object: - auto obj = value.objectNoRef; - if (!obj.length) + if (value.isOrdered) { - json.put("{}"); - } - else - { - putCharAndEOL('{'); - bool first = true; - - void emit(R)(R names) + auto obj = value.orderedObjectNoRef; + if (!obj.length) { - foreach (name; names) + json.put("{}"); + } + else + { + putCharAndEOL('{'); + bool first = true; + + foreach (pair; obj) { - auto member = obj[name]; if (!first) putCharAndEOL(','); first = false; putTabs(1); - toString(name); + toString(pair.key); json.put(':'); if (pretty) json.put(' '); - toValueImpl(member, indentLevel + 1); + toValueImpl(pair.value, indentLevel + 1); } - } - import std.algorithm.sorting : sort; - // https://issues.dlang.org/show_bug.cgi?id=14439 - // auto names = obj.keys; // aa.keys can't be called in @safe code - auto names = new string[obj.length]; - size_t i = 0; - foreach (k, v; obj) + putEOL(); + putTabs(); + json.put('}'); + } + } + else + { + auto obj = value.objectNoRef; + if (!obj.length) { - names[i] = k; - i++; + json.put("{}"); } - sort(names); - emit(names); + else + { + putCharAndEOL('{'); + bool first = true; - putEOL(); - putTabs(); - json.put('}'); + void emit(R)(R names) + { + foreach (name; names) + { + auto member = obj[name]; + if (!first) + putCharAndEOL(','); + first = false; + putTabs(1); + toString(name); + json.put(':'); + if (pretty) + json.put(' '); + toValueImpl(member, indentLevel + 1); + } + } + + import std.algorithm.sorting : sort; + // https://issues.dlang.org/show_bug.cgi?id=14439 + // auto names = obj.keys; // aa.keys can't be called in @safe code + auto names = new string[obj.length]; + size_t i = 0; + foreach (k, v; obj) + { + names[i] = k; + i++; + } + sort(names); + emit(names); + + putEOL(); + putTabs(); + json.put('}'); + } } break; @@ -2469,3 +2734,17 @@ pure nothrow @safe unittest assert(app.data == s, app.data); } + +// https://issues.dlang.org/show_bug.cgi?id=24823 - JSONOptions.preserveObjectOrder +@safe unittest +{ + import std.array : appender; + + string s = `{"b":2,"a":1}`; + JSONValue j = parseJSON(s, -1, JSONOptions.preserveObjectOrder); + + auto app = appender!string(); + j.toString(app); + + assert(app.data == s, app.data); +} diff --git a/libphobos/src/std/logger/core.d b/libphobos/src/std/logger/core.d index 0633bddee81aeec218d2562e0b562277a78a29db..cc938d4fd34970d09bbbc9faeeb93b1317f4d86f 100644 --- a/libphobos/src/std/logger/core.d +++ b/libphobos/src/std/logger/core.d @@ -293,7 +293,7 @@ template defaultLogFunction(LogLevel ll) string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(lazy A args) - if ((args.length > 0 && !is(Unqual!(A[0]) : bool)) || args.length == 0) + if ((args.length > 0 && !is(Unqual!(A[0]) : bool)) || args.length == 0) { stdThreadLocalLog.memLogFunctions!(ll).logImpl!(line, file, funcName, prettyFuncName, moduleName)(args); @@ -446,7 +446,7 @@ private struct MsgRange } void put(T)(T msg) @safe - if (isSomeString!T) + if (isSomeString!T) { log.logMsgPart(msg); } @@ -735,7 +735,7 @@ abstract class Logger string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(lazy A args) - if (args.length == 0 || (args.length > 0 && !is(A[0] : bool))) + if (args.length == 0 || (args.length > 0 && !is(A[0] : bool))) { synchronized (mutex) { @@ -948,7 +948,7 @@ abstract class Logger string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(const LogLevel ll, lazy bool condition, lazy A args) - if (args.length != 1) + if (args.length != 1) { synchronized (mutex) { @@ -1016,7 +1016,7 @@ abstract class Logger string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(const LogLevel ll, lazy A args) - if ((args.length > 1 && !is(Unqual!(A[0]) : bool)) || args.length == 0) + if ((args.length > 1 && !is(Unqual!(A[0]) : bool)) || args.length == 0) { synchronized (mutex) { @@ -1085,7 +1085,7 @@ abstract class Logger string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args) - if (args.length != 1) + if (args.length != 1) { synchronized (mutex) { @@ -1154,10 +1154,10 @@ abstract class Logger string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(lazy A args) - if ((args.length > 1 - && !is(Unqual!(A[0]) : bool) - && !is(immutable A[0] == immutable LogLevel)) - || args.length == 0) + if ((args.length > 1 + && !is(Unqual!(A[0]) : bool) + && !is(immutable A[0] == immutable LogLevel)) + || args.length == 0) { synchronized (mutex) { diff --git a/libphobos/src/std/numeric.d b/libphobos/src/std/numeric.d index 9f0fb564de378e28d02fbc8ee52db69779c63728..3fef8e4f1feaa9a5b08951886080575e38604124 100644 --- a/libphobos/src/std/numeric.d +++ b/libphobos/src/std/numeric.d @@ -496,7 +496,8 @@ public: static @property CustomFloat im() { return CustomFloat(0.0f); } /// Initialize from any `real` compatible type. - this(F)(F input) if (__traits(compiles, cast(real) input )) + this(F)(F input) + if (__traits(compiles, cast(real) input )) { this = input; } @@ -512,7 +513,7 @@ public: /// Assigns from any `real` compatible type. void opAssign(F)(F input) - if (__traits(compiles, cast(real) input)) + if (__traits(compiles, cast(real) input)) { import std.conv : text; @@ -546,7 +547,7 @@ public: /// Fetches the stored value either as a `float`, `double` or `real`. @property F get(F)() - if (staticIndexOf!(immutable F, immutable float, immutable double, immutable real) >= 0) + if (staticIndexOf!(immutable F, immutable float, immutable double, immutable real) >= 0) { import std.conv : text; @@ -574,7 +575,7 @@ public: /// Convert the CustomFloat to a real and perform the relevant operator on the result real opUnary(string op)() - if (__traits(compiles, mixin(op~`(get!real)`)) || op=="++" || op=="--") + if (__traits(compiles, mixin(op~`(get!real)`)) || op=="++" || op=="--") { static if (op=="++" || op=="--") { @@ -591,31 +592,31 @@ public: // do not match equally, which is disallowed by the spec: // https://dlang.org/spec/operatoroverloading.html#binary real opBinary(string op,T)(T b) - if (__traits(compiles, mixin(`get!real`~op~`b.get!real`))) - { - return mixin(`get!real`~op~`b.get!real`); - } + if (__traits(compiles, mixin(`get!real`~op~`b.get!real`))) + { + return mixin(`get!real`~op~`b.get!real`); + } /// ditto real opBinary(string op,T)(T b) - if ( __traits(compiles, mixin(`get!real`~op~`b`)) && - !__traits(compiles, mixin(`get!real`~op~`b.get!real`))) + if ( __traits(compiles, mixin(`get!real`~op~`b`)) && + !__traits(compiles, mixin(`get!real`~op~`b.get!real`))) { return mixin(`get!real`~op~`b`); } /// ditto real opBinaryRight(string op,T)(T a) - if ( __traits(compiles, mixin(`a`~op~`get!real`)) && - !__traits(compiles, mixin(`get!real`~op~`b`)) && - !__traits(compiles, mixin(`get!real`~op~`b.get!real`))) + if ( __traits(compiles, mixin(`a`~op~`get!real`)) && + !__traits(compiles, mixin(`get!real`~op~`b`)) && + !__traits(compiles, mixin(`get!real`~op~`b.get!real`))) { return mixin(`a`~op~`get!real`); } /// ditto int opCmp(T)(auto ref T b) - if (__traits(compiles, cast(real) b)) + if (__traits(compiles, cast(real) b)) { auto x = get!real; auto y = cast(real) b; @@ -624,7 +625,7 @@ public: /// ditto void opOpAssign(string op, T)(auto ref T b) - if (__traits(compiles, mixin(`get!real`~op~`cast(real) b`))) + if (__traits(compiles, mixin(`get!real`~op~`cast(real) b`))) { return mixin(`this = this `~op~` cast(real) b`); } @@ -3687,7 +3688,7 @@ public: * i.e., output[j] := sum[ exp(-2 PI i j k / N) input[k] ]. */ Complex!F[] fft(F = double, R)(R range) const - if (isFloatingPoint!F && isRandomAccessRange!R) + if (isFloatingPoint!F && isRandomAccessRange!R) { enforceSize(range); Complex!F[] ret; @@ -3710,7 +3711,7 @@ public: * property that can be both read and written and are floating point numbers. */ void fft(Ret, R)(R range, Ret buf) const - if (isRandomAccessRange!Ret && isComplexLike!(ElementType!Ret) && hasSlicing!Ret) + if (isRandomAccessRange!Ret && isComplexLike!(ElementType!Ret) && hasSlicing!Ret) { assert(buf.length == range.length); enforceSize(range); @@ -3759,7 +3760,7 @@ public: * output[j] := (1 / N) sum[ exp(+2 PI i j k / N) input[k] ]. */ Complex!F[] inverseFft(F = double, R)(R range) const - if (isRandomAccessRange!R && isComplexLike!(ElementType!R) && isFloatingPoint!F) + if (isRandomAccessRange!R && isComplexLike!(ElementType!R) && isFloatingPoint!F) { enforceSize(range); Complex!F[] ret; @@ -3781,7 +3782,7 @@ public: * must be some complex-like type. */ void inverseFft(Ret, R)(R range, Ret buf) const - if (isRandomAccessRange!Ret && isComplexLike!(ElementType!Ret) && hasSlicing!Ret) + if (isRandomAccessRange!Ret && isComplexLike!(ElementType!Ret) && hasSlicing!Ret) { enforceSize(range); diff --git a/libphobos/src/std/parallelism.d b/libphobos/src/std/parallelism.d index 7525d9b1491ce5287ee36c5c902d17bd4a48700d..bafdbb92f2c945cd5542321e36a174e3bd661ea8 100644 --- a/libphobos/src/std/parallelism.d +++ b/libphobos/src/std/parallelism.d @@ -2269,7 +2269,8 @@ public: call to `popFront` or, if thrown during construction, simply allowed to propagate to the caller. */ - auto asyncBuf(S)(S source, size_t bufSize = 100) if (isInputRange!S) + auto asyncBuf(S)(S source, size_t bufSize = 100) + if (isInputRange!S) { static final class AsyncBuf { diff --git a/libphobos/src/std/path.d b/libphobos/src/std/path.d index a45865a9f18136374fdcaf9cdf735fa764bb6097..9dae2a65530a3aa578762b828c14fda2d8bfb838 100644 --- a/libphobos/src/std/path.d +++ b/libphobos/src/std/path.d @@ -1446,9 +1446,8 @@ private auto _withDefaultExtension(R, C)(R path, C[] ext) of segments to assemble the path from. Returns: The assembled path. */ -immutable(ElementEncodingType!(ElementType!Range))[] - buildPath(Range)(scope Range segments) - if (isInputRange!Range && !isInfinite!Range && isSomeString!(ElementType!Range)) +immutable(ElementEncodingType!(ElementType!Range))[] buildPath(Range)(scope Range segments) +if (isInputRange!Range && !isInfinite!Range && isSomeString!(ElementType!Range)) { if (segments.empty) return null; @@ -3396,7 +3395,10 @@ do } else { + import core.memory : pureMalloc, pureFree; C[] pattmp; + scope(exit) if (pattmp !is null) (() @trusted => pureFree(pattmp.ptr))(); + for (size_t pi = 0; pi < pattern.length; pi++) { const pc = pattern[pi]; @@ -3482,9 +3484,12 @@ do * pattern[pi0 .. pi-1] ~ pattern[piRemain..$] */ if (pattmp is null) + { // Allocate this only once per function invocation. - // Should do it with malloc/free, but that would make it impure. - pattmp = new C[pattern.length]; + pattmp = (() @trusted => + (cast(C*) pureMalloc(C.sizeof * pattern.length))[0 .. pattern.length]) + (); + } const len1 = pi - 1 - pi0; pattmp[0 .. len1] = pattern[pi0 .. pi - 1]; @@ -3518,7 +3523,7 @@ do } /// -@safe unittest +@safe @nogc unittest { assert(globMatch("foo.bar", "*")); assert(globMatch("foo.bar", "*.*")); diff --git a/libphobos/src/std/process.d b/libphobos/src/std/process.d index 41f788d22901d84120d8d43789cbc7b50bac8a86..4f593bd067fe4970fc4eee0ec7f9b02d5c43e0ce 100644 --- a/libphobos/src/std/process.d +++ b/libphobos/src/std/process.d @@ -1050,54 +1050,103 @@ private Pid spawnProcessPosix(scope const(char[])[] args, static if (!__traits(compiles, closefrom)) { - // FIXME: This implementation crashes the system when RLIMIT_NOFILE - // has a big value. For a possible solution see: - // https://github.com/dlang/phobos/pull/8990 - void fallback (int lowfd) { - // NOTE: malloc() and getrlimit() are not on the POSIX async - // signal safe functions list, but practically this should - // not be a problem. Java VM and CPython also use malloc() - // in its own implementation via opendir(). - import core.stdc.stdlib : malloc; - import core.sys.posix.poll : pollfd, poll, POLLNVAL; + void fallback (int lowfd) + { + import core.sys.posix.dirent : dirent, opendir, readdir, closedir, DIR; + import core.sys.posix.unistd : close; + import core.sys.posix.stdlib : atoi, malloc, free; import core.sys.posix.sys.resource : rlimit, getrlimit, RLIMIT_NOFILE; // Get the maximum number of file descriptors that could be open. rlimit r; if (getrlimit(RLIMIT_NOFILE, &r) != 0) - { abortOnError(forkPipeOut, InternalError.getrlimit, .errno); - } + immutable maxDescriptors = cast(int) r.rlim_cur; - immutable maxToClose = maxDescriptors - lowfd; + // Missing druntime declaration + pragma(mangle, "dirfd") + extern(C) nothrow @nogc int dirfd(DIR* dir); - // Call poll() to see which ones are actually open: - auto pfds = cast(pollfd*) malloc(pollfd.sizeof * maxToClose); - if (pfds is null) - { - abortOnError(forkPipeOut, InternalError.malloc, .errno); - } - foreach (i; 0 .. maxToClose) + DIR* dir = null; + + // We read from /dev/fd or /proc/self/fd only if the limit is high enough + if (maxDescriptors > 128*1024) { - pfds[i].fd = i + lowfd; - pfds[i].events = 0; - pfds[i].revents = 0; + // Try to open the directory /dev/fd or /proc/self/fd + dir = opendir("/dev/fd"); + if (dir is null) dir = opendir("/proc/self/fd"); } - if (poll(pfds, maxToClose, 0) >= 0) + + // If we have a directory, close all file descriptors except stdin, stdout, and stderr + if (dir) { - foreach (i; 0 .. maxToClose) + scope(exit) closedir(dir); + + int opendirfd = dirfd(dir); + + // Iterate over all file descriptors + while (true) { - // POLLNVAL will be set if the file descriptor is invalid. - if (!(pfds[i].revents & POLLNVAL)) close(pfds[i].fd); + dirent* entry = readdir(dir); + + if (entry is null) break; + if (entry.d_name[0] == '.') continue; + + int fd = atoi(cast(char*) entry.d_name); + + // Don't close stdin, stdout, stderr, or the directory file descriptor + if (fd < lowfd || fd == opendirfd) continue; + + close(fd); } } else { - // Fall back to closing everything. - foreach (i; lowfd .. maxDescriptors) + // This is going to allocate 8 bytes for each possible file descriptor from lowfd to r.rlim_cur + if (maxDescriptors <= 128*1024) { - close(i); + // NOTE: malloc() and getrlimit() are not on the POSIX async + // signal safe functions list, but practically this should + // not be a problem. Java VM and CPython also use malloc() + // in its own implementation via opendir(). + import core.stdc.stdlib : malloc; + import core.sys.posix.poll : pollfd, poll, POLLNVAL; + + immutable maxToClose = maxDescriptors - lowfd; + + // Call poll() to see which ones are actually open: + auto pfds = cast(pollfd*) malloc(pollfd.sizeof * maxToClose); + if (pfds is null) + { + abortOnError(forkPipeOut, InternalError.malloc, .errno); + } + + foreach (i; 0 .. maxToClose) + { + pfds[i].fd = i + lowfd; + pfds[i].events = 0; + pfds[i].revents = 0; + } + + if (poll(pfds, maxToClose, 0) < 0) + // couldn't use poll, use the slow path. + goto LslowClose; + + foreach (i; 0 .. maxToClose) + { + // POLLNVAL will be set if the file descriptor is invalid. + if (!(pfds[i].revents & POLLNVAL)) close(pfds[i].fd); + } + } + else + { + LslowClose: + // Fall back to closing everything. + foreach (i; lowfd .. maxDescriptors) + { + close(i); + } } } } @@ -1106,6 +1155,7 @@ private Pid spawnProcessPosix(scope const(char[])[] args, // Until we find a way to perform this check we will try to use dlsym to // check for the function. See: https://github.com/dlang/phobos/pull/9048 version (CRuntime_Glibc) + { void closefrom (int lowfd) { static bool tryGlibcClosefrom (int lowfd) { import core.sys.posix.dlfcn : dlopen, dlclose, dlsym, dlerror, RTLD_LAZY; @@ -1129,6 +1179,7 @@ private Pid spawnProcessPosix(scope const(char[])[] args, if (!tryGlibcClosefrom(lowfd)) fallback(lowfd); } + } else alias closefrom = fallback; } diff --git a/libphobos/src/std/random.d b/libphobos/src/std/random.d index 87e63f3f6ee40a98c53d4213fa29e273c3feb36e..c2210248edf10f127bab18385288812f18b9dcf9 100644 --- a/libphobos/src/std/random.d +++ b/libphobos/src/std/random.d @@ -935,7 +935,8 @@ Parameters for the generator. `Exception` if the InputRange didn't provide enough elements to seed the generator. The number of elements required is the 'n' template parameter of the MersenneTwisterEngine struct. */ - void seed(T)(T range) if (isInputRange!T && is(immutable ElementType!T == immutable UIntType)) + void seed(T)(T range) + if (isInputRange!T && is(immutable ElementType!T == immutable UIntType)) { this.seedImpl(range, this.state); } @@ -945,7 +946,7 @@ Parameters for the generator. which can be used with an arbitrary `State` instance */ private static void seedImpl(T)(T range, ref State mtState) - if (isInputRange!T && is(immutable ElementType!T == immutable UIntType)) + if (isInputRange!T && is(immutable ElementType!T == immutable UIntType)) { size_t j; for (j = 0; j < n && !range.empty; ++j, range.popFront()) @@ -2215,6 +2216,7 @@ at least that number won't be represented fairly. Hence, our condition to reroll is `bucketFront > (UpperType.max - (upperDist - 1))` +/ +/// ditto auto uniform(string boundaries = "[)", T1, T2, RandomGen) (T1 a, T2 b, ref RandomGen rng) if ((isIntegral!(CommonType!(T1, T2)) || isSomeChar!(CommonType!(T1, T2))) && @@ -2277,9 +2279,14 @@ if ((isIntegral!(CommonType!(T1, T2)) || isSomeChar!(CommonType!(T1, T2))) && return cast(ResultType)(lower + offset); } +/// @safe unittest { import std.conv : to; + import std.meta : AliasSeq; + import std.range.primitives : isForwardRange; + import std.traits : isIntegral, isSomeChar; + auto gen = Mt19937(123_456_789); static assert(isForwardRange!(typeof(gen))); @@ -2290,7 +2297,7 @@ if ((isIntegral!(CommonType!(T1, T2)) || isSomeChar!(CommonType!(T1, T2))) && auto c = uniform(0.0, 1.0); assert(0 <= c && c < 1); - static foreach (T; std.meta.AliasSeq!(char, wchar, dchar, byte, ubyte, short, ushort, + static foreach (T; AliasSeq!(char, wchar, dchar, byte, ubyte, short, ushort, int, uint, long, ulong, float, double, real)) {{ T lo = 0, hi = 100; @@ -2344,7 +2351,7 @@ if ((isIntegral!(CommonType!(T1, T2)) || isSomeChar!(CommonType!(T1, T2))) && auto reproRng = Xorshift(239842); - static foreach (T; std.meta.AliasSeq!(char, wchar, dchar, byte, ubyte, short, + static foreach (T; AliasSeq!(char, wchar, dchar, byte, ubyte, short, ushort, int, uint, long, ulong)) {{ T lo = T.min + 10, hi = T.max - 10; diff --git a/libphobos/src/std/range/package.d b/libphobos/src/std/range/package.d index e2a2d7d2564da78c387974e8896e29e07bc638bf..3a135ebba5108925b10ec9364c4f9d26a02ba8d2 100644 --- a/libphobos/src/std/range/package.d +++ b/libphobos/src/std/range/package.d @@ -6095,8 +6095,10 @@ private struct LockstepMixin(Ranges...) { indexDef = q{ size_t index = ranges[0].length - 1; - enforce(this.stoppingPolicy == StoppingPolicy.requireSameLength, - "lockstep can only be used with foreach_reverse when stoppingPolicy == requireSameLength"); + enforce( + this.stoppingPolicy == StoppingPolicy.requireSameLength, + "Indexed lockstep can only be used with foreach_reverse when " ~ + "stoppingPolicy == requireSameLength"); foreach (range; ranges[1 .. $]) enforce(range.length == ranges[0].length); @@ -7490,7 +7492,8 @@ if (!isIntegral!(CommonType!(B, E)) && bool opEquals(Cyclic c) const { return current == c.current; } bool opEquals(int i) const { return current == i; } - void opUnary(string op)() if (op == "++") + void opUnary(string op)() + if (op == "++") { current = (current + 1) % wrapAround; } @@ -12444,7 +12447,7 @@ public: return (*_range).front; } - static if (is(typeof((*(cast(const R*)_range)).front))) @property auto front() const + static if (is(typeof(((const R* r) => (*r).front)(null)))) @property auto front() const { return (*_range).front; } @@ -12470,7 +12473,7 @@ public: return (*_range).empty; } - static if (is(typeof((*cast(const R*)_range).empty))) @property bool empty() const + static if (is(typeof(((const R* r) => (*r).empty)(null)))) @property bool empty() const { return (*_range).empty; } @@ -12500,10 +12503,11 @@ public: else static if (isForwardRange!R) { import std.traits : isSafe; - private alias S = typeof((*_range).save); + private alias S = typeof((() => (*_range).save)()); + + static if (is(typeof(((const R* r) => (*r).save)(null)))) + private alias CS = typeof(((const R* r) => (*r).save)(null)); - static if (is(typeof((*cast(const R*)_range).save))) - private alias CS = typeof((*cast(const R*)_range).save); static if (isSafe!((R* r) => (*r).save)) { @@ -12512,7 +12516,7 @@ public: mixin(_genSave()); } - static if (is(typeof((*cast(const R*)_range).save))) @property RefRange!CS save() @trusted const + static if (is(typeof(((const R* r) => (*r).save)(null)))) @property RefRange!CS save() @trusted const { mixin(_genSave()); } @@ -12524,7 +12528,7 @@ public: mixin(_genSave()); } - static if (is(typeof((*cast(const R*)_range).save))) @property RefRange!CS save() const + static if (is(typeof(((const R* r) => (*r).save)(null)))) @property RefRange!CS save() const { mixin(_genSave()); } @@ -12543,7 +12547,7 @@ public: private static string _genSave() @safe pure nothrow { return `import core.lifetime : emplace;` ~ - `alias S = typeof((*_range).save);` ~ + `alias S = typeof((() => (*_range).save)());` ~ `static assert(isForwardRange!S, S.stringof ~ " is not a forward range.");` ~ `auto mem = new void[S.sizeof];` ~ `emplace!S(mem, cast(S)(*_range).save);` ~ @@ -12572,7 +12576,7 @@ public: return (*_range).back; } - static if (is(typeof((*(cast(const R*)_range)).back))) @property auto back() const + static if (is(typeof(((const R* r) => (*r).back)(null)))) @property auto back() const { return (*_range).back; } @@ -12604,13 +12608,13 @@ public: else static if (isRandomAccessRange!R) { auto ref opIndex(IndexType)(IndexType index) - if (is(typeof((*_range)[index]))) + if (is(typeof((*_range)[index]))) { return (*_range)[index]; } auto ref opIndex(IndexType)(IndexType index) const - if (is(typeof((*cast(const R*)_range)[index]))) + if (is(typeof((*cast(const R*)_range)[index]))) { return (*_range)[index]; } @@ -12662,7 +12666,7 @@ public: { return (*_range).length; } - static if (is(typeof((*cast(const R*)_range).length))) @property auto length() const + static if (is(typeof(((const R* r) => (*r).length)(null)))) @property auto length() const { return (*_range).length; } @@ -12692,14 +12696,14 @@ public: RefRange!T opSlice(IndexType1, IndexType2) (IndexType1 begin, IndexType2 end) - if (is(typeof((*_range)[begin .. end]))) + if (is(typeof((*_range)[begin .. end]))) { mixin(_genOpSlice()); } RefRange!CT opSlice(IndexType1, IndexType2) (IndexType1 begin, IndexType2 end) const - if (is(typeof((*cast(const R*)_range)[begin .. end]))) + if (is(typeof((*cast(const R*)_range)[begin .. end]))) { mixin(_genOpSlice()); } @@ -13105,6 +13109,44 @@ private: auto rr2 = refRange(&r2); } +// https://issues.dlang.org/show_bug.cgi?id=24801 +@safe unittest +{ + + { + static struct R + { + int front() => 0; + void popFront() {} + bool empty() => false; + } + R range; + auto r = RefRange!R(&range); + } + + { + static struct R + { + size_t start, end; + size_t length() => end - start; + int opIndex(size_t i) => 0; + + + int front() => this[0]; + int back() => this[length-1]; + void popFront() { start++; } + void popBack() { end--; } + bool empty() => length == 0; + R save() const => R(); + } + + R range; + auto r = RefRange!R(&range); + } + + +} + /// ditto auto refRange(R)(R* range) if (isInputRange!R) diff --git a/libphobos/src/std/range/primitives.d b/libphobos/src/std/range/primitives.d index dddcae9afd2ecde76e985d841d57b9156658c3b9..84366250e7817b06df5a3202bb876bd70042d0ae 100644 --- a/libphobos/src/std/range/primitives.d +++ b/libphobos/src/std/range/primitives.d @@ -474,7 +474,8 @@ void put(R, E)(ref R r, E e) { string data; - void put(C)(C c) if (isSomeChar!C) + void put(C)(C c) + if (isSomeChar!C) { data ~= c; } diff --git a/libphobos/src/std/regex/internal/backtracking.d b/libphobos/src/std/regex/internal/backtracking.d index a488e0660d035e6c21cf6cf58bdf304757ce06c6..605ec03c481e4ef6b081faa0c69ea73c8216b89f 100644 --- a/libphobos/src/std/regex/internal/backtracking.d +++ b/libphobos/src/std/regex/internal/backtracking.d @@ -702,7 +702,7 @@ final: } void stackPush(T)(T val) - if (!isDynamicArray!T) + if (!isDynamicArray!T) { *cast(T*)&memory[lastState] = val; enum delta = (T.sizeof+size_t.sizeof/2)/size_t.sizeof; @@ -720,7 +720,7 @@ final: } void stackPop(T)(ref T val) - if (!isDynamicArray!T) + if (!isDynamicArray!T) { enum delta = (T.sizeof+size_t.sizeof/2)/size_t.sizeof; lastState -= delta; diff --git a/libphobos/src/std/regex/internal/parser.d b/libphobos/src/std/regex/internal/parser.d index ab2b297fb884d8088f00eb8c425d057d8ddf7aba..d8ebe59f0a6c31f46e7049fe6bb704eddaf89a7c 100644 --- a/libphobos/src/std/regex/internal/parser.d +++ b/libphobos/src/std/regex/internal/parser.d @@ -542,7 +542,7 @@ if (isForwardRange!R && is(ElementType!R : dchar)) Generator g; @trusted this(S)(R pattern, S flags) - if (isSomeString!S) + if (isSomeString!S) { pat = origin = pattern; //reserve slightly more then avg as sampled from unittests diff --git a/libphobos/src/std/regex/internal/thompson.d b/libphobos/src/std/regex/internal/thompson.d index f4643ae1bcb7bb0b6a61fd71cb54796cbe98ba39..195625f15eaf76dabe2f0cfa7017305439babf49 100644 --- a/libphobos/src/std/regex/internal/thompson.d +++ b/libphobos/src/std/regex/internal/thompson.d @@ -276,7 +276,7 @@ template ThompsonOps(E, S, bool withInput:true) } static bool op(IR code)(E e, S* state) - if (code == IR.RepeatEnd || code == IR.RepeatQEnd) + if (code == IR.RepeatEnd || code == IR.RepeatQEnd) { with(e) with(state) { @@ -331,7 +331,7 @@ template ThompsonOps(E, S, bool withInput:true) } static bool op(IR code)(E e, S* state) - if (code == IR.InfiniteEnd || code == IR.InfiniteQEnd) + if (code == IR.InfiniteEnd || code == IR.InfiniteQEnd) { with(e) with(state) { @@ -366,7 +366,7 @@ template ThompsonOps(E, S, bool withInput:true) } static bool op(IR code)(E e, S* state) - if (code == IR.InfiniteBloomEnd) + if (code == IR.InfiniteBloomEnd) { with(e) with(state) { @@ -507,7 +507,7 @@ template ThompsonOps(E, S, bool withInput:true) static bool op(IR code)(E e, S* state) - if (code == IR.LookbehindStart || code == IR.NeglookbehindStart) + if (code == IR.LookbehindStart || code == IR.NeglookbehindStart) { with(e) with(state) { @@ -534,7 +534,7 @@ template ThompsonOps(E, S, bool withInput:true) } static bool op(IR code)(E e, S* state) - if (code == IR.LookaheadStart || code == IR.NeglookaheadStart) + if (code == IR.LookaheadStart || code == IR.NeglookaheadStart) { with(e) with(state) { @@ -563,8 +563,8 @@ template ThompsonOps(E, S, bool withInput:true) } static bool op(IR code)(E e, S* state) - if (code == IR.LookaheadEnd || code == IR.NeglookaheadEnd || - code == IR.LookbehindEnd || code == IR.NeglookbehindEnd) + if (code == IR.LookaheadEnd || code == IR.NeglookaheadEnd || + code == IR.LookbehindEnd || code == IR.NeglookbehindEnd) { with(e) with(state) { @@ -675,7 +675,7 @@ template ThompsonOps(E,S, bool withInput:false) @trusted: // can't match these without input static bool op(IR code)(E e, S* state) - if (code == IR.Char || code == IR.OrChar || code == IR.CodepointSet + if (code == IR.Char || code == IR.OrChar || code == IR.CodepointSet || code == IR.Trie || code == IR.Char || code == IR.Any) { return state.popState(e); @@ -701,7 +701,7 @@ template ThompsonOps(E,S, bool withInput:false) // forward all control flow to normal versions static bool op(IR code)(E e, S* state) - if (code != IR.Char && code != IR.OrChar && code != IR.CodepointSet + if (code != IR.Char && code != IR.OrChar && code != IR.CodepointSet && code != IR.Trie && code != IR.Char && code != IR.Any && code != IR.Backref) { return ThompsonOps!(E,S,true).op!code(e,state); diff --git a/libphobos/src/std/regex/package.d b/libphobos/src/std/regex/package.d index d6a01e244865fa6b3decd746d1ac7a7072db2120..143b6835a58b5e15dabcb56c40da523206a6a1b8 100644 --- a/libphobos/src/std/regex/package.d +++ b/libphobos/src/std/regex/package.d @@ -688,7 +688,7 @@ public: ---- +/ R opIndex(String)(String i) /*const*/ //@@@BUG@@@ - if (isSomeString!String) + if (isSomeString!String) { size_t index = lookupNamedGroup(_names, i); return getMatch(index); diff --git a/libphobos/src/std/socket.d b/libphobos/src/std/socket.d index e86a51fe2df3d66e886cb17cd00c1ff8f8f40aa1..52fd33b1dd1e32e0b0ebf7c52ed9b2ee95b140fd 100644 --- a/libphobos/src/std/socket.d +++ b/libphobos/src/std/socket.d @@ -702,7 +702,7 @@ class InternetHost // must synchronize across all threads private bool getHost(string opMixin, T)(T param) @system { - synchronized(this.classinfo) + synchronized(typeid(this)) return getHostNoSync!(opMixin, T)(param); } } diff --git a/libphobos/src/std/stdio.d b/libphobos/src/std/stdio.d index b4744600cd5958392de1ace9e21b4346ae0b382c..e844297b8dc4a28d9fc0af63caae999203d2d73f 100644 --- a/libphobos/src/std/stdio.d +++ b/libphobos/src/std/stdio.d @@ -447,7 +447,7 @@ Throws: `ErrnoException` if the file could not be opened. /// ditto this(R1, R2)(R1 name) - if (isSomeFiniteCharInputRange!R1) + if (isSomeFiniteCharInputRange!R1) { import std.conv : to; this(name.to!string, "rb"); @@ -455,8 +455,8 @@ Throws: `ErrnoException` if the file could not be opened. /// ditto this(R1, R2)(R1 name, R2 mode) - if (isSomeFiniteCharInputRange!R1 && - isSomeFiniteCharInputRange!R2) + if (isSomeFiniteCharInputRange!R1 && + isSomeFiniteCharInputRange!R2) { import std.conv : to; this(name.to!string, mode.to!string); @@ -2340,6 +2340,24 @@ void main() Notice that neither example accesses the line data returned by `front` after the corresponding `popFront` call is made (because the contents may well have changed). +---- + +Windows specific Example: +---- +import std.stdio; + +version (Windows) +void main() +{ + + foreach (line; File("file.txt").byLine(No.keepTerminator, "\r\n")) + { + writeln("|"~line~"|"); + if (line == "HelloWorld") + writeln("^This Line is here."); + } + +} */ auto byLine(Terminator = char, Char = char) (KeepTerminator keepTerminator = No.keepTerminator, @@ -2997,10 +3015,10 @@ is empty, throws an `Exception`. In case of an I/O error throws /// Range primitive implementations. void put(A)(scope A writeme) - if ((isSomeChar!(ElementType!A) || - is(ElementType!A : const(ubyte))) && - isInputRange!A && - !isInfinite!A) + if ((isSomeChar!(ElementType!A) || + is(ElementType!A : const(ubyte))) && + isInputRange!A && + !isInfinite!A) { import std.exception : errnoEnforce; @@ -3026,7 +3044,8 @@ is empty, throws an `Exception`. In case of an I/O error throws } /// ditto - void put(C)(scope C c) @safe if (isSomeChar!C || is(C : const(ubyte))) + void put(C)(scope C c) @safe + if (isSomeChar!C || is(C : const(ubyte))) { import std.utf : decodeFront, encode, stride; diff --git a/libphobos/src/std/string.d b/libphobos/src/std/string.d index 21e1ca356f8605f3fb01ee0b28ef952fb58b4d0d..bcc9d7c13afa4a85c16dd6d5321ba02442cbf71e 100644 --- a/libphobos/src/std/string.d +++ b/libphobos/src/std/string.d @@ -457,26 +457,26 @@ pure nothrow @system unittest // https://issues.dlang.org/show_bug.cgi?id=15136 alias CaseSensitive = Flag!"caseSensitive"; /++ - Searches for character in range. + Searches for a character in a string or range. Params: - s = string or InputRange of characters to search in correct UTF format - c = character to search for - startIdx = starting index to a well-formed code point - cs = `Yes.caseSensitive` or `No.caseSensitive` + s = string or InputRange of characters to search for `c` in + c = character to search for in `s` + startIdx = index to a well-formed code point in `s` to start + searching from; defaults to 0 + cs = specifies whether comparisons are case-sensitive + (`Yes.caseSensitive`) or not (`No.caseSensitive`). Returns: - the index of the first occurrence of `c` in `s` with - respect to the start index `startIdx`. If `c` - is not found, then `-1` is returned. - If `c` is found the value of the returned index is at least - `startIdx`. - If the parameters are not valid UTF, the result will still - be in the range [-1 .. s.length], but will not be reliable otherwise. + If `c` is found in `s`, then the index of its first occurrence is + returned. If `c` is not found or `startIdx` is greater than or equal to + `s.length`, then -1 is returned. If the parameters are not valid UTF, + the result will still be either -1 or in the range [`startIdx` .. + `s.length`], but will not be reliable otherwise. Throws: - If the sequence starting at `startIdx` does not represent a well - formed codepoint, then a $(REF UTFException, std,utf) may be thrown. + If the sequence starting at `startIdx` does not represent a well-formed + code point, then a $(REF UTFException, std,utf) may be thrown. See_Also: $(REF countUntil, std,algorithm,searching) +/ @@ -901,30 +901,30 @@ private template _indexOfStr(CaseSensitive cs) } /++ - Searches for substring in `s`. + Searches for a substring in a string or range. Params: - s = string or ForwardRange of characters to search in correct UTF format - sub = substring to search for - startIdx = the index into s to start searching from - cs = `Yes.caseSensitive` (default) or `No.caseSensitive` + s = string or ForwardRange of characters to search for `sub` in + sub = substring to search for in `s` + startIdx = index to a well-formed code point in `s` to start + searching from; defaults to 0 + cs = specifies whether comparisons are case-sensitive + (`Yes.caseSensitive`) or not (`No.caseSensitive`) Returns: - the index of the first occurrence of `sub` in `s` with - respect to the start index `startIdx`. If `sub` is not found, - then `-1` is returned. - If the arguments are not valid UTF, the result will still - be in the range [-1 .. s.length], but will not be reliable otherwise. - If `sub` is found the value of the returned index is at least - `startIdx`. + The index of the first occurrence of `sub` in `s`. If `sub` is not found + or `startIdx` is greater than or equal to `s.length`, then -1 is + returned. If the arguments are not valid UTF, the result will still be + either -1 or in the range [`startIdx` .. `s.length`], but will not be + reliable otherwise. Throws: - If the sequence starting at `startIdx` does not represent a well - formed codepoint, then a $(REF UTFException, std,utf) may be thrown. + If the sequence starting at `startIdx` does not represent a well-formed + code point, then a $(REF UTFException, std,utf) may be thrown. Bugs: - Does not work with case insensitive strings where the mapping of - tolower and toupper is not 1:1. + Does not work with case-insensitive strings where the mapping of + $(REF toLower, std,uni) and $(REF toUpper, std,uni) is not 1:1. +/ ptrdiff_t indexOf(Range, Char)(Range s, const(Char)[] sub) if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) && @@ -1156,23 +1156,26 @@ unittest } /++ + Searches for the last occurrence of a character in a string. + Params: - s = string to search - c = character to search for - startIdx = the index into s to start searching from - cs = `Yes.caseSensitive` or `No.caseSensitive` + s = string to search for `c` in + c = character to search for in `s` + startIdx = index of a well-formed code point in `s` to start searching + from; defaults to 0 + cs = specifies whether comparisons are case-sensitive + (`Yes.caseSensitive`) or not (`No.caseSensitive`) Returns: - The index of the last occurrence of `c` in `s`. If `c` is not - found, then `-1` is returned. The `startIdx` slices `s` in - the following way $(D s[0 .. startIdx]). `startIdx` represents a - codeunit index in `s`. + If `c` is found in `s`, then the index of its last occurrence is + returned. If `c` is not found or `startIdx` is greater than or equal to + `s.length`, then -1 is returned. If the parameters are not valid UTF, + the result will still be either -1 or in the range [`startIdx` .. + `s.length`], but will not be reliable otherwise. Throws: - If the sequence ending at `startIdx` does not represent a well - formed codepoint, then a $(REF UTFException, std,utf) may be thrown. - - `cs` indicates whether the comparisons are case sensitive. + If the sequence ending at `startIdx` does not represent a well-formed + code point, then a $(REF UTFException, std,utf) may be thrown. +/ ptrdiff_t lastIndexOf(Char)(const(Char)[] s, in dchar c, in CaseSensitive cs = Yes.caseSensitive) @safe pure @@ -1345,23 +1348,30 @@ if (isSomeChar!Char) } /++ + Searches for the last occurrence of a substring in a string. + Params: - s = string to search - sub = substring to search for - startIdx = the index into s to start searching from - cs = `Yes.caseSensitive` or `No.caseSensitive` + s = string to search for `sub` in + sub = substring to search for in `s` + startIdx = index to a well-formed code point in `s` to start + searching from; defaults to 0 + cs = specifies whether comparisons are case-sensitive + (`Yes.caseSensitive`) or not (`No.caseSensitive`) Returns: - the index of the last occurrence of `sub` in `s`. If `sub` is - not found, then `-1` is returned. The `startIdx` slices `s` - in the following way $(D s[0 .. startIdx]). `startIdx` represents a - codeunit index in `s`. + The index of the last occurrence of `sub` in `s`. If `sub` is not found + or `startIdx` is greater than or equal to `s.length`, then -1 is + returned. If the parameters are not valid UTF, the result will still be + either -1 or in the range [`startIdx` .. `s.length`], but will not be + reliable otherwise. Throws: - If the sequence ending at `startIdx` does not represent a well - formed codepoint, then a $(REF UTFException, std,utf) may be thrown. + If the sequence starting at `startIdx` does not represent a well-formed + code point, then a $(REF UTFException, std,utf) may be thrown. - `cs` indicates whether the comparisons are case sensitive. + Bugs: + Does not work with case-insensitive strings where the mapping of + $(REF toLower, std,uni) and $(REF toUpper, std,uni) is not 1:1. +/ ptrdiff_t lastIndexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub, in CaseSensitive cs = Yes.caseSensitive) @safe pure @@ -1747,21 +1757,28 @@ if (isSomeChar!Char && isSomeChar!Char2) } /** - Returns the index of the first occurrence of any of the elements in $(D - needles) in `haystack`. If no element of `needles` is found, - then `-1` is returned. The `startIdx` slices `haystack` in the - following way $(D haystack[startIdx .. $]). `startIdx` represents a - codeunit index in `haystack`. If the sequence ending at `startIdx` - does not represent a well formed codepoint, then a $(REF UTFException, std,utf) - may be thrown. + Searches the string `haystack` for one of the characters in `needles` + starting at index `startIdx`. If `startIdx` is not given, it defaults to 0. Params: - haystack = String to search for needles in. - needles = Strings to search for in haystack. - startIdx = slices haystack like this $(D haystack[startIdx .. $]). If - the startIdx is greater than or equal to the length of haystack the - functions returns `-1`. - cs = Indicates whether the comparisons are case sensitive. + haystack = string to search for needles in + needles = characters to search for in `haystack` + startIdx = index of a well-formed code point in `haystack` to start + searching from; defaults to 0 + cs = specifies whether comparisons are case-sensitive + (`Yes.caseSensitive`) or not (`No.caseSensitive`) + + Returns: + The index of the first occurrence of any of the elements of `needles` in + `haystack`. If no element of `needles` is found or `startIdx` is greater + than or equal to `haystack.length`, then -1 is returned. If the + parameters are not valid UTF, the result will still be either -1 or in + the range [`startIdx` .. `haystack.length`], but will not be reliable + otherwise. + + Throws: + If the sequence starting at `startIdx` does not represent a well-formed + code point, then a $(REF UTFException, std,utf) may be thrown. */ ptrdiff_t indexOfAny(Char,Char2)(const(Char)[] haystack, const(Char2)[] needles, in CaseSensitive cs = Yes.caseSensitive) @safe pure @@ -1914,21 +1931,23 @@ if (isSomeChar!Char && isSomeChar!Char2) } /** - Returns the index of the last occurrence of any of the elements in $(D - needles) in `haystack`. If no element of `needles` is found, - then `-1` is returned. The `stopIdx` slices `haystack` in the - following way $(D s[0 .. stopIdx]). `stopIdx` represents a codeunit - index in `haystack`. If the sequence ending at `startIdx` does not - represent a well formed codepoint, then a $(REF UTFException, std,utf) may be - thrown. + Searches `haystack` for the last occurrence of any of the + characters in `needles`. Params: - haystack = String to search for needles in. - needles = Strings to search for in haystack. - stopIdx = slices haystack like this $(D haystack[0 .. stopIdx]). If - the stopIdx is greater than or equal to the length of haystack the - functions returns `-1`. - cs = Indicates whether the comparisons are case sensitive. + haystack = string to search needles in + needles = characters to search for in `haystack` + stopIdx = index in `haystack` to stop searching at (exclusive); defaults + to `haystack.length` + cs = specifies whether comparisons are case-sensitive + (`Yes.caseSensitive`) or not (`No.caseSensitive`) + + Returns: + The index of the last occurrence of any of the characters of `needles` + in `haystack`. If no character of `needles` is found or `stopIdx` is 0, + then -1 is returned. If the parameters are not valid UTF, the result + will still be in the range [-1 .. `stopIdx`], but will not be reliable + otherwise. */ ptrdiff_t lastIndexOfAny(Char,Char2)(const(Char)[] haystack, const(Char2)[] needles, in CaseSensitive cs = Yes.caseSensitive) @@ -2097,17 +2116,27 @@ if (isSomeChar!Char && isSomeChar!Char2) } /** - Returns the index of the first occurrence of any character not an elements - in `needles` in `haystack`. If all element of `haystack` are - element of `needles` `-1` is returned. + Searches `haystack` for a character not in `needles`. Params: - haystack = String to search for needles in. - needles = Strings to search for in haystack. - startIdx = slices haystack like this $(D haystack[startIdx .. $]). If - the startIdx is greater than or equal to the length of haystack the - functions returns `-1`. - cs = Indicates whether the comparisons are case sensitive. + haystack = string to search for needles in + needles = characters to search for in `haystack` + startIdx = index of a well-formed code point in `haystack` to start + searching from; defaults to 0 + cs = specifies whether comparisons are case-sensitive + (`Yes.caseSensitive`) or not (`No.caseSensitive`) + + Returns: + The index of the first character in `haystack` that is not an element of + `needles`. If all characters of `haystack` are elements of `needles` or + `startIdx` is greater than or equal to `haystack.length`, then -1 is + returned. If the parameters are not valid UTF, the result will still be + either -1 or in the range [`startIdx` .. `haystack.length`], but will + not be reliable otherwise. + + Throws: + If the sequence starting at `startIdx` does not represent a well-formed + code point, then a $(REF UTFException, std,utf) may be thrown. */ ptrdiff_t indexOfNeither(Char,Char2)(const(Char)[] haystack, const(Char2)[] needles, in CaseSensitive cs = Yes.caseSensitive) @@ -2257,17 +2286,22 @@ if (isSomeChar!Char && isSomeChar!Char2) } /** - Returns the last index of the first occurence of any character that is not - an elements in `needles` in `haystack`. If all element of - `haystack` are element of `needles` `-1` is returned. + Searches for the last character in `haystack` that is not in `needles`. Params: - haystack = String to search for needles in. - needles = Strings to search for in haystack. - stopIdx = slices haystack like this $(D haystack[0 .. stopIdx]) If - the stopIdx is greater than or equal to the length of haystack the - functions returns `-1`. - cs = Indicates whether the comparisons are case sensitive. + haystack = string to search for needles in + needles = characters to search for in `haystack` + stopIdx = index in `haystack` to stop searching at (exclusive); + defaults to `haystack.length` + cs = specifies whether comparisons are case-sensitive + (`Yes.caseSensitive`) or not (`No.caseSensitive`) + + Returns: + The index of the last character in `haystack` that is not an element of + `needles`. If all characters of `haystack` are in `needles` or `stopIdx` + is 0, then -1 is returned. If the parameters are not valid UTF, the + result will still be in the range [-1 .. `stopIdx`], but will not be + reliable otherwise. */ ptrdiff_t lastIndexOfNeither(Char,Char2)(const(Char)[] haystack, const(Char2)[] needles, in CaseSensitive cs = Yes.caseSensitive) diff --git a/libphobos/src/std/traits.d b/libphobos/src/std/traits.d index 5ed37a1f41838bdaf60d54bc1d0b7b8895b68308..69362c08695dd9d645a58232a705ad2db51f05ef 100644 --- a/libphobos/src/std/traits.d +++ b/libphobos/src/std/traits.d @@ -7367,10 +7367,12 @@ template isInstanceOf(alias S, alias T) static struct A(T = void) { // doesn't work as expected, only accepts A when T = void - void func(B)(B b) if (isInstanceOf!(A, B)) {} + void func(B)(B b) + if (isInstanceOf!(A, B)) {} // correct behavior - void method(B)(B b) if (isInstanceOf!(TemplateOf!(A), B)) {} + void method(B)(B b) + if (isInstanceOf!(TemplateOf!(A), B)) {} } A!(void) a1; diff --git a/libphobos/src/std/typecons.d b/libphobos/src/std/typecons.d index aceb2878d49cc76eb24656408cb2c20fd364d4fd..c874c0ff81166b822773efbdcc046d6232b050fc 100644 --- a/libphobos/src/std/typecons.d +++ b/libphobos/src/std/typecons.d @@ -2235,12 +2235,14 @@ template tuple(Names...) // e.g. Tuple!(int, "x", string, "y") template Interleave(A...) { - template and(B...) if (B.length == 1) + template and(B...) + if (B.length == 1) { alias and = AliasSeq!(A[0], B[0]); } - template and(B...) if (B.length != 1) + template and(B...) + if (B.length != 1) { alias and = AliasSeq!(A[0], B[0], Interleave!(A[1..$]).and!(B[1..$])); @@ -5134,7 +5136,7 @@ Params: non-release mode. */ void opAssign()(T value) - if (isAssignable!T) //@@@9416@@@ + if (isAssignable!T) //@@@9416@@@ { enum message = "Called `opAssign' on null NullableRef!" ~ T.stringof ~ "."; assert(!isNull, message); @@ -5469,15 +5471,17 @@ nothrow pure @safe unittest } } -// / ditto +/// ditto class NotImplementedError : Error { + /// this(string method) nothrow pure @safe { super(method ~ " is not implemented"); } } +/// @system unittest { import std.exception : assertThrown; @@ -7498,7 +7502,8 @@ Constructor that initializes the payload. Postcondition: `refCountedStore.isInitialized` */ - this(A...)(auto ref A args) if (A.length > 0) + this(A...)(auto ref A args) + if (A.length > 0) out { assert(refCountedStore.isInitialized); @@ -7931,7 +7936,8 @@ template borrow(alias fun) { import std.functional : unaryFun; - auto ref borrow(RC)(RC refCount) if + auto ref borrow(RC)(RC refCount) + if ( isInstanceOf!(SafeRefCounted, RC) && is(typeof(unaryFun!fun(refCount.refCountedPayload))) @@ -8140,7 +8146,7 @@ mixin template Proxy(alias a) } bool opEquals(T)(T b) - if (is(ValueType : T) || is(typeof(a.opEquals(b))) || is(typeof(b.opEquals(a)))) + if (is(ValueType : T) || is(typeof(a.opEquals(b))) || is(typeof(b.opEquals(a)))) { static if (is(typeof(a.opEquals(b)))) return a.opEquals(b); @@ -8164,7 +8170,7 @@ mixin template Proxy(alias a) } int opCmp(T)(auto ref const T b) - if (is(ValueType : T) || is(typeof(a.opCmp(b))) || is(typeof(b.opCmp(a)))) + if (is(ValueType : T) || is(typeof(a.opCmp(b))) || is(typeof(b.opCmp(a)))) { static if (is(typeof(a.opCmp(b)))) return a.opCmp(b); @@ -8274,7 +8280,8 @@ mixin template Proxy(alias a) } } - auto ref opAssign (this X, V )(auto ref V v) if (!is(V == typeof(this))) { return a = v; } + auto ref opAssign (this X, V )(auto ref V v) + if (!is(V == typeof(this))) { return a = v; } auto ref opIndexAssign(this X, V, D...)(auto ref V v, auto ref D i) { return a[i] = v; } auto ref opSliceAssign(this X, V )(auto ref V v) { return a[] = v; } auto ref opSliceAssign(this X, V, B, E)(auto ref V v, auto ref B b, auto ref E e) { return a[b .. e] = v; } @@ -9793,6 +9800,7 @@ Flag!"encryption".no). */ struct Yes { + /// template opDispatch(string name) { enum opDispatch = Flag!name.yes; @@ -9803,6 +9811,7 @@ struct Yes /// Ditto struct No { + /// template opDispatch(string name) { enum opDispatch = Flag!name.no; @@ -9941,7 +9950,7 @@ public: } this(T...)(T flags) - if (allSatisfy!(isBaseEnumType, T)) + if (allSatisfy!(isBaseEnumType, T)) { this = flags; } @@ -9952,19 +9961,19 @@ public: } Base opCast(B)() const - if (is(Base : B)) + if (is(Base : B)) { return mValue; } auto opUnary(string op)() const - if (op == "~") + if (op == "~") { return BitFlags(cast(E) cast(Base) ~mValue); } auto ref opAssign(T...)(T flags) - if (allSatisfy!(isBaseEnumType, T)) + if (allSatisfy!(isBaseEnumType, T)) { mValue = 0; foreach (E flag; flags) @@ -10005,7 +10014,7 @@ public: } auto opBinary(string op)(BitFlags flags) const - if (op == "|" || op == "&") + if (op == "|" || op == "&") { BitFlags result = this; result.opOpAssign!op(flags); @@ -10013,7 +10022,7 @@ public: } auto opBinary(string op)(E flag) const - if (op == "|" || op == "&") + if (op == "|" || op == "&") { BitFlags result = this; result.opOpAssign!op(flag); @@ -10021,7 +10030,7 @@ public: } auto opBinaryRight(string op)(E flag) const - if (op == "|" || op == "&") + if (op == "|" || op == "&") { return opBinary!op(flag); } @@ -10653,25 +10662,29 @@ struct Ternary $(TR $(TD `unknown`) $(TD `unknown`) $(TD) $(TD `unknown`) $(TD `unknown`) $(TD `unknown`)) ) */ - Ternary opUnary(string s)() if (s == "~") + Ternary opUnary(string s)() + if (s == "~") { return make((386 >> value) & 6); } /// ditto - Ternary opBinary(string s)(Ternary rhs) if (s == "|") + Ternary opBinary(string s)(Ternary rhs) + if (s == "|") { return make((25_512 >> (value + rhs.value)) & 6); } /// ditto - Ternary opBinary(string s)(Ternary rhs) if (s == "&") + Ternary opBinary(string s)(Ternary rhs) + if (s == "&") { return make((26_144 >> (value + rhs.value)) & 6); } /// ditto - Ternary opBinary(string s)(Ternary rhs) if (s == "^") + Ternary opBinary(string s)(Ternary rhs) + if (s == "^") { return make((26_504 >> (value + rhs.value)) & 6); } @@ -10937,7 +10950,8 @@ struct RefCounted(T, RefCountedAutoInitialize autoInit = return _refCounted; } - this(A...)(auto ref A args) if (A.length > 0) + this(A...)(auto ref A args) + if (A.length > 0) out { assert(refCountedStore.isInitialized); diff --git a/libphobos/src/std/uni/package.d b/libphobos/src/std/uni/package.d index f7610c022eb43baa35dfaad6f0d7664c074326fa..34d15e034ba1dc61ed555d4641e64c8d7b2ae96c 100644 --- a/libphobos/src/std/uni/package.d +++ b/libphobos/src/std/uni/package.d @@ -16,6 +16,7 @@ $(TR $(TD Decode) $(TD $(LREF byGrapheme) $(LREF decodeGrapheme) $(LREF graphemeStride) + $(LREF popGrapheme) )) $(TR $(TD Comparison) $(TD $(LREF icmp) @@ -708,8 +709,8 @@ import std.meta : AliasSeq; import std.range.primitives : back, ElementEncodingType, ElementType, empty, front, hasLength, hasSlicing, isForwardRange, isInputRange, isRandomAccessRange, popFront, put, save; -import std.traits : isConvertibleToString, isIntegral, isSomeChar, - isSomeString, Unqual, isDynamicArray; +import std.traits : isAutodecodableString, isConvertibleToString, isIntegral, + isSomeChar, isSomeString, Unqual, isDynamicArray; // debug = std_uni; import std.internal.unicode_tables; // generated file @@ -961,7 +962,7 @@ struct MultiArray(Types...) } void store(OutRange)(scope OutRange sink) const - if (isOutputRange!(OutRange, char)) + if (isOutputRange!(OutRange, char)) { import std.format.write : formattedWrite; formattedWrite(sink, "[%( 0x%x, %)]", offsets[]); @@ -1652,7 +1653,7 @@ if (is(T : ElementType!Range)) template sharMethod(alias uniLowerBound) { size_t sharMethod(alias _pred="a<b", Range, T)(Range range, T needle) - if (is(T : ElementType!Range)) + if (is(T : ElementType!Range)) { import std.functional : binaryFun; import std.math.algebraic : nextPow2, truncPow2; @@ -1768,19 +1769,19 @@ alias sharSwitchLowerBound = sharMethod!switchUniformLowerBound; } static void append(T, V)(ref T[] arr, V value) - if (!isInputRange!V) + if (!isInputRange!V) { arr ~= force!T(value); } static void append(T, V)(ref T[] arr, V value) - if (isInputRange!V) + if (isInputRange!V) { insertInPlace(arr, arr.length, value); } static void destroy(T)(ref T arr) pure // pure required for -dip25, inferred for -dip1000 - if (isDynamicArray!T && is(Unqual!T == T)) + if (isDynamicArray!T && is(Unqual!T == T)) { debug { @@ -1790,7 +1791,7 @@ alias sharSwitchLowerBound = sharMethod!switchUniformLowerBound; } static void destroy(T)(ref T arr) pure // pure required for -dip25, inferred for -dip1000 - if (isDynamicArray!T && !is(Unqual!T == T)) + if (isDynamicArray!T && !is(Unqual!T == T)) { arr = null; } @@ -1845,7 +1846,7 @@ alias sharSwitchLowerBound = sharMethod!switchUniformLowerBound; } static void append(T, V)(ref T[] arr, V value) - if (!isInputRange!V) + if (!isInputRange!V) { if (arr.length == size_t.max) assert(0); arr = realloc(arr, arr.length+1); @@ -1862,7 +1863,7 @@ alias sharSwitchLowerBound = sharMethod!switchUniformLowerBound; } static void append(T, V)(ref T[] arr, V value) - if (isInputRange!V && hasLength!V) + if (isInputRange!V && hasLength!V) { import core.checkedint : addu; bool overflow; @@ -2058,7 +2059,7 @@ public struct InversionList(SP=GcPolicy) Construct from another code point set of any type. */ this(Set)(Set set) pure - if (isCodepointSet!Set) + if (isCodepointSet!Set) { uint[] arr; foreach (v; set.byInterval) @@ -2073,7 +2074,7 @@ public struct InversionList(SP=GcPolicy) Construct a set from a forward range of code point intervals. */ this(Range)(Range intervals) pure - if (isForwardRange!Range && isIntegralPair!(ElementType!Range)) + if (isForwardRange!Range && isIntegralPair!(ElementType!Range)) { uint[] arr; foreach (v; intervals) @@ -2245,7 +2246,7 @@ public: ) */ This opBinary(string op, U)(U rhs) - if (isCodepointSet!U || is(U:dchar)) + if (isCodepointSet!U || is(U:dchar)) { static if (op == "&" || op == "|" || op == "~") {// symmetric ops thus can swap arguments to reuse r-value @@ -2310,7 +2311,7 @@ public: /// The 'op=' versions of the above overloaded operators. ref This opOpAssign(string op, U)(U rhs) - if (isCodepointSet!U || is(U:dchar)) + if (isCodepointSet!U || is(U:dchar)) { static if (op == "|") // union { @@ -2342,7 +2343,7 @@ public: the same as $(LREF opIndex). */ bool opBinaryRight(string op: "in", U)(U ch) const - if (is(U : dchar)) + if (is(U : dchar)) { return this[ch]; } @@ -2522,7 +2523,7 @@ private: package(std) // used from: std.regex.internal.parser ref intersect(U)(U rhs) - if (isCodepointSet!U) + if (isCodepointSet!U) { Marker mark; foreach ( i; rhs.byInterval) @@ -2556,7 +2557,7 @@ private: // same as the above except that skip & drop parts are swapped package(std) // used from: std.regex.internal.parser ref sub(U)(U rhs) - if (isCodepointSet!U) + if (isCodepointSet!U) { Marker mark; foreach (i; rhs.byInterval) @@ -2569,7 +2570,7 @@ private: package(std) // used from: std.regex.internal.parse ref add(U)(U rhs) - if (isCodepointSet!U) + if (isCodepointSet!U) { Marker start; foreach (i; rhs.byInterval) @@ -3206,7 +3207,7 @@ struct CowArray(SP=GcPolicy) } this(Range)(Range range) - if (isInputRange!Range && hasLength!Range) + if (isInputRange!Range && hasLength!Range) { import std.algorithm.mutation : copy; length = range.length; @@ -3214,7 +3215,7 @@ struct CowArray(SP=GcPolicy) } this(Range)(Range range) - if (isForwardRange!Range && !hasLength!Range) + if (isForwardRange!Range && !hasLength!Range) { import std.algorithm.mutation : copy; import std.range.primitives : walkLength; @@ -3336,7 +3337,7 @@ struct CowArray(SP=GcPolicy) } void append(Range)(Range range) - if (isInputRange!Range && hasLength!Range && is(ElementType!Range : uint)) + if (isInputRange!Range && hasLength!Range && is(ElementType!Range : uint)) { size_t nl = length + range.length; length = nl; @@ -3793,7 +3794,7 @@ auto arrayRepr(T)(T x) template mapTrieIndex(Prefix...) { size_t mapTrieIndex(Key)(Key key) - if (isValidPrefixForTrie!(Key, Prefix)) + if (isValidPrefixForTrie!(Key, Prefix)) { alias p = Prefix; size_t idx; @@ -4184,7 +4185,7 @@ if (isValidPrefixForTrie!(Key, Args) /// void store(OutRange)(scope OutRange sink) const - if (isOutputRange!(OutRange, char)) + if (isOutputRange!(OutRange, char)) { _table.store(sink); } @@ -4285,7 +4286,7 @@ public template codepointSetTrie(sizes...) if (sumOfIntegerTuple!sizes == 21) { auto codepointSetTrie(Set)(Set set) - if (isCodepointSet!Set) + if (isCodepointSet!Set) { auto builder = TrieBuilder!(bool, dchar, lastDchar+1, GetBitSlicing!(21, sizes))(false); foreach (ival; set.byInterval) @@ -4322,7 +4323,7 @@ if (sumOfIntegerTuple!sizes == 21) static if (is(TypeOfBitPacked!T == bool)) { auto codepointTrie(Set)(const scope Set set) - if (isCodepointSet!Set) + if (isCodepointSet!Set) { return codepointSetTrie(set); } @@ -4337,9 +4338,9 @@ if (sumOfIntegerTuple!sizes == 21) // unsorted range of pairs /// auto codepointTrie(R)(R range, T defValue=T.init) - if (isInputRange!R - && is(typeof(ElementType!R.init[0]) : T) - && is(typeof(ElementType!R.init[1]) : dchar)) + if (isInputRange!R + && is(typeof(ElementType!R.init[0]) : T) + && is(typeof(ElementType!R.init[1]) : dchar)) { // build from unsorted array of pairs // TODO: expose index sorting functions for Trie @@ -4467,8 +4468,8 @@ if (isValidArgsForTrie!(Key, Args)) $(REF setUnion, std,_algorithm). */ auto buildTrie(Range)(Range range, Value filler=Value.init) - if (isInputRange!Range && is(typeof(Range.init.front[0]) : Value) - && is(typeof(Range.init.front[1]) : Key)) + if (isInputRange!Range && is(typeof(Range.init.front[0]) : Value) + && is(typeof(Range.init.front[1]) : Key)) { auto builder = TrieBuilder!(Value, Key, Prefix)(filler); foreach (v; range) @@ -4487,9 +4488,9 @@ if (isValidArgsForTrie!(Key, Args)) and `filler` is false. */ auto buildTrie(Range)(Range range, Value filler=Value.init) - if (is(TypeOfBitPacked!Value == bool) - && isInputRange!Range && is(typeof(Range.init.front[0]) : Key) - && is(typeof(Range.init.front[1]) : Key)) + if (is(TypeOfBitPacked!Value == bool) + && isInputRange!Range && is(typeof(Range.init.front[0]) : Key) + && is(typeof(Range.init.front[1]) : Key)) { auto builder = TrieBuilder!(Value, Key, Prefix)(filler); foreach (ival; range) @@ -4498,9 +4499,9 @@ if (isValidArgsForTrie!(Key, Args)) } auto buildTrie(Range)(Range range, Value filler, bool unsorted) - if (isInputRange!Range - && is(typeof(Range.init.front[0]) : Value) - && is(typeof(Range.init.front[1]) : Key)) + if (isInputRange!Range + && is(typeof(Range.init.front[0]) : Value) + && is(typeof(Range.init.front[1]) : Key)) { import std.algorithm.sorting : multiSort; alias Comps = GetComparators!(Prefix.length); @@ -4519,8 +4520,8 @@ if (isValidArgsForTrie!(Key, Args)) If no filler provided keys map to true, and `filler` is false. */ auto buildTrie(Range)(Range range, Value filler=Value.init) - if (is(TypeOfBitPacked!Value == bool) - && isInputRange!Range && is(typeof(Range.init.front) : Key)) + if (is(TypeOfBitPacked!Value == bool) + && isInputRange!Range && is(typeof(Range.init.front) : Key)) { auto builder = TrieBuilder!(Value, Key, Prefix)(filler); foreach (v; range) @@ -4533,7 +4534,7 @@ if (isValidArgsForTrie!(Key, Args)) of values where array index serves as key. */ auto buildTrie()(Value[] array, Value filler=Value.init) - if (isUnsigned!Key) + if (isUnsigned!Key) { auto builder = TrieBuilder!(Value, Key, Prefix)(filler); foreach (idx, v; array) @@ -4595,21 +4596,21 @@ public struct MatcherConcept of the result of test.) */ public bool match(Range)(ref Range inp) - if (isRandomAccessRange!Range && is(ElementType!Range : char)) + if (isRandomAccessRange!Range && is(ElementType!Range : char)) { assert(false); } ///ditto public bool skip(Range)(ref Range inp) - if (isRandomAccessRange!Range && is(ElementType!Range : char)) + if (isRandomAccessRange!Range && is(ElementType!Range : char)) { assert(false); } ///ditto public bool test(Range)(ref Range inp) - if (isRandomAccessRange!Range && is(ElementType!Range : char)) + if (isRandomAccessRange!Range && is(ElementType!Range : char)) { assert(false); } @@ -4765,7 +4766,7 @@ template Utf8Matcher() } static auto encode(size_t sz)(dchar ch) - if (sz > 1) + if (sz > 1) { import std.utf : encodeUTF = encode; char[4] buf; @@ -4821,8 +4822,8 @@ template Utf8Matcher() enum dispatch = genDispatch(); public bool match(Range)(ref Range inp) const - if (isRandomAccessRange!Range && is(ElementType!Range : char) && - !isDynamicArray!Range) + if (isRandomAccessRange!Range && is(ElementType!Range : char) && + !isDynamicArray!Range) { enum mode = Mode.skipOnMatch; assert(!inp.empty); @@ -4846,8 +4847,8 @@ template Utf8Matcher() static if (Sizes.length == 4) // can skip iff can detect all encodings { public bool skip(Range)(ref Range inp) const - if (isRandomAccessRange!Range && is(ElementType!Range : char) && - !isDynamicArray!Range) + if (isRandomAccessRange!Range && is(ElementType!Range : char) && + !isDynamicArray!Range) { enum mode = Mode.alwaysSkip; assert(!inp.empty); @@ -4868,8 +4869,8 @@ template Utf8Matcher() } public bool test(Range)(ref Range inp) const - if (isRandomAccessRange!Range && is(ElementType!Range : char) && - !isDynamicArray!Range) + if (isRandomAccessRange!Range && is(ElementType!Range : char) && + !isDynamicArray!Range) { enum mode = Mode.neverSkip; assert(!inp.empty); @@ -4887,19 +4888,19 @@ template Utf8Matcher() } bool match(C)(ref C[] str) const - if (isSomeChar!C) + if (isSomeChar!C) { return fwdStr!"match"(str); } bool skip(C)(ref C[] str) const - if (isSomeChar!C) + if (isSomeChar!C) { return fwdStr!"skip"(str); } bool test(C)(ref C[] str) const - if (isSomeChar!C) + if (isSomeChar!C) { return fwdStr!"test"(str); } @@ -5056,8 +5057,8 @@ template Utf16Matcher() mixin template DefMatcher() { public bool match(Range)(ref Range inp) const - if (isRandomAccessRange!Range && is(ElementType!Range : wchar) && - !isDynamicArray!Range) + if (isRandomAccessRange!Range && is(ElementType!Range : wchar) && + !isDynamicArray!Range) { enum mode = Mode.skipOnMatch; assert(!inp.empty); @@ -5083,8 +5084,8 @@ template Utf16Matcher() static if (Sizes.length == 2) { public bool skip(Range)(ref Range inp) const - if (isRandomAccessRange!Range && is(ElementType!Range : wchar) && - !isDynamicArray!Range) + if (isRandomAccessRange!Range && is(ElementType!Range : wchar) && + !isDynamicArray!Range) { enum mode = Mode.alwaysSkip; assert(!inp.empty); @@ -5105,8 +5106,8 @@ template Utf16Matcher() } public bool test(Range)(ref Range inp) const - if (isRandomAccessRange!Range && is(ElementType!Range : wchar) && - !isDynamicArray!Range) + if (isRandomAccessRange!Range && is(ElementType!Range : wchar) && + !isDynamicArray!Range) { enum mode = Mode.neverSkip; assert(!inp.empty); @@ -5118,19 +5119,19 @@ template Utf16Matcher() } bool match(C)(ref C[] str) const - if (isSomeChar!C) + if (isSomeChar!C) { return fwdStr!"match"(str); } bool skip(C)(ref C[] str) const - if (isSomeChar!C) + if (isSomeChar!C) { return fwdStr!"skip"(str); } bool test(C)(ref C[] str) const - if (isSomeChar!C) + if (isSomeChar!C) { return fwdStr!"test"(str); } @@ -5139,7 +5140,7 @@ template Utf16Matcher() } struct Impl(Sizes...) - if (Sizes.length >= 1 && Sizes.length <= 2) + if (Sizes.length >= 1 && Sizes.length <= 2) { private: import std.meta : allSatisfy; @@ -5229,7 +5230,7 @@ template Utf16Matcher() } struct CherryPick(I, Sizes...) - if (Sizes.length >= 1 && Sizes.length <= 2) + if (Sizes.length >= 1 && Sizes.length <= 2) { private: import std.meta : allSatisfy; @@ -6105,7 +6106,7 @@ template SetSearcher(alias table, string kind) { /// Run-time checked search. static auto opCall(C)(const scope C[] name) - if (is(C : dchar)) + if (is(C : dchar)) { import std.conv : to; CodepointSet set; @@ -6765,7 +6766,7 @@ struct UnicodeSetParser(Range) sets. */ static auto opCall(C)(const scope C[] name) - if (is(C : dchar)) + if (is(C : dchar)) { return loadAny(name); } @@ -7148,17 +7149,25 @@ private immutable TransformRes TransformRes.goOn ]; -template genericDecodeGrapheme(bool getValue) -{ - static if (getValue) +enum GraphemeRet { none, step, value } + +template genericDecodeGrapheme(GraphemeRet retType) +{ alias Ret = GraphemeRet; + + static if (retType == Ret.value) alias Value = Grapheme; - else + else static if (retType == Ret.step) + alias Value = size_t; + else static if (retType == Ret.none) alias Value = void; Value genericDecodeGrapheme(Input)(ref Input range) { - static if (getValue) - Grapheme grapheme; + static if (retType == Ret.value) + Grapheme result; + else static if (retType == Ret.step) + size_t result = 0; + auto state = GraphemeState.Start; dchar ch; @@ -7173,8 +7182,10 @@ template genericDecodeGrapheme(bool getValue) with(TransformRes) { case goOn: - static if (getValue) - grapheme ~= ch; + static if (retType == Ret.value) + result ~= ch; + else static if (retType == Ret.step) + result++; range.popFront(); continue; @@ -7182,8 +7193,10 @@ template genericDecodeGrapheme(bool getValue) goto rerun; case retInclude: - static if (getValue) - grapheme ~= ch; + static if (retType == Ret.value) + result ~= ch; + else static if (retType == Ret.step) + result++; range.popFront(); break outer; @@ -7192,8 +7205,8 @@ template genericDecodeGrapheme(bool getValue) } } - static if (getValue) - return grapheme; + static if (retType != Ret.none) + return result; } } @@ -7217,7 +7230,7 @@ if (is(C : dchar)) { auto src = input[index..$]; auto n = src.length; - genericDecodeGrapheme!(false)(src); + genericDecodeGrapheme!(GraphemeRet.none)(src); return n - src.length; } @@ -7279,7 +7292,7 @@ if (is(C : dchar)) Grapheme decodeGrapheme(Input)(ref Input inp) if (isInputRange!Input && is(immutable ElementType!Input == immutable dchar)) { - return genericDecodeGrapheme!true(inp); + return genericDecodeGrapheme!(GraphemeRet.value)(inp); } @safe unittest @@ -7304,6 +7317,73 @@ if (isInputRange!Input && is(immutable ElementType!Input == immutable dchar)) assert(equal(decodeGrapheme(s)[], "\U0001F1EC\U0001F1E7")); } +/++ + Reads one full grapheme cluster from an + $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of dchar `inp`, + but doesn't return it. Instead returns the number of code units read. + This differs from number of code points read only if `input` is an + autodecodable string. + + Note: + This function modifies `inp` and thus `inp` + must be an L-value. ++/ +size_t popGrapheme(Input)(ref Input inp) +if (isInputRange!Input && is(immutable ElementType!Input == immutable dchar)) +{ + static if (isAutodecodableString!Input || hasLength!Input) + { + // Why count each step in the decoder when you can just + // measure the grapheme in one go? + auto n = inp.length; + genericDecodeGrapheme!(GraphemeRet.none)(inp); + return n - inp.length; + } + else return genericDecodeGrapheme!(GraphemeRet.step)(inp); +} + +/// +@safe pure unittest +{ + // Two Union Jacks of the Great Britain in each + string s = "\U0001F1EC\U0001F1E7\U0001F1EC\U0001F1E7"; + wstring ws = "\U0001F1EC\U0001F1E7\U0001F1EC\U0001F1E7"; + dstring ds = "\U0001F1EC\U0001F1E7\U0001F1EC\U0001F1E7"; + + // String pop length in code units, not points. + assert(s.popGrapheme() == 8); + assert(ws.popGrapheme() == 4); + assert(ds.popGrapheme() == 2); + + assert(s == "\U0001F1EC\U0001F1E7"); + assert(ws == "\U0001F1EC\U0001F1E7"); + assert(ds == "\U0001F1EC\U0001F1E7"); + + import std.algorithm.comparison : equal; + import std.algorithm.iteration : filter; + + // Also works for non-random access ranges as long as the + // character type is 32-bit. + auto testPiece = "\r\nhello!"d.filter!(x => !x.isAlpha); + // Windows-style line ending is two code points in a single grapheme. + assert(testPiece.popGrapheme() == 2); + assert(testPiece.equal("!"d)); +} + +// Attribute compliance test. Should be nothrow `@nogc` when +// no autodecoding needed. +@safe pure nothrow @nogc unittest +{ + import std.algorithm.iteration : filter; + + auto str = "abcdef"d; + assert(str.popGrapheme() == 1); + + // also test with non-random access + auto filtered = "abcdef"d.filter!(x => x%2); + assert(filtered.popGrapheme() == 1); +} + /++ $(P Iterate a string by $(LREF Grapheme).) @@ -7556,15 +7636,15 @@ if (isInputRange!Range && is(immutable ElementType!Range == immutable dchar)) public: /// Ctor this(C)(const scope C[] chars...) - if (is(C : dchar)) + if (is(C : dchar)) { this ~= chars; } ///ditto this(Input)(Input seq) - if (!isDynamicArray!Input - && isInputRange!Input && is(ElementType!Input : dchar)) + if (!isDynamicArray!Input + && isInputRange!Input && is(ElementType!Input : dchar)) { this ~= seq; } @@ -7683,7 +7763,7 @@ public: /// Append all $(CHARACTERS) from the input range `inp` to this Grapheme. ref opOpAssign(string op, Input)(scope Input inp) - if (isInputRange!Input && is(ElementType!Input : dchar)) + if (isInputRange!Input && is(ElementType!Input : dchar)) { static if (op == "~") { @@ -7722,7 +7802,7 @@ public: @property bool valid()() /*const*/ { auto r = this[]; - genericDecodeGrapheme!false(r); + genericDecodeGrapheme!(GraphemeRet.none)(r); return r.length == 0; } @@ -9868,7 +9948,7 @@ private template toCaseInPlaceAlloc(alias indexFn, uint maxIdx, alias tableFn) { void toCaseInPlaceAlloc(C)(ref C[] s, size_t curIdx, size_t destIdx) @trusted pure - if (is(C == char) || is(C == wchar) || is(C == dchar)) + if (is(C == char) || is(C == wchar) || is(C == dchar)) { import std.utf : decode; alias caseLength = toCaseLength!(indexFn, maxIdx, tableFn); diff --git a/libphobos/src/std/utf.d b/libphobos/src/std/utf.d index 9a326a5ac3aff8ed471f0fbce3388b7f9c0e591c..7db120befd5ec74fdc0310ca511831f27b304e3a 100644 --- a/libphobos/src/std/utf.d +++ b/libphobos/src/std/utf.d @@ -4303,13 +4303,13 @@ if (isSomeChar!C) else: auto ref byUTF(R)(R r) - if (isAutodecodableString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R)) + if (isAutodecodableString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R)) { return byUTF(r.byCodeUnit()); } auto ref byUTF(R)(R r) - if (!isAutodecodableString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R)) + if (!isAutodecodableString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R)) { static if (is(immutable ElementEncodingType!R == immutable RC, RC) && is(RC == C)) { diff --git a/libphobos/src/std/uuid.d b/libphobos/src/std/uuid.d index b8cac6ecdd7e6e5b3d6e78d4f32c1e6cb894f6ee..09ce2f73f380c1da4da4714210d8d47b9c25c900 100644 --- a/libphobos/src/std/uuid.d +++ b/libphobos/src/std/uuid.d @@ -275,7 +275,7 @@ public struct UUID * You need to pass exactly 16 ubytes. */ @safe pure this(T...)(T uuidData) - if (uuidData.length == 16 && allSatisfy!(isIntegral, T)) + if (uuidData.length == 16 && allSatisfy!(isIntegral, T)) { import std.conv : to; @@ -331,7 +331,8 @@ public struct UUID * * For a less strict parser, see $(LREF parseUUID) */ - this(T)(in T[] uuid) if (isSomeChar!T) + this(T)(in T[] uuid) + if (isSomeChar!T) { import std.conv : to, parse; if (uuid.length < 36) diff --git a/libphobos/src/std/variant.d b/libphobos/src/std/variant.d index f7832104d701b6ae9bbcbf1167a22ae5d566fd3d..a0b564440120ed0ca96b8fdfee0f4161f8519565 100644 --- a/libphobos/src/std/variant.d +++ b/libphobos/src/std/variant.d @@ -658,7 +658,7 @@ public: /// Allows assignment from a subset algebraic type this(T : VariantN!(tsize, Types), size_t tsize, Types...)(T value) - if (!is(T : VariantN) && Types.length > 0 && allSatisfy!(allowed, Types)) + if (!is(T : VariantN) && Types.length > 0 && allSatisfy!(allowed, Types)) { opAssign(value); } @@ -735,7 +735,7 @@ public: // Allow assignment from another variant which is a subset of this one VariantN opAssign(T : VariantN!(tsize, Types), size_t tsize, Types...)(T rhs) - if (!is(T : VariantN) && Types.length > 0 && allSatisfy!(allowed, Types)) + if (!is(T : VariantN) && Types.length > 0 && allSatisfy!(allowed, Types)) { // discover which type rhs is actually storing foreach (V; T.AllowedTypes) @@ -1098,7 +1098,7 @@ public: { return opLogic!(T, op)(lhs); } ///ditto VariantN opBinary(string op, T)(T rhs) - if (op == "~") + if (op == "~") { auto temp = this; temp ~= rhs; @@ -1191,7 +1191,8 @@ public: If the `VariantN` contains an array, applies `dg` to each element of the array in turn. Otherwise, throws an exception. */ - int opApply(Delegate)(scope Delegate dg) if (is(Delegate == delegate)) + int opApply(Delegate)(scope Delegate dg) + if (is(Delegate == delegate)) { alias A = Parameters!(Delegate)[0]; if (type == typeid(A[])) @@ -2410,7 +2411,7 @@ if (Handlers.length > 0) { /// auto visit(VariantType)(VariantType variant) - if (isAlgebraic!VariantType) + if (isAlgebraic!VariantType) { return visitImpl!(true, VariantType, Handlers)(variant); } @@ -2553,7 +2554,7 @@ if (Handlers.length > 0) { /// auto tryVisit(VariantType)(VariantType variant) - if (isAlgebraic!VariantType) + if (isAlgebraic!VariantType) { return visitImpl!(false, VariantType, Handlers)(variant); }