diff --git a/gcc/d/d-codegen.cc b/gcc/d/d-codegen.cc index e9962e6d56ba7e37f86c01aedafe4a362d23aab1..66f06e9086ed275a4b3a8baef19eb02a90495822 100644 --- a/gcc/d/d-codegen.cc +++ b/gcc/d/d-codegen.cc @@ -631,6 +631,37 @@ force_target_expr (tree exp) return build_target_expr (decl, exp); } +/* Determine whether expression EXP can have a copy of its value elided. */ + +static bool +can_elide_copy_p (Expression *exp) +{ + /* Explicit `__rvalue(exp)'. */ + if (exp->rvalue) + return true; + + /* Look for variable expression. */ + Expression *last = exp; + while (CommaExp *ce = last->isCommaExp ()) + last = ce->e2; + + if (VarExp *ve = last->isVarExp ()) + { + if (VarDeclaration *vd = ve->var->isVarDeclaration ()) + { + /* Variable is an implicit copy of an lvalue. */ + if (vd->storage_class & STCrvalue) + return true; + + /* The destructor is going to run on the variable. */ + if (vd->isArgDtorVar ()) + return true; + } + } + + return false; +} + /* Returns the address of the expression EXP. */ tree @@ -2280,8 +2311,10 @@ d_build_call (TypeFunction *tf, tree callable, tree object, - The ABI of the function expects the callee to destroy its arguments; when the caller is handles destruction, then `targ' has already been made into a temporary. */ - if (arg->op == EXP::structLiteral || (!sd->postblit && !sd->dtor) - || target.isCalleeDestroyingArgs (tf)) + if (!can_elide_copy_p (arg) + && (arg->op == EXP::structLiteral + || (!sd->postblit && !sd->dtor) + || target.isCalleeDestroyingArgs (tf))) targ = force_target_expr (targ); targ = convert (build_reference_type (TREE_TYPE (targ)), diff --git a/gcc/d/d-lang.cc b/gcc/d/d-lang.cc index 3e9067ea9c2b3fedc4d906721cde83125ae9d320..a4cde584f43f0539877687127621c853c3f7f9ba 100644 --- a/gcc/d/d-lang.cc +++ b/gcc/d/d-lang.cc @@ -925,7 +925,8 @@ d_post_options (const char ** fn) global.params.v.errorLimit = flag_max_errors; global.params.v.showColumns = flag_show_column; - global.params.v.printErrorContext = flag_diagnostics_show_caret; + global.params.v.errorPrintMode = flag_diagnostics_show_caret + ? ErrorPrintMode::printErrorContext : ErrorPrintMode::simpleError; /* Keep the front-end location type in sync with params. */ Loc::set (global.params.v.showColumns, global.params.v.messageStyle); diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE index bfdc9ea21e1e45e373e84f60baddf81994375890..e5884c6a798ad3aa9e7fb90bbbf4fe5b5ee25589 100644 --- a/gcc/d/dmd/MERGE +++ b/gcc/d/dmd/MERGE @@ -1,4 +1,4 @@ -82a5d2a7c4dd3d270537bcede2981e047bfd0e6a +c57da0cf5945cfb45eed06f1fd820435cda3ee3a 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/astenums.d b/gcc/d/dmd/astenums.d index 1e30b9f8fc2520b7cba716f388b69c7841411d46..551b45317e2dce8cb18096d3faffc8de5f274bb6 100644 --- a/gcc/d/dmd/astenums.d +++ b/gcc/d/dmd/astenums.d @@ -302,7 +302,7 @@ enum ThreeState : ubyte enum TRUST : ubyte { default_ = 0, - system = 1, // @system (same as TRUST.default) + system = 1, // @system (same as TRUST.default_ unless feature "safer" is enabled) trusted = 2, // @trusted safe = 3, // @safe } diff --git a/gcc/d/dmd/attrib.d b/gcc/d/dmd/attrib.d index 0b5cd9d41c5d042ee15ef090ffaac2a87c166fab..5da4721bba5c77e4e640efd0f5a26281a83f1def 100644 --- a/gcc/d/dmd/attrib.d +++ b/gcc/d/dmd/attrib.d @@ -104,16 +104,6 @@ extern (C++) abstract class AttribDeclaration : Dsymbol return sc2; } - - override void addComment(const(char)* comment) - { - //printf("AttribDeclaration::addComment %s\n", comment); - if (comment) - { - this.include(null).foreachDsymbol( s => s.addComment(comment) ); - } - } - override const(char)* kind() const { return "attribute"; @@ -653,20 +643,6 @@ extern (C++) class ConditionalDeclaration : AttribDeclaration } } - override final void addComment(const(char)* comment) - { - /* Because addComment is called by the parser, if we called - * include() it would define a version before it was used. - * But it's no problem to drill down to both decl and elsedecl, - * so that's the workaround. - */ - if (comment) - { - decl .foreachDsymbol( s => s.addComment(comment) ); - elsedecl.foreachDsymbol( s => s.addComment(comment) ); - } - } - override void accept(Visitor v) { v.visit(this); @@ -762,12 +738,6 @@ extern (C++) final class StaticForeachDeclaration : AttribDeclaration return false; } - override void addComment(const(char)* comment) - { - // do nothing - // change this to give semantics to documentation comments on static foreach declarations - } - override const(char)* kind() const { return "static foreach"; diff --git a/gcc/d/dmd/attrib.h b/gcc/d/dmd/attrib.h index d2d18dba18d94b5df014b794d0f2ff1aab247be0..ef01d0f9538f6794557000d15a32ee345438fb56 100644 --- a/gcc/d/dmd/attrib.h +++ b/gcc/d/dmd/attrib.h @@ -28,7 +28,6 @@ class AttribDeclaration : public Dsymbol { public: Dsymbols *decl; // array of Dsymbol's - void addComment(const utf8_t *comment) override; const char *kind() const override; bool oneMember(Dsymbol *&ps, Identifier *ident) override; bool hasPointers() override final; @@ -148,7 +147,6 @@ public: ConditionalDeclaration *syntaxCopy(Dsymbol *s) override; bool oneMember(Dsymbol *&ps, Identifier *ident) override final; - void addComment(const utf8_t *comment) override final; void accept(Visitor *v) override { v->visit(this); } }; @@ -176,7 +174,6 @@ public: StaticForeachDeclaration *syntaxCopy(Dsymbol *s) override; bool oneMember(Dsymbol *&ps, Identifier *ident) override; - void addComment(const utf8_t *comment) override; const char *kind() const override; void accept(Visitor *v) override { v->visit(this); } }; diff --git a/gcc/d/dmd/clone.d b/gcc/d/dmd/clone.d index bbfb1ee9f87d175e4aca052d4aeb25f4e3de74c0..1b838603c7e8d3c62d675ff5d87d6ef4abb1ba11 100644 --- a/gcc/d/dmd/clone.d +++ b/gcc/d/dmd/clone.d @@ -1610,13 +1610,15 @@ private Statement generateCopyCtorBody(StructDeclaration sd) * Params: * sd = the `struct` for which the copy constructor is generated * hasCpCtor = set to true if a copy constructor is already present + * hasMoveCtor = set to true if a move constructor is already present * * Returns: * `true` if one needs to be generated * `false` otherwise */ -bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor) +bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor, out bool hasMoveCtor) { + //printf("needCopyCtor() %s\n", sd.toChars()); if (global.errors) return false; @@ -1648,14 +1650,17 @@ bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor) return 0; } - if (isRvalueConstructor(sd, ctorDecl)) + if (ctorDecl.isMoveCtor) rvalueCtor = ctorDecl; return 0; }); + if (rvalueCtor) + hasMoveCtor = true; + if (cpCtor) { - if (rvalueCtor) + if (0 && rvalueCtor) { .error(sd.loc, "`struct %s` may not define both a rvalue constructor and a copy constructor", sd.toChars()); errorSupplemental(rvalueCtor.loc,"rvalue constructor defined here"); @@ -1710,6 +1715,7 @@ LcheckFields: * Params: * sd = the `struct` for which the copy constructor is generated * sc = the scope where the copy constructor is generated + * hasMoveCtor = set to true when a move constructor is also detected * * Returns: * `true` if `struct` sd defines a copy constructor (explicitly or generated), @@ -1717,10 +1723,10 @@ LcheckFields: * References: * https://dlang.org/spec/struct.html#struct-copy-constructor */ -bool buildCopyCtor(StructDeclaration sd, Scope* sc) +bool buildCopyCtor(StructDeclaration sd, Scope* sc, out bool hasMoveCtor) { bool hasCpCtor; - if (!needCopyCtor(sd, hasCpCtor)) + if (!needCopyCtor(sd, hasCpCtor, hasMoveCtor)) return hasCpCtor; //printf("generating copy constructor for %s\n", sd.toChars()); diff --git a/gcc/d/dmd/constfold.d b/gcc/d/dmd/constfold.d index 1578ef770a090cf72614de652c5a6cf53e288328..9d5aa96416b524101ac45206eed220867439339a 100644 --- a/gcc/d/dmd/constfold.d +++ b/gcc/d/dmd/constfold.d @@ -806,6 +806,7 @@ UnionExp Equal(EXP op, const ref Loc loc, Type type, Expression e1, Expression e UnionExp Identity(EXP op, const ref Loc loc, Type type, Expression e1, Expression e2) { + //printf("Identity %s %s\n", e1.toChars(), e2.toChars()); UnionExp ue = void; int cmp; if (e1.op == EXP.null_) @@ -820,7 +821,17 @@ UnionExp Identity(EXP op, const ref Loc loc, Type type, Expression e1, Expressio { SymOffExp es1 = e1.isSymOffExp(); SymOffExp es2 = e2.isSymOffExp(); - cmp = (es1.var == es2.var && es1.offset == es2.offset); + cmp = es1.offset == es2.offset; + if (cmp) + { + cmp = es1.var == es2.var; + if (!cmp && (es1.var.isParameter() || es2.var.isParameter())) + { + // because of ref's, they may still be the same, we cannot tell + cantExp(ue); + return ue; + } + } } else { diff --git a/gcc/d/dmd/declaration.d b/gcc/d/dmd/declaration.d index 995995400a55f0d78425f6a7e7a05d4290905a09..e6fb1ed65cd9ef981e9c3579807ef267e1c60efd 100644 --- a/gcc/d/dmd/declaration.d +++ b/gcc/d/dmd/declaration.d @@ -30,6 +30,7 @@ import dmd.func; import dmd.funcsem : overloadApply, getLevelAndCheck; import dmd.globals; import dmd.gluelayer; +import dmd.hdrgen; import dmd.id; import dmd.identifier; import dmd.init; @@ -95,6 +96,7 @@ extern (C++) abstract class Declaration : Dsymbol enum ignoreRead = 2; // ignore any reads of AliasDeclaration enum nounderscore = 4; // don't prepend _ to mangled name enum hidden = 8; // don't print this in .di files + enum nrvo = 0x10; /// forward to fd.nrvo_var when generating code // overridden symbol with pragma(mangle, "...") const(char)[] mangleOverride; @@ -441,8 +443,7 @@ extern (C++) final class AliasDeclaration : Declaration extern (D) this(const ref Loc loc, Identifier ident, Type type) @safe { super(loc, ident); - //printf("AliasDeclaration(id = '%s', type = %p)\n", ident.toChars(), type); - //printf("type = '%s'\n", type.toChars()); + //debug printf("AliasDeclaration(id = '%s', type = `%s`, %p)\n", ident.toChars(), dmd.hdrgen.toChars(type), type.isTypeIdentifier()); this.type = type; assert(type); } @@ -450,7 +451,7 @@ extern (C++) final class AliasDeclaration : Declaration extern (D) this(const ref Loc loc, Identifier ident, Dsymbol s) @safe { super(loc, ident); - //printf("AliasDeclaration(id = '%s', s = %p)\n", ident.toChars(), s); + //debug printf("AliasDeclaration(id = '%s', s = `%s`)\n", ident.toChars(), s.toChars()); assert(s != this); this.aliassym = s; assert(s); @@ -611,8 +612,9 @@ extern (C++) final class AliasDeclaration : Declaration override Dsymbol toAlias() { - //printf("[%s] AliasDeclaration::toAlias('%s', this = %p, aliassym = %p, kind = '%s', inuse = %d)\n", - // loc.toChars(), toChars(), this, aliassym, aliassym ? aliassym.kind() : "", inuse); + static if (0) + printf("[%s] AliasDeclaration::toAlias('%s', this = %p, aliassym: %s, kind: '%s', inuse = %d)\n", + loc.toChars(), toChars(), this, aliassym ? aliassym.toChars() : "", aliassym ? aliassym.kind() : "", inuse); assert(this != aliassym); //static int count; if (++count == 10) *(char*)0=0; diff --git a/gcc/d/dmd/declaration.h b/gcc/d/dmd/declaration.h index bdefd2d9f864cc5f029b7c5bc0bf181526ab78ea..a98213d9198c1921c0347a33479f44f757bb5a4c 100644 --- a/gcc/d/dmd/declaration.h +++ b/gcc/d/dmd/declaration.h @@ -782,6 +782,7 @@ class CtorDeclaration final : public FuncDeclaration { public: d_bool isCpCtor; + d_bool isMoveCtor; CtorDeclaration *syntaxCopy(Dsymbol *) override; const char *kind() const override; const char *toChars() const override; diff --git a/gcc/d/dmd/dscope.d b/gcc/d/dmd/dscope.d index aa48d57ba999e810c2d47c54c0012dd5cbd0fac8..76627bec33439daaf00b85e2d0dfb52100b57de6 100644 --- a/gcc/d/dmd/dscope.d +++ b/gcc/d/dmd/dscope.d @@ -24,6 +24,7 @@ import dmd.dclass; import dmd.declaration; import dmd.dmodule; import dmd.doc; +import dmd.dstruct; import dmd.dsymbol; import dmd.dsymbolsem; import dmd.dtemplate; @@ -146,6 +147,7 @@ extern (C++) struct Scope AliasDeclaration aliasAsg; /// if set, then aliasAsg is being assigned a new value, /// do not set wasRead for it + StructDeclaration argStruct; /// elimiate recursion when looking for rvalue construction extern (D) __gshared Scope* freelist; diff --git a/gcc/d/dmd/dstruct.d b/gcc/d/dmd/dstruct.d index d7b1ace34f0a01c01166c9053f03a35944f60abd..abeffcb39d2d73977d7b1519acaa49890ad24784 100644 --- a/gcc/d/dmd/dstruct.d +++ b/gcc/d/dmd/dstruct.d @@ -101,6 +101,7 @@ extern (C++) class StructDeclaration : AggregateDeclaration bool hasIdentityEquals; // true if has identity opEquals bool hasNoFields; // has no fields bool hasCopyCtor; // copy constructor + bool hasMoveCtor; // move constructor bool hasPointerField; // members with indirections bool hasVoidInitPointers; // void-initialized unsafe fields bool hasUnsafeBitpatterns; // @system members, pointers, bool @@ -171,11 +172,12 @@ extern (C++) class StructDeclaration : AggregateDeclaration { Dsymbol s = (*members)[i]; s.setFieldOffset(this, &fieldState, isunion); - } - if (type.ty == Terror) - { - errors = true; - return; + if (type.ty == Terror) + { + errorSupplemental(s.loc, "error on member `%s`", s.toPrettyChars); + errors = true; + return; + } } if (structsize == 0) @@ -320,11 +322,16 @@ extern (C++) class StructDeclaration : AggregateDeclaration import dmd.clone; bool hasCpCtorLocal; - needCopyCtor(this, hasCpCtorLocal); + bool hasMoveCtorLocal; + needCopyCtor(this, hasCpCtorLocal, hasMoveCtorLocal); if (enclosing || // is nested search(this, loc, Id.postblit) || // has postblit search(this, loc, Id.dtor) || // has destructor + /* This is commented out because otherwise buildkite vibe.d: + `canCAS!Task` fails to compile + */ + //hasMoveCtorLocal || // has move constructor hasCpCtorLocal) // has copy constructor { ispod = ThreeState.no; diff --git a/gcc/d/dmd/dsymbol.d b/gcc/d/dmd/dsymbol.d index af32f7afa1e8d5305a31dcccb18a837531aa997d..3aed16ab29af7b61323769d3f3ef23a0d4277644 100644 --- a/gcc/d/dmd/dsymbol.d +++ b/gcc/d/dmd/dsymbol.d @@ -925,22 +925,8 @@ extern (C++) class Dsymbol : ASTNode */ void addComment(const(char)* comment) { - if (!comment || !*comment) - return; - - //printf("addComment '%s' to Dsymbol %p '%s'\n", comment, this, toChars()); - void* h = cast(void*)this; // just the pointer is the key - auto p = h in commentHashTable; - if (!p) - { - commentHashTable[h] = comment; - return; - } - if (strcmp(*p, comment) != 0) - { - // Concatenate the two - *p = Lexer.combineComments((*p).toDString(), comment.toDString(), true); - } + import dmd.dsymbolsem; + dmd.dsymbolsem.addComment(this, comment); } /// get documentation comment for this Dsymbol @@ -958,7 +944,7 @@ extern (C++) class Dsymbol : ASTNode /* Shell around addComment() to avoid disruption for the moment */ final void comment(const(char)* comment) { addComment(comment); } - private extern (D) __gshared const(char)*[void*] commentHashTable; + extern (D) __gshared const(char)*[void*] commentHashTable; /********************************** diff --git a/gcc/d/dmd/dsymbol.h b/gcc/d/dmd/dsymbol.h index 7b33ed2910bfe00da252e8813449f702433e28fb..3936c3e4cfbb0f1189429ba3546f79fd355434cc 100644 --- a/gcc/d/dmd/dsymbol.h +++ b/gcc/d/dmd/dsymbol.h @@ -433,4 +433,5 @@ namespace dmd Dsymbols *include(Dsymbol *d, Scope *sc); void setScope(Dsymbol *d, Scope *sc); void importAll(Dsymbol *d, Scope *sc); + void addComment(Dsymbol *d, const char *comment); } diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d index 173532af397c810c2b9dcd5df219c84b01fe82bd..7e9843617c92d61ea63af9123b5ea80401e84b46 100644 --- a/gcc/d/dmd/dsymbolsem.d +++ b/gcc/d/dmd/dsymbolsem.d @@ -53,6 +53,7 @@ import dmd.init; import dmd.initsem; import dmd.intrange; import dmd.hdrgen; +import dmd.lexer; import dmd.location; import dmd.mtype; import dmd.mustuse; @@ -64,6 +65,7 @@ import dmd.parse; debug import dmd.printast; import dmd.root.array; import dmd.root.filename; +import dmd.root.string; import dmd.common.outbuffer; import dmd.root.rmem; import dmd.rootobject; @@ -256,6 +258,10 @@ Return: */ bool checkHasBothRvalueAndCpCtor(StructDeclaration sd, CtorDeclaration ctor, TemplateInstance ti) { + //printf("checkHasBothRvalueAndCpCtor() sd: %s ctor: %s ti: %s\n", sd.toChars(), ctor.toChars(), ti.toChars()); + /* cannot use ctor.isMoveCtor because semantic pass may not have been run yet, + * so use isRvalueConstructor() + */ if (sd && sd.hasCopyCtor && isRvalueConstructor(sd, ctor)) { .error(ctor.loc, "cannot define both an rvalue constructor and a copy constructor for `struct %s`", sd.toChars()); @@ -280,6 +286,7 @@ bool checkHasBothRvalueAndCpCtor(StructDeclaration sd, CtorDeclaration ctor, Tem */ bool isRvalueConstructor(StructDeclaration sd, CtorDeclaration ctor) { + // note commonality with setting isMoveCtor in the semantic code for CtorDeclaration auto tf = ctor.type.isTypeFunction(); const dim = tf.parameterList.length; if (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg)) @@ -306,6 +313,7 @@ bool isRvalueConstructor(StructDeclaration sd, CtorDeclaration ctor) */ Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool findOnly = false) { + //printf("resolveAliasThis() %s\n", toChars(e)); import dmd.typesem : dotExp; for (AggregateDeclaration ad = isAggregate(e.type); ad;) { @@ -2399,7 +2407,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor override void visit(CtorDeclaration ctd) { - //printf("CtorDeclaration::semantic() %s\n", toChars()); + //printf("CtorDeclaration::semantic() %p %s\n", ctd, ctd.toChars()); if (ctd.semanticRun >= PASS.semanticdone) return; if (ctd._scope) @@ -2432,6 +2440,24 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor sc.stc &= ~STC.static_; // not a static constructor funcDeclarationSemantic(sc, ctd); + // Check short constructor: this() => expr; + if (ctd.fbody) + { + if (auto s = ctd.fbody.isExpStatement()) + { + if (s.exp) + { + auto ce = s.exp.isCallExp(); + // check this/super before semantic + if (!ce || (!ce.e1.isThisExp() && !ce.e1.isSuperExp())) + { + s.exp = s.exp.expressionSemantic(sc); + if (s.exp.type.ty != Tvoid) + error(s.loc, "can only return void expression, `this` call or `super` call from constructor"); + } + } + } + } sc.pop(); @@ -2482,12 +2508,15 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } else if ((dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg))) { - //printf("tf: %s\n", tf.toChars()); + //printf("tf: %s\n", toChars(tf)); auto param = tf.parameterList[0]; - if (param.storageClass & STC.ref_ && param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf()) + if (param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf()) { - //printf("copy constructor\n"); - ctd.isCpCtor = true; + //printf("copy constructor %p\n", ctd); + if (param.storageClass & STC.ref_) + ctd.isCpCtor = true; // copy constructor + else + ctd.isMoveCtor = true; // move constructor } } } @@ -2978,7 +3007,10 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor buildDtors(sd, sc2); - sd.hasCopyCtor = buildCopyCtor(sd, sc2); + bool hasMoveCtor; + sd.hasCopyCtor = buildCopyCtor(sd, sc2, hasMoveCtor); + sd.hasMoveCtor = hasMoveCtor; + sd.postblit = buildPostBlit(sd, sc2); buildOpAssign(sd, sc2); @@ -5211,7 +5243,7 @@ void aliasInstanceSemantic(TemplateInstance tempinst, Scope* sc, TemplateDeclara // function used to perform semantic on AliasDeclaration void aliasSemantic(AliasDeclaration ds, Scope* sc) { - //printf("AliasDeclaration::semantic() %s\n", ds.toChars()); + //printf("AliasDeclaration::semantic() %s %p\n", ds.toChars(), ds.aliassym); // as DsymbolSemanticVisitor::visit(AliasDeclaration), in case we're called first. // see https://issues.dlang.org/show_bug.cgi?id=21001 @@ -7782,7 +7814,6 @@ private Expression callScopeDtor(VarDeclaration vd, Scope* sc) Expression ec; ec = new VarExp(vd.loc, vd); e = new DeleteExp(vd.loc, ec, true); - e.type = Type.tvoid; break; } return e; @@ -7845,3 +7876,57 @@ Lfail: } return false; } + +extern (C++) void addComment(Dsymbol d, const(char)* comment) +{ + scope v = new AddCommentVisitor(comment); + d.accept(v); +} + +extern (C++) class AddCommentVisitor: Visitor +{ + alias visit = Visitor.visit; + + const(char)* comment; + + this(const(char)* comment) + { + this.comment = comment; + } + + override void visit(Dsymbol d) + { + if (!comment || !*comment) + return; + + //printf("addComment '%s' to Dsymbol %p '%s'\n", comment, this, toChars()); + void* h = cast(void*)d; // just the pointer is the key + auto p = h in d.commentHashTable; + if (!p) + { + d.commentHashTable[h] = comment; + return; + } + if (strcmp(*p, comment) != 0) + { + // Concatenate the two + *p = Lexer.combineComments((*p).toDString(), comment.toDString(), true); + } + } + override void visit(AttribDeclaration atd) + { + if (comment) + { + atd.include(null).foreachDsymbol( s => s.addComment(comment) ); + } + } + override void visit(ConditionalDeclaration cd) + { + if (comment) + { + cd.decl .foreachDsymbol( s => s.addComment(comment) ); + cd.elsedecl.foreachDsymbol( s => s.addComment(comment) ); + } + } + override void visit(StaticForeachDeclaration sfd) {} +} diff --git a/gcc/d/dmd/expression.d b/gcc/d/dmd/expression.d index dc72b3a8df1b7c0d3edd203f55f8ea8429ac068e..355bdc42468e60e2ccee5861a9a62b826b08f8fe 100644 --- a/gcc/d/dmd/expression.d +++ b/gcc/d/dmd/expression.d @@ -293,10 +293,16 @@ enum WANTexpand = 1; // expand const/immutable variables if possible */ extern (C++) abstract class Expression : ASTNode { - Type type; // !=null means that semantic() has been run + /// Usually, this starts out as `null` and gets set to the final expression type by + /// `expressionSemantic`. However, for some expressions (such as `TypeExp`,`RealExp`, + /// `VarExp`), the field can get set to an assigned type before running semantic. + /// See `expressionSemanticDone` + Type type; + Loc loc; // file location const EXP op; // to minimize use of dynamic_cast bool parens; // if this is a parenthesized expression + bool rvalue; // true if this is considered to be an rvalue, even if it is an lvalue extern (D) this(const ref Loc loc, EXP op) scope @safe { @@ -724,7 +730,7 @@ extern (C++) abstract class Expression : ASTNode inout(TypeidExp) isTypeidExp() { return op == EXP.typeid_ ? cast(typeof(return))this : null; } inout(TraitsExp) isTraitsExp() { return op == EXP.traits ? cast(typeof(return))this : null; } inout(HaltExp) isHaltExp() { return op == EXP.halt ? cast(typeof(return))this : null; } - inout(IsExp) isExp() { return op == EXP.is_ ? cast(typeof(return))this : null; } + inout(IsExp) isIsExp() { return op == EXP.is_ ? cast(typeof(return))this : null; } inout(MixinExp) isMixinExp() { return op == EXP.mixin_ ? cast(typeof(return))this : null; } inout(ImportExp) isImportExp() { return op == EXP.import_ ? cast(typeof(return))this : null; } inout(AssertExp) isAssertExp() { return op == EXP.assert_ ? cast(typeof(return))this : null; } @@ -1307,7 +1313,7 @@ extern (C++) class IdentifierExp : Expression override final bool isLvalue() { - return true; + return !this.rvalue; } override void accept(Visitor v) @@ -1351,7 +1357,7 @@ extern (C++) final class DsymbolExp : Expression override bool isLvalue() { - return true; + return !rvalue; } override void accept(Visitor v) @@ -1397,7 +1403,7 @@ extern (C++) class ThisExp : Expression override final bool isLvalue() { // Class `this` should be an rvalue; struct `this` should be an lvalue. - return type.toBasetype().ty != Tclass; + return !rvalue && type.toBasetype().ty != Tclass; } override void accept(Visitor v) @@ -1782,7 +1788,7 @@ extern (C++) final class StringExp : Expression /* string literal is rvalue in default, but * conversion to reference of static array is only allowed. */ - return (type && type.toBasetype().ty == Tsarray); + return !rvalue && (type && type.toBasetype().ty == Tsarray); } /******************************** @@ -2719,7 +2725,7 @@ extern (C++) final class VarExp : SymbolExp override bool isLvalue() { - if (var.storage_class & (STC.lazy_ | STC.rvalue | STC.manifest)) + if (rvalue || var.storage_class & (STC.lazy_ | STC.rvalue | STC.manifest)) return false; return true; } @@ -3098,7 +3104,7 @@ extern (C++) class BinAssignExp : BinExp override final bool isLvalue() { - return true; + return !rvalue; } override void accept(Visitor v) @@ -3212,7 +3218,6 @@ extern (C++) final class ThrowExp : UnaExp extern (D) this(const ref Loc loc, Expression e) { super(loc, EXP.throw_, e); - this.type = Type.tnoreturn; } override ThrowExp syntaxCopy() @@ -3303,6 +3308,8 @@ extern (C++) final class DotVarExp : UnaExp override bool isLvalue() { + if (rvalue) + return false; if (e1.op != EXP.structLiteral) return true; auto vd = var.isVarDeclaration(); @@ -3530,6 +3537,8 @@ extern (C++) final class CallExp : UnaExp override bool isLvalue() { + if (rvalue) + return false; Type tb = e1.type.toBasetype(); if (tb.ty == Tdelegate || tb.ty == Tpointer) tb = tb.nextOf(); @@ -3648,7 +3657,7 @@ extern (C++) final class PtrExp : UnaExp override bool isLvalue() { - return true; + return !rvalue; } override void accept(Visitor v) @@ -3777,7 +3786,7 @@ extern (C++) final class CastExp : UnaExp override bool isLvalue() { //printf("e1.type = %s, to.type = %s\n", e1.type.toChars(), to.toChars()); - if (!e1.isLvalue()) + if (rvalue || !e1.isLvalue()) return false; return (to.ty == Tsarray && (e1.type.ty == Tvector || e1.type.ty == Tsarray)) || e1.type.mutableOf.unSharedOf().equals(to.mutableOf().unSharedOf()); @@ -3834,7 +3843,7 @@ extern (C++) final class VectorArrayExp : UnaExp override bool isLvalue() { - return e1.isLvalue(); + return !rvalue && e1.isLvalue(); } override void accept(Visitor v) @@ -3891,7 +3900,7 @@ extern (C++) final class SliceExp : UnaExp /* slice expression is rvalue in default, but * conversion to reference of static array is only allowed. */ - return (type && type.toBasetype().ty == Tsarray); + return !rvalue && (type && type.toBasetype().ty == Tsarray); } override Optional!bool toBool() @@ -3956,6 +3965,8 @@ extern (C++) final class ArrayExp : UnaExp override bool isLvalue() { + if (rvalue) + return false; if (type && type.toBasetype().ty == Tvoid) return false; return true; @@ -4005,7 +4016,7 @@ extern (C++) final class CommaExp : BinExp override bool isLvalue() { - return e2.isLvalue(); + return !rvalue && e2.isLvalue(); } override Optional!bool toBool() @@ -4080,7 +4091,7 @@ extern (C++) final class DelegatePtrExp : UnaExp override bool isLvalue() { - return e1.isLvalue(); + return !rvalue && e1.isLvalue(); } override void accept(Visitor v) @@ -4103,7 +4114,7 @@ extern (C++) final class DelegateFuncptrExp : UnaExp override bool isLvalue() { - return e1.isLvalue(); + return !rvalue && e1.isLvalue(); } override void accept(Visitor v) @@ -4143,6 +4154,8 @@ extern (C++) final class IndexExp : BinExp override bool isLvalue() { + if (rvalue) + return false; auto t1b = e1.type.toBasetype(); if (t1b.isTypeAArray() || t1b.isTypeSArray() || (e1.isIndexExp() && t1b != t1b.isTypeDArray())) @@ -4251,7 +4264,7 @@ extern (C++) class AssignExp : BinExp { return false; } - return true; + return !rvalue; } override void accept(Visitor v) @@ -4982,7 +4995,7 @@ extern (C++) final class CondExp : BinExp override bool isLvalue() { - return e1.isLvalue() && e2.isLvalue(); + return !rvalue && e1.isLvalue() && e2.isLvalue(); } override void accept(Visitor v) diff --git a/gcc/d/dmd/expression.h b/gcc/d/dmd/expression.h index c353a191a662067ef7652e9285a23747ee2b1bec..d62aea86d63716fbb7e448ea679e3add668666fd 100644 --- a/gcc/d/dmd/expression.h +++ b/gcc/d/dmd/expression.h @@ -78,6 +78,7 @@ public: Loc loc; // file location EXP op; // to minimize use of dynamic_cast d_bool parens; // if this is a parenthesized expression + d_bool rvalue; // consider this an rvalue, even if it is an lvalue size_t size() const; static void _init(); @@ -138,7 +139,7 @@ public: TypeidExp* isTypeidExp(); TraitsExp* isTraitsExp(); HaltExp* isHaltExp(); - IsExp* isExp(); + IsExp* isIsExp(); MixinExp* isMixinExp(); ImportExp* isImportExp(); AssertExp* isAssertExp(); diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d index e1baa4887e7928792a1baac4ae1ff6050a0abc9e..413d31a6339ff9f8d9107f194b2a08158d118f98 100644 --- a/gcc/d/dmd/expressionsem.d +++ b/gcc/d/dmd/expressionsem.d @@ -816,21 +816,58 @@ extern(D) bool arrayExpressionSemantic( * Params: * sc = the scope where the expression is encountered * e = the expression the needs to be moved or copied (source) - * t = if the struct defines a copy constructor, the type of the destination - * + * t = if the struct defines a copy constructor, the type of the destination (can be NULL) + * nrvo = true if the generated copy can be treated as NRVO + * move = true to allow a move constructor to be used, false to prevent infinite recursion * Returns: * The expression that copy constructs or moves the value. */ -extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t = null) +extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t, bool nrvo, bool move = false) { + //printf("doCopyOrMove() %s\n", toChars(e)); + StructDeclaration sd; + if (t) + { + if (auto ts = t.isTypeStruct()) + sd = ts.sym; + } + if (auto ce = e.isCondExp()) { - ce.e1 = doCopyOrMove(sc, ce.e1); - ce.e2 = doCopyOrMove(sc, ce.e2); + ce.e1 = doCopyOrMove(sc, ce.e1, null, nrvo); + ce.e2 = doCopyOrMove(sc, ce.e2, null, nrvo); + } + else if (e.isLvalue()) + { + e = callCpCtor(sc, e, t, nrvo); + } + else if (move && sd && sd.hasMoveCtor && !e.isCallExp() && !e.isStructLiteralExp()) + { + // #move + /* Rewrite as: + * S __copyrvalue; + * __copyrvalue.moveCtor(e); + * __copyrvalue; + */ + VarDeclaration vd = new VarDeclaration(e.loc, e.type, Identifier.generateId("__copyrvalue"), null); + if (nrvo) + vd.adFlags |= Declaration.nrvo; + vd.storage_class |= STC.nodtor; + vd.dsymbolSemantic(sc); + Expression de = new DeclarationExp(e.loc, vd); + Expression ve = new VarExp(e.loc, vd); + + Expression er; + er = new DotIdExp(e.loc, ve, Id.ctor); // ve.ctor + er = new CallExp(e.loc, er, e); // ve.ctor(e) + er = new CommaExp(e.loc, er, new VarExp(e.loc, vd)); // ve.ctor(e),vd + er = Expression.combine(de, er); // de,ve.ctor(e),vd + + e = er.expressionSemantic(sc); } else { - e = e.isLvalue() ? callCpCtor(sc, e, t) : valueNoDtor(e); + e = valueNoDtor(e); } return e; } @@ -839,13 +876,15 @@ extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t = null) * If e is an instance of a struct, and that struct has a copy constructor, * rewrite e as: * (tmp = e),tmp - * Input: + * Params: * sc = just used to specify the scope of created temporary variable * destinationType = the type of the object on which the copy constructor is called; * may be null if the struct defines a postblit + * nrvo = true if the generated copy can be treated as NRVO */ -private Expression callCpCtor(Scope* sc, Expression e, Type destinationType) +private Expression callCpCtor(Scope* sc, Expression e, Type destinationType, bool nrvo) { + //printf("callCpCtor(e: %s et: %s destinationType: %s\n", toChars(e), toChars(e.type), toChars(destinationType)); auto ts = e.type.baseElemOf().isTypeStruct(); if (!ts) @@ -860,7 +899,9 @@ private Expression callCpCtor(Scope* sc, Expression e, Type destinationType) * This is not the most efficient, ideally tmp would be constructed * directly onto the stack. */ - auto tmp = copyToTemp(STC.rvalue, "__copytmp", e); + VarDeclaration tmp = copyToTemp(STC.rvalue, "__copytmp", e); + if (nrvo) + tmp.adFlags |= Declaration.nrvo; if (sd.hasCopyCtor && destinationType) { // https://issues.dlang.org/show_bug.cgi?id=22619 @@ -888,6 +929,7 @@ private Expression callCpCtor(Scope* sc, Expression e, Type destinationType) */ Expression valueNoDtor(Expression e) { + //printf("valueNoDtor() %s\n", toChars(e)); auto ex = lastComma(e); if (auto ce = ex.isCallExp()) @@ -2706,7 +2748,7 @@ private Type arrayExpressionToCommonType(Scope* sc, ref Expressions exps) continue; } - e = doCopyOrMove(sc, e); + e = doCopyOrMove(sc, e, null, false); if (!foundType && t0 && !t0.equals(e.type)) { @@ -2952,7 +2994,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, Type* prettype, Expression* peprefix) { Expressions* arguments = argumentList.arguments; - //printf("functionParameters() %s\n", fd ? fd.toChars() : ""); + //printf("functionParameters() fd: %s tf: %s\n", fd ? fd.ident.toChars() : "", toChars(tf)); assert(arguments); assert(fd || tf.next); const size_t nparams = tf.parameterList.length; @@ -3677,7 +3719,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, */ Type tv = arg.type.baseElemOf(); if (!isRef && tv.ty == Tstruct) - arg = doCopyOrMove(sc, arg, parameter ? parameter.type : null); + arg = doCopyOrMove(sc, arg, parameter ? parameter.type : null, false); } (*arguments)[i] = arg; @@ -3886,11 +3928,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("IdentifierExp::semantic('%s')\n", exp.ident.toChars()); } - if (exp.type) // This is used as the dummy expression - { - result = exp; - return; - } + + scope (exit) result.rvalue = exp.rvalue; Dsymbol scopesym; Dsymbol s = sc.search(exp.loc, exp.ident, scopesym); @@ -4109,11 +4148,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("ThisExp::semantic()\n"); } - if (e.type) - { - result = e; - return; - } FuncDeclaration fd = hasThis(sc); // fd is the uplevel function with the 'this' variable AggregateDeclaration ad; @@ -4174,11 +4208,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("SuperExp::semantic('%s')\n", e.toChars()); } - if (e.type) - { - result = e; - return; - } FuncDeclaration fd = hasThis(sc); ClassDeclaration cd; @@ -4255,11 +4284,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("NullExp::semantic('%s')\n", e.toChars()); } // NULL is the same as (void *)0 - if (e.type) - { - result = e; - return; - } e.type = Type.tnull; result = e; } @@ -4348,11 +4372,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("StringExp::semantic() %s\n", e.toChars()); } - if (e.type) - { - result = e; - return; - } OutBuffer buffer; size_t newlen = 0; @@ -4461,11 +4480,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("+TupleExp::semantic(%s)\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } if (exp.e0) exp.e0 = exp.e0.expressionSemantic(sc); @@ -4503,11 +4517,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("ArrayLiteralExp::semantic('%s')\n", e.toChars()); } - if (e.type) - { - result = e; - return; - } /* Perhaps an empty array literal [ ] should be rewritten as null? */ @@ -4550,11 +4559,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("AssocArrayLiteralExp::semantic('%s')\n", e.toChars()); } - if (e.type) - { - result = e; - return; - } // Run semantic() on each element bool err_keys = arrayExpressionSemantic(e.keys.peekSlice(), sc); @@ -4593,11 +4597,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("StructLiteralExp::semantic('%s')\n", e.toChars()); } - if (e.type) - { - result = e; - return; - } e.sd.size(e.loc); if (e.sd.sizeok != Sizeok.done) @@ -4702,11 +4701,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("+ScopeExp::semantic(%p '%s')\n", exp, exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } ScopeDsymbol sds2 = exp.sds; TemplateInstance ti = sds2.isTemplateInstance(); @@ -4895,11 +4889,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("\tthisexp = %s\n", exp.thisexp.toChars()); printf("\tnewtype: %s\n", exp.newtype.toChars()); } - if (exp.type) // if semantic() already run - { - result = exp; - return; - } //for error messages if the argument in [] is not convertible to size_t const originalNewtype = exp.newtype; @@ -5677,7 +5666,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor symtab = sds.symtab; } assert(symtab); - Identifier id = Identifier.generateIdWithLoc(s, exp.loc); + Identifier id = Identifier.generateIdWithLoc(s, exp.loc, cast(string) toDString(sc.parent.toPrettyChars())); exp.fd.ident = id; if (exp.td) exp.td.ident = id; @@ -5693,11 +5682,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf(" treq = %s\n", exp.fd.treq.toChars()); } - if (exp.type) - { - result = exp; - return; - } Expression e = exp; @@ -5891,11 +5875,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("CallExp::semantic() %s\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; // semantic() already run - } Objects* tiargs = null; // initial list of template arguments Expression ethis = null; @@ -6718,7 +6697,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor errorSupplemental(exp.loc, "%s", failMessage); } - if (tf.callMatch(null, exp.argumentList, 0, &errorHelper, sc) == MATCH.nomatch) + if (callMatch(exp.f, tf, null, exp.argumentList, 0, &errorHelper, sc) == MATCH.nomatch) return setError(); // Purity and safety check should run after testing arguments matching @@ -6801,7 +6780,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.f = null; } - if (tf.callMatch(null, exp.argumentList, 0, &errorHelper2, sc) == MATCH.nomatch) + if (callMatch(exp.f, tf, null, exp.argumentList, 0, &errorHelper2, sc) == MATCH.nomatch) exp.f = null; } if (!exp.f || exp.f.errors) @@ -6954,11 +6933,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(DeclarationExp e) { - if (e.type) - { - result = e; - return; - } static if (LOGSEMANTIC) { printf("DeclarationExp::semantic() %s\n", e.toChars()); @@ -7575,11 +7549,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(BinAssignExp exp) { - if (exp.type) - { - result = exp; - return; - } Expression e = exp.op_overload(sc); if (e) @@ -8157,6 +8126,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { import dmd.statementsem; + te.type = Type.tnoreturn; if (throwSemantic(te.loc, te.e1, sc)) result = te; else @@ -8168,7 +8138,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor static if (LOGSEMANTIC) { printf("DotIdExp::semantic(this = %p, '%s')\n", exp, exp.toChars()); - //printf("e1.op = %d, '%s'\n", e1.op, Token.toChars(e1.op)); + printAST(exp); } if (sc.inCfile) @@ -8251,11 +8221,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(DotTemplateExp e) { - if (e.type) - { - result = e; - return; - } if (Expression ex = unaSemantic(e, sc)) { result = ex; @@ -8272,11 +8237,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("DotVarExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } exp.var = exp.var.toAlias().isDeclaration(); @@ -8444,11 +8404,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("DotTemplateInstanceExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } // Indicate we need to resolve by UFCS. Expression e = exp.dotTemplateSemanticProp(sc, DotExpFlag.gag); if (!e) @@ -8464,11 +8419,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("DelegateExp::semantic('%s')\n", e.toChars()); } - if (e.type) - { - result = e; - return; - } e.e1 = e.e1.expressionSemantic(sc); @@ -8530,11 +8480,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("DotTypeExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } if (auto e = unaSemantic(exp, sc)) { @@ -8552,11 +8497,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("AddrExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } if (Expression ex = unaSemantic(exp, sc)) { @@ -8853,11 +8793,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("PtrExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } Expression e = exp.op_overload(sc); if (e) @@ -8916,11 +8851,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("NegExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } Expression e = exp.op_overload(sc); if (e) @@ -8962,7 +8892,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("UAddExp::semantic('%s')\n", exp.toChars()); } - assert(!exp.type); Expression e = exp.op_overload(sc); if (e) @@ -8989,11 +8918,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(ComExp exp) { - if (exp.type) - { - result = exp; - return; - } Expression e = exp.op_overload(sc); if (e) @@ -9031,11 +8955,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(NotExp e) { - if (e.type) - { - result = e; - return; - } e.setNoderefOperand(); @@ -9140,11 +9059,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("CastExp::semantic('%s')\n", exp.toChars()); } //static int x; assert(++x < 10); - if (exp.type) - { - result = exp; - return; - } if ((sc && sc.inCfile) && exp.to && (exp.to.ty == Tident || exp.to.ty == Tsarray) && @@ -9429,11 +9343,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("VectorExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } exp.e1 = exp.e1.expressionSemantic(sc); exp.type = exp.to.typeSemantic(exp.loc, sc); @@ -9502,11 +9411,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("SliceExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } // operator overloading should be handled in ArrayExp already. if (Expression ex = unaSemantic(exp, sc)) @@ -9789,11 +9693,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("ArrayLengthExp::semantic('%s')\n", e.toChars()); } - if (e.type) - { - result = e; - return; - } if (Expression ex = unaSemantic(e, sc)) { @@ -9812,7 +9711,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("ArrayExp::semantic('%s')\n", exp.toChars()); } - assert(!exp.type); if (sc.inCfile) { @@ -9886,11 +9784,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(CommaExp e) { //printf("Semantic.CommaExp() %s\n", e.toChars()); - if (e.type) - { - result = e; - return; - } // Allow `((a,b),(x,y))` if (e.allowCommaExp) @@ -9936,11 +9829,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("IntervalExp::semantic('%s')\n", e.toChars()); } - if (e.type) - { - result = e; - return; - } Expression le = e.lwr; le = le.expressionSemantic(sc); @@ -10015,11 +9903,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("IndexExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } // operator overloading should be handled in ArrayExp already. if (!exp.e1.type) @@ -10242,11 +10125,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("PostExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } if (sc.inCfile) { @@ -10404,9 +10282,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { static if (LOGSEMANTIC) { - if (exp.op == EXP.blit) printf("BlitExp.toElem('%s')\n", exp.toChars()); - if (exp.op == EXP.assign) printf("AssignExp.toElem('%s')\n", exp.toChars()); - if (exp.op == EXP.construct) printf("ConstructExp.toElem('%s')\n", exp.toChars()); + if (exp.op == EXP.blit) printf("BlitExp.semantic('%s')\n", exp.toChars()); + if (exp.op == EXP.assign) printf("AssignExp.semantic('%s')\n", exp.toChars()); + if (exp.op == EXP.construct) printf("ConstructExp.semantic('%s')\n", exp.toChars()); } void setResult(Expression e, int line = __LINE__) @@ -10415,11 +10293,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = e; } - if (exp.type) - { - return setResult(exp); - } - Expression e1old = exp.e1; if (auto e2comma = exp.e2.isCommaExp()) @@ -10876,6 +10749,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor /* We have a copy constructor for this */ + //printf("exp: %s\n", toChars(exp)); + //printf("e2x: %s\n", toChars(e2x)); if (e2x.isLvalue()) { if (sd.hasCopyCtor) @@ -10922,6 +10797,38 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } } + else if (sd.hasMoveCtor && !e2x.isCallExp() && !e2x.isStructLiteralExp()) + { + // #move + /* The !e2x.isCallExp() is because it is already an rvalue + and the move constructor is unnecessary: + struct S { + alias TT this; + long TT(); + this(T)(int x) {} + this(S); + this(ref S); + ~this(); + } + S fun(ref S arg); + void test() { S st; fun(st); } + */ + /* Rewrite as: + * e1 = init, e1.moveCtor(e2); + */ + Expression einit = new BlitExp(exp.loc, exp.e1, getInitExp(sd, exp.loc, sc, t1)); + einit.type = e1x.type; + + Expression e; + e = new DotIdExp(exp.loc, e1x, Id.ctor); + e = new CallExp(exp.loc, e, e2x); + e = new CommaExp(exp.loc, einit, e); + + //printf("e: %s\n", e.toChars()); + + result = e.expressionSemantic(sc); + return; + } else { /* The struct value returned from the function is transferred @@ -11815,11 +11722,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(PowAssignExp exp) { - if (exp.type) - { - result = exp; - return; - } Expression e = exp.op_overload(sc); if (e) @@ -11899,11 +11801,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(CatAssignExp exp) { - if (exp.type) - { - result = exp; - return; - } //printf("CatAssignExp::semantic() %s\n", exp.toChars()); Expression e = exp.op_overload(sc); @@ -11985,7 +11882,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor ce.trusted = true; exp = new CatElemAssignExp(exp.loc, exp.type, exp.e1, ecast); - exp.e2 = doCopyOrMove(sc, exp.e2); + exp.e2 = doCopyOrMove(sc, exp.e2, null, false); } else if (tb1.ty == Tarray && (tb1next.ty == Tchar || tb1next.ty == Twchar) && @@ -12195,11 +12092,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("AddExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } if (Expression ex = binSemanticProp(exp, sc)) { @@ -12302,11 +12194,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("MinExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } if (Expression ex = binSemanticProp(exp, sc)) { @@ -12554,11 +12441,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { // https://dlang.org/spec/expression.html#cat_expressions //printf("CatExp.semantic() %s\n", toChars()); - if (exp.type) - { - result = exp; - return; - } if (Expression ex = binSemanticProp(exp, sc)) { @@ -12610,7 +12492,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { if (exp.e1.op == EXP.arrayLiteral) { - exp.e2 = doCopyOrMove(sc, exp.e2); + exp.e2 = doCopyOrMove(sc, exp.e2, null, false); // https://issues.dlang.org/show_bug.cgi?id=14686 // Postblit call appears in AST, and this is // finally translated to an ArrayLiteralExp in below optimize(). @@ -12649,7 +12531,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { if (exp.e2.op == EXP.arrayLiteral) { - exp.e1 = doCopyOrMove(sc, exp.e1); + exp.e1 = doCopyOrMove(sc, exp.e1, null, false); } else if (exp.e2.op == EXP.string_) { @@ -12741,11 +12623,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("MulExp::semantic() %s\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } if (Expression ex = binSemanticProp(exp, sc)) { @@ -12841,11 +12718,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(DivExp exp) { - if (exp.type) - { - result = exp; - return; - } if (Expression ex = binSemanticProp(exp, sc)) { @@ -12942,11 +12814,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(ModExp exp) { - if (exp.type) - { - result = exp; - return; - } if (Expression ex = binSemanticProp(exp, sc)) { @@ -13000,11 +12867,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(PowExp exp) { - if (exp.type) - { - result = exp; - return; - } //printf("PowExp::semantic() %s\n", toChars()); if (Expression ex = binSemanticProp(exp, sc)) @@ -13080,11 +12942,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor private void visitShift(BinExp exp) { - if (exp.type) - { - result = exp; - return; - } if (Expression ex = binSemanticProp(exp, sc)) { @@ -13133,11 +12990,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor private void visitBinaryBitOp(BinExp exp) { - if (exp.type) - { - result = exp; - return; - } if (Expression ex = binSemanticProp(exp, sc)) { @@ -13206,11 +13058,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("LogicalExp::semantic() %s\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } exp.setNoderefOperands(); @@ -13292,11 +13139,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("CmpExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } exp.setNoderefOperands(); @@ -13461,11 +13303,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(InExp exp) { - if (exp.type) - { - result = exp; - return; - } if (Expression ex = binSemanticProp(exp, sc)) { @@ -13531,11 +13368,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(EqualExp exp) { //printf("EqualExp::semantic('%s')\n", exp.toChars()); - if (exp.type) - { - result = exp; - return; - } exp.setNoderefOperands(); @@ -13754,11 +13586,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(IdentityExp exp) { - if (exp.type) - { - result = exp; - return; - } exp.setNoderefOperands(); @@ -13822,11 +13649,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("CondExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } if (auto die = exp.econd.isDotIdExp()) die.noderef = true; @@ -14188,9 +14010,25 @@ Expression binSemanticProp(BinExp e, Scope* sc) return null; } +/// Returns: whether expressionSemantic() has been run on expression `e` +private bool expressionSemanticDone(Expression e) +{ + // Usually, Expression.type gets set by expressionSemantic and is `null` beforehand + // There are some exceptions however: + return e.type !is null && !( + e.isRealExp() // type sometimes gets set already before semantic + || e.isTypeExp() // stores its type in the Expression.type field + || e.isCompoundLiteralExp() // stores its `(type) {}` in type field, gets rewritten to struct literal + || e.isVarExp() // type sometimes gets set already before semantic + ); +} + // entrypoint for semantic ExpressionSemanticVisitor Expression expressionSemantic(Expression e, Scope* sc) { + if (e.expressionSemanticDone) + return e; + scope v = new ExpressionSemanticVisitor(sc); e.accept(v); return v.result; @@ -14198,7 +14036,7 @@ Expression expressionSemantic(Expression e, Scope* sc) private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc) { - //printf("DotIdExp::semanticX(this = %p, '%s')\n", this, toChars()); + //printf("dotIdSemanticPropX() %s\n", toChars(exp)); if (Expression ex = unaSemantic(exp, sc)) return ex; @@ -14326,7 +14164,7 @@ private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc) */ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) { - //printf("DotIdExp::semanticY(this = %p, '%s')\n", exp, exp.toChars()); + //printf("dotIdSemanticProp('%s')\n", exp.toChars()); //{ static int z; fflush(stdout); if (++z == 10) *(char*)0=0; } @@ -14664,7 +14502,7 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) const flag = cast(DotExpFlag) (exp.noderef * DotExpFlag.noDeref | gag * DotExpFlag.gag); - Expression e = exp.e1.type.dotExp(sc, exp.e1, exp.ident, flag); + Expression e = dotExp(exp.e1.type, sc, exp.e1, exp.ident, flag); if (e) { e = e.expressionSemantic(sc); @@ -15482,6 +15320,7 @@ Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc) */ Expression addDtorHook(Expression e, Scope* sc) { + //printf("addDtorHook() %s\n", toChars(e)); Expression visit(Expression exp) { return exp; @@ -16510,7 +16349,7 @@ private bool fit(StructDeclaration sd, const ref Loc loc, Scope* sc, Expressions if (e.op == EXP.error) return false; - (*elements)[i] = doCopyOrMove(sc, e); + (*elements)[i] = doCopyOrMove(sc, e, null, false); } return true; } diff --git a/gcc/d/dmd/func.d b/gcc/d/dmd/func.d index 153befdb9a98b4cdf281e3ae0adcd74af7e0bdae..9c5a3d0bbb9691cddc97a16be69dc749cc20edc5 100644 --- a/gcc/d/dmd/func.d +++ b/gcc/d/dmd/func.d @@ -269,6 +269,10 @@ extern (C++) class FuncDeclaration : Declaration */ VarDeclarations outerVars; + // Most recent encountered `main` (`WinMain` or `DllMain`) function. + // Track it to give error messages for multiple entrypoints + __gshared FuncDeclaration lastMain; + /// Sibling nested functions which called this one FuncDeclarations siblingCallers; @@ -1366,11 +1370,13 @@ extern (C++) final class FuncLiteralDeclaration : FuncDeclaration */ extern (C++) final class CtorDeclaration : FuncDeclaration { - bool isCpCtor; - extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, Type type, bool isCpCtor = false) + bool isCpCtor; // copy constructor + bool isMoveCtor; // move constructor (aka rvalue constructor) + extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, Type type, bool isCpCtor = false, bool isMoveCtor = false) { super(loc, endloc, Id.ctor, stc, type); this.isCpCtor = isCpCtor; + this.isMoveCtor = isMoveCtor; //printf("CtorDeclaration(loc = %s) %s %p\n", loc.toChars(), toChars(), this); } diff --git a/gcc/d/dmd/funcsem.d b/gcc/d/dmd/funcsem.d index bfa0faca60f315580f881c2d3fc8b8d1e70d032c..eba93973a48e9cfc405203388237263d62475417 100644 --- a/gcc/d/dmd/funcsem.d +++ b/gcc/d/dmd/funcsem.d @@ -154,6 +154,28 @@ public: } } +/**************************************** + * Only one entry point function is allowed. Print error if more than one. + * Params: + * fd = a "main" function + * Returns: + * true if haven't seen "main" before + */ +extern (C++) bool onlyOneMain(FuncDeclaration fd) +{ + if (auto lastMain = FuncDeclaration.lastMain) + { + const format = (target.os == Target.OS.Windows) + ? "only one entry point `main`, `WinMain` or `DllMain` is allowed" + : "only one entry point `main` is allowed"; + error(fd.loc, format.ptr); + errorSupplemental(lastMain.loc, "previously found `%s` here", lastMain.toFullSignature()); + return false; + } + FuncDeclaration.lastMain = fd; + return true; +} + /********************************** * Main semantic routine for functions. */ @@ -1507,6 +1529,7 @@ enum FuncResolveFlag : ubyte FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, Objects* tiargs, Type tthis, ArgumentList argumentList, FuncResolveFlag flags) { + //printf("resolveFuncCall() %s\n", s.toChars()); auto fargs = argumentList.arguments; if (!s) return null; // no match @@ -2066,7 +2089,7 @@ MATCH leastAsSpecialized(FuncDeclaration f, FuncDeclaration g, Identifiers* name args.push(e); } - MATCH m = tg.callMatch(null, ArgumentList(&args, names), 1); + MATCH m = callMatch(g, tg, null, ArgumentList(&args, names), 1); if (m > MATCH.nomatch) { /* A variadic parameter list is less specialized than a @@ -2939,6 +2962,7 @@ extern (D) void checkMain(FuncDeclaration fd) */ extern (D) bool checkNRVO(FuncDeclaration fd) { + //printf("checkNRVO*() %s\n", fd.ident.toChars()); if (!fd.isNRVO() || fd.returns is null) return false; diff --git a/gcc/d/dmd/globals.d b/gcc/d/dmd/globals.d index 88b27d20bb54ef6c00e7229883a39f87a9d63d8b..901561fcf8e1561d1ded488b260ed08b5868ad62 100644 --- a/gcc/d/dmd/globals.d +++ b/gcc/d/dmd/globals.d @@ -82,6 +82,13 @@ enum CLIIdentifierTable : ubyte All = 4, /// The least restrictive set of all other tables } +/// Specifies the mode for error printing +enum ErrorPrintMode : ubyte +{ + simpleError, // Print errors without squiggles and carets + printErrorContext, // Print errors with context (source line and caret) +} + extern(C++) struct Output { bool doOutput; // Output is enabled @@ -126,10 +133,10 @@ extern(C++) struct Verbose bool complex = true; // identify complex/imaginary type usage bool vin; // identify 'in' parameters bool showGaggedErrors; // print gagged errors anyway - bool printErrorContext; // print errors with the error context (the error line in the source file) bool logo; // print compiler logo bool color; // use ANSI colors in console output bool cov; // generate code coverage data + ErrorPrintMode errorPrintMode; // enum for error printing mode MessageStyle messageStyle = MessageStyle.digitalmars; // style of file/line annotations on messages uint errorLimit = 20; uint errorSupplementLimit = 6; // Limit the number of supplemental messages for each error (0 means unlimited) diff --git a/gcc/d/dmd/globals.h b/gcc/d/dmd/globals.h index c5659ea10b6268f03cd62f2c65d161a813e5fe08..669f83e2101f0118d02559ddd9d143a30399c57c 100644 --- a/gcc/d/dmd/globals.h +++ b/gcc/d/dmd/globals.h @@ -94,6 +94,13 @@ enum class CLIIdentifierTable : unsigned char All = 4, /// The least restrictive set of all other tables }; +/// Specifies the mode for error printing +enum class ErrorPrintMode : unsigned char +{ + simpleError, // Print errors without squiggles and carets + printErrorContext, // Print errors with the error line and caret +}; + struct Output { /// Configuration for the compiler generator @@ -138,10 +145,10 @@ struct Verbose d_bool complex = true; // identify complex/imaginary type usage d_bool vin; // identify 'in' parameters d_bool showGaggedErrors; // print gagged errors anyway - d_bool printErrorContext; // print errors with the error context (the error line in the source file) d_bool logo; // print compiler logo d_bool color; // use ANSI colors in console output d_bool cov; // generate code coverage data + ErrorPrintMode errorPrintMode; // enum for error printing mode MessageStyle messageStyle; // style of file/line annotations on messages unsigned errorLimit; unsigned errorSupplementLimit; // Limit the number of supplemental messages for each error (0 means unlimited) @@ -192,7 +199,7 @@ struct Param // https://digitalmars.com/d/archives/digitalmars/D/Binding_rvalues_to_ref_parameters_redux_325087.html // Implementation: https://github.com/dlang/dmd/pull/9817 FeatureState safer; // safer by default (more @safe checks in unattributed code) - // https://github.com/WalterBright/documents/blob/38f0a846726b571f8108f6e63e5e217b91421c86/safer.md + // https://github.com/WalterBright/documents/blob/38f0a846726b571f8108f6e63e5e217b91421c86/safer.md FeatureState noSharedAccess; // read/write access to shared memory objects d_bool previewIn; // `in` means `[ref] scope const`, accepts rvalues diff --git a/gcc/d/dmd/hdrgen.d b/gcc/d/dmd/hdrgen.d index 86131f2f11c96ed239a999fbe9b20c7afff008ab..cfe4262ce44ffc3bc5dea892397f71445b127ad1 100644 --- a/gcc/d/dmd/hdrgen.d +++ b/gcc/d/dmd/hdrgen.d @@ -305,10 +305,9 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h buf.writenl(); } - void visitWhile(WhileStatement s) + void printConditionAssignment(Parameter p, Expression condition) { - buf.writestring("while ("); - if (auto p = s.param) + if (p) { // Print condition assignment StorageClass stc = p.storageClass; @@ -322,7 +321,13 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h buf.writestring(p.ident.toString()); buf.writestring(" = "); } - s.condition.expressionToBuffer(buf, hgs); + condition.expressionToBuffer(buf, hgs); + } + + void visitWhile(WhileStatement s) + { + buf.writestring("while ("); + printConditionAssignment(s.param, s.condition); buf.writeByte(')'); buf.writenl(); if (s._body) @@ -460,20 +465,7 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h void visitIf(IfStatement s) { buf.writestring("if ("); - if (Parameter p = s.prm) - { - StorageClass stc = p.storageClass; - if (!p.type && !stc) - stc = STC.auto_; - if (stcToBuffer(buf, stc)) - buf.writeByte(' '); - if (p.type) - typeToBuffer(p.type, p.ident, buf, hgs); - else - buf.writestring(p.ident.toString()); - buf.writestring(" = "); - } - s.condition.expressionToBuffer(buf, hgs); + printConditionAssignment(s.prm, s.condition); buf.writeByte(')'); buf.writenl(); if (s.ifbody.isScopeStatement()) @@ -572,21 +564,7 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h void visitSwitch(SwitchStatement s) { buf.writestring(s.isFinal ? "final switch (" : "switch ("); - if (auto p = s.param) - { - // Print condition assignment - StorageClass stc = p.storageClass; - if (!p.type && !stc) - stc = STC.auto_; - if (stcToBuffer(buf, stc)) - buf.writeByte(' '); - if (p.type) - typeToBuffer(p.type, p.ident, buf, hgs); - else - buf.writestring(p.ident.toString()); - buf.writestring(" = "); - } - s.condition.expressionToBuffer(buf, hgs); + printConditionAssignment(s.param, s.condition); buf.writeByte(')'); buf.writenl(); if (s._body) @@ -1718,7 +1696,10 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) void visitFuncDeclaration(FuncDeclaration f) { //printf("FuncDeclaration::toCBuffer() '%s'\n", f.toChars()); - if (stcToBuffer(buf, f.storage_class)) + + // https://issues.dlang.org/show_bug.cgi?id=24891 + // return/scope storage classes are printed as part of function type + if (stcToBuffer(buf, f.storage_class & ~(STC.scope_ | STC.return_ | STC.returnScope))) buf.writeByte(' '); typeToBuffer(f.type, f.ident, buf, hgs); auto tf = f.type.isTypeFunction(); @@ -2888,6 +2869,13 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt buf.writestring(e.value.toChars()); } + if (e.rvalue) + buf.writestring("__rvalue("); + + scope (exit) + if (e.rvalue) + buf.writeByte(')'); + switch (e.op) { default: @@ -2929,7 +2917,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt case EXP.typeid_: return visitTypeid(e.isTypeidExp()); case EXP.traits: return visitTraits(e.isTraitsExp()); case EXP.halt: return visitHalt(e.isHaltExp()); - case EXP.is_: return visitIs(e.isExp()); + case EXP.is_: return visitIs(e.isIsExp()); case EXP.comma: return visitComma(e.isCommaExp()); case EXP.mixin_: return visitMixin(e.isMixinExp()); case EXP.import_: return visitImport(e.isImportExp()); diff --git a/gcc/d/dmd/id.d b/gcc/d/dmd/id.d index aae07bc15370371555ba0f888081a5ced1338f7d..ee4214a4c123498935e5643f4381118a10f17941 100644 --- a/gcc/d/dmd/id.d +++ b/gcc/d/dmd/id.d @@ -519,6 +519,7 @@ immutable Msgtable[] msgtable = { "getLocation" }, { "hasPostblit" }, { "hasCopyConstructor" }, + { "hasMoveConstructor" }, { "isCopyable" }, { "toType" }, { "parameters" }, diff --git a/gcc/d/dmd/identifier.d b/gcc/d/dmd/identifier.d index 6fd0d3ad5ec6bde95c839c9b24791390c1bfd80f..74be1beb29f91cc33b4cc44a9982d5025a11ecb9 100644 --- a/gcc/d/dmd/identifier.d +++ b/gcc/d/dmd/identifier.d @@ -211,11 +211,14 @@ nothrow: * Params: * prefix = first part of the identifier name. * loc = source location to use in the identifier name. + * parent = (optional) extra part to be used in uniqueness check, + * if (prefix1, loc1) == (prefix2, loc2), but + * parent1 != parent2, no new name will be generated. * Returns: * Identifier (inside Identifier.idPool) with deterministic name based * on the source location. */ - extern (D) static Identifier generateIdWithLoc(string prefix, const ref Loc loc) + extern (D) static Identifier generateIdWithLoc(string prefix, const ref Loc loc, string parent = "") { // generate `<prefix>_L<line>_C<col>` OutBuffer idBuf; @@ -234,14 +237,20 @@ nothrow: * https://issues.dlang.org/show_bug.cgi?id=18880 * https://issues.dlang.org/show_bug.cgi?id=18868 * https://issues.dlang.org/show_bug.cgi?id=19058 + * + * It is a bit trickier for lambdas/dgliterals: we want them to be unique per + * module/mixin + function/template instantiation context. So we use extra parent + * argument for that when dealing with lambdas. We could have added it to prefix + * directly, but that would unnecessary lengthen symbols names. See issue: + * https://issues.dlang.org/show_bug.cgi?id=23722 */ - static struct Key { Loc loc; string prefix; } + static struct Key { Loc loc; string prefix; string parent; } __gshared uint[Key] counters; static if (__traits(compiles, counters.update(Key.init, () => 0u, (ref uint a) => 0u))) { // 2.082+ - counters.update(Key(loc, prefix), + counters.update(Key(loc, prefix, parent), () => 1u, // insertion (ref uint counter) // update { @@ -253,7 +262,7 @@ nothrow: } else { - const key = Key(loc, prefix); + const key = Key(loc, prefix, parent); if (auto pCounter = key in counters) { idBuf.writestring("_"); diff --git a/gcc/d/dmd/initsem.d b/gcc/d/dmd/initsem.d index 505a3e13636fb81fb3fccbdd29f80cd3fc93fb28..0c13bc70dadc4c8d5f15fce615ea0cd92c9293cb 100644 --- a/gcc/d/dmd/initsem.d +++ b/gcc/d/dmd/initsem.d @@ -1630,7 +1630,7 @@ Expressions* resolveStructLiteralNamedArgs(StructDeclaration sd, Type t, Scope* continue; } - elems[fieldi] = doCopyOrMove(sc, ex); + elems[fieldi] = doCopyOrMove(sc, ex, null, false); ++fieldi; } if (errors) diff --git a/gcc/d/dmd/json.d b/gcc/d/dmd/json.d index 021387d6f4380eda4c3870b9ac6bd19a86385740..174a2d95cd2005bf2560f763f8e8f725d3546cb2 100644 --- a/gcc/d/dmd/json.d +++ b/gcc/d/dmd/json.d @@ -42,7 +42,8 @@ import dmd.target; import dmd.visitor; version(Windows) { - extern (C) char* getcwd(char* buffer, size_t maxlen); + extern (C) char* _getcwd(char* buffer, size_t maxlen); + alias getcwd = _getcwd; } else { import core.sys.posix.unistd : getcwd; } diff --git a/gcc/d/dmd/mtype.d b/gcc/d/dmd/mtype.d index 6d57467314ce04606a76d1fc20f543b51ab9c8c1..4a4254fcbdd3798988d53634aecb78afa52223d5 100644 --- a/gcc/d/dmd/mtype.d +++ b/gcc/d/dmd/mtype.d @@ -1609,7 +1609,7 @@ extern (C++) abstract class TypeNext : Type /******************************* * For TypeFunction, nextOf() can return NULL if the function return - * type is meant to be inferred, and semantic() hasn't yet ben run + * type is meant to be inferred, and semantic() hasn't yet been run * on the function. After semantic(), it must no longer be NULL. */ override final Type nextOf() @safe @@ -3543,17 +3543,19 @@ extern (C++) final class TypeTuple : Type extern (D) this(Expressions* exps) { super(Ttuple); - auto arguments = new Parameters(exps ? exps.length : 0); - if (exps) + if (!exps) { - for (size_t i = 0; i < exps.length; i++) - { - Expression e = (*exps)[i]; - if (e.type.ty == Ttuple) - error(e.loc, "cannot form sequence of sequences"); - auto arg = new Parameter(e.loc, STC.undefined_, e.type, null, null, null); - (*arguments)[i] = arg; - } + this.arguments = new Parameters(0); + return; + } + auto arguments = new Parameters(exps.length); + + for (size_t i = 0; i < exps.length; i++) + { + Expression e = (*exps)[i]; + assert(e.type.ty != Ttuple); + auto arg = new Parameter(e.loc, STC.undefined_, e.type, null, null, null); + (*arguments)[i] = arg; } this.arguments = arguments; //printf("TypeTuple() %p, %s\n", this, toChars()); diff --git a/gcc/d/dmd/mtype.h b/gcc/d/dmd/mtype.h index eced43eeacff3a14a236b634f172d42a671fa212..e69d2ba5fbaa9e436d4592780a778e0f0a4b8649 100644 --- a/gcc/d/dmd/mtype.h +++ b/gcc/d/dmd/mtype.h @@ -469,7 +469,7 @@ enum RET enum class TRUST : unsigned char { default_ = 0, - system = 1, // @system (same as TRUSTdefault) + system = 1, // @system (same as TRUST.default_ unless feature "safer" is enabled) trusted = 2, // @trusted safe = 3 // @safe }; diff --git a/gcc/d/dmd/objc.d b/gcc/d/dmd/objc.d index 5cd928ca5f41f8ec28d1fefd8ae46f5f35637ecb..c5ee77ae791e53a282dad3919da988240e8e47e0 100644 --- a/gcc/d/dmd/objc.d +++ b/gcc/d/dmd/objc.d @@ -93,39 +93,66 @@ struct ObjcSelector return sel; } + static const(char)[] toPascalCase(const(char)[] id) { + OutBuffer buf; + char firstChar = id[0]; + if (firstChar >= 'a' && firstChar <= 'z') + firstChar = cast(char)(firstChar - 'a' + 'A'); + + buf.writeByte(firstChar); + buf.writestring(id[1..$]); + return cast(const(char)[])buf.extractSlice(false); + } + extern (C++) static ObjcSelector* create(FuncDeclaration fdecl) { OutBuffer buf; auto ftype = cast(TypeFunction)fdecl.type; const id = fdecl.ident.toString(); const nparams = ftype.parameterList.length; + // Special case: property setter if (ftype.isProperty && nparams == 1) { - // rewrite "identifier" as "setIdentifier" - char firstChar = id[0]; - if (firstChar >= 'a' && firstChar <= 'z') - firstChar = cast(char)(firstChar - 'a' + 'A'); - buf.writestring("set"); - buf.writeByte(firstChar); - buf.write(id[1 .. id.length - 1]); + + // Special case: "isXYZ:" + if (id.length >= 2 && id[0..2] == "is") + { + buf.writestring("set"); + buf.write(toPascalCase(id[2..$])); + } + else + { + buf.writestring("set"); + buf.write(toPascalCase(id)); + } buf.writeByte(':'); goto Lcomplete; } + // write identifier in selector buf.write(id[]); - // add mangled type and colon for each parameter - if (nparams) + + // To make it easier to match the selectors of objects nicely, + // the implementation has been replaced so that the parameter name followed by a colon + // is used instead. + // eg. void myFunction(int a, int b, int c) would be mangled to a selector as `myFunction:b:c: + if (nparams > 1) { - buf.writeByte('_'); - foreach (i, fparam; ftype.parameterList) + buf.writeByte(':'); + foreach(i; 1..nparams) { - mangleToBuffer(fparam.type, buf); + buf.write(ftype.parameterList[i].ident.toString()); buf.writeByte(':'); } } + else if (nparams == 1) + { + buf.writeByte(':'); + } Lcomplete: buf.writeByte('\0'); + // the slice is not expected to include a terminating 0 return lookup(cast(const(char)*)buf[].ptr, buf.length - 1, nparams); } @@ -565,6 +592,16 @@ extern(C++) private final class Supported : Objc return 0; }); + + // Avoid attempting to generate selectors for template instances. + if (fd.parent && fd.parent.isTemplateInstance()) + return; + + // No selector declared, generate one. + if (fd._linkage == LINK.objc && !fd.objc.selector) + { + fd.objc.selector = ObjcSelector.create(fd); + } } override void validateSelector(FuncDeclaration fd) diff --git a/gcc/d/dmd/parse.d b/gcc/d/dmd/parse.d index 3e145be5499ff8de3bbab926d485190cede09535..fd428389f618f7c55e33de79c04ff101ed852ed6 100644 --- a/gcc/d/dmd/parse.d +++ b/gcc/d/dmd/parse.d @@ -932,22 +932,14 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { const attrLoc = token.loc; - nextToken(); - - AST.Expression e = null; // default - if (token.value == TOK.leftParenthesis) - { - nextToken(); - e = parseAssignExp(); - check(TOK.rightParenthesis); - } + AST.Expression e = parseAlign(); if (pAttrs.setAlignment) { if (e) error("redundant alignment attribute `align(%s)`", e.toChars()); else - error("redundant alignment attribute `align`"); + error("redundant alignment attribute `align(default)`"); } pAttrs.setAlignment = true; @@ -4371,14 +4363,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } case TOK.align_: { - nextToken(); setAlignment = true; - if (token.value == TOK.leftParenthesis) - { - nextToken(); - ealign = parseExpression(); - check(TOK.rightParenthesis); - } + ealign = parseAlign(); continue; } default: @@ -4388,6 +4374,27 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } } + /** + * Parse `align` or `align(n)` + * Returns: + * expression `n` if it is present, or `null` otherwise. + */ + private AST.Expression parseAlign() + { + assert(token.value == TOK.align_); + AST.Expression e = null; + nextToken(); + if (token.value == TOK.leftParenthesis) + { + nextToken(); + if (token.value == TOK.default_) + nextToken(); + else + e = parseAssignExp(); + check(TOK.rightParenthesis); + } + return e; + } /********************************** * Parse Declarations. * These can be: @@ -5252,7 +5259,10 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer error("missing `do { ... }` after `in` or `out`"); const returnloc = token.loc; nextToken(); - f.fbody = new AST.ReturnStatement(returnloc, parseExpression()); + if (f.isCtorDeclaration) + f.fbody = new AST.ExpStatement(returnloc, parseExpression()); + else + f.fbody = new AST.ReturnStatement(returnloc, parseExpression()); f.endloc = token.loc; check(TOK.semicolon); break; @@ -5877,6 +5887,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.moduleString: case TOK.functionString: case TOK.prettyFunction: + case TOK.rvalue: Lexp: { AST.Expression exp = parseExpression(); @@ -8423,6 +8434,15 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer e = new AST.TypeidExp(loc, o); break; } + case TOK.rvalue: + { + nextToken(); + check(TOK.leftParenthesis, "`__rvalue`"); + e = parseAssignExp(); + e.rvalue = true; + check(TOK.rightParenthesis); + break; + } case TOK.traits: { /* __traits(identifier, args...) diff --git a/gcc/d/dmd/printast.d b/gcc/d/dmd/printast.d index 02dc65390b03c0b5e4f12e05c748e63deb99f754..26d27707c169741d3f7f44e56739abd3a4910a41 100644 --- a/gcc/d/dmd/printast.d +++ b/gcc/d/dmd/printast.d @@ -51,6 +51,12 @@ extern (C++) final class PrintASTVisitor : Visitor printf("%.*s %s\n", cast(int)s.length, s.ptr, e.type ? e.type.toChars() : ""); } + override void visit(IdentifierExp e) + { + printIndent(indent); + printf("Identifier `%s` %s\n", e.ident.toChars(), e.type ? e.type.toChars() : ""); + } + override void visit(IntegerExp e) { printIndent(indent); diff --git a/gcc/d/dmd/root/filename.d b/gcc/d/dmd/root/filename.d index a2f074dbfaec0167c19a52a091cfcb6cdf46ee07..cf00d8ad7f63257dbb8e1a0e973291d3f369e401 100644 --- a/gcc/d/dmd/root/filename.d +++ b/gcc/d/dmd/root/filename.d @@ -42,7 +42,8 @@ version (Windows) extern (Windows) DWORD GetFullPathNameW(LPCWSTR, DWORD, LPWSTR, LPWSTR*) nothrow @nogc; extern (Windows) void SetLastError(DWORD) nothrow @nogc; - extern (C) char* getcwd(char* buffer, size_t maxlen) nothrow; + extern (C) char* _getcwd(char* buffer, size_t maxlen) nothrow; + alias getcwd = _getcwd; } version (CRuntime_Glibc) diff --git a/gcc/d/dmd/scope.h b/gcc/d/dmd/scope.h index 7983a7ac93f2ed5c1905cd67ccfb44289e34e5ba..ac112664ae8acb7b66cdcd11766d53b080d31866 100644 --- a/gcc/d/dmd/scope.h +++ b/gcc/d/dmd/scope.h @@ -143,6 +143,7 @@ struct Scope final AliasDeclaration *aliasAsg; // if set, then aliasAsg is being assigned a new value, // do not set wasRead for it + StructDeclaration *argStruct; // elimiate recursion when looking for rvalue construction Dsymbol *search(const Loc &loc, Identifier *ident, Dsymbol *&pscopesym, SearchOptFlags flags = (SearchOptFlags)SearchOpt::all); }; diff --git a/gcc/d/dmd/semantic3.d b/gcc/d/dmd/semantic3.d index 4f10982a2d1cfd524a87a1e79090eece335cde15..1ea1ca12950f1bf3b3a5b12aee9564c0ca3dc24a 100644 --- a/gcc/d/dmd/semantic3.d +++ b/gcc/d/dmd/semantic3.d @@ -940,7 +940,7 @@ private extern(C++) final class Semantic3Visitor : Visitor * If NRVO is not possible, all returned lvalues should call their postblits. */ if (!funcdecl.isNRVO()) - exp = doCopyOrMove(sc2, exp, f.next); + exp = doCopyOrMove(sc2, exp, f.next, true, true); if (tret.hasPointers()) checkReturnEscape(*sc2, exp, false); diff --git a/gcc/d/dmd/statementsem.d b/gcc/d/dmd/statementsem.d index 8138bd2eb952711efe458d98d6ae7b93f0547b6a..d259abf47b0ee5444f1a5f63f699831a317d400d 100644 --- a/gcc/d/dmd/statementsem.d +++ b/gcc/d/dmd/statementsem.d @@ -2460,7 +2460,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) /* https://dlang.org/spec/statement.html#return-statement */ - //printf("ReturnStatement.dsymbolSemantic() %p, %s\n", rs, rs.toChars()); + //printf("ReturnStatement.dsymbolSemantic() %s\n", toChars(rs)); FuncDeclaration fd = sc.parent.isFuncDeclaration(); if (fd.fes) diff --git a/gcc/d/dmd/templatesem.d b/gcc/d/dmd/templatesem.d index 0e9c4338a05203214b4b37beaa042e99ce7f8605..8be9730dda5b8b05e38526fd04bc937b2b1fd722 100644 --- a/gcc/d/dmd/templatesem.d +++ b/gcc/d/dmd/templatesem.d @@ -623,7 +623,7 @@ MATCH leastAsSpecialized(Scope* sc, TemplateDeclaration td, TemplateDeclaration enum LOG_LEASTAS = 0; static if (LOG_LEASTAS) { - printf("%s.leastAsSpecialized(%s)\n", toChars(), td2.toChars()); + printf("%s.leastAsSpecialized(%s)\n", td.toChars(), td2.toChars()); } /* This works by taking the template parameters to this template @@ -1890,23 +1890,22 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, { version (none) { - printf("functionResolve() dstart = %s\n", dstart.toChars()); - printf(" tiargs:\n"); - if (tiargs) + printf("functionResolve() dstart: %s (", dstart.toChars()); + for (size_t i = 0; i < (tiargs ? (*tiargs).length : 0); i++) { - for (size_t i = 0; i < tiargs.length; i++) - { - RootObject arg = (*tiargs)[i]; - printf("\t%s\n", arg.toChars()); - } + if (i) printf(", "); + RootObject arg = (*tiargs)[i]; + printf("%s", arg.toChars()); } - printf(" fargs:\n"); - for (size_t i = 0; i < (fargs ? fargs.length : 0); i++) + printf(")("); + for (size_t i = 0; i < (argumentList.arguments ? (*argumentList.arguments).length : 0); i++) { - Expression arg = (*fargs)[i]; - printf("\t%s %s\n", arg.type.toChars(), arg.toChars()); + if (i) printf(", "); + Expression arg = (*argumentList.arguments)[i]; + printf("%s %s", arg.type.toChars(), arg.toChars()); //printf("\tty = %d\n", arg.type.ty); } + printf(")\n"); //printf("stc = %llx\n", dstart._scope.stc); //printf("match:t/f = %d/%d\n", ta_last, m.last); } @@ -1993,7 +1992,7 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, tf.mod = tthis_fd.mod; } const(char)* failMessage; - MATCH mfa = tf.callMatch(tthis_fd, argumentList, 0, errorHelper, sc); + MATCH mfa = callMatch(fd, tf, tthis_fd, argumentList, 0, errorHelper, sc); //printf("test1: mfa = %d\n", mfa); if (failMessage) errorHelper(failMessage); @@ -2198,7 +2197,7 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, Type tthis_fd = fd.needThis() && !fd.isCtorDeclaration() ? tthis : null; auto tf = fd.type.isTypeFunction(); - MATCH mfa = tf.callMatch(tthis_fd, argumentList, 0, null, sc); + MATCH mfa = callMatch(fd, tf, tthis_fd, argumentList, 0, null, sc); if (mfa < m.last) return 0; @@ -2300,8 +2299,8 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, // Disambiguate by tf.callMatch auto tf1 = fd.type.isTypeFunction(); auto tf2 = m.lastf.type.isTypeFunction(); - MATCH c1 = tf1.callMatch(tthis_fd, argumentList, 0, null, sc); - MATCH c2 = tf2.callMatch(tthis_best, argumentList, 0, null, sc); + MATCH c1 = callMatch(fd, tf1, tthis_fd, argumentList, 0, null, sc); + MATCH c2 = callMatch(m.lastf, tf2, tthis_best, argumentList, 0, null, sc); //printf("2: c1 = %d, c2 = %d\n", c1, c2); if (c1 > c2) goto Ltd; if (c1 < c2) goto Ltd_best; @@ -2404,7 +2403,7 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, if (m.lastf.type.ty == Terror) goto Lerror; auto tf = m.lastf.type.isTypeFunction(); - if (!tf.callMatch(tthis_best, argumentList, 0, null, sc)) + if (callMatch(m.lastf, tf, tthis_best, argumentList, 0, null, sc) == MATCH.nomatch) goto Lnomatch; /* As https://issues.dlang.org/show_bug.cgi?id=3682 shows, diff --git a/gcc/d/dmd/tokens.d b/gcc/d/dmd/tokens.d index da4a3ee209ef1f49116131d0645ab3d3e7d6a37e..b499c008eab30e608d639d38cd398718b725d39d 100644 --- a/gcc/d/dmd/tokens.d +++ b/gcc/d/dmd/tokens.d @@ -27,6 +27,9 @@ enum TOK : ubyte { reserved, + // if this list changes, update + // tokens.h, ../tests/cxxfrontend.cc and ../../test/unit/lexer/location_offset.d to match + // Other leftParenthesis, rightParenthesis, @@ -249,6 +252,7 @@ enum TOK : ubyte wchar_tLiteral, endOfLine, // \n, \r, \u2028, \u2029 whitespace, + rvalue, // C only keywords inline, @@ -425,6 +429,7 @@ enum EXP : ubyte interval, loweredAssignExp, + rvalue, } enum FirstCKeyword = TOK.inline; @@ -556,6 +561,7 @@ private immutable TOK[] keywords = TOK.prettyFunction, TOK.shared_, TOK.immutable_, + TOK.rvalue, // C only keywords TOK.inline, @@ -680,6 +686,7 @@ extern (C++) struct Token TOK.pragma_: "pragma", TOK.typeof_: "typeof", TOK.typeid_: "typeid", + TOK.rvalue: "__rvalue", TOK.template_: "template", TOK.void_: "void", TOK.int8: "byte", diff --git a/gcc/d/dmd/tokens.h b/gcc/d/dmd/tokens.h index 929897a3fa6f6e4962de90e170dc3b9978a6a114..2a984b4b7b621cf016dca07b6a428ab2a965ea4f 100644 --- a/gcc/d/dmd/tokens.h +++ b/gcc/d/dmd/tokens.h @@ -258,6 +258,7 @@ enum class TOK : unsigned char wchar_tLiteral, endOfLine, // \n, \r, \u2028, \u2029 whitespace, + rvalue, // C only keywords inline_, diff --git a/gcc/d/dmd/traits.d b/gcc/d/dmd/traits.d index f7f4cd2bb5e235a9ac258bdbf579ed8a774cb611..34cdf816eb0f801f6f09d70d274c500bed4581d7 100644 --- a/gcc/d/dmd/traits.d +++ b/gcc/d/dmd/traits.d @@ -544,7 +544,9 @@ Expression semanticTraits(TraitsExp e, Scope* sc) } return True(); } - if (e.ident == Id.hasCopyConstructor || e.ident == Id.hasPostblit) + if (e.ident == Id.hasCopyConstructor || + e.ident == Id.hasMoveConstructor || + e.ident == Id.hasPostblit) { if (dim != 1) return dimError(1); @@ -562,8 +564,14 @@ Expression semanticTraits(TraitsExp e, Scope* sc) auto ts = tb.isTypeStruct(); if (auto sd = ts ? ts.sym : null) { - return (e.ident == Id.hasPostblit) ? (sd.postblit ? True() : False()) - : (sd.hasCopyCtor ? True() : False()); + bool result; + if (e.ident == Id.hasPostblit) + result = sd.postblit !is null; + else if (e.ident == Id. hasCopyConstructor) + result = sd.hasCopyCtor; + else + result = sd.hasMoveCtor; + return result ? True() : False(); } return False(); } diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d index aea969a2e08df4746c8d93b578111cdd695182c7..2ec88f2363a0dc4d8dccb5fed9e0c77568ce7e7c 100644 --- a/gcc/d/dmd/typesem.d +++ b/gcc/d/dmd/typesem.d @@ -681,6 +681,7 @@ extern (D) bool checkComplexTransition(Type type, const ref Loc loc, Scope* sc) * 'args' are being matched to function type 'tf' * Determine match level. * Params: + * fd = function being called, if a symbol * tf = function type * tthis = type of `this` pointer, null if not member function * argumentList = arguments to function call @@ -690,9 +691,10 @@ extern (D) bool checkComplexTransition(Type type, const ref Loc loc, Scope* sc) * Returns: * MATCHxxxx */ -extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentList, int flag = 0, void delegate(const(char)*) scope errorHelper = null, Scope* sc = null) +extern (D) MATCH callMatch(FuncDeclaration fd, TypeFunction tf, Type tthis, ArgumentList argumentList, + int flag = 0, void delegate(const(char)*) scope errorHelper = null, Scope* sc = null) { - //printf("TypeFunction::callMatch() %s\n", tf.toChars()); + //printf("callMatch() fd: %s, tf: %s\n", fd ? fd.ident.toChars() : "null", toChars(tf)); MATCH match = MATCH.exact; // assume exact match ubyte wildmatch = 0; @@ -820,7 +822,7 @@ extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentLis Expression arg = args[u]; if (!arg) continue; // default argument - m = argumentMatchParameter(tf, p, arg, wildmatch, flag, sc, pMessage); + m = argumentMatchParameter(fd, tf, p, arg, wildmatch, flag, sc, pMessage); if (failMessage) { buf.reset(); @@ -887,14 +889,15 @@ extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentLis * * This is done by seeing if a call to the copy constructor can be made: * ``` - * typeof(tprm) __copytmp; - * copytmp.__copyCtor(arg); + * typeof(tprm) __copytemp; + * copytemp.__copyCtor(arg); * ``` */ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct, Expression arg, Type tprm, Scope* sc, const(char)** pMessage) { - auto tmp = new VarDeclaration(arg.loc, tprm, Identifier.generateId("__copytmp"), null); + //printf("isCopyConstructorCallable() argStruct: %s arg: %s tprm: %s\n", argStruct.toChars(), toChars(arg), toChars(tprm)); + auto tmp = new VarDeclaration(arg.loc, tprm, Identifier.generateId("__copytemp"), null); tmp.storage_class = STC.rvalue | STC.temp | STC.ctfe; tmp.dsymbolSemantic(sc); Expression ve = new VarExp(arg.loc, tmp); @@ -980,25 +983,28 @@ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct, * * This function is called by `TypeFunction.callMatch` while iterating over * the list of parameter. Here we check if `arg` is a match for `p`, - * which is mostly about checking if `arg.type` converts to `p`'s type + * which is mostly about checking if `arg.type` converts to type of `p` * and some check about value reference. * * Params: + * fd = the function being called if symbol, null if not * tf = The `TypeFunction`, only used for error reporting * p = The parameter of `tf` being matched * arg = Argument being passed (bound) to `p` * wildmatch = Wild (`inout`) matching level, derived from the full argument list - * flag = A non-zero value means we're doing a partial ordering check + * flag = A non-zero value means we are doing a partial ordering check * (no value semantic check) * sc = Scope we are in * pMessage = A buffer to write the error in, or `null` * * Returns: Whether `trailingArgs` match `p`. */ -private extern(D) MATCH argumentMatchParameter (TypeFunction tf, Parameter p, +private extern(D) MATCH argumentMatchParameter (FuncDeclaration fd, TypeFunction tf, Parameter p, Expression arg, ubyte wildmatch, int flag, Scope* sc, const(char)** pMessage) { - //printf("arg: %s, type: %s\n", arg.toChars(), arg.type.toChars()); + static if (0) + printf("argumentMatchParameter() sc: %p, fd: %s, tf: %s, p: %s, arg: %s, arg.type: %s\n", + sc, fd ? fd.ident.toChars() : "null", tf.toChars(), parameterToChars(p, tf, false), arg.toChars(), arg.type.toChars()); MATCH m; Type targ = arg.type; Type tprm = wildmatch ? p.type.substWildTo(wildmatch) : p.type; @@ -1013,18 +1019,47 @@ private extern(D) MATCH argumentMatchParameter (TypeFunction tf, Parameter p, else { const isRef = p.isReference(); - StructDeclaration argStruct, prmStruct; - // first look for a copy constructor - if (arg.isLvalue() && !isRef && targ.ty == Tstruct && tprm.ty == Tstruct) + StructDeclaration argStruct, prmStruct; + if (targ.ty == Tstruct && tprm.ty == Tstruct) { // if the argument and the parameter are of the same unqualified struct type argStruct = (cast(TypeStruct)targ).sym; prmStruct = (cast(TypeStruct)tprm).sym; + + /* if both a copy constructor and move constructor exist, then match + * the lvalue to the copy constructor only and the rvalue to the move constructor + * only + */ + if (argStruct == prmStruct && fd) + { + if (auto cfd = fd.isCtorDeclaration()) + { + /* Get struct that constructor is making + */ + + auto t1 = cfd.type.toBasetype(); + auto t2 = t1.nextOf(); + auto t3 = t2.isTypeStruct(); + if (t3) + { + auto ctorStruct = t3.sym; +// StructDeclaration ctorStruct = cfd.type.toBasetype().nextOf().isTypeStruct().sym; + + if (prmStruct == ctorStruct && ctorStruct.hasCopyCtor && ctorStruct.hasMoveCtor) + { + if (cfd.isCpCtor && !arg.isLvalue()) + return MATCH.nomatch; // copy constructor is only for lvalues + else if (cfd.isMoveCtor && arg.isLvalue()) + return MATCH.nomatch; // move constructor is only for rvalues + } + } + } + } } // check if the copy constructor may be called to copy the argument - if (argStruct && argStruct == prmStruct && argStruct.hasCopyCtor) + if (arg.isLvalue() && !isRef && argStruct && argStruct == prmStruct && argStruct.hasCopyCtor) { if (!isCopyConstructorCallable(argStruct, arg, tprm, sc, pMessage)) return MATCH.nomatch; @@ -4427,6 +4462,10 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type */ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag flag) { + enum LOGDOTEXP = false; + if (LOGDOTEXP) + printf("dotExp()\n"); + Expression visitType(Type mt) { VarDeclaration v = null; @@ -5047,8 +5086,41 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag return noMember(mt, sc, e, ident, flag); } // check before alias resolution; the alias itself might be deprecated! - if (s.isAliasDeclaration) + if (auto ad = s.isAliasDeclaration) + { s.checkDeprecated(e.loc, sc); + + // Fix for https://github.com/dlang/dmd/issues/20610 + if (ad.originalType) + { + if (auto tid = ad.originalType.isTypeIdentifier()) + { + if (tid.idents.length) + { + static if (0) + { + printf("TypeStruct::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); + printf("AliasDeclaration: %s\n", ad.toChars()); + if (ad.aliassym) + printf("aliassym: %s\n", ad.aliassym.toChars()); + printf("tid type: %s\n", toChars(tid)); + } + /* Rewrite e.s as e.(tid.ident).(tid.idents) + */ + Expression die = new DotIdExp(e.loc, e, tid.ident); + foreach (id; tid.idents) // maybe use typeToExpressionHelper() + die = new DotIdExp(e.loc, die, cast(Identifier)id); + /* Ambiguous syntax, only way to disambiguate it to try it + */ + die = dmd.expressionsem.trySemantic(die, sc); + if (die && die.isDotVarExp()) // shrink wrap around DotVarExp() + { + return die; + } + } + } + } + } s = s.toAlias(); if (auto em = s.isEnumMember()) @@ -6039,7 +6111,7 @@ Dsymbol toDsymbol(Type type, Scope* sc) Dsymbol visitIdentifier(TypeIdentifier type) { - //printf("TypeIdentifier::toDsymbol('%s')\n", toChars()); + //printf("TypeIdentifier::toDsymbol('%s')\n", toChars(type)); if (!sc) return null; @@ -6051,7 +6123,6 @@ Dsymbol toDsymbol(Type type, Scope* sc) s = t.toDsymbol(sc); if (e) s = getDsymbol(e); - return s; } diff --git a/gcc/testsuite/gdc.dg/copy1.d b/gcc/testsuite/gdc.dg/copy1.d new file mode 100644 index 0000000000000000000000000000000000000000..7b539fac2f07754a884545a3b072d19a5248c9f9 --- /dev/null +++ b/gcc/testsuite/gdc.dg/copy1.d @@ -0,0 +1,19 @@ +// { dg-do compile } +// { dg-options "-fno-moduleinfo -fdump-tree-original" } +struct S +{ + int i; + this(ref S); + void opAssign(S s) + { + i = s.i + 1; + } +} + +void test_opAssign() +{ + S s; + S t; + // { dg-final { scan-tree-dump-not "&TARGET_EXPR" "original" } } + t = s; +} diff --git a/gcc/testsuite/gdc.test/compilable/aligndefault.d b/gcc/testsuite/gdc.test/compilable/aligndefault.d new file mode 100644 index 0000000000000000000000000000000000000000..5c66bffdc24b4ad6183908b5accc38601559696a --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/aligndefault.d @@ -0,0 +1,28 @@ + +struct S +{ + align(1) + { + short x1; + int y1; + long z1; + + align(default) + { + short x; + int y; + long z; + } + } +} + +void fun() +{ + static assert(S.x1.alignof == 1); + static assert(S.y1.alignof == 1); + static assert(S.z1.alignof == 1); + + static assert(S.x.alignof == short.alignof); + static assert(S.y.alignof == int.alignof); + static assert(S.z.alignof == long.alignof); +} diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/header1.d b/gcc/testsuite/gdc.test/compilable/extra-files/header1.d index 9756264fcc3e753b29f363eade539aad8a94cb27..ee698f16dcf8bb2283c31e8b3eacd4d45ceb5c90 100644 --- a/gcc/testsuite/gdc.test/compilable/extra-files/header1.d +++ b/gcc/testsuite/gdc.test/compilable/extra-files/header1.d @@ -564,6 +564,11 @@ ref int* foo(scope return ref int* a) @safe struct SafeS { + this(int[1] x) scope {} + this(int[2] x) return scope {} + this(int[3] x) scope return {} + this(int[4] x) return {} + @safe: ref SafeS foo() return { diff --git a/gcc/testsuite/gdc.test/compilable/rvalue2.d b/gcc/testsuite/gdc.test/compilable/rvalue2.d new file mode 100644 index 0000000000000000000000000000000000000000..e1dc44e3bbf4533845e4d2c6b0ea6f278e36a8d5 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/rvalue2.d @@ -0,0 +1,22 @@ +/* PERMUTE_ARGS: -preview=rvaluerefparam + */ + +struct S +{ + alias TT this; + long TT(); + this(T)(int x) {} // works if `int` is `long` + + this(S); + this(ref S); + + ~this(); +} + +S fun(ref S arg); + +void test() +{ + S st; + fun(st); +} diff --git a/gcc/testsuite/gdc.test/compilable/shortened_methods.d b/gcc/testsuite/gdc.test/compilable/shortened_methods.d index 785cb8e5cd0a54ec9f63642a09e74c4af7c344b7..76a4c8a55db6f1bb905d28bcf78834e79f1ec24d 100644 --- a/gcc/testsuite/gdc.test/compilable/shortened_methods.d +++ b/gcc/testsuite/gdc.test/compilable/shortened_methods.d @@ -13,6 +13,10 @@ class A { // or normal method defintions bool isNull() => this is null; + + this() {} + this(int x) { _x = x; } + this(float y) => this(cast(int) y); } class B : A{ @@ -36,3 +40,12 @@ void func() { // Issue 24088 - https://issues.dlang.org/show_bug.cgi?id=24088 S!int f() => S!int(); } + +struct T +{ + void inc() {} + this(this) => inc(); + + void free() {} + ~this() => free(); +} diff --git a/gcc/testsuite/gdc.test/compilable/test19227.d b/gcc/testsuite/gdc.test/compilable/test19227.d index 6ff5349f3338b1a48a6b24230c38632dce046053..55fa59164f4377c0435e6f90063d99c23dd9ea97 100644 --- a/gcc/testsuite/gdc.test/compilable/test19227.d +++ b/gcc/testsuite/gdc.test/compilable/test19227.d @@ -1,9 +1,7 @@ // https://issues.dlang.org/show_bug.cgi?id=19227 /* TEST_OUTPUT: --- -compilable/test19227.d(18): Deprecation: use of complex type `cfloat` is deprecated, use `std.complex.Complex!(float)` instead -Deprecation: use of complex type `const(cfloat)` is deprecated, use `std.complex.Complex!(float)` instead -Deprecation: use of complex type `const(cfloat)` is deprecated, use `std.complex.Complex!(float)` instead +compilable/test19227.d(16): Deprecation: use of complex type `cfloat` is deprecated, use `std.complex.Complex!(float)` instead Deprecation: use of complex type `const(cfloat)` is deprecated, use `std.complex.Complex!(float)` instead --- */ diff --git a/gcc/testsuite/gdc.test/compilable/traits.d b/gcc/testsuite/gdc.test/compilable/traits.d index d5e2cb0f2201051058b4413988c2a6f226ae4153..09538b1dd4ba4197536e08d5ea13c383548ac88c 100644 --- a/gcc/testsuite/gdc.test/compilable/traits.d +++ b/gcc/testsuite/gdc.test/compilable/traits.d @@ -137,6 +137,8 @@ struct DisabledPostblit struct NoCpCtor { } class C19902 { } +struct MoveCtor { this(MoveCtor) { } } + static assert(__traits(hasCopyConstructor, S)); static assert(__traits(hasCopyConstructor, OuterS.S)); static assert(__traits(hasCopyConstructor, OuterS)); @@ -147,6 +149,8 @@ static assert(__traits(hasCopyConstructor, U!S)); static assert(!__traits(hasPostblit, U!S)); static assert(__traits(hasPostblit, SPostblit)); static assert(!__traits(hasCopyConstructor, SPostblit)); +static assert(__traits(hasMoveConstructor, MoveCtor)); +static assert(!__traits(hasMoveConstructor, NoCpCtor)); static assert(!__traits(hasCopyConstructor, NoCpCtor)); static assert(!__traits(hasCopyConstructor, C19902)); diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19871.d b/gcc/testsuite/gdc.test/fail_compilation/fail19871.d deleted file mode 100644 index ad458df200197b37a7550ea4d7986511d29fdffd..0000000000000000000000000000000000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail19871.d +++ /dev/null @@ -1,20 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail19871.d(10): Error: `struct Struct` may not define both a rvalue constructor and a copy constructor -fail_compilation/fail19871.d(19): rvalue constructor defined here -fail_compilation/fail19871.d(13): copy constructor defined here ---- -*/ - -struct Struct -{ - @disable this(); - this(ref Struct other) - { - const Struct s = void; - this(s); - } - - this(Struct) {} -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19931.d b/gcc/testsuite/gdc.test/fail_compilation/fail19931.d deleted file mode 100644 index 940a1faee028104fc59c335e55a467253dcf4ab6..0000000000000000000000000000000000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail19931.d +++ /dev/null @@ -1,15 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail19931.d(10): Error: `struct S` may not define both a rvalue constructor and a copy constructor -fail_compilation/fail19931.d(12): rvalue constructor defined here -fail_compilation/fail19931.d(13): copy constructor defined here ---- -*/ - -struct S -{ - this(S s) {} - this(ref S s) {} - this(this) {} -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail23036.d b/gcc/testsuite/gdc.test/fail_compilation/fail23036.d deleted file mode 100644 index 8920586c67a8a4ff0ce1def51727d0288d30c7f3..0000000000000000000000000000000000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail23036.d +++ /dev/null @@ -1,22 +0,0 @@ -// https://issues.dlang.org/show_bug.cgi?id=23036 - -/* -TEST_OUTPUT: ---- -fail_compilation/fail23036.d(12): Error: `struct S` may not define both a rvalue constructor and a copy constructor -fail_compilation/fail23036.d(15): rvalue constructor defined here -fail_compilation/fail23036.d(14): copy constructor defined here ---- -*/ - -struct S -{ - this(ref S) {} - this(S, int a = 2) {} -} - -void main() -{ - S a; - S b = a; -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice14642.d b/gcc/testsuite/gdc.test/fail_compilation/ice14642.d index 90b9867527bfed4275234475af8deec49b243377..91b1f950ca20648e988de756a62f73f930aa869d 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice14642.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice14642.d @@ -1,8 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/ice14642.d(47): Error: undefined identifier `errorValue` -fail_compilation/ice14642.d(23): Error: template instance `ice14642.X.NA!()` error instantiating +fail_compilation/ice14642.d(48): Error: undefined identifier `errorValue` +fail_compilation/ice14642.d(52): error on member `ice14642.Z.v` +fail_compilation/ice14642.d(24): Error: template instance `ice14642.X.NA!()` error instantiating --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice9865.d b/gcc/testsuite/gdc.test/fail_compilation/ice9865.d index 3d8e997946f78c0ca765f8aea4c7e6c536c86750..ec842634160546bde874b9f40993634a5f7052ca 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice9865.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice9865.d @@ -2,7 +2,8 @@ EXTRA_FILES: imports/ice9865b.d TEST_OUTPUT: --- -fail_compilation/ice9865.d(9): Error: alias `ice9865.Baz` recursive alias declaration +fail_compilation/ice9865.d(10): Error: alias `ice9865.Baz` recursive alias declaration +fail_compilation/ice9865.d(11): error on member `ice9865.Foo.f` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/parseStc2.d b/gcc/testsuite/gdc.test/fail_compilation/parseStc2.d index 936769a59d06b1ae7e24e6f958ab3b72e855a0ef..dbc98f91ca996a60463333adb8294a35f4171dfc 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/parseStc2.d +++ b/gcc/testsuite/gdc.test/fail_compilation/parseStc2.d @@ -53,10 +53,10 @@ public private void f10() {} /* TEST_OUTPUT: --- -fail_compilation/parseStc2.d(63): Error: redundant alignment attribute `align` +fail_compilation/parseStc2.d(63): Error: redundant alignment attribute `align(default)` fail_compilation/parseStc2.d(64): Error: redundant alignment attribute `align(1)` fail_compilation/parseStc2.d(65): Error: redundant alignment attribute `align(1)` -fail_compilation/parseStc2.d(66): Error: redundant alignment attribute `align` +fail_compilation/parseStc2.d(66): Error: redundant alignment attribute `align(default)` fail_compilation/parseStc2.d(67): Error: redundant alignment attribute `align(2)` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/short_fn.d b/gcc/testsuite/gdc.test/fail_compilation/short_fn.d new file mode 100644 index 0000000000000000000000000000000000000000..04f3e3fffbfae814c573e0a55627dea8aac13ab6 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/short_fn.d @@ -0,0 +1,15 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/short_fn.d(13): Error: can only return void expression, `this` call or `super` call from constructor +fail_compilation/short_fn.d(14): Error: can only return void expression, `this` call or `super` call from constructor +--- +*/ + +struct Number +{ + int x; + + this(int x) => this.x = x; + this(byte x) => Number(); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test19473.d b/gcc/testsuite/gdc.test/fail_compilation/test19473.d index ba6024b000b8f9f454cc4cb5d320971f77620f7e..67a66c57206a6bc2f4bf9c8779e28f523f9b2412 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test19473.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test19473.d @@ -1,6 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/test19473.d(14): Error: union `test19473.P` no size because of forward reference +fail_compilation/test19473.d(15): Error: union `test19473.P` no size because of forward reference +fail_compilation/test19473.d(31): error on member `test19473.P.p` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/test20719.d b/gcc/testsuite/gdc.test/fail_compilation/test20719.d index 4db59d62ae1f4baa1a7782003d93ea0f6a01eac9..56b80bbb1a644cab50c055335aa422bd01f18a3c 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test20719.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test20719.d @@ -1,8 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/test20719.d(13): Error: struct `test20719.SumType` no size because of forward reference -fail_compilation/test20719.d(32): Error: variable `test20719.isCopyable!(SumType).__lambda_L32_C22.foo` - size of type `SumType` is invalid -fail_compilation/test20719.d(18): Error: template instance `test20719.isCopyable!(SumType)` error instantiating +fail_compilation/test20719.d(14): Error: struct `test20719.SumType` no size because of forward reference +fail_compilation/test20719.d(17): error on member `test20719.SumType.storage` +fail_compilation/test20719.d(33): Error: variable `test20719.isCopyable!(SumType).__lambda_L33_C22.foo` - size of type `SumType` is invalid +fail_compilation/test20719.d(19): Error: template instance `test20719.isCopyable!(SumType)` error instantiating --- */ struct SumType diff --git a/gcc/testsuite/gdc.test/runnable/aliasassign.d b/gcc/testsuite/gdc.test/runnable/aliasassign.d index 986cccc4db89c60dd7bb53002833c35788dd5016..4f61e3787e320f62ba204305c175d23071fff060 100644 --- a/gcc/testsuite/gdc.test/runnable/aliasassign.d +++ b/gcc/testsuite/gdc.test/runnable/aliasassign.d @@ -15,7 +15,7 @@ template Qual(alias T) alias Qual = T; } -void test() +void test1() { int x = 3; int y = 4; @@ -25,7 +25,33 @@ void test() assert(XY[1] == 4); } -void main() +/**********************************************/ + +struct T +{ + int k,i = 2; +} + +struct S +{ + int x; + T t; + alias ti = t.i; +} + +void test2() +{ + T t = T(1, 2); + S s; + assert(s.ti == 2); +} + +/**********************************************/ + +int main() { - test(); + test1(); + test2(); + + return 0; } diff --git a/gcc/testsuite/gdc.test/runnable/imports/test23722_2b.d b/gcc/testsuite/gdc.test/runnable/imports/test23722_2b.d new file mode 100644 index 0000000000000000000000000000000000000000..b0e54a936fae5b90d42867e88532977f3d65837b --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/imports/test23722_2b.d @@ -0,0 +1,13 @@ +module imports.test23722_2b; + +struct T(alias fun) { } + +struct S(int i) { + auto t = T!(x => i)(); +} + +string g() { + S!0 s0; + S!1 s1; + return s1.t.init.mangleof; +} diff --git a/gcc/testsuite/gdc.test/runnable/mars1.d b/gcc/testsuite/gdc.test/runnable/mars1.d index f493eac181de7e40774488a40192ce9fe0a40c81..1c2ae55b79c4ed6a4941cd65f4e1608828b1cf87 100644 --- a/gcc/testsuite/gdc.test/runnable/mars1.d +++ b/gcc/testsuite/gdc.test/runnable/mars1.d @@ -2491,6 +2491,62 @@ void testDoWhileContinue() while(--i > 0); } +//////////////////////////////////////////////////////////////////////// +// https://github.com/dlang/dmd/issues/20574 + +int test20574x(int i, int y) +{ + return i ? y : y; +} + +void test20574() +{ + assert(test20574x(1, 2) == 2); + assert(test20574x(0, 2) == 2); +} + +//////////////////////////////////////////////////////////////////////// + +struct S8 +{ + int x,y,z; +} + +int test8x(S8 s) +{ + s = s; + return s.y; +} + +void test8() +{ + S8 s; + s.y = 2; + assert(test8x(s) == 2); +} + +//////////////////////////////////////////////////////////////////////// + +struct S9 +{ + int a,b; + ~this() { } +} + + +S9 test9x(ref S9 arg) +{ + return arg; +} + +void test9() +{ + S9 s; + s.b = 3; + S9 t = test9x(s); + assert(t.b == 3); +} + //////////////////////////////////////////////////////////////////////// int main() @@ -2592,6 +2648,9 @@ int main() test21816(); test21835(); testDoWhileContinue(); + test20574(); + test8(); + test9(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/nrvo.d b/gcc/testsuite/gdc.test/runnable/nrvo.d index 5c653fe45ad6645c05a8795ca64aade6557b2d12..bceba100e0bd88b08666df3774f8bce598344ee5 100644 --- a/gcc/testsuite/gdc.test/runnable/nrvo.d +++ b/gcc/testsuite/gdc.test/runnable/nrvo.d @@ -22,9 +22,35 @@ void test1() assert(&r == s1ptr); } +/***************************************************/ +// https://github.com/dlang/dmd/issues/20567 + +struct S2 +{ + int x; + this(ref S2 s) { x = s.x; } +} + +S2 returnRval(ref S2 arg1, ref S2 arg2, int i) +{ + return i ? arg1 : arg2; +} + +void test2() +{ + S2 s1, s2; + s1.x = 3; + s2.x = 4; + S2 s = returnRval(s1, s2, 0); + assert(s.x == 4); + s = returnRval(s1, s2, 1); + assert(s.x == 3); +} + /***************************************************/ void main() { test1(); + test2(); } diff --git a/gcc/testsuite/gdc.test/runnable/rvalue1.d b/gcc/testsuite/gdc.test/runnable/rvalue1.d new file mode 100644 index 0000000000000000000000000000000000000000..f8134f718a9f938441bc1987bf871e53bafa2353 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/rvalue1.d @@ -0,0 +1,209 @@ +/* PERMUTE_ARGS: -preview=rvaluerefparam +/* testing __rvalue */ + +import core.stdc.stdio; + +/********************************/ + +int foo(int) { printf("foo(int)\n"); return 1; } +int foo(ref int) { printf("foo(ref int)\n"); return 2; } + +void test1() +{ + int s; + assert(foo(s) == 2); + assert(foo(__rvalue(s)) == 1); +} + +/********************************/ + +struct S +{ + nothrow: + ~this() { printf("~this() %p\n", &this); } + this(ref S) { printf("this(ref S)\n"); } + void opAssign(S) { printf("opAssign(S)\n"); } +} + +void test2() +{ + S s; + S t; + + t = __rvalue(s); +} + +/********************************/ + +struct S3 +{ + int a, b, c; + + this(S3) {} + this(ref S3) {} +} + +void test3() +{ + S3 s; + S3 x = s; // this line causes the compiler to crash +} + +/********************************/ + +struct S4 +{ + void* p; + + this(ref S4) { } + + this(S4 s) + { + assert(&s is &x); // confirm the rvalue reference + } +} + +__gshared S4 x; + +void test4() +{ + S4 t = __rvalue(x); +} + +/********************************/ + +struct S5 +{ + this(S5 s) { printf("this(S5 s)\n"); } + this(ref inout S5 s) inout { printf("this(ref inout S5 s) inout\n"); } +} + +void test5() +{ + S5 t; + S5 t1 = t; + S5 t2 = __rvalue(t); +} + +/********************************/ + +int moveCtor, copyCtor, moveAss, copyAss; + +struct S6 +{ + this(S6 s) { ++moveCtor; } + this(ref S6 s) { ++copyCtor; } + void opAssign(S6 s) { ++moveAss; } + void opAssign(ref S6 s) { ++copyAss; } +} + +void test6() +{ + S6 x; + S6 s = x; +// printf("S6 s = x; moveCtor %d copyCtor %d moveAss %d copyAss %d\n", moveCtor, copyCtor, moveAss, copyAss); + assert(copyCtor == 1); + S6 s2 = __rvalue(x); +// printf("S6 s2 = __rvalue(x); moveCtor %d copyCtor %d moveAss %d copyAss %d\n", moveCtor, copyCtor, moveAss, copyAss); + assert(moveCtor == 1); + s2 = s; +// printf("s2 = s; moveCtor %d copyCtor %d moveAss %d copyAss %d\n", moveCtor, copyCtor, moveAss, copyAss); + assert(copyAss == 1); + s2 = __rvalue(s); +// printf("s2 = __rvalue(s); moveCtor %d copyCtor %d moveAss %d copyAss %d\n", moveCtor, copyCtor, moveAss, copyAss); + assert(moveAss == 1); + assert(copyCtor == 1 && moveCtor == 1 && copyAss == 1 && moveAss == 1); +} + +/********************************/ +// https://github.com/dlang/dmd/pull/17050#issuecomment-2543370370 + +struct MutableString(size_t E) {} + +struct StringTest +{ + alias toString this; + const(char)[] toString() const pure + => null; + + this(StringTest rhs) {} + this(ref inout StringTest rhs) inout pure {} + + this(typeof(null)) inout pure {} + this(size_t Embed)(MutableString!Embed str) inout {} + + ~this() {} +} + + +void test7() +{ + StringTest s = StringTest(null); +} + +/********************************/ +// https://github.com/dlang/dmd/issues/20562 + +struct S8 +{ + int a,b; + this(S8) { printf("this(S)\n"); b = 4; } + this(ref S8) { printf("this(ref S)\n"); assert(0); } +} + + +S8 returnRval(ref S8 arg) +{ +static if (0) +{ + /* __copytmp2 = 0 ; + _D7rvalue51S6__ctorMFNcKSQxQrZQg call (arg param #__copytmp2); + * __HID1 streq 1 __copytmp2; + __HID1; + */ + return arg; +} +else static if (1) +{ + /* * __HID1 streq 1 * arg; + __HID1; + */ + return __rvalue(arg); // should move-construct the NRVO value +} +else +{ + /* * t = 0 ; + t; + _TMP0 = t; + _D7rvalue51S6__ctorMFNcSQwQqZQg call (arg param _TMP0); + t; + */ + S8 t = __rvalue(arg); + return t; +} +} + + +void test8() +{ + S8 s; + S8 t = returnRval(s); + printf("t.b: %d\n", t.b); + assert(t.b == 4); +} + +/********************************/ + +int main() +{ + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + test7(); + test8(); + + return 0; +} diff --git a/gcc/testsuite/gdc.test/runnable/test23036.d b/gcc/testsuite/gdc.test/runnable/test23036.d new file mode 100644 index 0000000000000000000000000000000000000000..efb6fef3953ba81c322998528272f04b1f733cba --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/test23036.d @@ -0,0 +1,13 @@ +// https://issues.dlang.org/show_bug.cgi?id=23036 + +struct S +{ + this(ref S) {} + this(S, int a = 2) {} +} + +void main() +{ + S a; + S b = a; +} diff --git a/gcc/testsuite/gdc.test/runnable/test23722_2.d b/gcc/testsuite/gdc.test/runnable/test23722_2.d new file mode 100644 index 0000000000000000000000000000000000000000..cce4629933fda969cdde7fb32b5f0494ab9c88b0 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/test23722_2.d @@ -0,0 +1,10 @@ +// COMPILE_SEPARATELY: +// EXTRA_SOURCES: imports/test23722_2b.d +// https://issues.dlang.org/show_bug.cgi?id=23722 +// Lambdas are mangled incorrectly when using multiple compilation units, resulting in incorrect code +import imports.test23722_2b; + +void main() { + S!1 s1; + assert(s1.t.init.mangleof == g); +} diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE index bfdc9ea21e1e45e373e84f60baddf81994375890..e5884c6a798ad3aa9e7fb90bbbf4fe5b5ee25589 100644 --- a/libphobos/libdruntime/MERGE +++ b/libphobos/libdruntime/MERGE @@ -1,4 +1,4 @@ -82a5d2a7c4dd3d270537bcede2981e047bfd0e6a +c57da0cf5945cfb45eed06f1fd820435cda3ee3a 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/builtins.d b/libphobos/libdruntime/core/builtins.d index f10bb9f6f8b3065fa0d15d23083aed624c62aae9..bc12759e9a18fd4a07f5d8f29ea12e0d1b3f0a58 100644 --- a/libphobos/libdruntime/core/builtins.d +++ b/libphobos/libdruntime/core/builtins.d @@ -78,6 +78,8 @@ else version (DigitalMars) return val; } + /// Execute target dependent trap instruction, if supported. + /// Otherwise, abort execution. pragma(inline, true) void trap() { @@ -90,7 +92,8 @@ else version (DigitalMars) } } -/// Provide static branch hints +/// Provide static branch and value hints for the LDC/GDC compilers. +/// DMD ignores these hints. pragma(inline, true) bool likely(bool b) { return !!expect(b, true); } -/// +/// ditto pragma(inline, true) bool unlikely(bool b) { return !!expect(b, false); } diff --git a/libphobos/libdruntime/core/gc/gcinterface.d b/libphobos/libdruntime/core/gc/gcinterface.d index fa3d859ce3615d47e0c78b7b2fe8f58760061bd0..173e6abecffe6ff3450638cb061181d0bedeeccf 100644 --- a/libphobos/libdruntime/core/gc/gcinterface.d +++ b/libphobos/libdruntime/core/gc/gcinterface.d @@ -190,4 +190,73 @@ interface GC * GC.stats().allocatedInCurrentThread, but faster. */ ulong allocatedInCurrentThread() nothrow; + + // ARRAY FUNCTIONS + /** + * Get the current used capacity of an array block. Note that this is only + * needed if you are about to change the array used size and need to deal + * with the memory that is about to go away. For appending or shrinking + * arrays that have no destructors, you probably don't need this function. + * Params: + * ptr = The pointer to check. This can be an interior pointer, but if it + * is beyond the end of the used space, the return value may not be + * valid. + * atomic = If true, the value is fetched atomically (for shared arrays) + * Returns: Current array slice, or null if the pointer does not point to a + * valid appendable GC block. + */ + void[] getArrayUsed(void *ptr, bool atomic = false) nothrow; + + /** + * Expand the array used size. Used for appending and expanding the length + * of the array slice. If the operation can be performed without + * reallocating, the function succeeds. Newly expanded data is not + * initialized. + * + * slices that do not point at expandable GC blocks cannot be affected, and + * this function will always return false. + * Params: + * slice = the slice to attempt expanding in place. + * newUsed = the size that should be stored as used. + * atomic = if true, the array may be shared between threads, and this + * operation should be done atomically. + * Returns: true if successful. + */ + bool expandArrayUsed(void[] slice, size_t newUsed, bool atomic = false) nothrow @safe; + + /** + * Expand the array capacity. Used for reserving space that can be used for + * appending. If the operation can be performed without reallocating, the + * function succeeds. The used size is not changed. + * + * slices that do not point at expandable GC blocks cannot be affected, and + * this function will always return zero. + * Params: + * slice = the slice to attempt reserving capacity for. + * request = the requested size to expand to. Includes the existing data. + * Passing a value less than the current array size will result in no + * changes, but will return the current capacity. + * atomic = if true, the array may be shared between threads, and this + * operation should be done atomically. + * Returns: resulting capacity size, 0 if the operation could not be performed. + */ + size_t reserveArrayCapacity(void[] slice, size_t request, bool atomic = false) nothrow @safe; + + /** + * Shrink used space of a slice. Unlike the other array functions, the + * array slice passed in is the target slice, and the existing used space + * is passed separately. This is to discourage code that ends up with a + * slice to dangling valid data. + * If slice.ptr[0 .. existingUsed] does not point to the end of a valid GC + * appendable slice, then the operation fails. + * Params: + * slice = The proposed valid slice data. + * existingUsed = The amount of data in the block (starting at slice.ptr) + * that is currently valid in the array. If this amount does not match + * the current used size, the operation fails. + * atomic = If true, the slice may be shared between threads, and the + * operation should be atomic. + * Returns: true if successful. + */ + bool shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic = false) nothrow; } diff --git a/libphobos/libdruntime/core/internal/array/utils.d b/libphobos/libdruntime/core/internal/array/utils.d index c45913d87e1b0d9483b3325da92c3733e879f506..54cfc26af5646850b4e9aadded351ca70c5a1e33 100644 --- a/libphobos/libdruntime/core/internal/array/utils.d +++ b/libphobos/libdruntime/core/internal/array/utils.d @@ -264,6 +264,8 @@ void *__arrayStart()(return scope BlkInfo info) nothrow pure bool __setArrayAllocLength(T)(ref BlkInfo info, size_t newLength, bool isShared, size_t oldLength = ~0) { import core.lifetime : TypeInfoSize; - import core.internal.gc.blockmeta : __setArrayAllocLengthImpl; - return __setArrayAllocLengthImpl(info, newLength, isShared, typeid(T), oldLength, TypeInfoSize!T); + import core.internal.gc.blockmeta : __setArrayAllocLengthImpl, __setBlockFinalizerInfo; + static if (TypeInfoSize!T) + __setBlockFinalizerInfo(info, typeid(T)); + return __setArrayAllocLengthImpl(info, newLength, isShared, oldLength, TypeInfoSize!T); } diff --git a/libphobos/libdruntime/core/internal/gc/blkcache.d b/libphobos/libdruntime/core/internal/gc/blkcache.d index c555c22f636fb728e7e9d8d23ab7407dcd7abe04..908f66656e13bef9cbb81338018586ab3e89fbcb 100644 --- a/libphobos/libdruntime/core/internal/gc/blkcache.d +++ b/libphobos/libdruntime/core/internal/gc/blkcache.d @@ -38,7 +38,7 @@ else int __nextBlkIdx; } -@property BlkInfo *__blkcache() nothrow +@property BlkInfo *__blkcache() nothrow @nogc { if (!__blkcache_storage) { @@ -135,7 +135,7 @@ unittest so any use of the returned BlkInfo should copy it and then check the base ptr of the copy before actually using it. */ -BlkInfo *__getBlkInfo(void *interior) nothrow +BlkInfo *__getBlkInfo(void *interior) nothrow @nogc { BlkInfo *ptr = __blkcache; if (ptr is null) @@ -175,7 +175,7 @@ BlkInfo *__getBlkInfo(void *interior) nothrow return null; // not in cache. } -void __insertBlkInfoCache(BlkInfo bi, BlkInfo *curpos) nothrow +void __insertBlkInfoCache(BlkInfo bi, BlkInfo *curpos) nothrow @nogc { auto cache = __blkcache; if (cache is null) @@ -241,3 +241,16 @@ void __insertBlkInfoCache(BlkInfo bi, BlkInfo *curpos) nothrow } } } + +debug(PRINTF) +{ + extern(C) void printArrayCache() + { + auto ptr = __blkcache; + printf("CACHE: \n"); + foreach (i; 0 .. N_CACHE_BLOCKS) + { + printf(" %d\taddr:% .8x\tsize:% .10d\tflags:% .8x\n", i, ptr[i].base, ptr[i].size, ptr[i].attr); + } + } +} diff --git a/libphobos/libdruntime/core/internal/gc/blockmeta.d b/libphobos/libdruntime/core/internal/gc/blockmeta.d index c7dfeb6507529e9cf560ea0ec155e66b0760fa5e..9d959d54721d6d40427ab28dcb157ec741de2513 100644 --- a/libphobos/libdruntime/core/internal/gc/blockmeta.d +++ b/libphobos/libdruntime/core/internal/gc/blockmeta.d @@ -71,12 +71,15 @@ size_t structTypeInfoSize(const TypeInfo ti) pure nothrow @nogc */ bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, const TypeInfo tinext, size_t oldlength = ~0) pure nothrow { - size_t typeInfoSize = structTypeInfoSize(tinext); - return __setArrayAllocLengthImpl(info, newlength, isshared, tinext, oldlength, typeInfoSize); + size_t typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0; + if (typeInfoSize) + __setBlockFinalizerInfo(info, tinext); + + return __setArrayAllocLengthImpl(info, newlength, isshared, oldlength, typeInfoSize); } // the impl function, used both above and in core.internal.array.utils -bool __setArrayAllocLengthImpl(ref BlkInfo info, size_t newlength, bool isshared, const TypeInfo tinext, size_t oldlength, size_t typeInfoSize) pure nothrow +bool __setArrayAllocLengthImpl(ref BlkInfo info, size_t newlength, bool isshared, size_t oldlength, size_t typeInfoSize) pure nothrow { import core.atomic; @@ -113,11 +116,6 @@ bool __setArrayAllocLengthImpl(ref BlkInfo info, size_t newlength, bool isshared // setting the initial length, no cas needed *length = cast(ubyte)newlength; } - if (typeInfoSize) - { - auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof); - *typeInfo = cast() tinext; - } } else if (info.size < PAGESIZE) { @@ -144,11 +142,6 @@ bool __setArrayAllocLengthImpl(ref BlkInfo info, size_t newlength, bool isshared // setting the initial length, no cas needed *length = cast(ushort)newlength; } - if (typeInfoSize) - { - auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof); - *typeInfo = cast() tinext; - } } else { @@ -175,29 +168,80 @@ bool __setArrayAllocLengthImpl(ref BlkInfo info, size_t newlength, bool isshared // setting the initial length, no cas needed *length = newlength; } - if (typeInfoSize) - { - auto typeInfo = cast(TypeInfo*)(info.base + size_t.sizeof); - *typeInfo = cast()tinext; - } } return true; // resize succeeded } /** - get the allocation size of the array for the given block (without padding or type info) + The block finalizer info is set separately from the array length, as that is + only needed on the initial setup of the block. No shared is needed, since + this should only happen when the block is new. + */ +void __setBlockFinalizerInfo(ref BlkInfo info, const TypeInfo ti) pure nothrow +{ + if ((info.attr & BlkAttr.APPENDABLE) && info.size >= PAGESIZE) + { + // array used size goes at the beginning. We can stuff the typeinfo + // right after it, as we need to use 16 bytes anyway. + auto typeInfo = cast(TypeInfo*)(info.base + size_t.sizeof); + *typeInfo = cast() ti; + } + else + { + // all other cases the typeinfo gets put at the end of the block + auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof); + *typeInfo = cast() ti; + } +} + +/** + get the used size of the array for the given block */ -size_t __arrayAllocLength(ref BlkInfo info, const TypeInfo tinext) pure nothrow +size_t __arrayAllocLength(ref BlkInfo info) pure nothrow + in(info.attr & BlkAttr.APPENDABLE) { + auto typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0; if (info.size <= 256) - return *cast(ubyte *)(info.base + info.size - structTypeInfoSize(tinext) - SMALLPAD); + return *cast(ubyte *)(info.base + info.size - typeInfoSize - SMALLPAD); if (info.size < PAGESIZE) - return *cast(ushort *)(info.base + info.size - structTypeInfoSize(tinext) - MEDPAD); + return *cast(ushort *)(info.base + info.size - typeInfoSize - MEDPAD); return *cast(size_t *)(info.base); } +/** + Atomically get the used size of the array for the given block + */ +size_t __arrayAllocLengthAtomic(ref BlkInfo info) pure nothrow + in(info.attr & BlkAttr.APPENDABLE) +{ + import core.atomic; + auto typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0; + if (info.size <= 256) + return atomicLoad(*cast(shared(ubyte)*)(info.base + info.size - typeInfoSize - SMALLPAD)); + + if (info.size < PAGESIZE) + return atomicLoad(*cast(shared(ushort)*)(info.base + info.size - typeInfoSize - MEDPAD)); + + return atomicLoad(*cast(shared(size_t)*)(info.base)); +} + +/** + Get the maximum bytes that can be stored in the given block. + */ +size_t __arrayAllocCapacity(ref BlkInfo info) pure nothrow + in(info.attr & BlkAttr.APPENDABLE) +{ + // Capacity is a calculation based solely on the block info. + if (info.size >= PAGESIZE) + return info.size - LARGEPAD; + + auto typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0; + auto padsize = info.size <= 256 ? SMALLPAD : MEDPAD; + return info.size - typeInfoSize - padsize; +} + /** get the padding required to allocate size bytes. Note that the padding is NOT included in the passed in size. Therefore, do NOT call this function diff --git a/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d index 149cc5d4cfbd2459d4d56f2903f9ec330f3dc90e..6ddd4c1fb6058fc438e6e98c9766298c4d90aa7f 100644 --- a/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d +++ b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d @@ -1377,6 +1377,193 @@ class ConservativeGC : GC stats.freeSize += freeListSize; stats.allocatedInCurrentThread = bytesAllocated; } + + // ARRAY FUNCTIONS + void[] getArrayUsed(void *ptr, bool atomic = false) nothrow + { + import core.internal.gc.blockmeta; + import core.internal.gc.blkcache; + import core.internal.array.utils; + + // lookup the block info, using the cache if possible. + auto bic = atomic ? null : __getBlkInfo(ptr); + auto info = bic ? *bic : query(ptr); + + if (!(info.attr & BlkAttr.APPENDABLE)) + // not appendable + return null; + + assert(info.base); // sanity check. + if (!bic && !atomic) + // cache the lookup for next time + __insertBlkInfoCache(info, null); + + auto usedSize = atomic ? __arrayAllocLengthAtomic(info) : __arrayAllocLength(info); + return __arrayStart(info)[0 .. usedSize]; + } + + /* NOTE about @trusted in these functions: + * These functions do a lot of pointer manipulation, and has writeable + * access to BlkInfo which is used to interface with other parts of the GC, + * including the block metadata and block cache. Marking these functions as + * @safe would mean that any modification of BlkInfo fields should be + * considered @safe, which is not the case. For example, it would be + * perfectly legal to change the BlkInfo size to some huge number, and then + * store it in the block cache to blow up later. The utility functions + * count on the BlkInfo representing the correct information inside the GC. + * + * In order to mark these @safe, we would need a BlkInfo that has + * restrictive access (i.e. @system only) to the information inside the + * BlkInfo. Until then any use of these structures needs to be @trusted, + * and therefore the entire functions are @trusted. The API is still @safe + * because the information is stored and looked up by the GC, not the + * caller. + */ + bool expandArrayUsed(void[] slice, size_t newUsed, bool atomic = false) nothrow @trusted + { + import core.internal.gc.blockmeta; + import core.internal.gc.blkcache; + import core.internal.array.utils; + + if (newUsed < slice.length) + // cannot "expand" by shrinking. + return false; + + // lookup the block info, using the cache if possible + auto bic = atomic ? null : __getBlkInfo(slice.ptr); + auto info = bic ? *bic : query(slice.ptr); + + if (!(info.attr & BlkAttr.APPENDABLE)) + // not appendable + return false; + + assert(info.base); // sanity check. + + immutable offset = slice.ptr - __arrayStart(info); + newUsed += offset; + auto existingUsed = slice.length + offset; + + size_t typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0; + if (__setArrayAllocLengthImpl(info, offset + newUsed, atomic, existingUsed, typeInfoSize)) + { + // could expand without extending + if (!bic && !atomic) + // cache the lookup for next time + __insertBlkInfoCache(info, null); + return true; + } + + // if we got here, just setting the used size did not work. + if (info.size < PAGESIZE) + // nothing else we can do + return false; + + // try extending the block into subsequent pages. + immutable requiredExtension = newUsed - info.size - LARGEPAD; + auto extendedSize = extend(info.base, requiredExtension, requiredExtension, null); + if (extendedSize == 0) + // could not extend, can't satisfy the request + return false; + + info.size = extendedSize; + if (bic) + *bic = info; + else if (!atomic) + __insertBlkInfoCache(info, null); + + // this should always work. + return __setArrayAllocLengthImpl(info, newUsed, atomic, existingUsed, typeInfoSize); + } + + bool shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic = false) nothrow + { + import core.internal.gc.blockmeta; + import core.internal.gc.blkcache; + import core.internal.array.utils; + + if (existingUsed < slice.length) + // cannot "shrink" by growing. + return false; + + // lookup the block info, using the cache if possible. + auto bic = atomic ? null : __getBlkInfo(slice.ptr); + auto info = bic ? *bic : query(slice.ptr); + + if (!(info.attr & BlkAttr.APPENDABLE)) + // not appendable + return false; + + assert(info.base); // sanity check + + immutable offset = slice.ptr - __arrayStart(info); + existingUsed += offset; + auto newUsed = slice.length + offset; + + size_t typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0; + + if (__setArrayAllocLengthImpl(info, newUsed, atomic, existingUsed, typeInfoSize)) + { + if (!bic && !atomic) + __insertBlkInfoCache(info, null); + return true; + } + + return false; + } + + size_t reserveArrayCapacity(void[] slice, size_t request, bool atomic = false) nothrow @trusted + { + import core.internal.gc.blockmeta; + import core.internal.gc.blkcache; + import core.internal.array.utils; + + // lookup the block info, using the cache if possible. + auto bic = atomic ? null : __getBlkInfo(slice.ptr); + auto info = bic ? *bic : query(slice.ptr); + + if (!(info.attr & BlkAttr.APPENDABLE)) + // not appendable + return 0; + + assert(info.base); // sanity check + + immutable offset = slice.ptr - __arrayStart(info); + request += offset; + auto existingUsed = slice.length + offset; + + // make sure this slice ends at the used space + auto blockUsed = atomic ? __arrayAllocLengthAtomic(info) : __arrayAllocLength(info); + if (existingUsed != blockUsed) + // not an expandable slice. + return 0; + + // see if the capacity can contain the existing data + auto existingCapacity = __arrayAllocCapacity(info); + if (existingCapacity < request) + { + if (info.size < PAGESIZE) + // no possibility to extend + return 0; + + immutable requiredExtension = request - existingCapacity; + auto extendedSize = extend(info.base, requiredExtension, requiredExtension, null); + if (extendedSize == 0) + // could not extend, can't satisfy the request + return 0; + + info.size = extendedSize; + + // update the block info cache if it was used + if (bic) + *bic = info; + else if (!atomic) + __insertBlkInfoCache(info, null); + + existingCapacity = __arrayAllocCapacity(info); + } + + return existingCapacity - offset; + } } diff --git a/libphobos/libdruntime/core/internal/gc/impl/manual/gc.d b/libphobos/libdruntime/core/internal/gc/impl/manual/gc.d index 36add7a2d6afdccbc01aaa044d47302b0fd6d6c6..3da92db14e78b0253b33d3b7038a7eb6a6e268ef 100644 --- a/libphobos/libdruntime/core/internal/gc/impl/manual/gc.d +++ b/libphobos/libdruntime/core/internal/gc/impl/manual/gc.d @@ -267,4 +267,24 @@ class ManualGC : GC { return typeof(return).init; } + + void[] getArrayUsed(void *ptr, bool atomic = false) nothrow + { + return null; + } + + bool expandArrayUsed(void[] slice, size_t newUsed, bool atomic = false) nothrow @safe + { + return false; + } + + size_t reserveArrayCapacity(void[] slice, size_t request, bool atomic = false) nothrow @safe + { + return 0; + } + + bool shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic = false) nothrow + { + return false; + } } diff --git a/libphobos/libdruntime/core/internal/gc/impl/proto/gc.d b/libphobos/libdruntime/core/internal/gc/impl/proto/gc.d index dbe86007115e32b4c571c46e37ea22d35ed2a802..cbdcdb8852b5ff783b47022d75bda72ef639b86c 100644 --- a/libphobos/libdruntime/core/internal/gc/impl/proto/gc.d +++ b/libphobos/libdruntime/core/internal/gc/impl/proto/gc.d @@ -241,4 +241,24 @@ class ProtoGC : GC { return stats().allocatedInCurrentThread; } + + void[] getArrayUsed(void *ptr, bool atomic = false) nothrow + { + return null; + } + + bool expandArrayUsed(void[] slice, size_t newUsed, bool atomic = false) nothrow @safe + { + return false; + } + + size_t reserveArrayCapacity(void[] slice, size_t request, bool atomic = false) nothrow @safe + { + return 0; + } + + bool shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic = false) nothrow + { + return false; + } } diff --git a/libphobos/libdruntime/core/internal/gc/proxy.d b/libphobos/libdruntime/core/internal/gc/proxy.d index c7dab714e6c5c0e4ef7d33d8acb303fec88cad53..d46cb8cd775b888334d2ed6ad6b2fb60880cb7d7 100644 --- a/libphobos/libdruntime/core/internal/gc/proxy.d +++ b/libphobos/libdruntime/core/internal/gc/proxy.d @@ -246,6 +246,26 @@ extern (C) return instance.allocatedInCurrentThread(); } + void[] gc_getArrayUsed(void *ptr, bool atomic) nothrow + { + return instance.getArrayUsed( ptr, atomic ); + } + + bool gc_expandArrayUsed(void[] slice, size_t newUsed, bool atomic) nothrow + { + return instance.expandArrayUsed( slice, newUsed, atomic ); + } + + size_t gc_reserveArrayCapacity(void[] slice, size_t request, bool atomic) nothrow + { + return instance.reserveArrayCapacity( slice, request, atomic ); + } + + bool gc_shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic) nothrow + { + return instance.shrinkArrayUsed( slice, existingUsed, atomic ); + } + GC gc_getProxy() nothrow { return instance; diff --git a/libphobos/libdruntime/core/internal/traits.d b/libphobos/libdruntime/core/internal/traits.d index 02898082e402a6010f9e8e8e05a4e40e6754a4d2..b1b29130eff308cb6af8453b64d7a861dd95b003 100644 --- a/libphobos/libdruntime/core/internal/traits.d +++ b/libphobos/libdruntime/core/internal/traits.d @@ -538,18 +538,173 @@ unittest template hasIndirections(T) { - static if (is(T == struct) || is(T == union)) + static if (is(T == enum)) + enum hasIndirections = hasIndirections!(OriginalType!T); + else static if (is(T == struct) || is(T == union)) enum hasIndirections = anySatisfy!(.hasIndirections, typeof(T.tupleof)); else static if (__traits(isAssociativeArray, T) || is(T == class) || is(T == interface)) enum hasIndirections = true; else static if (is(T == E[N], E, size_t N)) - enum hasIndirections = T.sizeof && is(E == void) ? true : hasIndirections!(BaseElemOf!E); + enum hasIndirections = T.sizeof && (is(E == void) || hasIndirections!(BaseElemOf!E)); else static if (isFunctionPointer!T) enum hasIndirections = false; else enum hasIndirections = isPointer!T || isDelegate!T || isDynamicArray!T; } +@safe unittest +{ + static assert(!hasIndirections!int); + static assert(!hasIndirections!(const int)); + + static assert( hasIndirections!(int*)); + static assert( hasIndirections!(const int*)); + + static assert( hasIndirections!(int[])); + static assert(!hasIndirections!(int[42])); + static assert(!hasIndirections!(int[0])); + + static assert( hasIndirections!(int*)); + static assert( hasIndirections!(int*[])); + static assert( hasIndirections!(int*[42])); + static assert(!hasIndirections!(int*[0])); + + static assert( hasIndirections!string); + static assert( hasIndirections!(string[])); + static assert( hasIndirections!(string[42])); + static assert(!hasIndirections!(string[0])); + + static assert( hasIndirections!(void[])); + static assert( hasIndirections!(void[17])); + static assert(!hasIndirections!(void[0])); + + static assert( hasIndirections!(string[int])); + static assert( hasIndirections!(string[int]*)); + static assert( hasIndirections!(string[int][])); + static assert( hasIndirections!(string[int][12])); + static assert(!hasIndirections!(string[int][0])); + + static assert(!hasIndirections!(int function(string))); + static assert( hasIndirections!(int delegate(string))); + static assert(!hasIndirections!(const(int function(string)))); + static assert( hasIndirections!(const(int delegate(string)))); + static assert(!hasIndirections!(immutable(int function(string)))); + static assert( hasIndirections!(immutable(int delegate(string)))); + + static class C {} + static assert( hasIndirections!C); + + static interface I {} + static assert( hasIndirections!I); + + { + enum E : int { a } + static assert(!hasIndirections!E); + } + { + enum E : int* { a } + static assert( hasIndirections!E); + } + { + enum E : string { a = "" } + static assert( hasIndirections!E); + } + { + enum E : int[] { a = null } + static assert( hasIndirections!E); + } + { + enum E : int[3] { a = [1, 2, 3] } + static assert(!hasIndirections!E); + } + { + enum E : int*[3] { a = [null, null, null] } + static assert( hasIndirections!E); + } + { + enum E : int*[0] { a = int*[0].init } + static assert(!hasIndirections!E); + } + { + enum E : C { a = null } + static assert( hasIndirections!E); + } + { + enum E : I { a = null } + static assert( hasIndirections!E); + } + + { + static struct S {} + static assert(!hasIndirections!S); + + enum E : S { a = S.init } + static assert(!hasIndirections!S); + } + { + static struct S { int i; } + static assert(!hasIndirections!S); + + enum E : S { a = S.init } + static assert(!hasIndirections!S); + } + { + static struct S { C c; } + static assert( hasIndirections!S); + + enum E : S { a = S.init } + static assert( hasIndirections!S); + } + { + static struct S { int[] arr; } + static assert( hasIndirections!S); + + enum E : S { a = S.init } + static assert( hasIndirections!S); + } + { + int local; + struct S { void foo() { ++local; } } + static assert( hasIndirections!S); + + enum E : S { a = S.init } + static assert( hasIndirections!S); + } + + { + static union U {} + static assert(!hasIndirections!U); + } + { + static union U { int i; } + static assert(!hasIndirections!U); + } + { + static union U { C c; } + static assert( hasIndirections!U); + } + { + static union U { int[] arr; } + static assert( hasIndirections!U); + } +} + +// https://issues.dlang.org/show_bug.cgi?id=12000 +@safe unittest +{ + static struct S(T) + { + static assert(hasIndirections!T); + } + + static class A(T) + { + S!A a; + } + + A!int dummy; +} + template hasUnsharedIndirections(T) { static if (is(T == immutable)) diff --git a/libphobos/libdruntime/core/sys/posix/fcntl.d b/libphobos/libdruntime/core/sys/posix/fcntl.d index c0e9006a04381ae1a1c658a341ab31500003a1f3..3da9fd72e6d7da378cf52855779aa19b1b97b8cd 100644 --- a/libphobos/libdruntime/core/sys/posix/fcntl.d +++ b/libphobos/libdruntime/core/sys/posix/fcntl.d @@ -475,6 +475,7 @@ else version (Darwin) enum F_UNLCK = 2; enum F_WRLCK = 3; + enum O_NOFOLLOW = 0x0100; enum O_CREAT = 0x0200; enum O_EXCL = 0x0800; enum O_NOCTTY = 0; diff --git a/libphobos/libdruntime/core/sys/windows/aclapi.d b/libphobos/libdruntime/core/sys/windows/aclapi.d index 4905351b565c328fec4fa2ae3b1460d57606d41f..0b9f755ed65572497adf4fe22ac3a91f2bf49f8c 100644 --- a/libphobos/libdruntime/core/sys/windows/aclapi.d +++ b/libphobos/libdruntime/core/sys/windows/aclapi.d @@ -15,7 +15,7 @@ pragma(lib, "advapi32"); import core.sys.windows.accctrl, core.sys.windows.basetyps, core.sys.windows.w32api, core.sys.windows.winnt; -extern (Windows) { +extern (Windows) nothrow @nogc { VOID BuildExplicitAccessWithNameA(PEXPLICIT_ACCESS_A, LPSTR, DWORD, ACCESS_MODE, DWORD); VOID BuildExplicitAccessWithNameW(PEXPLICIT_ACCESS_W, LPWSTR, DWORD, diff --git a/libphobos/libdruntime/core/sys/windows/aclui.d b/libphobos/libdruntime/core/sys/windows/aclui.d index 08be626ad2d6c1b40e35bbc021412fb2f91fb764..d7db31738f950a5cb85033106e7acc2a71c0f515 100644 --- a/libphobos/libdruntime/core/sys/windows/aclui.d +++ b/libphobos/libdruntime/core/sys/windows/aclui.d @@ -114,7 +114,7 @@ alias ISecurityInformation LPSECURITYINFO; // FIXME: linkage attribute? extern (C) /+DECLSPEC_IMPORT+/ extern const IID IID_ISecurityInformation; -extern (Windows) { +extern (Windows) nothrow @nogc { HPROPSHEETPAGE CreateSecurityPage(LPSECURITYINFO psi); BOOL EditSecurity(HWND hwndOwner, LPSECURITYINFO psi); } diff --git a/libphobos/libdruntime/core/sys/windows/commctrl.d b/libphobos/libdruntime/core/sys/windows/commctrl.d index fe4ebbc75d8fd4a52b1dd4231b3bfb21f44cef69..073587f01c152efe43610546c3832c7b70457859 100644 --- a/libphobos/libdruntime/core/sys/windows/commctrl.d +++ b/libphobos/libdruntime/core/sys/windows/commctrl.d @@ -5014,7 +5014,7 @@ BOOL Animate_Seek(HWND hwnd, int frame) { return Animate_Play(hwnd, frame, frame, 1); } -extern (Windows) { +extern (Windows) nothrow @nogc { HBITMAP CreateMappedBitmap(HINSTANCE, INT_PTR, UINT, LPCOLORMAP, int); HWND CreateStatusWindowA(LONG, LPCSTR, HWND, UINT); HWND CreateStatusWindowW(LONG, LPCWSTR, HWND, UINT); @@ -5068,7 +5068,7 @@ BOOL DateTime_SetSystemtime(HWND hwnd, WPARAM flag, LPSYSTEMTIME lpSysTime) { cast(LPARAM) lpSysTime); } -extern (Windows) { +extern (Windows) nothrow @nogc { void DrawInsert(HWND, HWND, int); void DrawStatusTextA(HDC, LPRECT, LPCSTR, UINT); void DrawStatusTextW(HDC, LPRECT, LPCWSTR, UINT); @@ -5142,7 +5142,7 @@ static if (_WIN32_IE >= 0x400) { } } -extern (Windows) { +extern (Windows) nothrow @nogc { HDSA DSA_Create(INT, INT); BOOL DSA_Destroy(HDSA); VOID DSA_DestroyCallback(HDSA, PFNDSAENUMCALLBACK, PVOID); @@ -5758,7 +5758,7 @@ BOOL MonthCal_SetRange(HWND w, DWORD f, LPSYSTEMTIME st) { cast(LPARAM) st); } -extern (Windows) BOOL ShowHideMenuCtl(HWND, UINT_PTR, PINT); +extern (Windows) nothrow @nogc BOOL ShowHideMenuCtl(HWND, UINT_PTR, PINT); BOOL TabCtrl_GetItem(HWND w, int i, LPTCITEM p) { return cast(BOOL) SendMessage(w, TCM_GETITEM, i, cast(LPARAM) p); @@ -6069,7 +6069,7 @@ static if (_WIN32_IE >= 0x300) { return cast(BOOL) SendMessage(w, LVM_SETITEMCOUNT, i, cast(LPARAM) f); } - extern (Windows) { + extern (Windows) nothrow @nogc { WINBOOL ImageList_SetImageCount(HIMAGELIST, UINT); WINBOOL ImageList_Copy(HIMAGELIST, int, HIMAGELIST, int, UINT); WINBOOL ImageList_DrawIndirect(IMAGELISTDRAWPARAMS*); diff --git a/libphobos/libdruntime/core/sys/windows/custcntl.d b/libphobos/libdruntime/core/sys/windows/custcntl.d index f9234ac4f893e82da094093eccc0721ac2c1840e..ddac9607382c8ad2f9e6ed16332645623d523c5f 100644 --- a/libphobos/libdruntime/core/sys/windows/custcntl.d +++ b/libphobos/libdruntime/core/sys/windows/custcntl.d @@ -99,6 +99,7 @@ extern (Windows) { alias INT function(DWORD, DWORD, HFONT, LPWSTR) LPFNCCSIZETOTEXTW; alias UINT function(LPCCINFOA) LPFNCCINFOA; alias UINT function(LPCCINFOW) LPFNCCINFOW; +nothrow @nogc: UINT CustomControlInfoA(LPCCINFOA acci); UINT CustomControlInfoW(LPCCINFOW acci); } diff --git a/libphobos/libdruntime/core/sys/windows/dde.d b/libphobos/libdruntime/core/sys/windows/dde.d index d9b8bec515219cafce6a25e5206d444acea930e2..975c62e42f31a642c9472c187702899328b5b610 100644 --- a/libphobos/libdruntime/core/sys/windows/dde.d +++ b/libphobos/libdruntime/core/sys/windows/dde.d @@ -148,7 +148,7 @@ deprecated struct DDEUP { @property bool fAckReq(bool f) { _bf = cast(ushort) ((_bf & ~0x8000) | (f << 15)); return f; } } -extern (Windows) { +extern (Windows) nothrow @nogc { BOOL DdeSetQualityOfService(HWND, const(SECURITY_QUALITY_OF_SERVICE)*, PSECURITY_QUALITY_OF_SERVICE); BOOL ImpersonateDdeClientWindow(HWND, HWND); diff --git a/libphobos/libdruntime/core/sys/windows/ddeml.d b/libphobos/libdruntime/core/sys/windows/ddeml.d index 2e8e3ee25d65adaa51d8712c93cc2eb3e52c405b..0ff1a3f4f1eb704fbad422f5de1edcb72c17b748 100644 --- a/libphobos/libdruntime/core/sys/windows/ddeml.d +++ b/libphobos/libdruntime/core/sys/windows/ddeml.d @@ -332,7 +332,7 @@ struct MONMSGSTRUCT { } alias MONMSGSTRUCT* PMONMSGSTRUCT; -extern (Windows) { +extern (Windows) nothrow @nogc { BOOL DdeAbandonTransaction(DWORD, HCONV, DWORD); PBYTE DdeAccessData(HDDEDATA, PDWORD); HDDEDATA DdeAddData(HDDEDATA, PBYTE, DWORD, DWORD); diff --git a/libphobos/libdruntime/core/sys/windows/dhcpcsdk.d b/libphobos/libdruntime/core/sys/windows/dhcpcsdk.d index d21169a7346cebe8d47cfb69be7cb09190fdf611..6171469c8c7317dd1f0c2e1e59d03e8225009798 100644 --- a/libphobos/libdruntime/core/sys/windows/dhcpcsdk.d +++ b/libphobos/libdruntime/core/sys/windows/dhcpcsdk.d @@ -45,7 +45,7 @@ struct DHCPCAPI_PARAMS_ARRAY { } alias DHCPCAPI_PARAMS_ARRAY* PDHCPCAPI_PARAMS_ARRAY, LPDHCPCAPI_PARAMS_ARRAY; -extern (Windows) { +extern (Windows) nothrow @nogc { void DhcpCApiCleanup(); DWORD DhcpCApiInitialize(LPDWORD); DWORD DhcpDeRegisterParamChange(DWORD, LPVOID, LPVOID); diff --git a/libphobos/libdruntime/core/sys/windows/errorrep.d b/libphobos/libdruntime/core/sys/windows/errorrep.d index 42fad9a39ec37814b7018811c1d7a8564aa3e92a..840ea61f3ceb088c014aeb4a868d21d5d7a36987 100644 --- a/libphobos/libdruntime/core/sys/windows/errorrep.d +++ b/libphobos/libdruntime/core/sys/windows/errorrep.d @@ -29,7 +29,7 @@ enum EFaultRepRetVal { frrvOkHeadless // = 7 } -extern (Windows) { +extern (Windows) nothrow @nogc { BOOL AddERExcludedApplicationA(LPCSTR); BOOL AddERExcludedApplicationW(LPCWSTR); EFaultRepRetVal ReportFault(LPEXCEPTION_POINTERS, DWORD); diff --git a/libphobos/libdruntime/core/sys/windows/httpext.d b/libphobos/libdruntime/core/sys/windows/httpext.d index 200b2843c4df6253a5a0336d042bcebad3e21212..b7f6aedcbdcfbf93aad6d6f0bc760b370c765ee5 100644 --- a/libphobos/libdruntime/core/sys/windows/httpext.d +++ b/libphobos/libdruntime/core/sys/windows/httpext.d @@ -109,7 +109,7 @@ struct HSE_SEND_HEADER_EX_INFO { } alias HSE_SEND_HEADER_EX_INFO* LPHSE_SEND_HEADER_EX_INF; -extern (Windows) { +extern (Windows) nothrow @nogc { BOOL GetExtensionVersion(HSE_VERSION_INFO*); DWORD HttpExtensionProc(EXTENSION_CONTROL_BLOCK*); BOOL TerminateExtension(DWORD); diff --git a/libphobos/libdruntime/core/sys/windows/imagehlp.d b/libphobos/libdruntime/core/sys/windows/imagehlp.d index 68d88d787807dbb9b21e9d56071545b3d60472a3..c6d28e68fd124188817902cd703d86cf73c61e55 100644 --- a/libphobos/libdruntime/core/sys/windows/imagehlp.d +++ b/libphobos/libdruntime/core/sys/windows/imagehlp.d @@ -295,6 +295,7 @@ extern (Windows) { alias BOOL function(DIGEST_HANDLE refdata, PBYTE pData, DWORD dwLength) DIGEST_FUNCTION; +nothrow @nogc: PIMAGE_NT_HEADERS CheckSumMappedFile(LPVOID, DWORD, LPDWORD, LPDWORD); DWORD MapFileAndCheckSumA(LPSTR, LPDWORD, LPDWORD); DWORD MapFileAndCheckSumW(PWSTR, LPDWORD, LPDWORD); diff --git a/libphobos/libdruntime/core/sys/windows/intshcut.d b/libphobos/libdruntime/core/sys/windows/intshcut.d index ab662e418e7da47059a760b2c28c1dbbcdc4fe8d..cc4bf6235425650051cf9f4a4585cbba7442dac5 100644 --- a/libphobos/libdruntime/core/sys/windows/intshcut.d +++ b/libphobos/libdruntime/core/sys/windows/intshcut.d @@ -70,7 +70,7 @@ interface IUniformResourceLocator : IUnknown { alias IUniformResourceLocator PIUniformResourceLocator, PCIUniformResourceLocator; -extern (Windows) { +extern (Windows) nothrow @nogc { BOOL InetIsOffline(DWORD); HRESULT MIMEAssociationDialogA(HWND, DWORD, PCSTR, PCSTR, PSTR, UINT); HRESULT MIMEAssociationDialogW(HWND, DWORD, PCWSTR, PCWSTR, PWSTR, UINT); diff --git a/libphobos/libdruntime/core/sys/windows/iphlpapi.d b/libphobos/libdruntime/core/sys/windows/iphlpapi.d index 4a8e64cbef018b789a7d201bf61c9c8994cdff3f..d8f0948ca3f5b8b0a5bec53be9b0a10e60a019be 100644 --- a/libphobos/libdruntime/core/sys/windows/iphlpapi.d +++ b/libphobos/libdruntime/core/sys/windows/iphlpapi.d @@ -13,7 +13,7 @@ version (Windows): import core.sys.windows.ipexport, core.sys.windows.iprtrmib, core.sys.windows.iptypes; import core.sys.windows.winbase, core.sys.windows.windef; -extern (Windows) { +extern (Windows) nothrow @nogc { DWORD AddIPAddress(IPAddr, IPMask, DWORD, PULONG, PULONG); DWORD CreateIpForwardEntry(PMIB_IPFORWARDROW); DWORD CreateIpNetEntry(PMIB_IPNETROW); diff --git a/libphobos/libdruntime/core/sys/windows/lmaccess.d b/libphobos/libdruntime/core/sys/windows/lmaccess.d index 9791ff6e391722b039756d2e0b3f93595f35f51d..cf6f9d4c4d199eebb1bed712b93f26c63c12dee1 100644 --- a/libphobos/libdruntime/core/sys/windows/lmaccess.d +++ b/libphobos/libdruntime/core/sys/windows/lmaccess.d @@ -708,7 +708,7 @@ struct NETLOGON_INFO_3{ } alias NETLOGON_INFO_3* PNETLOGON_INFO_3; -extern (Windows) { +extern (Windows) nothrow @nogc { deprecated { /* These are obsolete */ NET_API_STATUS NetAccessAdd(LPCWSTR,DWORD,PBYTE,PDWORD); diff --git a/libphobos/libdruntime/core/sys/windows/lmalert.d b/libphobos/libdruntime/core/sys/windows/lmalert.d index ad0c3ca7b18e64ce8797227cb8aa55e50aef4c8f..acb96cfbc907414291f3c1d1ecdcc572c0aba88d 100644 --- a/libphobos/libdruntime/core/sys/windows/lmalert.d +++ b/libphobos/libdruntime/core/sys/windows/lmalert.d @@ -71,7 +71,7 @@ struct USER_OTHER_INFO{ } alias USER_OTHER_INFO* PUSER_OTHER_INFO, LPUSER_OTHER_INFO; -extern (Windows) { +extern (Windows) nothrow @nogc { NET_API_STATUS NetAlertRaise(LPCWSTR,PVOID,DWORD); NET_API_STATUS NetAlertRaiseEx(LPCWSTR,PVOID,DWORD,LPCWSTR); } diff --git a/libphobos/libdruntime/core/sys/windows/lmapibuf.d b/libphobos/libdruntime/core/sys/windows/lmapibuf.d index e8559543f3eafb51810fabd8b259549800c4a8c7..85ccd1f49f25cfb63312d5612fc5329bf5a4124b 100644 --- a/libphobos/libdruntime/core/sys/windows/lmapibuf.d +++ b/libphobos/libdruntime/core/sys/windows/lmapibuf.d @@ -12,7 +12,7 @@ pragma(lib, "netapi32"); import core.sys.windows.lmcons, core.sys.windows.windef; -extern (Windows) { +extern (Windows) nothrow @nogc { NET_API_STATUS NetApiBufferAllocate(DWORD, PVOID*); NET_API_STATUS NetApiBufferFree(PVOID); NET_API_STATUS NetApiBufferReallocate(PVOID, DWORD, PVOID*); diff --git a/libphobos/libdruntime/core/sys/windows/lmmsg.d b/libphobos/libdruntime/core/sys/windows/lmmsg.d index a3abd60542268857be5dee19e3ebf687c4721334..eab878827472d0b3f75ffbd2070852a5de0e8732 100644 --- a/libphobos/libdruntime/core/sys/windows/lmmsg.d +++ b/libphobos/libdruntime/core/sys/windows/lmmsg.d @@ -32,7 +32,7 @@ struct MSG_INFO_1 { } alias MSG_INFO_1* PMSG_INFO_1, LPMSG_INFO_1; -extern (Windows) { +extern (Windows) nothrow @nogc { NET_API_STATUS NetMessageBufferSend(LPCWSTR, LPCWSTR, LPCWSTR, PBYTE, DWORD); NET_API_STATUS NetMessageNameAdd(LPCWSTR, LPCWSTR); diff --git a/libphobos/libdruntime/core/sys/windows/lmremutl.d b/libphobos/libdruntime/core/sys/windows/lmremutl.d index 8c90df73f2a30c90453c3cf8b5b6aa741c6d7e27..196be667fecaa7ee9705e9228ac947f31e8ee45d 100644 --- a/libphobos/libdruntime/core/sys/windows/lmremutl.d +++ b/libphobos/libdruntime/core/sys/windows/lmremutl.d @@ -52,7 +52,7 @@ struct TIME_OF_DAY_INFO { } alias TIME_OF_DAY_INFO* PTIME_OF_DAY_INFO, LPTIME_OF_DAY_INFO; -extern (Windows) { +extern (Windows) nothrow @nogc { NET_API_STATUS NetRemoteTOD(LPCWSTR, PBYTE*); NET_API_STATUS NetRemoteComputerSupports(LPCWSTR, DWORD, PDWORD); NET_API_STATUS RxRemoteApi(DWORD, LPCWSTR, LPDESC, LPDESC, LPDESC, diff --git a/libphobos/libdruntime/core/sys/windows/mgmtapi.d b/libphobos/libdruntime/core/sys/windows/mgmtapi.d index 8f84eea54e88fc88340b7754eac0f58632b07405..336d24e750e07cae14d9b1d9c2c134ee2089494d 100644 --- a/libphobos/libdruntime/core/sys/windows/mgmtapi.d +++ b/libphobos/libdruntime/core/sys/windows/mgmtapi.d @@ -29,7 +29,7 @@ enum MGMCTL_SETAGENTPORT = 1; alias PVOID LPSNMP_MGR_SESSION; -extern (Windows) { +extern (Windows) nothrow @nogc { BOOL SnmpMgrClose(LPSNMP_MGR_SESSION); BOOL SnmpMgrCtl(LPSNMP_MGR_SESSION, DWORD, LPVOID, DWORD, LPVOID, DWORD, LPDWORD); diff --git a/libphobos/libdruntime/core/sys/windows/ntdll.d b/libphobos/libdruntime/core/sys/windows/ntdll.d index 28d560c537b31423340917879a9604a482c70475..7f0c47a15fc2ce0db6c4492d3cb9c059931523ac 100644 --- a/libphobos/libdruntime/core/sys/windows/ntdll.d +++ b/libphobos/libdruntime/core/sys/windows/ntdll.d @@ -19,4 +19,4 @@ enum SHUTDOWN_ACTION { ShutdownPowerOff } -extern (Windows) uint NtShutdownSystem(SHUTDOWN_ACTION Action); +extern (Windows) nothrow @nogc uint NtShutdownSystem(SHUTDOWN_ACTION Action); diff --git a/libphobos/libdruntime/core/sys/windows/ntsecapi.d b/libphobos/libdruntime/core/sys/windows/ntsecapi.d index fa08a74ee5bb557294004f99a77ae070a1954e8e..77101f669b4a6b3b9f1ade758522fdb2e642a334 100644 --- a/libphobos/libdruntime/core/sys/windows/ntsecapi.d +++ b/libphobos/libdruntime/core/sys/windows/ntsecapi.d @@ -726,7 +726,7 @@ struct TRUSTED_DOMAIN_FULL_INFORMATION { } alias TRUSTED_DOMAIN_FULL_INFORMATION* PTRUSTED_DOMAIN_FULL_INFORMATION; -extern (Windows) { +extern (Windows) nothrow @nogc { NTSTATUS LsaAddAccountRights(LSA_HANDLE, PSID, PLSA_UNICODE_STRING, ULONG); NTSTATUS LsaCallAuthenticationPackage(HANDLE, ULONG, PVOID, ULONG, diff --git a/libphobos/libdruntime/core/sys/windows/ole.d b/libphobos/libdruntime/core/sys/windows/ole.d index b9e55a3b1298abdf723d61e5c38a8c85ae810b54..7c51d7a1be83084b5a853025da1ff1df74288854 100644 --- a/libphobos/libdruntime/core/sys/windows/ole.d +++ b/libphobos/libdruntime/core/sys/windows/ole.d @@ -283,7 +283,7 @@ struct OLESERVERDOC { } alias OLESERVERDOC* LPOLESERVERDOC; -extern (Windows) { +extern (Windows) nothrow @nogc { OLESTATUS OleDelete(LPOLEOBJECT); OLESTATUS OleRelease(LPOLEOBJECT); OLESTATUS OleSaveToStream(LPOLEOBJECT, LPOLESTREAM); diff --git a/libphobos/libdruntime/core/sys/windows/ole2.d b/libphobos/libdruntime/core/sys/windows/ole2.d index 3fef058411f8b17d2ec0151a561b976667a98514..fcbbc8a3d2cb6d3ebb5a72d60c22434c245b173d 100644 --- a/libphobos/libdruntime/core/sys/windows/ole2.d +++ b/libphobos/libdruntime/core/sys/windows/ole2.d @@ -48,7 +48,7 @@ extern (Windows) { } alias OLESTREAMVTBL* LPOLESTREAMVTBL; -extern (Windows) { +extern (Windows) nothrow @nogc { HRESULT CreateDataAdviseHolder(LPDATAADVISEHOLDER*); DWORD OleBuildVersion(); HRESULT ReadClassStg(LPSTORAGE, CLSID*); diff --git a/libphobos/libdruntime/core/sys/windows/oleacc.d b/libphobos/libdruntime/core/sys/windows/oleacc.d index b19855d9af078042fff14fe29a267fff715adf19..8a97a93e34796b7d491c64029b77a3e094411d8c 100644 --- a/libphobos/libdruntime/core/sys/windows/oleacc.d +++ b/libphobos/libdruntime/core/sys/windows/oleacc.d @@ -186,7 +186,7 @@ interface IAccessible : IDispatch { alias IAccessible LPACCESSIBLE; -extern (Windows) { +extern (Windows) nothrow @nogc { HRESULT AccessibleChildren(IAccessible, LONG, LONG, VARIANT*, LONG*); HRESULT AccessibleObjectFromEvent(HWND, DWORD, DWORD, IAccessible, VARIANT*); HRESULT AccessibleObjectFromPoint(POINT, IAccessible*, VARIANT*); diff --git a/libphobos/libdruntime/core/sys/windows/oleauto.d b/libphobos/libdruntime/core/sys/windows/oleauto.d index 188813609ff683a301989ba122619978a090b7e5..f4bb40259ab8630949a9cce6dee645b9c22435d2 100644 --- a/libphobos/libdruntime/core/sys/windows/oleauto.d +++ b/libphobos/libdruntime/core/sys/windows/oleauto.d @@ -226,7 +226,7 @@ deprecated { // not actually deprecated, but they aren't converted yet. alias ICreateTypeLib2 LPCREATETYPELIB2; } -extern (Windows) { +extern (Windows) nothrow @nogc { BSTR SysAllocString(const(OLECHAR)*); int SysReAllocString(BSTR*, const(OLECHAR)*); BSTR SysAllocStringLen(const(OLECHAR)*, uint); diff --git a/libphobos/libdruntime/core/sys/windows/oledlg.d b/libphobos/libdruntime/core/sys/windows/oledlg.d index f810f6c13dae3af639aeba63550f64f458794db9..26e0a791609e1605855f202fc03ccb87db207c73 100644 --- a/libphobos/libdruntime/core/sys/windows/oledlg.d +++ b/libphobos/libdruntime/core/sys/windows/oledlg.d @@ -387,7 +387,7 @@ struct OLEUIINSERTOBJECTA { } alias OLEUIINSERTOBJECTA* POLEUIINSERTOBJECTA, LPOLEUIINSERTOBJECTA; -extern (Windows) { +extern (Windows) nothrow @nogc { UINT OleUIInsertObjectW(LPOLEUIINSERTOBJECTW); UINT OleUIInsertObjectA(LPOLEUIINSERTOBJECTA); } @@ -849,7 +849,7 @@ struct OLEUIOBJECTPROPSA { } alias OLEUIOBJECTPROPSA* POLEUIOBJECTPROPSA, LPOLEUIOBJECTPROPSA; -extern (Windows) { +extern (Windows) nothrow @nogc { BOOL OleUIAddVerbMenuW(LPOLEOBJECT, LPCWSTR, HMENU, UINT, UINT, UINT, BOOL, UINT, HMENU*); BOOL OleUIAddVerbMenuA(LPOLEOBJECT, LPCSTR, HMENU, UINT, UINT, UINT, BOOL, UINT, HMENU*); UINT OleUIBusyW(LPOLEUIBUSYW); diff --git a/libphobos/libdruntime/core/sys/windows/prsht.d b/libphobos/libdruntime/core/sys/windows/prsht.d index 053b1fa2ac98945f72c3eff6260842ec9acd32e4..570d1689b38c1c48b4d0658b4aeab0427b9a8d97 100644 --- a/libphobos/libdruntime/core/sys/windows/prsht.d +++ b/libphobos/libdruntime/core/sys/windows/prsht.d @@ -331,7 +331,7 @@ struct PSHNOTIFY { } alias PSHNOTIFY* LPPSHNOTIFY; -extern (Windows) { +extern (Windows) nothrow @nogc { HPROPSHEETPAGE CreatePropertySheetPageA(LPCPROPSHEETPAGEA); HPROPSHEETPAGE CreatePropertySheetPageW(LPCPROPSHEETPAGEW); BOOL DestroyPropertySheetPage(HPROPSHEETPAGE); diff --git a/libphobos/libdruntime/core/sys/windows/psapi.d b/libphobos/libdruntime/core/sys/windows/psapi.d index 968ce6c45306c9697396c35c76a1e8876e863119..42fc5fdd6bd9db19446d56fdc301a3a520f8b06a 100644 --- a/libphobos/libdruntime/core/sys/windows/psapi.d +++ b/libphobos/libdruntime/core/sys/windows/psapi.d @@ -90,7 +90,7 @@ alias BOOL function(LPVOID, PENUM_PAGE_FILE_INFORMATION, LPCSTR) // Grouped by application, not in alphabetical order. -extern (Windows) { +extern (Windows) nothrow @nogc { /* Process Information * http://windowssdk.msdn.microsoft.com/library/ms684870.aspx */ BOOL EnumProcesses(DWORD*, DWORD, DWORD*); /* NT/2000/XP/Server2003/Vista/Longhorn */ diff --git a/libphobos/libdruntime/core/sys/windows/rpc.d b/libphobos/libdruntime/core/sys/windows/rpc.d index b432bc7c6811d3e75a895972104ac1c7e623f4ed..3b7e28d213c434d3b0d6ec2cfd5f69269e7551ba 100644 --- a/libphobos/libdruntime/core/sys/windows/rpc.d +++ b/libphobos/libdruntime/core/sys/windows/rpc.d @@ -26,6 +26,6 @@ public import core.sys.windows.winerror; alias MIDL_user_allocate midl_user_allocate; alias MIDL_user_free midl_user_free; -extern (Windows) { +extern (Windows) nothrow @nogc { int I_RpcMapWin32Status(RPC_STATUS); } diff --git a/libphobos/libdruntime/core/sys/windows/winhttp.d b/libphobos/libdruntime/core/sys/windows/winhttp.d index 63d59cb157c49af79c5cc2ff7010222777e8064b..1de3b9366a9f9b9ffa966025fefc3f094328de26 100644 --- a/libphobos/libdruntime/core/sys/windows/winhttp.d +++ b/libphobos/libdruntime/core/sys/windows/winhttp.d @@ -771,7 +771,7 @@ static if (_WIN32_WINNT >= 0x602) } -extern (Windows) { +extern (Windows) nothrow @nogc { BOOL WinHttpAddRequestHeaders(HINTERNET hRequest, LPCWSTR pwszHeaders, DWORD dwHeadersLength, DWORD dwModifiers); BOOL WinHttpCheckPlatform(); diff --git a/libphobos/libdruntime/core/sys/windows/winnetwk.d b/libphobos/libdruntime/core/sys/windows/winnetwk.d index 9bf2e64614a12ece59a77b4567a0c834ff3fa701..568c2e89e4f9d88b4c1cda93f61e12a4bcdb5a85 100644 --- a/libphobos/libdruntime/core/sys/windows/winnetwk.d +++ b/libphobos/libdruntime/core/sys/windows/winnetwk.d @@ -315,7 +315,7 @@ struct NETCONNECTINFOSTRUCT { } alias NETCONNECTINFOSTRUCT* LPNETCONNECTINFOSTRUCT; -extern (Windows) { +extern (Windows) nothrow @nogc { DWORD WNetAddConnection2A(LPNETRESOURCEA, LPCSTR, LPCSTR, DWORD); DWORD WNetAddConnection2W(LPNETRESOURCEW, LPCWSTR, LPCWSTR, DWORD); DWORD WNetAddConnection3A(HWND, LPNETRESOURCEA, LPCSTR, LPCSTR, DWORD); diff --git a/libphobos/libdruntime/core/sys/windows/winsvc.d b/libphobos/libdruntime/core/sys/windows/winsvc.d index e6418ac8ac6b49497518bd55e1b4c061b13b2030..a34cd2582057f666f1bb74dab1739b8c03667ad6 100644 --- a/libphobos/libdruntime/core/sys/windows/winsvc.d +++ b/libphobos/libdruntime/core/sys/windows/winsvc.d @@ -272,7 +272,7 @@ static if (_WIN32_WINNT >= 0x500) { alias SERVICE_FAILURE_ACTIONSW* LPSERVICE_FAILURE_ACTIONSW; } -extern (Windows) { +extern (Windows) nothrow @nogc { BOOL ChangeServiceConfigA(SC_HANDLE, DWORD, DWORD, DWORD, LPCSTR, LPCSTR, LPDWORD, LPCSTR, LPCSTR, LPCSTR, LPCSTR); BOOL ChangeServiceConfigW(SC_HANDLE, DWORD, DWORD, DWORD, LPCWSTR, diff --git a/libphobos/libdruntime/core/sys/windows/winuser.d b/libphobos/libdruntime/core/sys/windows/winuser.d index 469e68e666844e321c499271d21c156a4247707c..f6414440783a57a93cf1de3cbc1515bffd48b845 100644 --- a/libphobos/libdruntime/core/sys/windows/winuser.d +++ b/libphobos/libdruntime/core/sys/windows/winuser.d @@ -3848,7 +3848,7 @@ int GetMenuStringA(HMENU, UINT, LPSTR, int, UINT); int GetMenuStringW(HMENU, UINT, LPWSTR, int, UINT); BOOL GetMessageA(LPMSG, HWND, UINT, UINT); BOOL GetMessageW(LPMSG, HWND, UINT, UINT); -LONG GetMessageExtraInfo(); +LPARAM GetMessageExtraInfo(); DWORD GetMessagePos(); LONG GetMessageTime(); diff --git a/libphobos/libdruntime/core/sys/windows/winver.d b/libphobos/libdruntime/core/sys/windows/winver.d index dc3d881d2c4ef7f21f84975d493ca12ed5105453..646891b1be9ad5946c0197c5dcdf16817e235943 100644 --- a/libphobos/libdruntime/core/sys/windows/winver.d +++ b/libphobos/libdruntime/core/sys/windows/winver.d @@ -139,7 +139,7 @@ struct VS_FIXEDFILEINFO { DWORD dwFileDateLS; } -extern (Windows) { +extern (Windows) nothrow @nogc { DWORD VerFindFileA(DWORD, LPCSTR, LPCSTR, LPCSTR, LPSTR, PUINT, LPSTR, PUINT); DWORD VerFindFileW(DWORD, LPCWSTR, LPCWSTR, LPCWSTR, LPWSTR, PUINT, LPWSTR, diff --git a/libphobos/libdruntime/rt/lifetime.d b/libphobos/libdruntime/rt/lifetime.d index 86f5f820cc024ee43d880f2e088cbcf5cd2f2e3d..7851f664297fd5d6b4d62cce71044dbdb8beb17e 100644 --- a/libphobos/libdruntime/rt/lifetime.d +++ b/libphobos/libdruntime/rt/lifetime.d @@ -23,6 +23,14 @@ static import rt.tlsgc; alias BlkInfo = GC.BlkInfo; alias BlkAttr = GC.BlkAttr; +// for now, all GC array functions are not exposed via core.memory. +extern(C) { + void[] gc_getArrayUsed(void *ptr, bool atomic) nothrow; + bool gc_expandArrayUsed(void[] slice, size_t newUsed, bool atomic) nothrow; + size_t gc_reserveArrayCapacity(void[] slice, size_t request, bool atomic) nothrow; + bool gc_shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic) nothrow; +} + private { alias bool function(Object) CollectHandler; @@ -210,31 +218,13 @@ private BlkInfo __arrayAlloc(size_t arrsize, const scope TypeInfo ti, const Type { import core.checkedint; - size_t typeInfoSize = structTypeInfoSize(tinext); - size_t padsize = arrsize > MAXMEDSIZE ? LARGEPAD : ((arrsize > MAXSMALLSIZE ? MEDPAD : SMALLPAD) + typeInfoSize); - - bool overflow; - auto padded_size = addu(arrsize, padsize, overflow); - - if (overflow) - return BlkInfo(); - - uint attr = (!(tinext.flags & 1) ? BlkAttr.NO_SCAN : 0) | BlkAttr.APPENDABLE; - if (typeInfoSize) - attr |= BlkAttr.STRUCTFINAL | BlkAttr.FINALIZE; - - auto bi = GC.qalloc(padded_size, attr, tinext); - __arrayClearPad(bi, arrsize, padsize); - return bi; + return __arrayAlloc(arrsize, null, ti, tinext); } -private BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const scope TypeInfo ti, const TypeInfo tinext) +private BlkInfo __arrayAlloc(size_t arrsize, void* copyAttrsFrom, const scope TypeInfo ti, const TypeInfo tinext) nothrow pure { import core.checkedint; - if (!info.base) - return __arrayAlloc(arrsize, ti, tinext); - immutable padsize = __arrayPad(arrsize, tinext); bool overflow; auto padded_size = addu(arrsize, padsize, overflow); @@ -243,7 +233,18 @@ private BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const scope TypeI return BlkInfo(); } - auto bi = GC.qalloc(padded_size, info.attr, tinext); + uint attr = (!(tinext.flags & 1) ? BlkAttr.NO_SCAN : 0); + if (copyAttrsFrom) + { + // try to copy attrs from the given block + auto info = GC.query(copyAttrsFrom); + if (info.base) + attr = info.attr; + } + // always make sure the appendable attr is set. + attr |= BlkAttr.APPENDABLE; + + auto bi = GC.qalloc(padded_size, attr, tinext); __arrayClearPad(bi, arrsize, padsize); return bi; } @@ -260,54 +261,43 @@ Params: */ extern(C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) nothrow { - // note, we do not care about shared. We are setting the length no matter - // what, so no lock is required. debug(PRINTF) printf("_d_arrayshrinkfit, elemsize = %d, arr.ptr = x%x arr.length = %d\n", ti.next.tsize, arr.ptr, arr.length); auto tinext = unqualify(ti.next); auto size = tinext.tsize; // array element size - auto cursize = arr.length * size; + auto reqsize = arr.length * size; auto isshared = typeid(ti) is typeid(TypeInfo_Shared); - auto bic = isshared ? null : __getBlkInfo(arr.ptr); - auto info = bic ? *bic : GC.query(arr.ptr); - if (info.base && (info.attr & BlkAttr.APPENDABLE)) - { - auto newsize = (arr.ptr - __arrayStart(info)) + cursize; - debug(PRINTF) printf("setting allocated size to %d\n", (arr.ptr - info.base) + cursize); + auto curArr = gc_getArrayUsed(arr.ptr, isshared); + if (curArr.ptr is null) + // not a valid GC pointer + return; + + // align the array. + auto offset = arr.ptr - curArr.ptr; + auto cursize = curArr.length - offset; + if (cursize <= reqsize) + // invalid situation, or no change. + return; - // destroy structs that become unused memory when array size is shrinked - if (typeid(tinext) is typeid(TypeInfo_Struct)) // avoid a complete dynamic type cast + // if the type has a destructor, destroy elements we are about to remove. + if (typeid(tinext) is typeid(TypeInfo_Struct)) // avoid a complete dynamic type cast + { + auto sti = cast(TypeInfo_Struct)cast(void*)tinext; + if (sti.xdtor) { - auto sti = cast(TypeInfo_Struct)cast(void*)tinext; - if (sti.xdtor) + try { - auto oldsize = __arrayAllocLength(info, tinext); - if (oldsize > cursize) - { - try - { - finalize_array(arr.ptr + cursize, oldsize - cursize, sti); - } - catch (Exception e) - { - import core.exception : onFinalizeError; - onFinalizeError(sti, e); - } - } + finalize_array(arr.ptr + reqsize, cursize - reqsize, sti); + } + catch (Exception e) + { + import core.exception : onFinalizeError; + onFinalizeError(sti, e); } } - // Note: Since we "assume" the append is safe, it means it is not shared. - // Since it is not shared, we also know it won't throw (no lock). - if (!__setArrayAllocLength(info, newsize, false, tinext)) - { - import core.exception : onInvalidMemoryOperationError; - onInvalidMemoryOperationError(); - } - - // cache the block if not already done. - if (!isshared && !bic) - __insertBlkInfoCache(info, null); } + + gc_shrinkArrayUsed(arr.ptr[0 .. reqsize], cursize, isshared); } package bool hasPostblit(in TypeInfo ti) nothrow pure @@ -372,10 +362,7 @@ do import core.stdc.string; import core.exception : onOutOfMemoryError; - // step 1, get the block auto isshared = typeid(ti) is typeid(TypeInfo_Shared); - auto bic = isshared ? null : __getBlkInfo((*p).ptr); - auto info = bic ? *bic : GC.query((*p).ptr); auto tinext = unqualify(ti.next); auto size = tinext.tsize; version (D_InlineAsm_X86) @@ -416,77 +403,21 @@ Loverflow: assert(0); Lcontinue: - // step 2, get the actual "allocated" size. If the allocated size does not - // match what we expect, then we will need to reallocate anyways. - - // TODO: this probably isn't correct for shared arrays - size_t curallocsize = void; - size_t curcapacity = void; - size_t offset = void; - size_t arraypad = void; - if (info.base && (info.attr & BlkAttr.APPENDABLE)) - { - if (info.size <= 256) - { - arraypad = SMALLPAD + structTypeInfoSize(tinext); - curallocsize = *(cast(ubyte *)(info.base + info.size - arraypad)); - } - else if (info.size < PAGESIZE) - { - arraypad = MEDPAD + structTypeInfoSize(tinext); - curallocsize = *(cast(ushort *)(info.base + info.size - arraypad)); - } - else - { - curallocsize = *(cast(size_t *)(info.base)); - arraypad = LARGEPAD; - } - - - offset = (*p).ptr - __arrayStart(info); - if (offset + (*p).length * size != curallocsize) - { - curcapacity = 0; - } - else - { - // figure out the current capacity of the block from the point - // of view of the array. - curcapacity = info.size - offset - arraypad; - } - } - else - { - curallocsize = curcapacity = offset = 0; - } - debug(PRINTF) printf("_d_arraysetcapacity, p = x%d,%d, newcapacity=%d, info.size=%d, reqsize=%d, curallocsize=%d, curcapacity=%d, offset=%d\n", (*p).ptr, (*p).length, newcapacity, info.size, reqsize, curallocsize, curcapacity, offset); - - if (curcapacity >= reqsize) - { - // no problems, the current allocated size is large enough. - return curcapacity / size; - } - - // step 3, try to extend the array in place. - if (info.size >= PAGESIZE && curcapacity != 0) - { - auto extendsize = reqsize + offset + LARGEPAD - info.size; - auto u = GC.extend(info.base, extendsize, extendsize); - if (u) - { - // extend worked, save the new current allocated size - if (bic) - bic.size = u; // update cache - curcapacity = u - offset - LARGEPAD; - return curcapacity / size; - } - } - - // step 4, if extending doesn't work, allocate a new array with at least the requested allocated size. + // step 1, see if we can ensure the capacity is valid in-place auto datasize = (*p).length * size; - // copy attributes from original block, or from the typeinfo if the - // original block doesn't exist. - info = __arrayAlloc(reqsize, info, ti, tinext); + auto curCapacity = gc_reserveArrayCapacity((*p).ptr[0 .. datasize], reqsize, isshared); + if (curCapacity != 0) + // in-place worked! + return curCapacity / size; + + if (reqsize <= datasize) + // requested size is less than array size, the current array satisfies + // the request. But this is not an appendable GC array, so return 0. + return 0; + + // step 2, if reserving in-place doesn't work, allocate a new array with at + // least the requested allocated size. + auto info = __arrayAlloc(reqsize, (*p).ptr, ti, tinext); if (info.base is null) goto Loverflow; // copy the data over. @@ -512,22 +443,12 @@ Lcontinue: // set up the correct length __setArrayAllocLength(info, datasize, isshared, tinext); if (!isshared) - __insertBlkInfoCache(info, bic); + __insertBlkInfoCache(info, null); *p = (cast(void*)tgt)[0 .. (*p).length]; - // determine the padding. This has to be done manually because __arrayPad - // assumes you are not counting the pad size, and info.size does include - // the pad. - if (info.size <= 256) - arraypad = SMALLPAD + structTypeInfoSize(tinext); - else if (info.size < PAGESIZE) - arraypad = MEDPAD + structTypeInfoSize(tinext); - else - arraypad = LARGEPAD; - - curcapacity = info.size - arraypad; - return curcapacity / size; + curCapacity = __arrayAllocCapacity(info); + return curCapacity / size; } /** @@ -673,25 +594,12 @@ extern (C) void* _d_newitemU(scope const TypeInfo _ti) pure nothrow @weak { // the GC might not have cleared the padding area in the block *cast(TypeInfo*)(p + (itemSize & ~(size_t.sizeof - 1))) = null; - *cast(TypeInfo*)(p + blkInf.size - tiSize) = cast() ti; + __setBlockFinalizerInfo(blkInf, cast() ti); } return p; } -debug(PRINTF) -{ - extern(C) void printArrayCache() - { - auto ptr = __blkcache; - printf("CACHE: \n"); - foreach (i; 0 .. N_CACHE_BLOCKS) - { - printf(" %d\taddr:% .8x\tsize:% .10d\tflags:% .8x\n", i, ptr[i].base, ptr[i].size, ptr[i].attr); - } - } -} - /** * */ @@ -804,33 +712,30 @@ void finalize_array2(void* p, size_t size) nothrow { debug(PRINTF) printf("rt_finalize_array2(p = %p)\n", p); - TypeInfo_Struct si = void; + // construct a BlkInfo to match the array API + auto info = BlkInfo( + base: p, + size: size, + attr: BlkAttr.APPENDABLE | BlkAttr.STRUCTFINAL + ); + auto usedsize = __arrayAllocLength(info); + auto arrptr = __arrayStart(info); + debug (VALGRIND) { auto block = p[0..size]; disableAddrReportingInRange(block); } - if (size <= 256) - { - si = *cast(TypeInfo_Struct*)(p + size - size_t.sizeof); - size = *cast(ubyte*)(p + size - size_t.sizeof - SMALLPAD); - } - else if (size < PAGESIZE) - { - si = *cast(TypeInfo_Struct*)(p + size - size_t.sizeof); - size = *cast(ushort*)(p + size - size_t.sizeof - MEDPAD); - } - else - { - si = *cast(TypeInfo_Struct*)(p + size_t.sizeof); - size = *cast(size_t*)p; - p += LARGEPREFIX; - } + + TypeInfo_Struct si = size < PAGESIZE ? + *cast(TypeInfo_Struct*)(p + size - size_t.sizeof) : // small + *cast(TypeInfo_Struct*)(p + size_t.sizeof); // large + debug (VALGRIND) enableAddrReportingInRange(block); try { - finalize_array(p, size, si); + finalize_array(arrptr, usedsize, si); } catch (Exception e) { @@ -977,8 +882,7 @@ do if (newlength <= (*p).length) { *p = (*p)[0 .. newlength]; - void* newdata = (*p).ptr; - return newdata[0 .. newlength]; + return *p; } auto tinext = unqualify(ti.next); size_t sizeelem = tinext.tsize; @@ -1027,6 +931,7 @@ do if (!(*p).ptr) { + assert((*p).length == 0); // pointer was null, need to allocate auto info = __arrayAlloc(newsize, ti, tinext); if (info.base is null) @@ -1044,102 +949,31 @@ do } const size_t size = (*p).length * sizeelem; - auto bic = isshared ? null : __getBlkInfo((*p).ptr); - auto info = bic ? *bic : GC.query((*p).ptr); /* Attempt to extend past the end of the existing array. * If not possible, allocate new space for entire array and copy. */ - bool allocateAndCopy = false; void* newdata = (*p).ptr; - if (info.base && (info.attr & BlkAttr.APPENDABLE)) - { - // calculate the extent of the array given the base. - const size_t offset = (*p).ptr - __arrayStart(info); - if (info.size >= PAGESIZE) - { - // size of array is at the front of the block - if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - // check to see if it failed because there is not - // enough space - if (*(cast(size_t*)info.base) == size + offset) - { - // not enough space, try extending - auto extendsize = newsize + offset + LARGEPAD - info.size; - auto u = GC.extend(info.base, extendsize, extendsize); - if (u) - { - // extend worked, now try setting the length - // again. - info.size = u; - if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - if (!isshared) - __insertBlkInfoCache(info, bic); - memset(newdata + size, 0, newsize - size); - *p = newdata[0 .. newlength]; - return *p; - } - } - } - - // couldn't do it, reallocate - allocateAndCopy = true; - } - else if (!isshared && !bic) - { - // add this to the cache, it wasn't present previously. - __insertBlkInfoCache(info, null); - } - } - else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - // could not resize in place - allocateAndCopy = true; - } - else if (!isshared && !bic) - { - // add this to the cache, it wasn't present previously. - __insertBlkInfoCache(info, null); - } - } - else - allocateAndCopy = true; - - if (allocateAndCopy) + if (!gc_expandArrayUsed(newdata[0 .. size], newsize, isshared)) { - if (info.base) - { - if (bic) - { - // a chance that flags have changed since this was cached, we should fetch the most recent flags - info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE; - } - info = __arrayAlloc(newsize, info, ti, tinext); - } - else - { - info = __arrayAlloc(newsize, ti, tinext); - } - + auto info = __arrayAlloc(newsize, (*p).ptr, ti, tinext); if (info.base is null) { onOutOfMemoryError(); assert(0); } - __setArrayAllocLength(info, newsize, isshared, tinext); - if (!isshared) - __insertBlkInfoCache(info, bic); - newdata = cast(byte *)__arrayStart(info); + newdata = __arrayStart(info); newdata[0 .. size] = (*p).ptr[0 .. size]; - /* Do postblit processing, as we are making a copy and the - * original array may have references. - * Note that this may throw. - */ + __setArrayAllocLength(info, newsize, isshared, tinext); + + // Do postblit processing, as we are making a copy. __doPostblit(newdata, size, tinext); + + // this hasn't been added to the cache yet. + if (!isshared) + __insertBlkInfoCache(info, null); } // Zero the unused portion of the newly allocated space @@ -1162,7 +996,7 @@ do debug(PRINTF) { - //printf("_d_arraysetlengthiT(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength); + //printf("_d_arraysetlengthT(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength); if (p) printf("\tp.ptr = %p, p.length = %d\n", (*p).ptr, (*p).length); } @@ -1170,8 +1004,7 @@ do if (newlength <= (*p).length) { *p = (*p)[0 .. newlength]; - void* newdata = (*p).ptr; - return newdata[0 .. newlength]; + return *p; } auto tinext = unqualify(ti.next); size_t sizeelem = tinext.tsize; @@ -1237,6 +1070,7 @@ do if (!(*p).ptr) { + assert((*p).length == 0); // pointer was null, need to allocate auto info = __arrayAlloc(newsize, ti, tinext); if (info.base is null) @@ -1254,103 +1088,31 @@ do } const size_t size = (*p).length * sizeelem; - auto bic = isshared ? null : __getBlkInfo((*p).ptr); - auto info = bic ? *bic : GC.query((*p).ptr); /* Attempt to extend past the end of the existing array. * If not possible, allocate new space for entire array and copy. */ - bool allocateAndCopy = false; void* newdata = (*p).ptr; - - if (info.base && (info.attr & BlkAttr.APPENDABLE)) + if (!gc_expandArrayUsed(newdata[0 .. size], newsize, isshared)) { - // calculate the extent of the array given the base. - const size_t offset = (*p).ptr - __arrayStart(info); - if (info.size >= PAGESIZE) - { - // size of array is at the front of the block - if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - // check to see if it failed because there is not - // enough space - if (*(cast(size_t*)info.base) == size + offset) - { - // not enough space, try extending - auto extendsize = newsize + offset + LARGEPAD - info.size; - auto u = GC.extend(info.base, extendsize, extendsize); - if (u) - { - // extend worked, now try setting the length - // again. - info.size = u; - if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - if (!isshared) - __insertBlkInfoCache(info, bic); - doInitialize(newdata + size, newdata + newsize, tinext.initializer); - *p = newdata[0 .. newlength]; - return *p; - } - } - } - - // couldn't do it, reallocate - allocateAndCopy = true; - } - else if (!isshared && !bic) - { - // add this to the cache, it wasn't present previously. - __insertBlkInfoCache(info, null); - } - } - else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - // could not resize in place - allocateAndCopy = true; - } - else if (!isshared && !bic) - { - // add this to the cache, it wasn't present previously. - __insertBlkInfoCache(info, null); - } - } - else - allocateAndCopy = true; - - if (allocateAndCopy) - { - if (info.base) - { - if (bic) - { - // a chance that flags have changed since this was cached, we should fetch the most recent flags - info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE; - } - info = __arrayAlloc(newsize, info, ti, tinext); - } - else - { - info = __arrayAlloc(newsize, ti, tinext); - } - + auto info = __arrayAlloc(newsize, (*p).ptr, ti, tinext); if (info.base is null) { onOutOfMemoryError(); assert(0); } - __setArrayAllocLength(info, newsize, isshared, tinext); - if (!isshared) - __insertBlkInfoCache(info, bic); - newdata = cast(byte *)__arrayStart(info); + newdata = __arrayStart(info); newdata[0 .. size] = (*p).ptr[0 .. size]; - /* Do postblit processing, as we are making a copy and the - * original array may have references. - * Note that this may throw. - */ + __setArrayAllocLength(info, newsize, isshared, tinext); + + // Do postblit processing, as we are making a copy. __doPostblit(newdata, size, tinext); + + // this hasn't been added to the cache yet. + if (!isshared) + __insertBlkInfoCache(info, null); } // Initialize the unused portion of the newly allocated space @@ -1442,100 +1204,53 @@ extern (C) byte[] _d_arrayappendcTX(const TypeInfo ti, return scope ref byte[] px, size_t n) @weak { import core.stdc.string; + import core.exception : onOutOfMemoryError; // This is a cut&paste job from _d_arrayappendT(). Should be refactored. // only optimize array append where ti is not a shared type auto tinext = unqualify(ti.next); auto sizeelem = tinext.tsize; // array element size auto isshared = typeid(ti) is typeid(TypeInfo_Shared); - auto bic = isshared ? null : __getBlkInfo(px.ptr); - auto info = bic ? *bic : GC.query(px.ptr); auto length = px.length; auto newlength = length + n; auto newsize = newlength * sizeelem; auto size = length * sizeelem; - size_t newcap = void; // for scratch space - // calculate the extent of the array given the base. - size_t offset = cast(void*)px.ptr - __arrayStart(info); - if (info.base && (info.attr & BlkAttr.APPENDABLE)) + if (!gc_expandArrayUsed(px.ptr[0 .. size], newsize, isshared)) { - if (info.size >= PAGESIZE) - { - // size of array is at the front of the block - if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - // check to see if it failed because there is not - // enough space - newcap = newCapacity(newlength, sizeelem); - if (*(cast(size_t*)info.base) == size + offset) - { - // not enough space, try extending - auto extendoffset = offset + LARGEPAD - info.size; - auto u = GC.extend(info.base, newsize + extendoffset, newcap + extendoffset); - if (u) - { - // extend worked, now try setting the length - // again. - info.size = u; - if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - if (!isshared) - __insertBlkInfoCache(info, bic); - goto L1; - } - } - } - - // couldn't do it, reallocate - goto L2; - } - else if (!isshared && !bic) - { - __insertBlkInfoCache(info, null); - } - } - else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - // could not resize in place - newcap = newCapacity(newlength, sizeelem); - goto L2; - } - else if (!isshared && !bic) - { - __insertBlkInfoCache(info, null); - } - } - else - { - // not appendable or is null - newcap = newCapacity(newlength, sizeelem); - if (info.base) - { - L2: - if (bic) - { - // a chance that flags have changed since this was cached, we should fetch the most recent flags - info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE; - } - info = __arrayAlloc(newcap, info, ti, tinext); - } - else + // could not set the size, we must reallocate. + auto newcap = newCapacity(newlength, sizeelem); + auto info = __arrayAlloc(newcap, px.ptr, ti, tinext); + if (info.base is null) { - info = __arrayAlloc(newcap, ti, tinext); + onOutOfMemoryError(); + assert(0); } + __setArrayAllocLength(info, newsize, isshared, tinext); if (!isshared) - __insertBlkInfoCache(info, bic); + __insertBlkInfoCache(info, null); + auto newdata = cast(byte*)__arrayStart(info); - memcpy(newdata, px.ptr, length * sizeelem); - // do postblit processing - __doPostblit(newdata, length * sizeelem, tinext); - (cast(void**)&px)[1] = newdata; + memcpy(newdata, px.ptr, size); + + + // For small blocks that are always fully scanned, if we allocated more + // capacity than was requested, we are responsible for zeroing that + // memory. + // large blocks are only scanned up to the used size. + if (!(info.attr & BlkAttr.NO_SCAN) && newcap > newcap && info.size < PAGESIZE) + memset(newdata + newsize, 0, newcap - newsize); + + // do potsblit processing. + __doPostblit(newdata, size, tinext); + + px = newdata[0 .. newlength]; + return px; } - L1: - *cast(size_t*)&px = newlength; + // we were able to expand in place, just update the length + px = px.ptr[0 .. newlength]; return px; } diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE index 0522cf85a3a8ba8645a49bf4185c0c050e9fde21..b6f3488306cd5841c48acb87b6ecf150ad9fb76f 100644 --- a/libphobos/src/MERGE +++ b/libphobos/src/MERGE @@ -1,4 +1,4 @@ -dbc09d8230f0e273af8a78546e5431a7783478b5 +ad8ee55872a25c93beacfb0ae1cca80dc8115916 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/etc/c/curl.d b/libphobos/src/etc/c/curl.d index e6a10435d2bf144154f35499d460aade3544ace1..a32a6b86dfa8087f718f3ebfa51f289a2cd5a1ec 100644 --- a/libphobos/src/etc/c/curl.d +++ b/libphobos/src/etc/c/curl.d @@ -117,7 +117,7 @@ alias curl_socket_t = socket_t; /// jdrewsen - Would like to get socket error constant from std.socket by it is private atm. version (Windows) { - import core.sys.windows.windows, core.sys.windows.winsock2; + import core.sys.windows.winsock2; enum CURL_SOCKET_BAD = SOCKET_ERROR; } version (Posix) enum CURL_SOCKET_BAD = -1; diff --git a/libphobos/src/std/algorithm/searching.d b/libphobos/src/std/algorithm/searching.d index b7119d24b608ccca21f5dc46e34ec34d686eb82d..e0d24359a516e786297cdbcd483d7963d8d75bc4 100644 --- a/libphobos/src/std/algorithm/searching.d +++ b/libphobos/src/std/algorithm/searching.d @@ -792,19 +792,18 @@ if (isInputRange!R && !isInfinite!R) `haystack` before reaching an element for which `startsWith!pred(haystack, needles)` is `true`. If `startsWith!pred(haystack, needles)` is not `true` for any element in - `haystack`, then `-1` is returned. If only `pred` is provided, - `pred(haystack)` is tested for each element. + `haystack`, then `-1` is returned. If more than one needle is provided, + `countUntil` will wrap the result in a tuple similar to + `Tuple!(ptrdiff_t, "steps", ptrdiff_t needle)` See_Also: $(REF indexOf, std,string) +/ -ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) +auto countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) if (isForwardRange!R && Rs.length > 0 && isForwardRange!(Rs[0]) == isInputRange!(Rs[0]) && allSatisfy!(canTestStartsWith!(pred, R), Rs)) { - typeof(return) result; - static if (needles.length == 1) { static if (hasLength!R) //Note: Narrow strings don't have length. @@ -814,14 +813,16 @@ if (isForwardRange!R auto len = haystack.length; auto r2 = find!pred(haystack, needles[0]); if (!r2.empty) - return cast(typeof(return)) (len - r2.length); + return ptrdiff_t(len - r2.length); } else { import std.range : dropOne; if (needles[0].empty) - return 0; + return ptrdiff_t(0); + + ptrdiff_t result; //Default case, slower route doing startsWith iteration for ( ; !haystack.empty ; ++result ) @@ -836,21 +837,39 @@ if (isForwardRange!R //haystack we pop in all paths, so we do that, and then save. haystack.popFront(); if (startsWith!pred(haystack.save, needles[0].save.dropOne())) - return result; + return result; } else - haystack.popFront(); + { + haystack.popFront(); + } } } + return ptrdiff_t(-1); } else { + static struct Result + { + ptrdiff_t steps, needle; // both -1 when nothing was found + alias steps this; // compatible with previous ptrdiff_t return type + ptrdiff_t opIndex(size_t idx) // faking a tuple + { + assert(idx < 2, "Type has only two members: pos and needle"); + return idx == 0 ? steps : needle; + } + } + Result result; + foreach (i, Ri; Rs) { static if (isForwardRange!Ri) { if (needles[i].empty) - return 0; + { + result.needle = i; + return result; + } } } Tuple!Rs t; @@ -861,7 +880,7 @@ if (isForwardRange!R t[i] = needles[i]; } } - for (; !haystack.empty ; ++result, haystack.popFront()) + for (; !haystack.empty ; ++result.steps, haystack.popFront()) { foreach (i, Ri; Rs) { @@ -870,18 +889,18 @@ if (isForwardRange!R t[i] = needles[i].save; } } - if (startsWith!pred(haystack.save, t.expand)) + if (auto needle = startsWith!pred(haystack.save, t.expand)) { + result.needle = needle - 1; return result; } } - } - // Because of https://issues.dlang.org/show_bug.cgi?id=8804 - // Avoids both "unreachable code" or "no return statement" - static if (isInfinite!R) assert(false, R.stringof ~ " must not be an" - ~ " infinite range"); - else return -1; + // no match was found + result.needle = -1; + result.steps = -1; + return result; + } } /// ditto @@ -906,6 +925,16 @@ if (isInputRange!R && assert(countUntil([0, 7, 12, 22, 9], [12, 22]) == 2); assert(countUntil([0, 7, 12, 22, 9], 9) == 4); assert(countUntil!"a > b"([0, 7, 12, 22, 9], 20) == 3); + + // supports multiple needles + auto res = "...hello".countUntil("ha", "he"); + assert(res.steps == 3); + assert(res.needle == 1); + + // returns -1 if no needle was found + res = "hello".countUntil("ha", "hu"); + assert(res.steps == -1); + assert(res.needle == -1); } @safe unittest @@ -943,6 +972,42 @@ if (isInputRange!R && assert(countUntil("hello world", "world", 'l') == 2); } +@safe unittest +{ + auto res = "...hello".countUntil("hella", "hello"); + assert(res == 3); + assert(res.steps == 3); + assert(res.needle == 1); + // test tuple emulation + assert(res[0] == 3); + assert(res[1] == 1); + + // the first matching needle is chosen + res = "...hello".countUntil("hell", "hello"); + assert(res == 3); + assert(res.needle == 0); + + // no match + auto noMatch = "hello world".countUntil("ha", "hu"); + assert(noMatch == -1); + assert(noMatch.steps == -1); + assert(noMatch.needle == -1); + // test tuple emulation + assert(noMatch[0] == -1); + assert(noMatch[1] == -1); +} + +// test infinite ranges +@safe unittest +{ + import std.algorithm.iteration : joiner; + import std.range : iota, repeat; + import std.stdio; + assert(10.iota.repeat.joiner.countUntil(9) == 9); + assert(10.iota.repeat.joiner.countUntil(1, 2) == 1); + assert(10.iota.repeat.joiner.countUntil!(a => a >= 9) == 9); +} + /// ditto ptrdiff_t countUntil(alias pred, R)(R haystack) if (isInputRange!R && diff --git a/libphobos/src/std/array.d b/libphobos/src/std/array.d index 3313dbb28d4492900bec0b56061dfad86a916c11..fea70258ebf094ab1877ec7a17c758a1cfc3b55f 100644 --- a/libphobos/src/std/array.d +++ b/libphobos/src/std/array.d @@ -1297,6 +1297,64 @@ if (is(typeof(a.ptr < b.ptr) == bool)) static assert(test == "three"d); } +/// +@safe pure nothrow unittest +{ + import std.meta : AliasSeq; + + // can be used as an alternative implementation of overlap that returns + // `true` or `false` instead of a slice of the overlap + bool isSliceOf(T)(const scope T[] part, const scope T[] whole) + { + return part.overlap(whole) is part; + } + + auto x = [1, 2, 3, 4, 5]; + + assert(isSliceOf(x[3..$], x)); + assert(isSliceOf(x[], x)); + assert(!isSliceOf(x, x[3..$])); + assert(!isSliceOf([7, 8], x)); + assert(isSliceOf(null, x)); + + // null is a slice of itself + assert(isSliceOf(null, null)); + + foreach (T; AliasSeq!(int[], const(int)[], immutable(int)[], const int[], immutable int[])) + { + T a = [1, 2, 3, 4, 5]; + T b = a; + T c = a[1 .. $]; + T d = a[0 .. 1]; + T e = null; + + assert(isSliceOf(a, a)); + assert(isSliceOf(b, a)); + assert(isSliceOf(a, b)); + + assert(isSliceOf(c, a)); + assert(isSliceOf(c, b)); + assert(!isSliceOf(a, c)); + assert(!isSliceOf(b, c)); + + assert(isSliceOf(d, a)); + assert(isSliceOf(d, b)); + assert(!isSliceOf(a, d)); + assert(!isSliceOf(b, d)); + + assert(isSliceOf(e, a)); + assert(isSliceOf(e, b)); + assert(isSliceOf(e, c)); + assert(isSliceOf(e, d)); + + //verifies R-value compatibilty + assert(!isSliceOf(a[$ .. $], a)); + assert(isSliceOf(a[0 .. 0], a)); + assert(isSliceOf(a, a[0.. $])); + assert(isSliceOf(a[0 .. $], a)); + } +} + @safe pure nothrow unittest { static void test(L, R)(L l, R r) diff --git a/libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d b/libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d index ca83785914eb777b4d7155c282e1c981557a6c3d..c6d5fca3dde223a8669915b570b36feeea34eeab 100644 --- a/libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d +++ b/libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d @@ -40,6 +40,11 @@ longer used. It does so by destroying empty allocators. However, in order to avoid thrashing (excessive creation/destruction of allocators under certain use patterns), it keeps unused allocators for a while. +The shared version of `AllocatorList` is `SharedAllocatorList`, which has +identical semantics to its single-threaded version. Both `BookkeepingAllocator` +and `Allocator` provided by `factoryFunction` must be shared, in order to +ensure corectness. + Params: factoryFunction = A function or template function (including function literals). New allocators are created by calling `factoryFunction(n)` with strictly @@ -661,65 +666,285 @@ version (Posix) @system unittest assert(b1.length == 1024 * 10); } +/// Ditto +shared struct SharedAllocatorList(Factory, BookkeepingAllocator = GCAllocator) +{ + import std.typecons : Ternary; + import std.traits : hasMember; + import core.internal.spinlock : SpinLock; + +private: + // Forward all calls to 'impl' and protect them by the lock below + AllocatorList!(Factory, BookkeepingAllocator) impl; + SpinLock lock = SpinLock(SpinLock.Contention.brief); + + // This could be potentially removed in the future, + // should a successor to <https://github.com/dlang/druntime/pull/2156> + // or a solution to <https://github.com/dlang/dmd/issues/17128> get merged. + static ref T assumeUnshared(T)(ref shared T val) @trusted @nogc pure nothrow + { + return *cast(T*) &val; + } + + // Debug function used for testing + version (unittest) + auto allocators() + { + return impl.allocators; + } + +// Everything is inherited from the 'AllocatorList' implementation +public: + + /* + Note: This does not work well with rvalues because it copies them once more. + Probably not a problem here because all parameters are cheap. + <https://github.com/dlang/phobos/pull/6465/files#r189629862> + */ + + /** + The alignment offered. + */ + enum alignment = impl.alignment; + + /** + Allocate a block of size `s`. First tries to allocate from the existing + list of already-created allocators. If neither can satisfy the request, + creates a new allocator by calling `make(s)` and delegates the request + to it. However, if the allocation fresh off a newly created allocator + fails, subsequent calls to `allocate` will not cause more calls to $(D + make). + */ + static if (hasMember!(typeof(impl), "allocate")) + void[] allocate(size_t s) + { + lock.lock(); + scope(exit) lock.unlock(); + + return assumeUnshared(impl).allocate(s); + } + + /** + Allocate a block of size `s` with alignment `a`. First tries to allocate + from the existing list of already-created allocators. If neither can + satisfy the request, creates a new allocator by calling `make(s + a - 1)` + and delegates the request to it. However, if the allocation fresh off a + newly created allocator fails, subsequent calls to `alignedAllocate` + will not cause more calls to `make`. + */ + static if (hasMember!(typeof(impl), "alignedAllocate")) + void[] alignedAllocate(size_t s, uint a) + { + lock.lock(); + scope(exit) lock.unlock(); + + return assumeUnshared(impl).alignedAllocate(s, a); + } + + /** + Defined if `Allocator.deallocate` and `Allocator.owns` are defined. + */ + static if (hasMember!(typeof(impl), "deallocate")) + bool deallocate(void[] b) + { + lock.lock(); + scope(exit) lock.unlock(); + + return assumeUnshared(impl).deallocate(b); + } + + /** + Defined only if `Allocator` defines `owns`. Tries each allocator in + turn, in most-recently-used order. If the owner is found, it is moved to + the front of the list as a side effect under the assumption it will be used + soon. + + Returns: `Ternary.yes` if one allocator was found to return `Ternary.yes`, + `Ternary.no` if all component allocators returned `Ternary.no`, and + `Ternary.unknown` if no allocator returned `Ternary.yes` and at least one + returned `Ternary.unknown`. + */ + static if (hasMember!(typeof(impl), "owns")) + Ternary owns(void[] b) + { + lock.lock(); + scope(exit) lock.unlock(); + + return assumeUnshared(impl).owns(b); + } + + /** + Defined only if `Allocator.expand` is defined. Finds the owner of `b` + and calls `expand` for it. The owner is not brought to the head of the + list. + */ + static if (hasMember!(typeof(impl), "expand")) + bool expand(ref void[] b, size_t delta) + { + lock.lock(); + scope(exit) lock.unlock(); + + return assumeUnshared(impl).expand(b, delta); + } + + /** + Defined only if `Allocator.reallocate` is defined. Finds the owner of + `b` and calls `reallocate` for it. If that fails, calls the global + `reallocate`, which allocates a new block and moves memory. + */ + static if (hasMember!(typeof(impl), "reallocate")) + bool reallocate(ref void[] b, size_t s) + { + lock.lock(); + scope(exit) lock.unlock(); + + return assumeUnshared(impl).reallocate(b, s); + } + + /** + Defined only if `Allocator.owns` and `Allocator.deallocateAll` are + defined. + */ + static if (hasMember!(typeof(impl), "deallocateAll")) + bool deallocateAll() + { + lock.lock(); + scope(exit) lock.unlock(); + + return assumeUnshared(impl).deallocateAll(); + } + + /** + Returns `Ternary.yes` if no allocators are currently active, + `Ternary.no` otherwise. This methods never returns `Ternary.unknown`. + */ + static if (hasMember!(typeof(impl), "empty")) + Ternary empty() + { + lock.lock(); + scope(exit) lock.unlock(); + + return assumeUnshared(impl).empty(); + } +} + +/// Ditto +template SharedAllocatorList(alias factoryFunction, + BookkeepingAllocator = GCAllocator) +{ + alias A = typeof(factoryFunction(1)); + static assert( + // is a template function (including literals) + is(typeof({A function(size_t) @system x = factoryFunction!size_t;})) + || + // or a function (including literals) + is(typeof({A function(size_t) @system x = factoryFunction;})) + , + "Only function names and function literals that take size_t" + ~ " and return an allocator are accepted, not " + ~ typeof(factoryFunction).stringof + ); + static struct Factory + { + A opCall(size_t n) { return factoryFunction(n); } + } + alias SharedAllocatorList = .SharedAllocatorList!(Factory, BookkeepingAllocator); +} + @system unittest { - // Create an allocator based upon 4MB regions, fetched from the GC heap. import std.algorithm.comparison : max; - import std.experimental.allocator.building_blocks.region : Region; + import std.experimental.allocator.building_blocks.region : Region, SharedRegion; + + static void testAlloc(Allocator)(ref Allocator a) + { + const b1 = a.allocate(1024 * 8192); + assert(b1 !is null); // still works due to overdimensioning + const b2 = a.allocate(1024 * 10); + assert(b2.length == 1024 * 10); + a.deallocateAll(); + } + + // Create an allocator based upon 4MB regions, fetched from the GC heap. AllocatorList!((n) => Region!GCAllocator(new ubyte[max(n, 1024 * 4096)]), - NullAllocator) a; - const b1 = a.allocate(1024 * 8192); - assert(b1 !is null); // still works due to overdimensioning - const b2 = a.allocate(1024 * 10); - assert(b2.length == 1024 * 10); - a.deallocateAll(); + NullAllocator) reg1; + + SharedAllocatorList!((n) => SharedRegion!GCAllocator(new ubyte[max(n, 1024 * 4096)]), + NullAllocator) reg2; + + testAlloc(reg1); + testAlloc(reg2); } @system unittest { - // Create an allocator based upon 4MB regions, fetched from the GC heap. import std.algorithm.comparison : max; - import std.experimental.allocator.building_blocks.region : BorrowedRegion; - AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a; - auto b1 = a.alignedAllocate(1024 * 8192, 1024); - assert(b1 !is null); // still works due to overdimensioning - assert(b1.length == 1024 * 8192); - assert(b1.ptr.alignedAt(1024)); - assert(a.allocators.length == 1); + import std.experimental.allocator.building_blocks.region : BorrowedRegion, SharedBorrowedRegion; - b1 = a.alignedAllocate(0, 1024); - assert(b1.length == 0); - assert(a.allocators.length == 1); + static void testAlloc(Allocator)(ref Allocator a) + { + auto b1 = a.alignedAllocate(1024 * 8192, 1024); + assert(b1 !is null); // still works due to overdimensioning + assert(b1.length == 1024 * 8192); + assert(b1.ptr.alignedAt(1024)); + assert(a.allocators.length == 1); - b1 = a.allocate(1024 * 10); - assert(b1.length == 1024 * 10); + b1 = a.alignedAllocate(0, 1024); + assert(b1.length == 0); + assert(a.allocators.length == 1); - assert(a.reallocate(b1, 1024)); - assert(b1.length == 1024); + b1 = a.allocate(1024 * 10); + assert(b1.length == 1024 * 10); - a.deallocateAll(); + assert(a.reallocate(b1, 1024)); + assert(b1.length == 1024); + + a.deallocateAll(); + } + + // Create an allocator based upon 4MB regions, fetched from the GC heap. + AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a1; + SharedAllocatorList!((n) => SharedBorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a2; + + testAlloc(a1); + testAlloc(a2); } @system unittest { import core.exception : AssertError; import std.exception : assertThrown; - - // Create an allocator based upon 4MB regions, fetched from the GC heap. import std.algorithm.comparison : max; - import std.experimental.allocator.building_blocks.region : BorrowedRegion; - AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a; - auto b1 = a.alignedAllocate(0, 1); - assert(b1 is null); + import std.experimental.allocator.building_blocks.region : BorrowedRegion, SharedBorrowedRegion; + + static void testAlloc(Allocator)(ref Allocator a) + { + auto b1 = a.alignedAllocate(0, 1); + assert(b1 is null); + + b1 = a.alignedAllocate(1, 0); + assert(b1 is null); - b1 = a.alignedAllocate(1, 0); - assert(b1 is null); + b1 = a.alignedAllocate(0, 0); + assert(b1 is null); - b1 = a.alignedAllocate(0, 0); - assert(b1 is null); + assertThrown!AssertError(a.alignedAllocate(size_t.max, 1024)); - assertThrown!AssertError(a.alignedAllocate(size_t.max, 1024)); - a.deallocateAll(); + // FIXME: This special-casing might note be necessary. + // At the moment though, this call would take potentially forever + // for the `SharedAllocatorList` from below. + static if (!is(Allocator == shared)) + { + a.deallocateAll(); + } + } + + // Create an allocator based upon 4MB regions, fetched from the GC heap. + AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a1; + SharedAllocatorList!((n) => SharedBorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a2; + + testAlloc(a1); + testAlloc(a2); } @system unittest @@ -728,52 +953,74 @@ version (Posix) @system unittest // Create an allocator based upon 4MB regions, fetched from the GC heap. import std.algorithm.comparison : max; - import std.experimental.allocator.building_blocks.region : BorrowedRegion; - AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a; - auto b0 = a.alignedAllocate(1, 1024); - assert(b0.length == 1); - assert(b0.ptr.alignedAt(1024)); - assert(a.allocators.length == 1); + import std.experimental.allocator.building_blocks.region : BorrowedRegion, SharedBorrowedRegion; - auto b1 = a.alignedAllocate(1024 * 4096, 1024); - assert(b1.length == 1024 * 4096); - assert(b1.ptr.alignedAt(1024)); - assert(a.allocators.length == 2); - - auto b2 = a.alignedAllocate(1024, 128); - assert(b2.length == 1024); - assert(b2.ptr.alignedAt(128)); - assert(a.allocators.length == 2); + static void testAlloc(Allocator)(ref Allocator a) + { + auto b0 = a.alignedAllocate(1, 1024); + assert(b0.length == 1); + assert(b0.ptr.alignedAt(1024)); + assert(a.allocators.length == 1); + + auto b1 = a.alignedAllocate(1024 * 4096, 1024); + assert(b1.length == 1024 * 4096); + assert(b1.ptr.alignedAt(1024)); + assert(a.allocators.length == 2); + + auto b2 = a.alignedAllocate(1024, 128); + assert(b2.length == 1024); + assert(b2.ptr.alignedAt(128)); + assert(a.allocators.length == 2); + + auto b3 = a.allocate(1024); + assert(b3.length == 1024); + assert(a.allocators.length == 2); + + auto b4 = a.allocate(1024 * 4096); + assert(b4.length == 1024 * 4096); + assert(a.allocators.length == 3); + + static if (!is(Allocator == shared)) + { + assert(a.root.empty == Ternary.no); + assert(a.deallocate(b4)); + assert(a.root.empty == Ternary.yes); - auto b3 = a.allocate(1024); - assert(b3.length == 1024); - assert(a.allocators.length == 2); + assert(a.deallocate(b1)); + } - auto b4 = a.allocate(1024 * 4096); - assert(b4.length == 1024 * 4096); - assert(a.allocators.length == 3); + a.deallocateAll(); + } - assert(a.root.empty == Ternary.no); - assert(a.deallocate(b4)); - assert(a.root.empty == Ternary.yes); + AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a1; + SharedAllocatorList!((n) => SharedBorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a2; - assert(a.deallocate(b1)); - a.deallocateAll(); + testAlloc(a1); + testAlloc(a2); } @system unittest { - // Create an allocator based upon 4MB regions, fetched from the GC heap. import std.algorithm.comparison : max; - import std.experimental.allocator.building_blocks.region : BorrowedRegion; - AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a; - auto b1 = a.allocate(1024 * 8192); - assert(b1 !is null); // still works due to overdimensioning - b1 = a.allocate(1024 * 10); - assert(b1.length == 1024 * 10); - assert(a.reallocate(b1, 1024)); - assert(b1.length == 1024); - a.deallocateAll(); + import std.experimental.allocator.building_blocks.region : BorrowedRegion, SharedBorrowedRegion; + + static void testAlloc(Allocator)(ref Allocator a) + { + auto b1 = a.allocate(1024 * 8192); + assert(b1 !is null); // still works due to overdimensioning + b1 = a.allocate(1024 * 10); + assert(b1.length == 1024 * 10); + assert(a.reallocate(b1, 1024)); + assert(b1.length == 1024); + a.deallocateAll(); + } + + // Create an allocator based upon 4MB regions, fetched from the GC heap. + AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a1; + SharedAllocatorList!((n) => SharedBorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a2; + + testAlloc(a1); + testAlloc(a2); } @system unittest @@ -798,24 +1045,33 @@ version (Posix) @system unittest @system unittest { - import std.experimental.allocator.building_blocks.region : Region; + import std.experimental.allocator.building_blocks.region : Region, SharedRegion; enum bs = GCAllocator.alignment; - AllocatorList!((n) => Region!GCAllocator(256 * bs)) a; - auto b1 = a.allocate(192 * bs); - assert(b1.length == 192 * bs); - assert(a.allocators.length == 1); - auto b2 = a.allocate(64 * bs); - assert(b2.length == 64 * bs); - assert(a.allocators.length == 1); - auto b3 = a.allocate(192 * bs); - assert(b3.length == 192 * bs); - assert(a.allocators.length == 2); - // Ensure deallocate inherits from parent allocators - () nothrow @nogc { a.deallocate(b1); }(); - b1 = a.allocate(64 * bs); - assert(b1.length == 64 * bs); - assert(a.allocators.length == 2); - a.deallocateAll(); + + static void testAlloc(Allocator)(ref Allocator a) + { + auto b1 = a.allocate(192 * bs); + assert(b1.length == 192 * bs); + assert(a.allocators.length == 1); + auto b2 = a.allocate(64 * bs); + assert(b2.length == 64 * bs); + assert(a.allocators.length == 1); + auto b3 = a.allocate(192 * bs); + assert(b3.length == 192 * bs); + assert(a.allocators.length == 2); + // Ensure deallocate inherits from parent allocators + () nothrow @nogc { a.deallocate(b1); }(); + b1 = a.allocate(64 * bs); + assert(b1.length == 64 * bs); + assert(a.allocators.length == 2); + a.deallocateAll(); + } + + AllocatorList!((n) => Region!GCAllocator(256 * bs)) a1; + SharedAllocatorList!((n) => SharedRegion!GCAllocator(256 * bs)) a2; + + testAlloc(a1); + testAlloc(a2); } @system unittest @@ -878,11 +1134,15 @@ version (Posix) @system unittest @system unittest { - import std.experimental.allocator.building_blocks.ascending_page_allocator : AscendingPageAllocator; + import std.experimental.allocator.building_blocks.ascending_page_allocator : + AscendingPageAllocator, SharedAscendingPageAllocator; import std.experimental.allocator.mallocator : Mallocator; import std.algorithm.comparison : max; import std.typecons : Ternary; + enum pageSize = 4096; + enum numPages = 2; + static void testrw(void[] b) { ubyte* buf = cast(ubyte*) b.ptr; @@ -893,53 +1153,56 @@ version (Posix) @system unittest } } - enum numPages = 2; - AllocatorList!((n) => AscendingPageAllocator(max(n, numPages * pageSize)), NullAllocator) a; + static void testAlloc(Allocator)(ref Allocator a) + { + void[] b1 = a.allocate(1); + assert(b1.length == 1); + b1 = a.allocate(2); + assert(b1.length == 2); + testrw(b1); - void[] b1 = a.allocate(1); - assert(b1.length == 1); - b1 = a.allocate(2); - assert(b1.length == 2); - testrw(b1); + void[] b2 = a.allocate((numPages + 1) * pageSize); + assert(b2.length == (numPages + 1) * pageSize); + testrw(b2); - void[] b2 = a.allocate((numPages + 1) * pageSize); - assert(b2.length == (numPages + 1) * pageSize); - testrw(b2); + void[] b3 = a.allocate(3); + assert(b3.length == 3); + testrw(b3); - void[] b3 = a.allocate(3); - assert(b3.length == 3); - testrw(b3); + void[] b4 = a.allocate(0); + assert(b4.length == 0); - void[] b4 = a.allocate(0); - assert(b4.length == 0); + assert(a.allocators.length == 3); + assert(a.owns(b1) == Ternary.yes); + assert(a.owns(b2) == Ternary.yes); + assert(a.owns(b3) == Ternary.yes); - assert(a.allocators.length == 3); - assert(a.owns(b1) == Ternary.yes); - assert(a.owns(b2) == Ternary.yes); - assert(a.owns(b3) == Ternary.yes); + assert(a.expand(b1, pageSize - b1.length)); + assert(b1.length == pageSize); + assert(!a.expand(b1, 1)); + assert(!a.expand(b2, 1)); - assert(a.expand(b1, pageSize - b1.length)); - assert(b1.length == pageSize); - assert(!a.expand(b1, 1)); - assert(!a.expand(b2, 1)); + testrw(b1); + testrw(b2); + testrw(b3); - testrw(b1); - testrw(b2); - testrw(b3); + assert(a.deallocate(b1)); + assert(a.deallocate(b2)); - assert(a.deallocate(b1)); - assert(a.deallocate(b2)); + const alignment = cast(uint) (70 * pageSize); + b3 = a.alignedAllocate(70 * pageSize, alignment); + assert(b3.length == 70 * pageSize); + assert(b3.ptr.alignedAt(alignment)); + testrw(b3); + assert(a.allocators.length == 4); + assert(a.deallocate(b3)); - const alignment = cast(uint) (70 * pageSize); - b3 = a.alignedAllocate(70 * pageSize, alignment); - assert(b3.length == 70 * pageSize); - assert(b3.ptr.alignedAt(alignment)); - testrw(b3); - assert(a.allocators.length == 4); - assert(a.deallocate(b3)); + assert(a.deallocateAll()); + } - assert(a.deallocateAll()); + AllocatorList!((n) => AscendingPageAllocator(max(n, numPages * pageSize)), NullAllocator) a1; + SharedAllocatorList!((n) => SharedAscendingPageAllocator(max(n, numPages * pageSize)), NullAllocator) a2; } @system unittest @@ -998,3 +1261,109 @@ version (Posix) @system unittest assert(a.deallocateAll()); } + +@system unittest +{ + import std.experimental.allocator.building_blocks.ascending_page_allocator : AscendingPageAllocator; + import std.experimental.allocator.mallocator : Mallocator; + import std.algorithm.comparison : max; + import std.typecons : Ternary; + + enum pageSize = 4096; + + static void testrw(void[] b) + { + ubyte* buf = cast(ubyte*) b.ptr; + for (int i = 0; i < b.length; i += pageSize) + { + buf[i] = cast(ubyte) (i % 256); + assert(buf[i] == cast(ubyte) (i % 256)); + } + } + + enum numPages = 5; + AllocatorList!((n) => AscendingPageAllocator(max(n, numPages * pageSize)), NullAllocator) a; + auto b = a.alignedAllocate(1, pageSize * 2); + assert(b.length == 1); + assert(a.expand(b, 4095)); + assert(b.ptr.alignedAt(2 * 4096)); + assert(b.length == 4096); + + b = a.allocate(4096); + assert(b.length == 4096); + assert(a.allocators.length == 1); + + assert(a.allocate(4096 * 5).length == 4096 * 5); + assert(a.allocators.length == 2); + + assert(a.deallocateAll()); +} + +@system unittest +{ + import std.experimental.allocator.building_blocks.region : SharedRegion; + import core.thread : ThreadGroup; + import std.algorithm.comparison : max; + + enum numThreads = 10; + SharedAllocatorList!((n) => SharedRegion!(GCAllocator)(new ubyte[max(n, 1024)])) a; + + void fun() + { + void[] b1 = a.allocate(1024); + assert(b1.length == 1024); + + void[] b2 = a.alignedAllocate(1024, 1024); + assert(b2.length == 1024); + assert(b2.ptr.alignedAt(1024)); + + assert(a.deallocate(b1)); + assert(a.deallocate(b2)); + } + + auto tg = new ThreadGroup; + foreach (i; 0 .. numThreads) + { + tg.create(&fun); + } + tg.joinAll(); + + assert(a.deallocateAll()); +} + +@system unittest +{ + import std.experimental.allocator.mallocator : Mallocator; + import std.experimental.allocator.building_blocks.ascending_page_allocator : SharedAscendingPageAllocator; + import core.thread : ThreadGroup; + import std.algorithm.comparison : max; + + enum numThreads = 100; + enum pageSize = 4096; + enum numPages = 10; + SharedAllocatorList!((n) => SharedAscendingPageAllocator(max(n, pageSize * numPages)), Mallocator) a; + + void fun() + { + void[] b1 = a.allocate(512); + assert(b1.length == 512); + assert(a.expand(b1, 512)); + assert(b1.length == 1024); + + void[] b2 = a.alignedAllocate(1024, 4096); + assert(b2.length == 1024); + assert(b2.ptr.alignedAt(1024)); + + assert(a.deallocate(b1)); + assert(a.deallocate(b2)); + } + + auto tg = new ThreadGroup; + foreach (i; 0 .. numThreads) + { + tg.create(&fun); + } + tg.joinAll(); + + assert(a.deallocateAll()); +} diff --git a/libphobos/src/std/format/package.d b/libphobos/src/std/format/package.d index e02c1202a4b5a1cedc887f99612521035593e524..5dadce0877acb29c7aa523f52b05ab8c2b210d65 100644 --- a/libphobos/src/std/format/package.d +++ b/libphobos/src/std/format/package.d @@ -147,7 +147,7 @@ limited to a $(B '-') flag. $(H4 $(LNAME2 format-indicator, Format Indicator)) The $(I format indicator) can either be a single character or an -expression surrounded by $(B %\() and $(B %\)). It specifies the +expression surrounded by $(B '%$(LPAREN)') and $(B '%$(RPAREN)'). It specifies the basic manner in which a value will be formatted and is the minimum requirement to format a value. @@ -187,7 +187,7 @@ $(BOOKTABLE , The $(I compound indicator) can be used to describe compound types like arrays or structs in more detail. A compound type is enclosed -within $(B '%\(') and $(B '%\)'). The enclosed sub-format string is +within $(B '%$(LPAREN)') and $(B '%$(RPAREN)'). The enclosed sub-format string is applied to individual elements. The trailing portion of the sub-format string following the specifier for the element is interpreted as the delimiter, and is therefore omitted following the diff --git a/libphobos/src/std/functional.d b/libphobos/src/std/functional.d index 5022702ace90656b830174622edce17c8af8fbce..cf7e3ff8cc2eb92f1501864988f414701d958481 100644 --- a/libphobos/src/std/functional.d +++ b/libphobos/src/std/functional.d @@ -1289,6 +1289,15 @@ alias pipe(fun...) = compose!(Reverse!(fun)); assert(compose!(`a + 0.5`, `to!(int)(a) + 1`, foo)(1) == 2.5); } +private template getOverloads(alias fun) +{ + import std.meta : AliasSeq; + static if (__traits(compiles, __traits(getOverloads, __traits(parent, fun), __traits(identifier, fun), true))) + alias getOverloads = __traits(getOverloads, __traits(parent, fun), __traits(identifier, fun), true); + else + alias getOverloads = AliasSeq!fun; +} + /** * $(LINK2 https://en.wikipedia.org/wiki/Memoization, Memoizes) a function so as * to avoid repeated computation. The memoization structure is a hash table keyed by a @@ -1324,87 +1333,131 @@ Note: */ template memoize(alias fun) { - import std.traits : ReturnType; - // https://issues.dlang.org/show_bug.cgi?id=13580 - // alias Args = Parameters!fun; + import std.traits : Parameters; + import std.meta : anySatisfy; + + // Specific overloads: + alias overloads = getOverloads!fun; + static foreach (fn; overloads) + static if (is(Parameters!fn)) + alias memoize = impl!(Parameters!fn); + + enum isTemplate(alias a) = __traits(isTemplate, a); + static if (anySatisfy!(isTemplate, overloads)) + { + // Generic implementation + alias memoize = impl; + } - ReturnType!fun memoize(Parameters!fun args) + auto impl(Args...)(Args args) if (is(typeof(fun(args)))) { - alias Args = Parameters!fun; - import std.typecons : Tuple; + import std.typecons : Tuple, tuple; import std.traits : Unqual; - static Unqual!(ReturnType!fun)[Tuple!Args] memo; - auto t = Tuple!Args(args); - if (auto p = t in memo) - return *p; - auto r = fun(args); - memo[t] = r; - return r; + static if (args.length > 0) + { + static Unqual!(typeof(fun(args)))[Tuple!(typeof(args))] memo; + + auto t = Tuple!Args(args); + if (auto p = t in memo) + return *p; + auto r = fun(args); + memo[t] = r; + return r; + } + else + { + static typeof(fun(args)) result; + result = fun(args); + return result; + } } } /// ditto template memoize(alias fun, uint maxSize) { - import std.traits : ReturnType; - // https://issues.dlang.org/show_bug.cgi?id=13580 - // alias Args = Parameters!fun; - ReturnType!fun memoize(Parameters!fun args) + import std.traits : Parameters; + import std.meta : anySatisfy; + + // Specific overloads: + alias overloads = getOverloads!fun; + static foreach (fn; overloads) + static if (is(Parameters!fn)) + alias memoize = impl!(Parameters!fn); + + enum isTemplate(alias a) = __traits(isTemplate, a); + static if (anySatisfy!(isTemplate, overloads)) { - import std.meta : staticMap; - import std.traits : hasIndirections, Unqual; - import std.typecons : tuple; - static struct Value { staticMap!(Unqual, Parameters!fun) args; Unqual!(ReturnType!fun) res; } - static Value[] memo; - static size_t[] initialized; + // Generic implementation + alias memoize = impl; + } - if (!memo.length) + auto impl(Args...)(Args args) if (is(typeof(fun(args)))) + { + static if (args.length > 0) { - import core.memory : GC; + import std.meta : staticMap; + import std.traits : hasIndirections, Unqual; + import std.typecons : tuple; + alias returnType = typeof(fun(args)); + static struct Value { staticMap!(Unqual, Args) args; Unqual!returnType res; } + static Value[] memo; + static size_t[] initialized; - // Ensure no allocation overflows - static assert(maxSize < size_t.max / Value.sizeof); - static assert(maxSize < size_t.max - (8 * size_t.sizeof - 1)); + if (!memo.length) + { + import core.memory : GC; - enum attr = GC.BlkAttr.NO_INTERIOR | (hasIndirections!Value ? 0 : GC.BlkAttr.NO_SCAN); - memo = (cast(Value*) GC.malloc(Value.sizeof * maxSize, attr))[0 .. maxSize]; - enum nwords = (maxSize + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof); - initialized = (cast(size_t*) GC.calloc(nwords * size_t.sizeof, attr | GC.BlkAttr.NO_SCAN))[0 .. nwords]; - } + // Ensure no allocation overflows + static assert(maxSize < size_t.max / Value.sizeof); + static assert(maxSize < size_t.max - (8 * size_t.sizeof - 1)); - import core.bitop : bt, bts; - import core.lifetime : emplace; + enum attr = GC.BlkAttr.NO_INTERIOR | (hasIndirections!Value ? 0 : GC.BlkAttr.NO_SCAN); + memo = (cast(Value*) GC.malloc(Value.sizeof * maxSize, attr))[0 .. maxSize]; + enum nwords = (maxSize + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof); + initialized = (cast(size_t*) GC.calloc(nwords * size_t.sizeof, attr | GC.BlkAttr.NO_SCAN))[0 .. nwords]; + } - size_t hash; - foreach (ref arg; args) - hash = hashOf(arg, hash); - // cuckoo hashing - immutable idx1 = hash % maxSize; - if (!bt(initialized.ptr, idx1)) - { - emplace(&memo[idx1], args, fun(args)); - // only set to initialized after setting args and value - // https://issues.dlang.org/show_bug.cgi?id=14025 - bts(initialized.ptr, idx1); + import core.bitop : bt, bts; + import core.lifetime : emplace; + + size_t hash; + foreach (ref arg; args) + hash = hashOf(arg, hash); + // cuckoo hashing + immutable idx1 = hash % maxSize; + if (!bt(initialized.ptr, idx1)) + { + emplace(&memo[idx1], args, fun(args)); + // only set to initialized after setting args and value + // https://issues.dlang.org/show_bug.cgi?id=14025 + bts(initialized.ptr, idx1); + return memo[idx1].res; + } + else if (memo[idx1].args == args) + return memo[idx1].res; + // FNV prime + immutable idx2 = (hash * 16_777_619) % maxSize; + if (!bt(initialized.ptr, idx2)) + { + emplace(&memo[idx2], memo[idx1]); + bts(initialized.ptr, idx2); + } + else if (memo[idx2].args == args) + return memo[idx2].res; + else if (idx1 != idx2) + memo[idx2] = memo[idx1]; + + memo[idx1] = Value(args, fun(args)); return memo[idx1].res; } - else if (memo[idx1].args == args) - return memo[idx1].res; - // FNV prime - immutable idx2 = (hash * 16_777_619) % maxSize; - if (!bt(initialized.ptr, idx2)) + else { - emplace(&memo[idx2], memo[idx1]); - bts(initialized.ptr, idx2); + static typeof(fun(args)) result; + result = fun(args); + return result; } - else if (memo[idx2].args == args) - return memo[idx2].res; - else if (idx1 != idx2) - memo[idx2] = memo[idx1]; - - memo[idx1] = Value(args, fun(args)); - return memo[idx1].res; } } @@ -1464,6 +1517,37 @@ unittest assert(fact(10) == 3628800); } +// Issue 20099 +@system unittest // not @safe due to memoize +{ + int i = 3; + alias a = memoize!((n) => i + n); + alias b = memoize!((n) => i + n, 3); + + assert(a(3) == 6); + assert(b(3) == 6); +} + +@system unittest // not @safe due to memoize +{ + static Object objNum(int a) { return new Object(); } + assert(memoize!objNum(0) is memoize!objNum(0U)); + assert(memoize!(objNum, 3)(0) is memoize!(objNum, 3)(0U)); +} + +@system unittest // not @safe due to memoize +{ + struct S + { + static int fun() { return 0; } + static int fun(int i) { return 1; } + } + assert(memoize!(S.fun)() == 0); + assert(memoize!(S.fun)(3) == 1); + assert(memoize!(S.fun, 3)() == 0); + assert(memoize!(S.fun, 3)(3) == 1); +} + @system unittest // not @safe due to memoize { import core.math : sqrt; @@ -1626,6 +1710,19 @@ unittest }} } +// memoize should continue to work with functions that cannot be evaluated at compile time +@system unittest +{ + __gshared string[string] glob; + + static bool foo() + { + return (":-)" in glob) is null; + } + + assert(memoize!foo); +} + private struct DelegateFaker(F) { import std.typecons : FuncInfo, MemberFunctionGenerator; @@ -1717,13 +1814,45 @@ if (isCallable!(F)) { return fp; } + else static if (is(F Func == Func*) && is(Func == function)) + { + return function(ref F fp) @trusted + { + return buildDelegate(fp); + }(fp); + } else static if (is(typeof(&F.opCall) == delegate) || (is(typeof(&F.opCall) V : V*) && is(V == function))) { return toDelegate(&fp.opCall); } + else static if (is(typeof(&fp.opCall!()))) + { + return toDelegate(&fp.opCall!()); + } else { + static assert(false, "Unsupported type of callable, please open an issue."); + } +} + +/// +@safe unittest +{ + static int inc(ref uint num) { + num++; + return 8675309; + } + + uint myNum = 0; + auto incMyNumDel = toDelegate(&inc); + auto returnVal = incMyNumDel(myNum); + assert(myNum == 1); +} + +private template buildDelegate(F) +{ + auto buildDelegate(auto ref F fp) { alias DelType = typeof(&(new DelegateFaker!(F)).doIt); static struct DelegateFields { @@ -1753,21 +1882,22 @@ if (isCallable!(F)) } } -/// -@system unittest +@safe unittest { static int inc(ref uint num) { num++; return 8675309; } - uint myNum = 0; - auto incMyNumDel = toDelegate(&inc); - auto returnVal = incMyNumDel(myNum); - assert(myNum == 1); + uint myNum = 0x1337; + struct S1 { int opCall() { inc(myNum); return myNum; } } + static assert(!is(typeof(&s1.opCall) == delegate)); + S1 s1; + auto getvals1 = toDelegate(s1); + assert(getvals1() == 0x1338); } -@system unittest // not @safe due to toDelegate +@system unittest { static int inc(ref uint num) { num++; @@ -1852,6 +1982,36 @@ if (isCallable!(F)) } } + +@safe unittest +{ + static struct S1 { static void opCall()() { } } + static struct S2 { static T opCall(T = int)(T x) {return x; } } + + S1 i1; + const dg1 = toDelegate(i1); + dg1(); + + S2 i2; + assert(toDelegate(i2)(0xBED) == 0xBED); +} + +@safe unittest +{ + static void fun() @system pure nothrow @nogc + { + return; + } + + auto fn = &fun; + static assert( is(typeof(fn) == void function() @system pure nothrow @nogc)); + static assert(!is(typeof(fn) == void function() @safe pure nothrow @nogc)); + + auto dg = fn.toDelegate(); + static assert( is(typeof(dg) == void delegate() @system pure nothrow @nogc)); + static assert(!is(typeof(dg) == void delegate() @safe pure nothrow @nogc)); +} + /** * Passes the fields of a struct as arguments to a function. * diff --git a/libphobos/src/std/logger/filelogger.d b/libphobos/src/std/logger/filelogger.d index 5ba167c7bdb368734caad9e1f4535dd066a73989..754175c7dc823dd087135430198e7918fd15e2c3 100644 --- a/libphobos/src/std/logger/filelogger.d +++ b/libphobos/src/std/logger/filelogger.d @@ -276,7 +276,7 @@ class FileLogger : Logger { // we don't need to actually run the code, only make sure // it compiles - static _() { + static void _() { auto l = new shared FileLogger(""); } } diff --git a/libphobos/src/std/logger/package.d b/libphobos/src/std/logger/package.d index 215ca20b8772d76bd94c6ade717cc8aac559d69b..5a8474914785cb02525ba23fc9d255db6bc0e3ff 100644 --- a/libphobos/src/std/logger/package.d +++ b/libphobos/src/std/logger/package.d @@ -17,7 +17,7 @@ The easiest way to create a log message is to write: import std.logger; void main() { - log("Hello World"); + info("Hello World"); } ------------- This will print a message to the `stderr` device. The message will contain @@ -59,7 +59,7 @@ $(UL $(LI `fatal`) ) The default `Logger` will by default log to `stderr` and has a default -`LogLevel` of `LogLevel.all`. The default Logger can be accessed by +`LogLevel` of `LogLevel.info`. The default Logger can be accessed by using the property called `sharedLog`. This property is a reference to the current default `Logger`. This reference can be used to assign a new default `Logger`. diff --git a/libphobos/src/std/math/traits.d b/libphobos/src/std/math/traits.d index 81ab1b789db0999a9bf61b34d6e4083acfaf1ec1..8aba6a6b1c67deec6d241fa38815abfbc9d2cc5e 100644 --- a/libphobos/src/std/math/traits.d +++ b/libphobos/src/std/math/traits.d @@ -21,6 +21,7 @@ module std.math.traits; import std.traits : isFloatingPoint, isIntegral, isNumeric, isSigned; + /********************************* * Determines if $(D_PARAM x) is NaN. * Params: @@ -466,8 +467,27 @@ if (isFloatingPoint!(X)) */ bool isIdentical(real x, real y) @trusted pure nothrow @nogc { - import std.math.traits : floatTraits, RealFormat; - + if (__ctfe) + { + if (x !is y) return false; + if (x == x) return true; // If not NaN `is` implies identical representation. + static if (double.mant_dig != real.mant_dig) + { + // Works because we are in CTFE and there is no way in CTFE to set more + // bits of NaN payload than can fit in a double, and since 2.087 + // changed real.init to be non-signaling I *think* there is no way in + // CTFE for a real to be a signaling NaN unless real and double have + // the same representation so real's bits can be manipulated directly. + double d1 = x, d2 = y; + } + else + { + // Alias to avoid converting signaling to quiet. + alias d1 = x; + alias d2 = y; + } + return *cast(long*) &d1 == *cast(long*) &d2; + } // We're doing a bitwise comparison so the endianness is irrelevant. long* pxs = cast(long *)&x; long* pys = cast(long *)&y; @@ -491,20 +511,44 @@ bool isIdentical(real x, real y) @trusted pure nothrow @nogc assert(0, "isIdentical not implemented"); } } - /// @safe @nogc pure nothrow unittest { + // We're forcing the CTFE to run by assigning the result of the function to an enum + enum test1 = isIdentical(1.0,1.0); + enum test2 = isIdentical(real.nan,real.nan); + enum test3 = isIdentical(real.infinity, real.infinity); + enum test4 = isIdentical(real.infinity, real.infinity); + enum test5 = isIdentical(0.0, 0.0); + + assert(test1); + assert(test2); + assert(test3); + assert(test4); + assert(test5); + + enum test6 = !isIdentical(0.0, -0.0); + enum test7 = !isIdentical(real.nan, -real.nan); + enum test8 = !isIdentical(real.infinity, -real.infinity); + + assert(test6); + assert(test7); + assert(test8); +} + +@safe @nogc pure nothrow unittest +{ + assert( !isIdentical(1.2,1.3)); assert( isIdentical(0.0, 0.0)); assert( isIdentical(1.0, 1.0)); assert( isIdentical(real.infinity, real.infinity)); assert( isIdentical(-real.infinity, -real.infinity)); + assert( isIdentical(real.nan, real.nan)); assert(!isIdentical(0.0, -0.0)); assert(!isIdentical(real.nan, -real.nan)); assert(!isIdentical(real.infinity, -real.infinity)); } - /********************************* * Return 1 if sign bit of e is set, 0 if not. */ diff --git a/libphobos/src/std/path.d b/libphobos/src/std/path.d index 9dae2a65530a3aa578762b828c14fda2d8bfb838..5fb31566baf3c802c2d2fbbad5f366aa76feae05 100644 --- a/libphobos/src/std/path.d +++ b/libphobos/src/std/path.d @@ -311,7 +311,6 @@ if (isBidirectionalRange!R && isSomeChar!(ElementType!R) || @safe unittest { import std.array; - import std.utf : byDchar; assert(rtrimDirSeparators("//abc//").array == "//abc"); assert(rtrimDirSeparators("//abc//"d).array == "//abc"d); @@ -329,7 +328,6 @@ if (isBidirectionalRange!R && isSomeChar!(ElementType!R) || @safe unittest { import std.array; - import std.utf : byDchar; assert(trimDirSeparators("//abc//").array == "abc"); assert(trimDirSeparators("//abc//"d).array == "abc"d); diff --git a/libphobos/src/std/process.d b/libphobos/src/std/process.d index 2efbcaa84c8cd70e3e405529cc6f548c83faf889..1cb2264f6d3478bf268a8dac7fe53e308fe9fca5 100644 --- a/libphobos/src/std/process.d +++ b/libphobos/src/std/process.d @@ -2815,6 +2815,10 @@ void kill(Pid pid, int codeOrSignal) else version (Posix) { import core.sys.posix.signal : kill; + if (pid.osHandle == Pid.invalid) + throw new ProcessException("Pid is invalid"); + if (pid.osHandle == Pid.terminated) + throw new ProcessException("Pid is already terminated"); if (kill(pid.osHandle, codeOrSignal) == -1) throw ProcessException.newFromErrno(); } @@ -2856,7 +2860,7 @@ void kill(Pid pid, int codeOrSignal) do { s = tryWait(pid); } while (!s.terminated); version (Windows) assert(s.status == 123); else version (Posix) assert(s.status == -SIGKILL); - assertThrown!ProcessException(kill(pid)); + assertThrown!ProcessException(kill(pid)); // Already terminated } @system unittest // wait() and kill() detached process diff --git a/libphobos/src/std/stdio.d b/libphobos/src/std/stdio.d index e844297b8dc4a28d9fc0af63caae999203d2d73f..b0d96f2d54a8e17283fb9758042f18611070bcf9 100644 --- a/libphobos/src/std/stdio.d +++ b/libphobos/src/std/stdio.d @@ -2409,13 +2409,13 @@ void main() private struct ByLineCopy(Char, Terminator) { private: - import std.typecons : RefCounted, RefCountedAutoInitialize; + import std.typecons : SafeRefCounted, RefCountedAutoInitialize; /* Ref-counting stops the source range's ByLineCopyImpl * from getting out of sync after the range is copied, e.g. * when accessing range.front, then using std.range.take, * then accessing range.front again. */ - alias Impl = RefCounted!(ByLineCopyImpl!(Char, Terminator), + alias Impl = SafeRefCounted!(ByLineCopyImpl!(Char, Terminator), RefCountedAutoInitialize.no); Impl impl; @@ -4626,7 +4626,7 @@ struct lines f = File to read lines from. terminator = Line separator (`'\n'` by default). */ - this(File f, dchar terminator = '\n') + this(File f, dchar terminator = '\n') @safe { this.f = f; this.terminator = terminator; @@ -4721,6 +4721,47 @@ struct lines } } +@safe unittest +{ + /* + As pointed out in <https://github.com/dlang/phobos/issues/10605>, + it's a pity that `byLine()` & co. aren't @safe to use yet. + + This is a first attempt at working towards that goal. + For now, this test doesn't do much; as there isn't much to do safely yet. + */ + auto deleteMe = testFilename(); + scope(exit) { imported!"std.file".remove(deleteMe); } + + // Setup + { + auto f = File(deleteMe, "w"); + scope(exit) { f.close(); } + foreach (i; 1 .. 11) + f.writeln(i); + } + + // Actual tests + { + auto f = File(deleteMe, "r"); + scope(exit) { f.close(); } + + auto myLines = lines(f); + foreach (string line; myLines) + continue; + + auto myByLineCopy = f.byLineCopy; // but cannot safely iterate yet + /* + still `@system`: + - cannot call `@system` function `std.stdio.File.ByLineCopy!(immutable(char), char).ByLineCopy.empty` + - cannot call `@system` function `std.stdio.File.ByLineCopy!(immutable(char), char).ByLineCopy.popFront` + - cannot call `@system` function `std.stdio.File.ByLineCopy!(immutable(char), char).ByLineCopy.front` + */ + //foreach (line; myByLineCopy) + // continue; + } +} + @system unittest { static import std.file; diff --git a/libphobos/testsuite/libphobos.init_fini/custom_gc.d b/libphobos/testsuite/libphobos.init_fini/custom_gc.d index ee2dcee92e4efc212ed127ba1a66e4330318a94d..cb72384095944cabd412c7f566e521d30b953513 100644 --- a/libphobos/testsuite/libphobos.init_fini/custom_gc.d +++ b/libphobos/testsuite/libphobos.init_fini/custom_gc.d @@ -179,6 +179,26 @@ nothrow @nogc: return stats().allocatedInCurrentThread; } + void[] getArrayUsed(void *ptr, bool atomic = false) nothrow + { + return null; + } + + bool expandArrayUsed(void[] slice, size_t newUsed, bool atomic = false) nothrow @safe + { + return false; + } + + size_t reserveArrayCapacity(void[] slice, size_t request, bool atomic = false) nothrow @safe + { + return 0; + } + + bool shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic = false) nothrow + { + return false; + } + private: // doesn't care for alignment static void* sentinelAdd(void* p, size_t value)