From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mout-p-102.mailbox.org (mout-p-102.mailbox.org [80.241.56.152]) by sourceware.org (Postfix) with ESMTPS id 2ACAC3858403 for ; Fri, 2 Feb 2024 23:20:58 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 2ACAC3858403 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=gdcproject.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gdcproject.org ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 2ACAC3858403 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=80.241.56.152 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1706916065; cv=none; b=FDFBOGs7wiqaDr3dWKi+5RgPmP0whTJdommDXfYAwTzRaCF2D/XEftW6ItGmBo8PWfUXa+vaQ8kBn0V/m8RaD/xdeZm7+RtQfqwol2orp5r9MHr2VX57tWrVhl4liWSnsGa8AgxGvoxNtot8Go+/3FZhbote2y6+gsBMy/w/BDA= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1706916065; c=relaxed/simple; bh=l0ihFWvli11awFXNrkYmG0laE2vCJ7VkLHsiWjR3W70=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=jntLEr2LdKNYk6SgvB6qHZy9+HVAGL05Xc0Cj6wf455V3fcXwOSbFmOSozreSb7sosxqhEoW1DPLtyxOyd4k+Xj16btttkIM6gcUbNSVlL+0X2R3MepHnaK5WiOFgd/KHdIij4DMkxTIfEyDd7soDUFc3CoZpggphrPIR5ibXFE= ARC-Authentication-Results: i=1; server2.sourceware.org Received: from smtp202.mailbox.org (smtp202.mailbox.org [IPv6:2001:67c:2050:b231:465::202]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-102.mailbox.org (Postfix) with ESMTPS id 4TRWwk0Msrz9skW; Sat, 3 Feb 2024 00:20:54 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gdcproject.org; s=MBO0001; t=1706916054; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=D3nEgCc7ZpGTgJ83VIkiLGxcKF9tLJNJbFZzeWt/AYk=; b=LLBJUfs8GF9itWOG0SW+V/cZhg0hJ0TJ/NYG6WfsUoixEqDvxed2ooe5QkK37NGp0kQxL9 khWB7AQXFu0xVbM6R1oTs06KXiLuqdba6QYv0e04SQ5ICLi0pLxDMVsEUP0cCfPMPje8ZH mjsrHQKUZYEcvBicEAx61KCy78NpP7PO5QoiEGChJenYUg0EOblHanppBJ3EDR5G4qQyqt K+xcdQbU4Q6Qaw8pJMMZvVsZnVtLKH+HyEGf4Mm5wr00YIe5bQ+kHQm+WVAmgkBSC3FoVy JzVguxahRizXTcvmh+AAw/KGpi4VF1eMjYY9VKUtgBY17tlArX4vd9x8mBI9/Q== From: Iain Buclaw To: gcc-patches@gcc.gnu.org Cc: Iain Buclaw Subject: [committed] d: Merge upstream dmd, druntime f1a045928e Date: Sat, 3 Feb 2024 00:20:50 +0100 Message-Id: <20240202232050.92754-1-ibuclaw@gdcproject.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Rspamd-Queue-Id: 4TRWwk0Msrz9skW X-Spam-Status: No, score=-13.2 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,RCVD_IN_DNSWL_LOW,SPF_HELO_NONE,SPF_PASS,TXREP,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: Hi, This patch merges the D front-end and runtime library with upstream as of dmd f1a045928e. Synchronizing with the upstream RC release of v2.106.1. D front-end changes: - Import dmd v2.106.1-rc.1. - Unrecognized pragmas are no longer an error by default. D runtime changes: - Import druntime v2.106.1-rc.1. Bootstrapped and regression tested on x86_64-linux-gnu/-m32, committed to mainline. Regards, Iain. --- gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd f1a045928e. * dmd/VERSION: Bump version to v2.106.1-rc.1. * gdc.texi (fignore-unknown-pragmas): Update documentation. * d-builtins.cc (covariant_with_builtin_type_p): Update for new front-end interface. * d-lang.cc (d_parse_file): Likewise. * typeinfo.cc (make_frontend_typeinfo): Likewise. libphobos/ChangeLog: * libdruntime/MERGE: Merge upstream druntime f1a045928e. * libdruntime/Makefile.am (DRUNTIME_DSOURCES): Add core/stdc/stdatomic.d. * libdruntime/Makefile.in: Regenerate. --- gcc/d/d-builtins.cc | 2 +- gcc/d/d-lang.cc | 3 +- gcc/d/dmd/MERGE | 2 +- gcc/d/dmd/VERSION | 2 +- gcc/d/dmd/aliasthis.d | 166 +-- gcc/d/dmd/astcodegen.d | 2 +- gcc/d/dmd/attrib.d | 103 -- gcc/d/dmd/attrib.h | 5 - gcc/d/dmd/cparse.d | 113 +- gcc/d/dmd/ctfeexpr.d | 3 + gcc/d/dmd/dcast.d | 1 + gcc/d/dmd/dclass.d | 5 +- gcc/d/dmd/declaration.d | 287 ----- gcc/d/dmd/declaration.h | 1 - gcc/d/dmd/dimport.d | 42 - gcc/d/dmd/dmodule.d | 118 +- gcc/d/dmd/doc.d | 11 +- gcc/d/dmd/dscope.d | 37 +- gcc/d/dmd/dstruct.d | 2 +- gcc/d/dmd/dsymbol.d | 75 +- gcc/d/dmd/dsymbol.h | 35 +- gcc/d/dmd/dsymbolsem.d | 749 ++++++++++- gcc/d/dmd/dtemplate.d | 11 +- gcc/d/dmd/dtoh.d | 2 +- gcc/d/dmd/errorsink.d | 14 + gcc/d/dmd/expression.d | 3 +- gcc/d/dmd/expressionsem.d | 32 +- gcc/d/dmd/func.d | 5 + gcc/d/dmd/globals.d | 2 +- gcc/d/dmd/import.h | 1 - gcc/d/dmd/init.d | 6 - gcc/d/dmd/initsem.d | 77 +- gcc/d/dmd/lambdacomp.d | 2 +- gcc/d/dmd/lexer.d | 35 +- gcc/d/dmd/module.h | 5 +- gcc/d/dmd/mtype.d | 834 +----------- gcc/d/dmd/mtype.h | 4 +- gcc/d/dmd/nspace.d | 16 - gcc/d/dmd/nspace.h | 1 - gcc/d/dmd/scope.h | 2 +- gcc/d/dmd/semantic3.d | 3 +- gcc/d/dmd/statement.d | 8 +- gcc/d/dmd/statement.h | 2 +- gcc/d/dmd/statementsem.d | 22 +- gcc/d/dmd/staticassert.d | 3 - gcc/d/dmd/template.h | 1 - gcc/d/dmd/traits.d | 6 +- gcc/d/dmd/typesem.d | 802 +++++++++++- gcc/d/gdc.texi | 6 +- gcc/d/typeinfo.cc | 3 +- .../gdc.test/compilable/imports/defines.c | 4 + gcc/testsuite/gdc.test/compilable/test9565.d | 86 -- .../gdc.test/compilable/testdefines.d | 3 + .../gdc.test/fail_compilation/fail19890a.d | 2 +- .../gdc.test/fail_compilation/fail19890b.d | 2 +- .../gdc.test/fail_compilation/fail4611.d | 2 +- .../gdc.test/fail_compilation/pragmas.d | 3 +- libphobos/libdruntime/MERGE | 2 +- libphobos/libdruntime/Makefile.am | 40 +- libphobos/libdruntime/Makefile.in | 76 +- .../core/internal/array/operations.d | 35 +- libphobos/libdruntime/core/internal/atomic.d | 105 +- libphobos/libdruntime/core/stdc/stdatomic.d | 1124 +++++++++++++++++ libphobos/libdruntime/core/thread/osthread.d | 7 + libphobos/libdruntime/object.d | 6 + 65 files changed, 3209 insertions(+), 1960 deletions(-) delete mode 100644 gcc/testsuite/gdc.test/compilable/test9565.d create mode 100644 libphobos/libdruntime/core/stdc/stdatomic.d diff --git a/gcc/d/d-builtins.cc b/gcc/d/d-builtins.cc index 59a1b4ca01c..9d604974f85 100644 --- a/gcc/d/d-builtins.cc +++ b/gcc/d/d-builtins.cc @@ -724,7 +724,7 @@ static bool covariant_with_builtin_type_p (Type *t1, Type *t2) { /* Check whether the declared function matches the built-in. */ - if (same_type_p (t1, t2) || t1->covariant (t2) == Covariant::yes) + if (same_type_p (t1, t2) || covariant (t1, t2) == Covariant::yes) return true; /* May not be covariant because of D attributes applied on t1. diff --git a/gcc/d/d-lang.cc b/gcc/d/d-lang.cc index 7840cf8a132..a25d0316da6 100644 --- a/gcc/d/d-lang.cc +++ b/gcc/d/d-lang.cc @@ -23,6 +23,7 @@ along with GCC; see the file COPYING3. If not see #include "dmd/cond.h" #include "dmd/declaration.h" #include "dmd/doc.h" +#include "dmd/dsymbol.h" #include "dmd/errors.h" #include "dmd/expression.h" #include "dmd/hdrgen.h" @@ -1226,7 +1227,7 @@ d_parse_file (void) if (global.params.v.verbose) message ("importall %s", m->toChars ()); - m->importAll (NULL); + importAll (m, NULL); } if (global.errors) diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE index 5edcee1c84d..fa7004b7a41 100644 --- a/gcc/d/dmd/MERGE +++ b/gcc/d/dmd/MERGE @@ -1,4 +1,4 @@ -2bbf64907cbbb483d003e0a8fcf8b502e4883799 +f1a045928e03239b9477f9497f43f2cf0e61e959 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/VERSION b/gcc/d/dmd/VERSION index 8c95cd04f80..9d7be5b4326 100644 --- a/gcc/d/dmd/VERSION +++ b/gcc/d/dmd/VERSION @@ -1 +1 @@ -v2.106.0 +v2.106.1-rc.1 diff --git a/gcc/d/dmd/aliasthis.d b/gcc/d/dmd/aliasthis.d index a8933f6d2fe..0c156360fdc 100644 --- a/gcc/d/dmd/aliasthis.d +++ b/gcc/d/dmd/aliasthis.d @@ -14,16 +14,10 @@ module dmd.aliasthis; import core.stdc.stdio; -import dmd.aggregate; -import dmd.dscope; + import dmd.dsymbol; -import dmd.expression; -import dmd.expressionsem; -import dmd.globals; import dmd.identifier; import dmd.location; -import dmd.mtype; -import dmd.tokens; import dmd.visitor; /*********************************************************** @@ -71,161 +65,3 @@ extern (C++) final class AliasThis : Dsymbol return this.isDeprecated_; } } - -/************************************* - * Find the `alias this` symbol of e's type. - * Params: - * sc = context - * e = expression forming the `this` - * gag = do not print errors, return `null` instead - * findOnly = don't do further processing like resolving properties, - * i.e. just return plain dotExp() result. - * Returns: - * Expression that is `e.aliasthis` - */ -Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool findOnly = false) -{ - import dmd.typesem : dotExp; - for (AggregateDeclaration ad = isAggregate(e.type); ad;) - { - if (ad.aliasthis) - { - Loc loc = e.loc; - Type tthis = (e.op == EXP.type ? e.type : null); - const flags = cast(DotExpFlag) (DotExpFlag.noAliasThis | (gag * DotExpFlag.gag)); - uint olderrors = gag ? global.startGagging() : 0; - e = dotExp(ad.type, sc, e, ad.aliasthis.ident, flags); - if (!e || findOnly) - return gag && global.endGagging(olderrors) ? null : e; - - if (tthis && ad.aliasthis.sym.needThis()) - { - if (auto ve = e.isVarExp()) - { - if (auto fd = ve.var.isFuncDeclaration()) - { - // https://issues.dlang.org/show_bug.cgi?id=13009 - // Support better match for the overloaded alias this. - bool hasOverloads; - if (auto f = fd.overloadModMatch(loc, tthis, hasOverloads)) - { - if (!hasOverloads) - fd = f; // use exact match - e = new VarExp(loc, fd, hasOverloads); - e.type = f.type; - e = new CallExp(loc, e); - goto L1; - } - } - } - /* non-@property function is not called inside typeof(), - * so resolve it ahead. - */ - { - int save = sc.intypeof; - sc.intypeof = 1; // bypass "need this" error check - e = resolveProperties(sc, e); - sc.intypeof = save; - } - L1: - e = new TypeExp(loc, new TypeTypeof(loc, e)); - e = e.expressionSemantic(sc); - } - e = resolveProperties(sc, e); - if (!gag) - ad.aliasthis.checkDeprecatedAliasThis(loc, sc); - else if (global.endGagging(olderrors)) - e = null; - } - - import dmd.dclass : ClassDeclaration; - auto cd = ad.isClassDeclaration(); - if ((!e || !ad.aliasthis) && cd && cd.baseClass && cd.baseClass != ClassDeclaration.object) - { - ad = cd.baseClass; - continue; - } - break; - } - return e; -} - -/** - * Check if an `alias this` is deprecated - * - * Usually one would use `expression.checkDeprecated(scope, aliasthis)` to - * check if `expression` uses a deprecated `aliasthis`, but this calls - * `toPrettyChars` which lead to the following message: - * "Deprecation: alias this `fullyqualified.aggregate.__anonymous` is deprecated" - * - * Params: - * at = The `AliasThis` object to check - * loc = `Loc` of the expression triggering the access to `at` - * sc = `Scope` of the expression - * (deprecations do not trigger in deprecated scopes) - * - * Returns: - * Whether the alias this was reported as deprecated. - */ -bool checkDeprecatedAliasThis(AliasThis at, const ref Loc loc, Scope* sc) -{ - import dmd.errors : deprecation, Classification; - import dmd.dsymbolsem : getMessage; - - if (global.params.useDeprecated != DiagnosticReporting.off - && at.isDeprecated() && !sc.isDeprecated()) - { - const(char)* message = null; - for (Dsymbol p = at; p; p = p.parent) - { - message = p.depdecl ? p.depdecl.getMessage() : null; - if (message) - break; - } - if (message) - deprecation(loc, "`alias %s this` is deprecated - %s", - at.sym.toChars(), message); - else - deprecation(loc, "`alias %s this` is deprecated", - at.sym.toChars()); - - if (auto ti = sc.parent ? sc.parent.isInstantiated() : null) - ti.printInstantiationTrace(Classification.deprecation); - - return true; - } - return false; -} - -/************************************** - * Check and set 'att' if 't' is a recursive 'alias this' type - * - * The goal is to prevent endless loops when there is a cycle in the alias this chain. - * Since there is no multiple `alias this`, the chain either ends in a leaf, - * or it loops back on itself as some point. - * - * Example: S0 -> (S1 -> S2 -> S3 -> S1) - * - * `S0` is not a recursive alias this, so this returns `false`, and a rewrite to `S1` can be tried. - * `S1` is a recursive alias this type, but since `att` is initialized to `null`, - * this still returns `false`, but `att1` is set to `S1`. - * A rewrite to `S2` and `S3` can be tried, but when we want to try a rewrite to `S1` again, - * we notice `att == t`, so we're back at the start of the loop, and this returns `true`. - * - * Params: - * att = type reference used to detect recursion. Should be initialized to `null`. - * t = type of 'alias this' rewrite to attempt - * - * Returns: - * `false` if the rewrite is safe, `true` if it would loop back around - */ -bool isRecursiveAliasThis(ref Type att, Type t) -{ - //printf("+isRecursiveAliasThis(att = %s, t = %s)\n", att ? att.toChars() : "null", t.toChars()); - auto tb = t.toBasetype(); - if (att && tb.equivalent(att)) - return true; - else if (!att && tb.checkAliasThisRec()) - att = tb; - return false; -} diff --git a/gcc/d/dmd/astcodegen.d b/gcc/d/dmd/astcodegen.d index d40f836faae..f17907719a7 100644 --- a/gcc/d/dmd/astcodegen.d +++ b/gcc/d/dmd/astcodegen.d @@ -97,6 +97,6 @@ struct ASTCodegen alias isExpression = dmd.dtemplate.isExpression; alias isTuple = dmd.dtemplate.isTuple; - alias IgnoreErrors = dmd.dsymbol.IgnoreErrors; + alias SearchOpt = dmd.dsymbol.SearchOpt; alias PASS = dmd.dsymbol.PASS; } diff --git a/gcc/d/dmd/attrib.d b/gcc/d/dmd/attrib.d index faf04890e8e..cc6ef9c431b 100644 --- a/gcc/d/dmd/attrib.d +++ b/gcc/d/dmd/attrib.d @@ -123,19 +123,6 @@ extern (C++) abstract class AttribDeclaration : Dsymbol return sc; } - override void importAll(Scope* sc) - { - Dsymbols* d = include(sc); - //printf("\tAttribDeclaration::importAll '%s', d = %p\n", toChars(), d); - if (d) - { - Scope* sc2 = newScope(sc); - d.foreachDsymbol( s => s.importAll(sc2) ); - if (sc2 != sc) - sc2.pop(); - } - } - override void addComment(const(char)* comment) { //printf("AttribDeclaration::addComment %s\n", comment); @@ -156,11 +143,6 @@ extern (C++) abstract class AttribDeclaration : Dsymbol return Dsymbol.oneMembers(d, ps, ident); } - override void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion) - { - include(null).foreachDsymbol( s => s.setFieldOffset(ad, fieldState, isunion) ); - } - override final bool hasPointers() { return include(null).foreachDsymbol( (s) { return s.hasPointers(); } ) != 0; @@ -675,81 +657,6 @@ extern (C++) final class AnonDeclaration : AttribDeclaration return new AnonDeclaration(loc, isunion, Dsymbol.arraySyntaxCopy(decl)); } - override void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion) - { - //printf("\tAnonDeclaration::setFieldOffset %s %p\n", isunion ? "union" : "struct", this); - if (decl) - { - /* This works by treating an AnonDeclaration as an aggregate 'member', - * so in order to place that member we need to compute the member's - * size and alignment. - */ - size_t fieldstart = ad.fields.length; - - /* Hackishly hijack ad's structsize and alignsize fields - * for use in our fake anon aggregate member. - */ - uint savestructsize = ad.structsize; - uint savealignsize = ad.alignsize; - ad.structsize = 0; - ad.alignsize = 0; - - FieldState fs; - decl.foreachDsymbol( (s) - { - s.setFieldOffset(ad, fs, this.isunion); - if (this.isunion) - fs.offset = 0; - }); - - /* https://issues.dlang.org/show_bug.cgi?id=13613 - * If the fields in this.members had been already - * added in ad.fields, just update *poffset for the subsequent - * field offset calculation. - */ - if (fieldstart == ad.fields.length) - { - ad.structsize = savestructsize; - ad.alignsize = savealignsize; - fieldState.offset = ad.structsize; - return; - } - - anonstructsize = ad.structsize; - anonalignsize = ad.alignsize; - ad.structsize = savestructsize; - ad.alignsize = savealignsize; - - // 0 sized structs are set to 1 byte - if (anonstructsize == 0) - { - anonstructsize = 1; - anonalignsize = 1; - } - - assert(_scope); - auto alignment = _scope.alignment(); - - /* Given the anon 'member's size and alignment, - * go ahead and place it. - */ - anonoffset = placeField( - fieldState.offset, - anonstructsize, anonalignsize, alignment, - ad.structsize, ad.alignsize, - isunion); - - // Add to the anon fields the base offset of this anonymous aggregate - //printf("anon fields, anonoffset = %d\n", anonoffset); - foreach (const i; fieldstart .. ad.fields.length) - { - VarDeclaration v = ad.fields[i]; - //printf("\t[%d] %s %d\n", i, v.toChars(), v.offset); - v.offset += anonoffset; - } - } - } - override const(char)* kind() const { return (isunion ? "anonymous union" : "anonymous struct"); @@ -943,11 +850,6 @@ extern (C++) final class StaticIfDeclaration : ConditionalDeclaration } } - override void importAll(Scope* sc) - { - // do not evaluate condition before semantic pass - } - override const(char)* kind() const { return "static if"; @@ -1057,11 +959,6 @@ extern (C++) final class StaticForeachDeclaration : AttribDeclaration // change this to give semantics to documentation comments on static foreach declarations } - override void importAll(Scope* sc) - { - // do not evaluate aggregate before semantic pass - } - override const(char)* kind() const { return "static foreach"; diff --git a/gcc/d/dmd/attrib.h b/gcc/d/dmd/attrib.h index 98c5e521977..35628e26105 100644 --- a/gcc/d/dmd/attrib.h +++ b/gcc/d/dmd/attrib.h @@ -26,11 +26,9 @@ public: virtual Dsymbols *include(Scope *sc); virtual Scope *newScope(Scope *sc); - void importAll(Scope *sc) override; void addComment(const utf8_t *comment) override; const char *kind() const override; bool oneMember(Dsymbol **ps, Identifier *ident) override; - void setFieldOffset(AggregateDeclaration *ad, FieldState& fieldState, bool isunion) override; bool hasPointers() override final; bool hasStaticCtorOrDtor() override final; void checkCtorConstInit() override final; @@ -132,7 +130,6 @@ public: unsigned anonalignsize; // size of anonymous struct for alignment purposes AnonDeclaration *syntaxCopy(Dsymbol *s) override; - void setFieldOffset(AggregateDeclaration *ad, FieldState& fieldState, bool isunion) override; const char *kind() const override; AnonDeclaration *isAnonDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } @@ -171,7 +168,6 @@ public: StaticIfDeclaration *syntaxCopy(Dsymbol *s) override; Dsymbols *include(Scope *sc) override; - void importAll(Scope *sc) override; StaticIfDeclaration *isStaticIfDeclaration() override { return this; } const char *kind() const override; void accept(Visitor *v) override { v->visit(this); } @@ -190,7 +186,6 @@ public: bool oneMember(Dsymbol **ps, Identifier *ident) override; Dsymbols *include(Scope *sc) override; void addComment(const utf8_t *comment) override; - void importAll(Scope *sc) override; const char *kind() const override; void accept(Visitor *v) override { v->visit(this); } }; diff --git a/gcc/d/dmd/cparse.d b/gcc/d/dmd/cparse.d index 89a594823ae..4c0b96a4c8c 100644 --- a/gcc/d/dmd/cparse.d +++ b/gcc/d/dmd/cparse.d @@ -247,6 +247,8 @@ final class CParser(AST) : Parser!AST break; case TOK.charLiteral: + case TOK.wcharLiteral: + case TOK.dcharLiteral: case TOK.int32Literal: case TOK.uns32Literal: case TOK.int64Literal: @@ -725,6 +727,12 @@ final class CParser(AST) : Parser!AST nextToken(); break; + case TOK.wcharLiteral: + e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tuns16); + nextToken(); + break; + + case TOK.dcharLiteral: case TOK.uns32Literal: e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns32); nextToken(); @@ -1899,6 +1907,7 @@ final class CParser(AST) : Parser!AST } bool isalias = true; + Identifier idt; if (auto ts = dt.isTypeStruct()) { if (ts.sym.isAnonymous()) @@ -1908,6 +1917,7 @@ final class CParser(AST) : Parser!AST ts.sym.ident = id; isalias = false; } + idt = ts.sym.ident; } else if (auto te = dt.isTypeEnum()) { @@ -1917,6 +1927,7 @@ final class CParser(AST) : Parser!AST te.sym.ident = id; isalias = false; } + idt = te.sym.ident; } else if (auto tt = dt.isTypeTag()) { @@ -1930,11 +1941,13 @@ final class CParser(AST) : Parser!AST Specifier spec; declareTag(tt, spec); } + idt = tt.id; } if (isalias) { auto ad = new AST.AliasDeclaration(token.loc, id, dt); - ad.adFlags |= ad.hidden; // do not print when generating .di files + if (id == idt) + ad.adFlags |= ad.hidden; // do not print when generating .di files s = ad; } @@ -4272,6 +4285,7 @@ final class CParser(AST) : Parser!AST case TOK.rightParenthesis: case TOK.rightBracket: case TOK.endOfFile: + case TOK.endOfLine: if (!any) return false; break; @@ -4940,6 +4954,8 @@ final class CParser(AST) : Parser!AST { case TOK.identifier: case TOK.charLiteral: + case TOK.wcharLiteral: + case TOK.dcharLiteral: case TOK.int32Literal: case TOK.uns32Literal: case TOK.int64Literal: @@ -5794,6 +5810,9 @@ final class CParser(AST) : Parser!AST buf.writeByte(0); auto slice = buf.peekChars()[0 .. length]; resetDefineLines(slice); // reset lexer + auto save = eSink; + auto eLatch = new ErrorSinkLatch(); + eSink = eLatch; const(char)* endp = &slice[length - 7]; @@ -5801,40 +5820,40 @@ final class CParser(AST) : Parser!AST // indexed by Identifier, returns index into symbols[] // The memory for this is leaked - void addVar(AST.VarDeclaration v) + void addVar(AST.Dsymbol s) { - //printf("addVar() %s\n", v.toChars()); - v.isCmacro(true); // mark it as coming from a C #define + //printf("addVar() %s\n", s.toChars()); + if (auto v = s.isVarDeclaration()) + v.isCmacro(true); // mark it as coming from a C #define /* If it's already defined, replace the earlier * definition */ - if (size_t* pd = cast(void*)v.ident in defineTab) + if (size_t* pd = cast(void*)s.ident in defineTab) { //printf("replacing %s\n", v.toChars()); - (*symbols)[*pd] = v; + (*symbols)[*pd] = s; return; } - defineTab[cast(void*)v.ident] = symbols.length; - symbols.push(v); + defineTab[cast(void*)s.ident] = symbols.length; + symbols.push(s); } - Token n; - while (p < endp) { if (p[0 .. 7] == "#define") { p += 7; - scan(&n); - //printf("%s\n", n.toChars()); - if (n.value == TOK.identifier) + nextToken(); + //printf("define %s\n", token.toChars()); + if (token.value == TOK.identifier) { - auto id = n.ident; - scan(&n); + auto id = token.ident; + const params = *p == '('; + nextToken(); AST.Type t; - switch (n.value) + switch (token.value) { case TOK.endOfLine: // #define identifier nextDefineLine(); @@ -5842,14 +5861,16 @@ final class CParser(AST) : Parser!AST case TOK.int32Literal: case TOK.charLiteral: t = AST.Type.tint32; goto Linteger; + case TOK.wcharLiteral: t = AST.Type.tuns16; goto Linteger; + case TOK.dcharLiteral: case TOK.uns32Literal: t = AST.Type.tuns32; goto Linteger; case TOK.int64Literal: t = AST.Type.tint64; goto Linteger; case TOK.uns64Literal: t = AST.Type.tuns64; goto Linteger; Linteger: - const intvalue = n.intvalue; - scan(&n); - if (n.value == TOK.endOfLine) + const intvalue = token.intvalue; + nextToken(); + if (token.value == TOK.endOfLine) { /* Declare manifest constant: * enum id = intvalue; @@ -5870,9 +5891,9 @@ final class CParser(AST) : Parser!AST case TOK.imaginary80Literal: t = AST.Type.timaginary80; goto Lfloat; Lfloat: - const floatvalue = n.floatvalue; - scan(&n); - if (n.value == TOK.endOfLine) + const floatvalue = token.floatvalue; + nextToken(); + if (token.value == TOK.endOfLine) { /* Declare manifest constant: * enum id = floatvalue; @@ -5886,11 +5907,11 @@ final class CParser(AST) : Parser!AST break; case TOK.string_: - const str = n.ustring; - const len = n.len; - const postfix = n.postfix; - scan(&n); - if (n.value == TOK.endOfLine) + const str = token.ustring; + const len = token.len; + const postfix = token.postfix; + nextToken(); + if (token.value == TOK.endOfLine) { /* Declare manifest constant: * enum id = "string"; @@ -5903,6 +5924,39 @@ final class CParser(AST) : Parser!AST } break; + case TOK.leftParenthesis: + /* Look for: + * #define ID ( expression ) + * and rewrite it to a template function: + * auto ID()() { return expression; } + */ + if (params) + break; // no parameters + nextToken(); + eLatch.sawErrors = false; + auto exp = cparseExpression(); + if (eLatch.sawErrors) // parsing errors + break; // abandon this #define + if (token.value != TOK.rightParenthesis) + break; + nextToken(); + if (token.value != TOK.endOfLine) + break; + auto ret = new AST.ReturnStatement(exp.loc, exp); + auto parameterList = AST.ParameterList(new AST.Parameters(), VarArg.none, 0); + StorageClass stc = STC.auto_; + auto tf = new AST.TypeFunction(parameterList, null, LINK.d, stc); + auto fd = new AST.FuncDeclaration(exp.loc, exp.loc, id, stc, tf, 0); + fd.fbody = ret; + AST.Dsymbols* decldefs = new AST.Dsymbols(); + decldefs.push(fd); + AST.TemplateParameters* tpl = new AST.TemplateParameters(); + AST.Expression constraint = null; + auto tempdecl = new AST.TemplateDeclaration(exp.loc, id, tpl, constraint, decldefs, false); + addVar(tempdecl); + nextDefineLine(); + continue; + default: break; } @@ -5911,8 +5965,8 @@ final class CParser(AST) : Parser!AST } else { - scan(&n); - if (n.value != TOK.endOfLine) + scan(&token); + if (token.value != TOK.endOfLine) { skipToNextLine(); } @@ -5920,6 +5974,7 @@ final class CParser(AST) : Parser!AST nextDefineLine(); } + eSink = save; defines = buf; } diff --git a/gcc/d/dmd/ctfeexpr.d b/gcc/d/dmd/ctfeexpr.d index 43efc05b5d3..993bab023f9 100644 --- a/gcc/d/dmd/ctfeexpr.d +++ b/gcc/d/dmd/ctfeexpr.d @@ -636,8 +636,11 @@ bool isSafePointerCast(Type srcPointee, Type destPointee) // It's OK if function pointers differ only in safe/pure/nothrow if (srcPointee.ty == Tfunction && destPointee.ty == Tfunction) + { + import dmd.typesem : covariant; return srcPointee.covariant(destPointee) == Covariant.yes || destPointee.covariant(srcPointee) == Covariant.yes; + } // it's OK to cast to void* if (destPointee.ty == Tvoid) return true; diff --git a/gcc/d/dmd/dcast.d b/gcc/d/dmd/dcast.d index bb86b080be6..cfa374c970b 100644 --- a/gcc/d/dmd/dcast.d +++ b/gcc/d/dmd/dcast.d @@ -24,6 +24,7 @@ import dmd.dinterpret; import dmd.dscope; import dmd.dstruct; import dmd.dsymbol; +import dmd.dsymbolsem; import dmd.errors; import dmd.escape; import dmd.expression; diff --git a/gcc/d/dmd/dclass.d b/gcc/d/dmd/dclass.d index 72b85cfc64e..e066d877e8a 100644 --- a/gcc/d/dmd/dclass.d +++ b/gcc/d/dmd/dclass.d @@ -594,7 +594,7 @@ extern (C++) class ClassDeclaration : AggregateDeclaration fieldState.offset = structsize; foreach (s; *members) { - s.setFieldOffset(this, fieldState, false); + s.setFieldOffset(this, &fieldState, false); } sizeok = Sizeok.done; @@ -614,7 +614,7 @@ extern (C++) class ClassDeclaration : AggregateDeclaration final bool isFuncHidden(FuncDeclaration fd) { //printf("ClassDeclaration.isFuncHidden(class = %s, fd = %s)\n", toChars(), fd.toPrettyChars()); - Dsymbol s = this.search(Loc.initial, fd.ident, IgnoreAmbiguous | IgnoreErrors); + Dsymbol s = this.search(Loc.initial, fd.ident, SearchOpt.ignoreAmbiguous | SearchOpt.ignoreErrors); if (!s) { //printf("not found\n"); @@ -670,6 +670,7 @@ extern (C++) class ClassDeclaration : AggregateDeclaration void searchVtbl(ref Dsymbols vtbl) { + import dmd.typesem : covariant; bool seenInterfaceVirtual; foreach (s; vtbl) { diff --git a/gcc/d/dmd/declaration.d b/gcc/d/dmd/declaration.d index 0e125fdd001..bdc91f4f1f2 100644 --- a/gcc/d/dmd/declaration.d +++ b/gcc/d/dmd/declaration.d @@ -1214,88 +1214,6 @@ extern (C++) class VarDeclaration : Declaration return v; } - override void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion) - { - //printf("VarDeclaration::setFieldOffset(ad = %s) %s\n", ad.toChars(), toChars()); - - if (aliasTuple) - { - // If this variable was really a tuple, set the offsets for the tuple fields - aliasTuple.foreachVar((s) { s.setFieldOffset(ad, fieldState, isunion); }); - return; - } - - if (!isField()) - return; - assert(!(storage_class & (STC.static_ | STC.extern_ | STC.parameter))); - - //printf("+VarDeclaration::setFieldOffset(ad = %s) %s\n", ad.toChars(), toChars()); - - /* Fields that are tuples appear both as part of TupleDeclarations and - * as members. That means ignore them if they are already a field. - */ - if (offset) - { - // already a field - fieldState.offset = ad.structsize; // https://issues.dlang.org/show_bug.cgi?id=13613 - return; - } - for (size_t i = 0; i < ad.fields.length; i++) - { - if (ad.fields[i] == this) - { - // already a field - fieldState.offset = ad.structsize; // https://issues.dlang.org/show_bug.cgi?id=13613 - return; - } - } - - // Check for forward referenced types which will fail the size() call - Type t = type.toBasetype(); - if (storage_class & STC.ref_) - { - // References are the size of a pointer - t = Type.tvoidptr; - } - Type tv = t.baseElemOf(); - if (tv.ty == Tstruct) - { - auto ts = cast(TypeStruct)tv; - assert(ts.sym != ad); // already checked in ad.determineFields() - if (!ts.sym.determineSize(loc)) - { - type = Type.terror; - errors = true; - return; - } - } - - // List in ad.fields. Even if the type is error, it's necessary to avoid - // pointless error diagnostic "more initializers than fields" on struct literal. - ad.fields.push(this); - - if (t.ty == Terror) - return; - - /* If coming after a bit field in progress, - * advance past the field - */ - fieldState.inFlight = false; - - const sz = t.size(loc); - assert(sz != SIZE_INVALID && sz < uint.max); - uint memsize = cast(uint)sz; // size of member - uint memalignsize = target.fieldalign(t); // size of member for alignment purposes - offset = placeField( - fieldState.offset, - memsize, memalignsize, alignment, - ad.structsize, ad.alignsize, - isunion); - - //printf("\t%s: memalignsize = %d\n", toChars(), memalignsize); - //printf(" addField '%s' to '%s' at offset %d, size = %d\n", toChars(), ad.toChars(), offset, memsize); - } - override const(char)* kind() const { return "variable"; @@ -1803,211 +1721,6 @@ extern (C++) class BitFieldDeclaration : VarDeclaration : (1L << (width - 1)) - 1); return v; } - - override final void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion) - { - enum log = false; - static if (log) - { - printf("BitFieldDeclaration::setFieldOffset(ad: %s, field: %s)\n", ad.toChars(), toChars()); - void print(const ref FieldState fieldState) - { - fieldState.print(); - printf(" fieldWidth = %d bits\n", fieldWidth); - } - print(fieldState); - } - - Type t = type.toBasetype(); - const bool anon = isAnonymous(); - - // List in ad.fields. Even if the type is error, it's necessary to avoid - // pointless error diagnostic "more initializers than fields" on struct literal. - if (!anon) - ad.fields.push(this); - - if (t.ty == Terror) - return; - - const sz = t.size(loc); - assert(sz != SIZE_INVALID && sz < uint.max); - uint memsize = cast(uint)sz; // size of member - uint memalignsize = target.fieldalign(t); // size of member for alignment purposes - if (log) printf(" memsize: %u memalignsize: %u\n", memsize, memalignsize); - - if (fieldWidth == 0 && !anon) - error(loc, "named bit fields cannot have 0 width"); - if (fieldWidth > memsize * 8) - error(loc, "bit field width %d is larger than type", fieldWidth); - - const style = target.c.bitFieldStyle; - - void startNewField() - { - if (log) printf("startNewField()\n"); - uint alignsize; - if (style == TargetC.BitFieldStyle.Gcc_Clang) - { - if (fieldWidth > 32) - alignsize = memalignsize; - else if (fieldWidth > 16) - alignsize = 4; - else if (fieldWidth > 8) - alignsize = 2; - else - alignsize = 1; - } - else - alignsize = memsize; // not memalignsize - - uint dummy; - offset = placeField( - fieldState.offset, - memsize, alignsize, alignment, - ad.structsize, - (anon && style == TargetC.BitFieldStyle.Gcc_Clang) ? dummy : ad.alignsize, - isunion); - - fieldState.inFlight = true; - fieldState.fieldOffset = offset; - fieldState.bitOffset = 0; - fieldState.fieldSize = memsize; - } - - if (style == TargetC.BitFieldStyle.Gcc_Clang) - { - if (fieldWidth == 0) - { - if (!isunion) - { - // Use type of zero width field to align to next field - fieldState.offset = (fieldState.offset + memalignsize - 1) & ~(memalignsize - 1); - ad.structsize = fieldState.offset; - } - - fieldState.inFlight = false; - return; - } - - if (ad.alignsize == 0) - ad.alignsize = 1; - if (!anon && - ad.alignsize < memalignsize) - ad.alignsize = memalignsize; - } - else if (style == TargetC.BitFieldStyle.MS) - { - if (ad.alignsize == 0) - ad.alignsize = 1; - if (fieldWidth == 0) - { - if (fieldState.inFlight && !isunion) - { - // documentation says align to next int - //const alsz = cast(uint)Type.tint32.size(); - const alsz = memsize; // but it really does this - fieldState.offset = (fieldState.offset + alsz - 1) & ~(alsz - 1); - ad.structsize = fieldState.offset; - } - - fieldState.inFlight = false; - return; - } - } - else if (style == TargetC.BitFieldStyle.DM) - { - if (anon && fieldWidth && (!fieldState.inFlight || fieldState.bitOffset == 0)) - return; // this probably should be a bug in DMC - if (ad.alignsize == 0) - ad.alignsize = 1; - if (fieldWidth == 0) - { - if (fieldState.inFlight && !isunion) - { - const alsz = memsize; - fieldState.offset = (fieldState.offset + alsz - 1) & ~(alsz - 1); - ad.structsize = fieldState.offset; - } - - fieldState.inFlight = false; - return; - } - } - - if (!fieldState.inFlight) - { - //printf("not in flight\n"); - startNewField(); - } - else if (style == TargetC.BitFieldStyle.Gcc_Clang) - { - // If the bit-field spans more units of alignment than its type, - // start a new field at the next alignment boundary. - if (fieldState.bitOffset == fieldState.fieldSize * 8 && - fieldState.bitOffset + fieldWidth > memalignsize * 8) - { - if (log) printf("more units of alignment than its type\n"); - startNewField(); // the bit field is full - } - else - { - // if alignment boundary is crossed - uint start = fieldState.fieldOffset * 8 + fieldState.bitOffset; - uint end = start + fieldWidth; - //printf("%s start: %d end: %d memalignsize: %d\n", ad.toChars(), start, end, memalignsize); - if (start / (memalignsize * 8) != (end - 1) / (memalignsize * 8)) - { - if (log) printf("alignment is crossed\n"); - startNewField(); - } - } - } - else if (style == TargetC.BitFieldStyle.DM || - style == TargetC.BitFieldStyle.MS) - { - if (memsize != fieldState.fieldSize || - fieldState.bitOffset + fieldWidth > fieldState.fieldSize * 8) - { - //printf("new field\n"); - startNewField(); - } - } - else - assert(0); - - offset = fieldState.fieldOffset; - bitOffset = fieldState.bitOffset; - - const pastField = bitOffset + fieldWidth; - if (style == TargetC.BitFieldStyle.Gcc_Clang) - { - auto size = (pastField + 7) / 8; - fieldState.fieldSize = size; - //printf(" offset: %d, size: %d\n", offset, size); - if (isunion) - { - const newstructsize = offset + size; - if (newstructsize > ad.structsize) - ad.structsize = newstructsize; - } - else - ad.structsize = offset + size; - } - else - fieldState.fieldSize = memsize; - //printf("at end: ad.structsize = %d\n", cast(int)ad.structsize); - //print(fieldState); - - if (!isunion) - { - fieldState.offset = offset + fieldState.fieldSize; - fieldState.bitOffset = pastField; - } - - //printf("\t%s: offset = %d bitOffset = %d fieldWidth = %d memsize = %d\n", toChars(), offset, bitOffset, fieldWidth, memsize); - //printf("\t%s: memalignsize = %d\n", toChars(), memalignsize); - //printf(" addField '%s' to '%s' at offset %d, size = %d\n", toChars(), ad.toChars(), offset, memsize); - } } /*********************************************************** diff --git a/gcc/d/dmd/declaration.h b/gcc/d/dmd/declaration.h index a65fb4467e5..adbc26bf570 100644 --- a/gcc/d/dmd/declaration.h +++ b/gcc/d/dmd/declaration.h @@ -281,7 +281,6 @@ public: bool systemInferred(bool v); static VarDeclaration *create(const Loc &loc, Type *t, Identifier *id, Initializer *init, StorageClass storage_class = STCundefined); VarDeclaration *syntaxCopy(Dsymbol *) override; - void setFieldOffset(AggregateDeclaration *ad, FieldState& fieldState, bool isunion) override final; const char *kind() const override; AggregateDeclaration *isThis() override final; bool needThis() override final; diff --git a/gcc/d/dmd/dimport.d b/gcc/d/dmd/dimport.d index 5c01a9f5889..51b9220c185 100644 --- a/gcc/d/dmd/dimport.d +++ b/gcc/d/dmd/dimport.d @@ -222,48 +222,6 @@ extern (C++) final class Import : Dsymbol return global.errors != errors; } - override void importAll(Scope* sc) - { - if (mod) return; // Already done - - /* - * https://issues.dlang.org/show_bug.cgi?id=15525 - * - * Loading the import has failed, - * most likely because of parsing errors. - * Therefore we cannot trust the resulting AST. - */ - if (load(sc)) - { - // https://issues.dlang.org/show_bug.cgi?id=23873 - // For imports that are not at module or function level, - // e.g. aggregate level, the import symbol is added to the - // symbol table and later semantic is performed on it. - // This leads to semantic analysis on an malformed AST - // which causes all kinds of segfaults. - // The fix is to note that the module has errors and avoid - // semantic analysis on it. - if(mod) - mod.errors = true; - return; - } - - if (!mod) return; // Failed - - if (sc.stc & STC.static_) - isstatic = true; - mod.importAll(null); - mod.checkImportDeprecation(loc, sc); - if (sc.explicitVisibility) - visibility = sc.visibility; - if (!isstatic && !aliasId && !names.length) - sc.scopesym.importScope(mod, visibility); - // Enable access to pkgs/mod as soon as posible, because compiler - // can traverse them before the import gets semantic (Issue: 21501) - if (!aliasId && !names.length) - addPackageAccess(sc.scopesym); - } - /******************************* * Mark the imported packages as accessible from the current * scope. This access check is necessary when using FQN b/c diff --git a/gcc/d/dmd/dmodule.d b/gcc/d/dmd/dmodule.d index d096e437cf9..6c9e90a2c8f 100644 --- a/gcc/d/dmd/dmodule.d +++ b/gcc/d/dmd/dmodule.d @@ -401,7 +401,7 @@ extern (C++) final class Module : Package Identifier searchCacheIdent; Dsymbol searchCacheSymbol; // cached value of search - int searchCacheFlags; // cached flags + SearchOptFlags searchCacheFlags; // cached flags bool insearch; /** @@ -921,70 +921,6 @@ extern (C++) final class Module : Package return this; } - override void importAll(Scope* prevsc) - { - //printf("+Module::importAll(this = %p, '%s'): parent = %p\n", this, toChars(), parent); - if (_scope) - return; // already done - if (filetype == FileType.ddoc) - { - error(loc, "%s `%s` is a Ddoc file, cannot import it", kind, toPrettyChars); - return; - } - - /* Note that modules get their own scope, from scratch. - * This is so regardless of where in the syntax a module - * gets imported, it is unaffected by context. - * Ignore prevsc. - */ - Scope* sc = Scope.createGlobal(this, global.errorSink); // create root scope - - if (md && md.msg) - md.msg = semanticString(sc, md.msg, "deprecation message"); - - // Add import of "object", even for the "object" module. - // If it isn't there, some compiler rewrites, like - // classinst == classinst -> .object.opEquals(classinst, classinst) - // would fail inside object.d. - if (filetype != FileType.c && - (members.length == 0 || - (*members)[0].ident != Id.object || - (*members)[0].isImport() is null)) - { - auto im = new Import(Loc.initial, null, Id.object, null, 0); - members.shift(im); - } - if (!symtab) - { - // Add all symbols into module's symbol table - symtab = new DsymbolTable(); - for (size_t i = 0; i < members.length; i++) - { - Dsymbol s = (*members)[i]; - s.addMember(sc, sc.scopesym); - } - } - // anything else should be run after addMember, so version/debug symbols are defined - /* Set scope for the symbols so that if we forward reference - * a symbol, it can possibly be resolved on the spot. - * If this works out well, it can be extended to all modules - * before any semantic() on any of them. - */ - this.setScope(sc); // remember module scope for semantic - for (size_t i = 0; i < members.length; i++) - { - Dsymbol s = (*members)[i]; - s.setScope(sc); - } - for (size_t i = 0; i < members.length; i++) - { - Dsymbol s = (*members)[i]; - s.importAll(sc); - } - sc = sc.pop(); - sc.pop(); // 2 pops because Scope.createGlobal() created 2 - } - /********************************** * Determine if we need to generate an instance of ModuleInfo * for this Module. @@ -1021,14 +957,14 @@ extern (C++) final class Module : Package } } - override bool isPackageAccessible(Package p, Visibility visibility, int flags = 0) + override bool isPackageAccessible(Package p, Visibility visibility, SearchOptFlags flags = SearchOpt.all) { if (insearch) // don't follow import cycles return false; insearch = true; scope (exit) insearch = false; - if (flags & IgnorePrivateImports) + if (flags & SearchOpt.ignorePrivateImports) visibility = Visibility(Visibility.Kind.public_); // only consider public imports return super.isPackageAccessible(p, visibility); } @@ -1384,7 +1320,53 @@ extern (C++) void getLocalClasses(Module mod, ref ClassDeclarations aclasses) return 0; } - ScopeDsymbol._foreach(null, mod.members, &pushAddClassDg); + _foreach(null, mod.members, &pushAddClassDg); +} + + +alias ForeachDg = int delegate(size_t idx, Dsymbol s); + +/*************************************** + * Expands attribute declarations in members in depth first + * order. Calls dg(size_t symidx, Dsymbol *sym) for each + * member. + * If dg returns !=0, stops and returns that value else returns 0. + * Use this function to avoid the O(N + N^2/2) complexity of + * calculating dim and calling N times getNth. + * Returns: + * last value returned by dg() + */ +int _foreach(Scope* sc, Dsymbols* members, scope ForeachDg dg, size_t* pn = null) +{ + assert(dg); + if (!members) + return 0; + size_t n = pn ? *pn : 0; // take over index + int result = 0; + foreach (size_t i; 0 .. members.length) + { + import dmd.attrib : AttribDeclaration; + import dmd.dtemplate : TemplateMixin; + + Dsymbol s = (*members)[i]; + if (AttribDeclaration a = s.isAttribDeclaration()) + result = _foreach(sc, a.include(sc), dg, &n); + else if (TemplateMixin tm = s.isTemplateMixin()) + result = _foreach(sc, tm.members, dg, &n); + else if (s.isTemplateInstance()) + { + } + else if (s.isUnitTestDeclaration()) + { + } + else + result = dg(n++, s); + if (result) + break; + } + if (pn) + *pn = n; // update index + return result; } /** diff --git a/gcc/d/dmd/doc.d b/gcc/d/dmd/doc.d index 5488d5a0008..03848c0274b 100644 --- a/gcc/d/dmd/doc.d +++ b/gcc/d/dmd/doc.d @@ -748,7 +748,8 @@ void emitAnchor(ref OutBuffer buf, Dsymbol s, Scope* sc, bool forHeader = false) auto a = imp.aliases[i]; auto id = a ? a : imp.names[i]; auto loc = Loc.init; - if (auto symFromId = sc.search(loc, id, null)) + Dsymbol pscopesym; + if (auto symFromId = sc.search(loc, id, pscopesym)) { emitAnchor(buf, symFromId, sc, forHeader); } @@ -3636,11 +3637,12 @@ struct MarkdownLinkReferences if (id) { auto loc = Loc(); - auto symbol = _scope.search(loc, id, null, IgnoreErrors); + Dsymbol pscopesym; + auto symbol = _scope.search(loc, id, pscopesym, SearchOpt.ignoreErrors); for (size_t i = 1; symbol && i < ids.length; ++i) { id = Identifier.lookup(ids[i].ptr, ids[i].length); - symbol = id !is null ? symbol.search(loc, id, IgnoreErrors) : null; + symbol = id !is null ? symbol.search(loc, id, SearchOpt.ignoreErrors) : null; } if (symbol) link = MarkdownLink(createHref(symbol), null, name, symbol); @@ -4997,7 +4999,8 @@ void highlightCode(Scope* sc, Dsymbol s, ref OutBuffer buf, size_t offset) auto a = imp.aliases[i]; auto id = a ? a : imp.names[i]; auto loc = Loc.init; - if (auto symFromId = sc.search(loc, id, null)) + Dsymbol pscopesym; + if (auto symFromId = sc.search(loc, id, pscopesym)) { highlightCode(sc, symFromId, buf, offset); } diff --git a/gcc/d/dmd/dscope.d b/gcc/d/dmd/dscope.d index d68bcdaad40..cd177a66f50 100644 --- a/gcc/d/dmd/dscope.d +++ b/gcc/d/dmd/dscope.d @@ -344,13 +344,13 @@ extern (C++) struct Scope * Params: * loc = location to use for error messages * ident = name to look up - * pscopesym = if supplied and name is found, set to scope that ident was found in + * pscopesym = if supplied and name is found, set to scope that ident was found in, otherwise set to null * flags = modify search based on flags * * Returns: * symbol if found, null if not */ - extern (C++) Dsymbol search(const ref Loc loc, Identifier ident, Dsymbol* pscopesym, int flags = IgnoreNone) + extern (C++) Dsymbol search(const ref Loc loc, Identifier ident, out Dsymbol pscopesym, SearchOptFlags flags = SearchOpt.all) { version (LOGSEARCH) { @@ -371,7 +371,7 @@ extern (C++) struct Scope } // This function is called only for unqualified lookup - assert(!(flags & (SearchLocalsOnly | SearchImportsOnly))); + assert(!(flags & (SearchOpt.localsOnly | SearchOpt.importsOnly))); /* If ident is "start at module scope", only look at module scope */ @@ -386,15 +386,14 @@ extern (C++) struct Scope if (Dsymbol s = sc.scopesym.isModule()) { //printMsg("\tfound", s); - if (pscopesym) - *pscopesym = sc.scopesym; + pscopesym = sc.scopesym; return s; } } return null; } - Dsymbol checkAliasThis(AggregateDeclaration ad, Identifier ident, int flags, Expression* exp) + Dsymbol checkAliasThis(AggregateDeclaration ad, Identifier ident, SearchOptFlags flags, Expression* exp) { import dmd.mtype; if (!ad || !ad.aliasthis) @@ -458,7 +457,7 @@ extern (C++) struct Scope return s; } - Dsymbol searchScopes(int flags) + Dsymbol searchScopes(SearchOptFlags flags) { for (Scope* sc = &this; sc; sc = sc.enclosing) { @@ -468,13 +467,13 @@ extern (C++) struct Scope //printf("\tlooking in scopesym '%s', kind = '%s', flags = x%x\n", sc.scopesym.toChars(), sc.scopesym.kind(), flags); if (sc.scopesym.isModule()) - flags |= SearchUnqualifiedModule; // tell Module.search() that SearchLocalsOnly is to be obeyed + flags |= SearchOpt.unqualifiedModule; // tell Module.search() that SearchOpt.localsOnly is to be obeyed else if (sc.flags & SCOPE.Cfile && sc.scopesym.isStructDeclaration()) continue; // C doesn't have struct scope if (Dsymbol s = sc.scopesym.search(loc, ident, flags)) { - if (flags & TagNameSpace) + if (flags & SearchOpt.tagNameSpace) { // ImportC: if symbol is not a tag, look for it in tag table if (!s.isScopeDsymbol()) @@ -486,8 +485,7 @@ extern (C++) struct Scope } } //printMsg("\tfound local", s); - if (pscopesym) - *pscopesym = sc.scopesym; + pscopesym = sc.scopesym; return s; } @@ -499,8 +497,7 @@ extern (C++) struct Scope if (aliasSym) { //printf("found aliassym: %s\n", aliasSym.toChars()); - if (pscopesym) - *pscopesym = new ExpressionDsymbol(exp); + pscopesym = new ExpressionDsymbol(exp); return aliasSym; } } @@ -513,15 +510,15 @@ extern (C++) struct Scope } if (this.flags & SCOPE.ignoresymbolvisibility) - flags |= IgnoreSymbolVisibility; + flags |= SearchOpt.ignoreVisibility; // First look in local scopes - Dsymbol s = searchScopes(flags | SearchLocalsOnly); + Dsymbol s = searchScopes(flags | SearchOpt.localsOnly); version (LOGSEARCH) if (s) printMsg("-Scope.search() found local", s); if (!s) { // Second look in imported modules - s = searchScopes(flags | SearchImportsOnly); + s = searchScopes(flags | SearchOpt.importsOnly); version (LOGSEARCH) if (s) printMsg("-Scope.search() found import", s); } return s; @@ -554,8 +551,8 @@ extern (C++) struct Scope return null; Scope* sc = &this; Module.clearCache(); - Dsymbol scopesym = null; - Dsymbol s = sc.search(Loc.initial, id, &scopesym, IgnoreErrors); + Dsymbol scopesym; + Dsymbol s = sc.search(Loc.initial, id, scopesym, SearchOpt.ignoreErrors); if (!s) return null; @@ -579,9 +576,9 @@ extern (C++) struct Scope return s; } - Dsymbol scopesym = null; + Dsymbol scopesym; // search for exact name first - if (auto s = search(Loc.initial, ident, &scopesym, IgnoreErrors)) + if (auto s = search(Loc.initial, ident, scopesym, SearchOpt.ignoreErrors)) return s; return speller!scope_search_fp(ident.toString()); } diff --git a/gcc/d/dmd/dstruct.d b/gcc/d/dmd/dstruct.d index 36e847c3f88..0e0a1f52f88 100644 --- a/gcc/d/dmd/dstruct.d +++ b/gcc/d/dmd/dstruct.d @@ -289,7 +289,7 @@ extern (C++) class StructDeclaration : AggregateDeclaration for (size_t i = 0; i < members.length; i++) { Dsymbol s = (*members)[i]; - s.setFieldOffset(this, fieldState, isunion); + s.setFieldOffset(this, &fieldState, isunion); } if (type.ty == Terror) { diff --git a/gcc/d/dmd/dsymbol.d b/gcc/d/dmd/dsymbol.d index 8f5a292a284..6613c2bc5c2 100644 --- a/gcc/d/dmd/dsymbol.d +++ b/gcc/d/dmd/dsymbol.d @@ -203,20 +203,21 @@ enum PASS : ubyte } // Search options -enum : int +alias SearchOptFlags = uint; +enum SearchOpt : SearchOptFlags { - IgnoreNone = 0x00, // default - IgnorePrivateImports = 0x01, // don't search private imports - IgnoreErrors = 0x02, // don't give error messages - IgnoreAmbiguous = 0x04, // return NULL if ambiguous - SearchLocalsOnly = 0x08, // only look at locals (don't search imports) - SearchImportsOnly = 0x10, // only look in imports - SearchUnqualifiedModule = 0x20, // the module scope search is unqualified, + all = 0x00, // search for all symbols + ignorePrivateImports = 0x01, // don't search private imports + ignoreErrors = 0x02, // don't give error messages + ignoreAmbiguous = 0x04, // return NULL if ambiguous + localsOnly = 0x08, // only look at locals (don't search imports) + importsOnly = 0x10, // only look in imports + unqualifiedModule = 0x20, // the module scope search is unqualified, // meaning don't search imports in that scope, // because qualified module searches search // their imports - IgnoreSymbolVisibility = 0x80, // also find private and package protected symbols - TagNameSpace = 0x100, // search ImportC tag symbol table + tagNameSpace = 0x40, // search ImportC tag symbol table + ignoreVisibility = 0x80, // also find private and package protected symbols } /*********************************************************** @@ -712,10 +713,6 @@ extern (C++) class Dsymbol : ASTNode return toAlias(); } - void importAll(Scope* sc) - { - } - bool overloadInsert(Dsymbol s) { //printf("Dsymbol::overloadInsert('%s')\n", s.toChars()); @@ -922,10 +919,6 @@ extern (C++) class Dsymbol : ASTNode return true; } - void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion) - { - } - /***************************************** * Is Dsymbol a variable that contains pointers? */ @@ -1263,7 +1256,7 @@ public: (*pary)[p.tag] = true; } - bool isPackageAccessible(Package p, Visibility visibility, int flags = 0) nothrow + bool isPackageAccessible(Package p, Visibility visibility, SearchOptFlags flags = SearchOpt.all) nothrow { if (p.tag < accessiblePackages.length && accessiblePackages[p.tag] || visibility.kind == Visibility.Kind.private_ && p.tag < privateAccessiblePackages.length && privateAccessiblePackages[p.tag]) @@ -1272,7 +1265,7 @@ public: { // only search visible scopes && imported modules should ignore private imports if (visibility.kind <= visibilities[i] && - ss.isScopeDsymbol.isPackageAccessible(p, visibility, IgnorePrivateImports)) + ss.isScopeDsymbol.isPackageAccessible(p, visibility, SearchOpt.ignorePrivateImports)) return true; } return false; @@ -1371,48 +1364,6 @@ public: return false; } - extern (D) alias ForeachDg = int delegate(size_t idx, Dsymbol s); - - /*************************************** - * Expands attribute declarations in members in depth first - * order. Calls dg(size_t symidx, Dsymbol *sym) for each - * member. - * If dg returns !=0, stops and returns that value else returns 0. - * Use this function to avoid the O(N + N^2/2) complexity of - * calculating dim and calling N times getNth. - * Returns: - * last value returned by dg() - */ - extern (D) static int _foreach(Scope* sc, Dsymbols* members, scope ForeachDg dg, size_t* pn = null) - { - assert(dg); - if (!members) - return 0; - size_t n = pn ? *pn : 0; // take over index - int result = 0; - foreach (size_t i; 0 .. members.length) - { - Dsymbol s = (*members)[i]; - if (AttribDeclaration a = s.isAttribDeclaration()) - result = _foreach(sc, a.include(sc), dg, &n); - else if (TemplateMixin tm = s.isTemplateMixin()) - result = _foreach(sc, tm.members, dg, &n); - else if (s.isTemplateInstance()) - { - } - else if (s.isUnitTestDeclaration()) - { - } - else - result = dg(n++, s); - if (result) - break; - } - if (pn) - *pn = n; // update index - return result; - } - override final inout(ScopeDsymbol) isScopeDsymbol() inout { return this; diff --git a/gcc/d/dmd/dsymbol.h b/gcc/d/dmd/dsymbol.h index 15c997027da..d029c00fc1c 100644 --- a/gcc/d/dmd/dsymbol.h +++ b/gcc/d/dmd/dsymbol.h @@ -147,20 +147,21 @@ enum /* Flags for symbol search */ -enum +typedef uint SearchOptFlags; +enum class SearchOpt : SearchOptFlags { - IgnoreNone = 0x00, // default - IgnorePrivateImports = 0x01, // don't search private imports - IgnoreErrors = 0x02, // don't give error messages - IgnoreAmbiguous = 0x04, // return NULL if ambiguous - SearchLocalsOnly = 0x08, // only look at locals (don't search imports) - SearchImportsOnly = 0x10, // only look in imports - SearchUnqualifiedModule = 0x20, // the module scope search is unqualified, - // meaning don't search imports in that scope, - // because qualified module searches search - // their imports - IgnoreSymbolVisibility = 0x80, // also find private and package protected symbols - TagNameSpace = 0x100, // search ImportC tag symbol table + all = 0x00, // default + ignorePrivateImports = 0x01, // don't search private imports + ignoreErrors = 0x02, // don't give error messages + ignoreAmbiguous = 0x04, // return NULL if ambiguous + localsOnly = 0x08, // only look at locals (don't search imports) + importsOnly = 0x10, // only look in imports + unqualifiedModule = 0x20, // the module scope search is unqualified, + // meaning don't search imports in that scope, + // because qualified module searches search + // their imports + tagNameSpace = 0x40, // search ImportC tag symbol table + ignoreVisibility = 0x80, // also find private and package protected symbols }; struct FieldState @@ -227,7 +228,6 @@ public: virtual const char *kind() const; virtual Dsymbol *toAlias(); // resolve real symbol virtual Dsymbol *toAlias2(); - virtual void importAll(Scope *sc); virtual bool overloadInsert(Dsymbol *s); virtual uinteger_t size(const Loc &loc); virtual bool isforwardRef(); @@ -247,7 +247,6 @@ public: virtual Visibility visible(); virtual Dsymbol *syntaxCopy(Dsymbol *s); // copy only syntax trees virtual bool oneMember(Dsymbol **ps, Identifier *ident); - virtual void setFieldOffset(AggregateDeclaration *ad, FieldState& fieldState, bool isunion); virtual bool hasPointers(); virtual bool hasStaticCtorOrDtor(); virtual void addObjcSymbols(ClassDeclarations *, ClassDeclarations *) { } @@ -336,7 +335,7 @@ private: public: ScopeDsymbol *syntaxCopy(Dsymbol *s) override; virtual void importScope(Dsymbol *s, Visibility visibility); - virtual bool isPackageAccessible(Package *p, Visibility visibility, int flags = 0); + virtual bool isPackageAccessible(Package *p, Visibility visibility, SearchOptFlags flags = (SearchOptFlags)SearchOpt::all); bool isforwardRef() override final; static void multiplyDefined(const Loc &loc, Dsymbol *s1, Dsymbol *s2); const char *kind() const override; @@ -427,6 +426,8 @@ public: }; void addMember(Dsymbol *dsym, Scope *sc, ScopeDsymbol *sds); -Dsymbol *search(Dsymbol *d, const Loc &loc, Identifier *ident, int flags = SearchLocalsOnly); +Dsymbol *search(Dsymbol *d, const Loc &loc, Identifier *ident, SearchOptFlags flags = (SearchOptFlags)SearchOpt::localsOnly); bool checkDeprecated(Dsymbol *d, const Loc &loc, Scope *sc); void setScope(Dsymbol *d, Scope *sc); +void importAll(Dsymbol *d, Scope *sc); +void setFieldOffset(Dsymbol *d, AggregateDeclaration *ad, FieldState& fieldState, bool isunion); diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d index 060abfe1896..df0a9a536f4 100644 --- a/gcc/d/dmd/dsymbolsem.d +++ b/gcc/d/dmd/dsymbolsem.d @@ -246,6 +246,20 @@ bool checkDeprecated(Dsymbol d, const ref Loc loc, Scope* sc) return true; } +/********************************* + * Check type to see if it is based on a deprecated symbol. + */ +private void checkDeprecated(Type type, const ref Loc loc, Scope* sc) +{ + if (Dsymbol s = type.toDsymbol(sc)) + { + s.checkDeprecated(loc, sc); + } + + if (auto tn = type.nextOf()) + tn.checkDeprecated(loc, sc); +} + // Returns true if a contract can appear without a function body. package bool allowsContractWithoutBody(FuncDeclaration funcdecl) { @@ -304,6 +318,128 @@ bool checkHasBothRvalueAndCpCtor(StructDeclaration sd, CtorDeclaration ctor, Tem return false; } +/************************************* + * Find the `alias this` symbol of e's type. + * Params: + * sc = context + * e = expression forming the `this` + * gag = do not print errors, return `null` instead + * findOnly = don't do further processing like resolving properties, + * i.e. just return plain dotExp() result. + * Returns: + * Expression that is `e.aliasthis` + */ +Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool findOnly = false) +{ + import dmd.typesem : dotExp; + for (AggregateDeclaration ad = isAggregate(e.type); ad;) + { + if (ad.aliasthis) + { + Loc loc = e.loc; + Type tthis = (e.op == EXP.type ? e.type : null); + const flags = cast(DotExpFlag) (DotExpFlag.noAliasThis | (gag * DotExpFlag.gag)); + uint olderrors = gag ? global.startGagging() : 0; + e = dotExp(ad.type, sc, e, ad.aliasthis.ident, flags); + if (!e || findOnly) + return gag && global.endGagging(olderrors) ? null : e; + + if (tthis && ad.aliasthis.sym.needThis()) + { + if (auto ve = e.isVarExp()) + { + if (auto fd = ve.var.isFuncDeclaration()) + { + // https://issues.dlang.org/show_bug.cgi?id=13009 + // Support better match for the overloaded alias this. + bool hasOverloads; + if (auto f = fd.overloadModMatch(loc, tthis, hasOverloads)) + { + if (!hasOverloads) + fd = f; // use exact match + e = new VarExp(loc, fd, hasOverloads); + e.type = f.type; + e = new CallExp(loc, e); + goto L1; + } + } + } + /* non-@property function is not called inside typeof(), + * so resolve it ahead. + */ + { + int save = sc.intypeof; + sc.intypeof = 1; // bypass "need this" error check + e = resolveProperties(sc, e); + sc.intypeof = save; + } + L1: + e = new TypeExp(loc, new TypeTypeof(loc, e)); + e = e.expressionSemantic(sc); + } + e = resolveProperties(sc, e); + if (!gag) + ad.aliasthis.checkDeprecatedAliasThis(loc, sc); + else if (global.endGagging(olderrors)) + e = null; + } + + import dmd.dclass : ClassDeclaration; + auto cd = ad.isClassDeclaration(); + if ((!e || !ad.aliasthis) && cd && cd.baseClass && cd.baseClass != ClassDeclaration.object) + { + ad = cd.baseClass; + continue; + } + break; + } + return e; +} + +/** + * Check if an `alias this` is deprecated + * + * Usually one would use `expression.checkDeprecated(scope, aliasthis)` to + * check if `expression` uses a deprecated `aliasthis`, but this calls + * `toPrettyChars` which lead to the following message: + * "Deprecation: alias this `fullyqualified.aggregate.__anonymous` is deprecated" + * + * Params: + * at = The `AliasThis` object to check + * loc = `Loc` of the expression triggering the access to `at` + * sc = `Scope` of the expression + * (deprecations do not trigger in deprecated scopes) + * + * Returns: + * Whether the alias this was reported as deprecated. + */ +private bool checkDeprecatedAliasThis(AliasThis at, const ref Loc loc, Scope* sc) +{ + if (global.params.useDeprecated != DiagnosticReporting.off + && at.isDeprecated() && !sc.isDeprecated()) + { + const(char)* message = null; + for (Dsymbol p = at; p; p = p.parent) + { + message = p.depdecl ? p.depdecl.getMessage() : null; + if (message) + break; + } + if (message) + deprecation(loc, "`alias %s this` is deprecated - %s", + at.sym.toChars(), message); + else + deprecation(loc, "`alias %s this` is deprecated", + at.sym.toChars()); + + if (auto ti = sc.parent ? sc.parent.isInstantiated() : null) + ti.printInstantiationTrace(Classification.deprecation); + + return true; + } + return false; +} + private extern(C++) final class DsymbolSemanticVisitor : Visitor { alias visit = Visitor.visit; @@ -359,7 +495,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor Dsymbol s = ad.search(dsym.loc, dsym.ident); if (!s) { - s = sc.search(dsym.loc, dsym.ident, null); + Dsymbol pscopesym; + s = sc.search(dsym.loc, dsym.ident, pscopesym); if (s) error(dsym.loc, "`%s` is not a member of `%s`", s.toChars(), ad.toChars()); else @@ -449,7 +586,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor printf(" type = %s\n", dsym.type ? dsym.type.toChars() : "null"); printf(" stc = x%llx\n", dsym.storage_class); printf(" storage_class = x%llx\n", dsym.storage_class); - printf("linkage = %d\n", dsym.linkage); + printf("linkage = %d\n", dsym._linkage); //if (strcmp(toChars(), "mul") == 0) assert(0); } //if (semanticRun > PASS.initial) @@ -1494,7 +1631,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor { AliasDeclaration ad = imp.aliasdecls[i]; //printf("\tImport %s alias %s = %s, scope = %p\n", toPrettyChars(), aliases[i].toChars(), names[i].toChars(), ad._scope); - Dsymbol sym = imp.mod.search(imp.loc, imp.names[i], IgnorePrivateImports); + Dsymbol sym = imp.mod.search(imp.loc, imp.names[i], SearchOpt.ignorePrivateImports); if (sym) { import dmd.access : symbolIsVisible; @@ -4121,7 +4258,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor // check if `_d_cmain` is defined bool cmainTemplateExists() { - auto rootSymbol = sc.search(funcdecl.loc, Id.empty, null); + Dsymbol pscopesym; + auto rootSymbol = sc.search(funcdecl.loc, Id.empty, pscopesym); if (auto moduleSymbol = rootSymbol.search(funcdecl.loc, Id.object)) if (moduleSymbol.search(funcdecl.loc, Id.CMain)) return true; @@ -7206,7 +7344,7 @@ private void aliasAssignSemantic(AliasAssign ds, Scope* sc) AliasDeclaration findAliasDeclaration(AliasAssign ds, Scope* sc) { Dsymbol scopesym; - Dsymbol as = sc.search(ds.loc, ds.ident, &scopesym); + Dsymbol as = sc.search(ds.loc, ds.ident, scopesym); if (!as) { .error(ds.loc, "%s `%s` undefined identifier `%s`", ds.kind, ds.toPrettyChars, ds.ident.toChars()); @@ -7833,11 +7971,11 @@ void checkPrintfScanfSignature(FuncDeclaration funcdecl, TypeFunction f, Scope* * d = dsymbol where ident is searched for * loc = location to print for error messages * ident = identifier to search for - * flags = IgnoreXXXX + * flags = search options * Returns: * null if not found */ -extern(C++) Dsymbol search(Dsymbol d, const ref Loc loc, Identifier ident, int flags = IgnoreNone) +extern(C++) Dsymbol search(Dsymbol d, const ref Loc loc, Identifier ident, SearchOptFlags flags = SearchOpt.all) { scope v = new SearchVisitor(loc, ident, flags); d.accept(v); @@ -7862,13 +8000,13 @@ Dsymbol search_correct(Dsymbol d, Identifier ident) cost = 0; // all the same cost Dsymbol s = d; Module.clearCache(); - return s.search(Loc.initial, id, IgnoreErrors); + return s.search(Loc.initial, id, SearchOpt.ignoreErrors); } if (global.gag) return null; // don't do it for speculative compiles; too time consuming // search for exact name first - if (auto s = d.search(Loc.initial, ident, IgnoreErrors)) + if (auto s = d.search(Loc.initial, ident, SearchOpt.ignoreErrors)) return s; import dmd.root.speller : speller; @@ -7881,10 +8019,10 @@ private extern(C++) class SearchVisitor : Visitor const Loc loc; Identifier ident; - int flags; + SearchOptFlags flags; Dsymbol result; - this(const ref Loc loc, Identifier ident, int flags) + this(const ref Loc loc, Identifier ident, SearchOptFlags flags) { this.loc = loc; this.ident = ident; @@ -7908,7 +8046,7 @@ private extern(C++) class SearchVisitor : Visitor //if (strcmp(ident.toChars(),"c") == 0) *(char*)0=0; // Look in symbols declared in this module - if (sds.symtab && !(flags & SearchImportsOnly)) + if (sds.symtab && !(flags & SearchOpt.importsOnly)) { //printf(" look in locals\n"); auto s1 = sds.symtab.lookup(ident); @@ -7931,30 +8069,30 @@ private extern(C++) class SearchVisitor : Visitor for (size_t i = 0; i < sds.importedScopes.length; i++) { // If private import, don't search it - if ((flags & IgnorePrivateImports) && sds.visibilities[i] == Visibility.Kind.private_) + if ((flags & SearchOpt.ignorePrivateImports) && sds.visibilities[i] == Visibility.Kind.private_) continue; - int sflags = flags & (IgnoreErrors | IgnoreAmbiguous); // remember these in recursive searches + SearchOptFlags sflags = flags & (SearchOpt.ignoreErrors | SearchOpt.ignoreAmbiguous); // remember these in recursive searches Dsymbol ss = (*sds.importedScopes)[i]; //printf("\tscanning import '%s', visibilities = %d, isModule = %p, isImport = %p\n", ss.toChars(), visibilities[i], ss.isModule(), ss.isImport()); if (ss.isModule()) { - if (flags & SearchLocalsOnly) + if (flags & SearchOpt.localsOnly) continue; } else // mixin template { - if (flags & SearchImportsOnly) + if (flags & SearchOpt.importsOnly) continue; - sflags |= SearchLocalsOnly; + sflags |= SearchOpt.localsOnly; } /* Don't find private members if ss is a module */ - Dsymbol s2 = ss.search(loc, ident, sflags | (ss.isModule() ? IgnorePrivateImports : IgnoreNone)); + Dsymbol s2 = ss.search(loc, ident, sflags | (ss.isModule() ? SearchOpt.ignorePrivateImports : SearchOpt.all)); import dmd.access : symbolIsVisible; - if (!s2 || !(flags & IgnoreSymbolVisibility) && !symbolIsVisible(sds, s2)) + if (!s2 || !(flags & SearchOpt.ignoreVisibility) && !symbolIsVisible(sds, s2)) continue; if (!s) { @@ -8025,7 +8163,7 @@ private extern(C++) class SearchVisitor : Visitor } } - if (flags & IgnoreAmbiguous) // if return NULL on ambiguity + if (flags & SearchOpt.ignoreAmbiguous) // if return NULL on ambiguity return setResult(null); /* If two imports from C import files, pick first one, as C has global name space @@ -8033,7 +8171,7 @@ private extern(C++) class SearchVisitor : Visitor if (s.isCsymbol() && s2.isCsymbol()) continue; - if (!(flags & IgnoreErrors)) + if (!(flags & SearchOpt.ignoreErrors)) ScopeDsymbol.multiplyDefined(loc, s, s2); break; } @@ -8060,7 +8198,7 @@ private extern(C++) class SearchVisitor : Visitor override void visit(WithScopeSymbol ws) { //printf("WithScopeSymbol.search(%s)\n", ident.toChars()); - if (flags & SearchImportsOnly) + if (flags & SearchOpt.importsOnly) return setResult(null); // Acts as proxy to the with class declaration Dsymbol s = null; @@ -8301,7 +8439,7 @@ private extern(C++) class SearchVisitor : Visitor if (!ns.members || !ns.symtab) // opaque or semantic() is not yet called { - if (!(flags & IgnoreErrors)) + if (!(flags & SearchOpt.ignoreErrors)) .error(loc, "%s `%s` is forward referenced when looking for `%s`", ns.kind, ns.toPrettyChars, ident.toChars()); return setResult(null); } @@ -8324,7 +8462,7 @@ private extern(C++) class SearchVisitor : Visitor override void visit(Package pkg) { //printf("%s Package.search('%s', flags = x%x)\n", pkg.toChars(), ident.toChars(), flags); - flags &= ~SearchLocalsOnly; // searching an import is always transitive + flags &= ~cast(int)SearchOpt.localsOnly; // searching an import is always transitive if (!pkg.isModule() && pkg.mod) { // Prefer full package name. @@ -8351,8 +8489,8 @@ private extern(C++) class SearchVisitor : Visitor /* Qualified module searches always search their imports, * even if SearchLocalsOnly */ - if (!(flags & SearchUnqualifiedModule)) - flags &= ~(SearchUnqualifiedModule | SearchLocalsOnly); + if (!(flags & SearchOpt.unqualifiedModule)) + flags &= ~(SearchOpt.unqualifiedModule | SearchOpt.localsOnly); if (m.searchCacheIdent == ident && m.searchCacheFlags == flags) { @@ -8401,7 +8539,7 @@ private extern(C++) class SearchVisitor : Visitor if (!sd.members || !sd.symtab) // opaque or semantic() is not yet called { // .stringof is always defined (but may be hidden by some other symbol) - if(ident != Id.stringof && !(flags & IgnoreErrors) && sd.semanticRun < PASS.semanticdone) + if(ident != Id.stringof && !(flags & SearchOpt.ignoreErrors) && sd.semanticRun < PASS.semanticdone) .error(loc, "%s `%s` is forward referenced when looking for `%s`", sd.kind, sd.toPrettyChars, ident.toChars()); return setResult(null); } @@ -8427,7 +8565,7 @@ private extern(C++) class SearchVisitor : Visitor if (!cd.members || !cd.symtab) // opaque or addMember is not yet done { // .stringof is always defined (but may be hidden by some other symbol) - if (ident != Id.stringof && !(flags & IgnoreErrors) && cd.semanticRun < PASS.semanticdone) + if (ident != Id.stringof && !(flags & SearchOpt.ignoreErrors) && cd.semanticRun < PASS.semanticdone) cd.classError("%s `%s` is forward referenced when looking for `%s`", ident.toChars()); //*(char*)0=0; return setResult(null); @@ -8437,7 +8575,7 @@ private extern(C++) class SearchVisitor : Visitor auto s = result; // don't search imports of base classes - if (flags & SearchImportsOnly) + if (flags & SearchOpt.importsOnly) return setResult(s); if (s) @@ -8462,7 +8600,7 @@ private extern(C++) class SearchVisitor : Visitor continue; else if (s == cd) // happens if s is nested in this and derives from this s = null; - else if (!(flags & IgnoreSymbolVisibility) && !(s.visible().kind == Visibility.Kind.protected_) && !symbolIsVisible(cd, s)) + else if (!(flags & SearchOpt.ignoreVisibility) && !(s.visible().kind == Visibility.Kind.protected_) && !symbolIsVisible(cd, s)) s = null; else break; @@ -8621,3 +8759,554 @@ private extern(C++) class SetScopeVisitor : Visitor visit(cast(AttribDeclaration)uad); } } + +extern(C++) void importAll(Dsymbol d, Scope* sc) +{ + scope iav = new ImportAllVisitor(sc); + d.accept(iav); +} + +extern(C++) class ImportAllVisitor : Visitor +{ + alias visit = typeof(super).visit; + Scope* sc; + + this(Scope* sc) + { + this.sc = sc; + } + + override void visit(Dsymbol d) {} + + override void visit(Import imp) + { + if (imp.mod) return; // Already done + + /* + * https://issues.dlang.org/show_bug.cgi?id=15525 + * + * Loading the import has failed, + * most likely because of parsing errors. + * Therefore we cannot trust the resulting AST. + */ + if (imp.load(sc)) + { + // https://issues.dlang.org/show_bug.cgi?id=23873 + // For imports that are not at module or function level, + // e.g. aggregate level, the import symbol is added to the + // symbol table and later semantic is performed on it. + // This leads to semantic analysis on an malformed AST + // which causes all kinds of segfaults. + // The fix is to note that the module has errors and avoid + // semantic analysis on it. + if(imp.mod) + imp.mod.errors = true; + return; + } + + if (!imp.mod) return; // Failed + + if (sc.stc & STC.static_) + imp.isstatic = true; + imp.mod.importAll(null); + imp.mod.checkImportDeprecation(imp.loc, sc); + if (sc.explicitVisibility) + imp.visibility = sc.visibility; + if (!imp.isstatic && !imp.aliasId && !imp.names.length) + sc.scopesym.importScope(imp.mod, imp.visibility); + // Enable access to pkgs/mod as soon as posible, because compiler + // can traverse them before the import gets semantic (Issue: 21501) + if (!imp.aliasId && !imp.names.length) + imp.addPackageAccess(sc.scopesym); + } + + override void visit(Module m) + { + //printf("+Module::importAll(this = %p, '%s'): parent = %p\n", m, m.toChars(), m.parent); + if (m._scope) + return; // already done + if (m.filetype == FileType.ddoc) + { + error(m.loc, "%s `%s` is a Ddoc file, cannot import it", m.kind, m.toPrettyChars); + return; + } + + /* Note that modules get their own scope, from scratch. + * This is so regardless of where in the syntax a module + * gets imported, it is unaffected by context. + * Ignore prevsc. + */ + Scope* sc = Scope.createGlobal(m, global.errorSink); // create root scope + + if (m.md && m.md.msg) + m.md.msg = semanticString(sc, m.md.msg, "deprecation message"); + + // Add import of "object", even for the "object" module. + // If it isn't there, some compiler rewrites, like + // classinst == classinst -> .object.opEquals(classinst, classinst) + // would fail inside object.d. + if (m.filetype != FileType.c && + (m.members.length == 0 || + (*m.members)[0].ident != Id.object || + (*m.members)[0].isImport() is null)) + { + auto im = new Import(Loc.initial, null, Id.object, null, 0); + m.members.shift(im); + } + if (!m.symtab) + { + // Add all symbols into module's symbol table + m.symtab = new DsymbolTable(); + for (size_t i = 0; i < m.members.length; i++) + { + Dsymbol s = (*m.members)[i]; + s.addMember(sc, sc.scopesym); + } + } + // anything else should be run after addMember, so version/debug symbols are defined + /* Set scope for the symbols so that if we forward reference + * a symbol, it can possibly be resolved on the spot. + * If this works out well, it can be extended to all modules + * before any semantic() on any of them. + */ + m.setScope(sc); // remember module scope for semantic + for (size_t i = 0; i < m.members.length; i++) + { + Dsymbol s = (*m.members)[i]; + s.setScope(sc); + } + for (size_t i = 0; i < m.members.length; i++) + { + Dsymbol s = (*m.members)[i]; + s.importAll(sc); + } + sc = sc.pop(); + sc.pop(); // 2 pops because Scope.createGlobal() created 2 + } + + override void visit(AttribDeclaration atb) + { + Dsymbols* d = atb.include(sc); + //printf("\tAttribDeclaration::importAll '%s', d = %p\n", toChars(), d); + if (d) + { + Scope* sc2 = atb.newScope(sc); + d.foreachDsymbol( s => s.importAll(sc2) ); + if (sc2 != sc) + sc2.pop(); + } + } + + // do not evaluate condition before semantic pass + override void visit(StaticIfDeclaration _) {} + // do not evaluate aggregate before semantic pass + override void visit(StaticForeachDeclaration _) {} +} + +extern(C++) void setFieldOffset(Dsymbol d, AggregateDeclaration ad, FieldState* fieldState, bool isunion) +{ + scope v = new SetFieldOffsetVisitor(ad, fieldState, isunion); + d.accept(v); +} + +private extern(C++) class SetFieldOffsetVisitor : Visitor +{ + alias visit = Visitor.visit; + + AggregateDeclaration ad; + FieldState* fieldState; + bool isunion; + + this(AggregateDeclaration ad, FieldState* fieldState, bool isunion) + { + this.ad = ad; + this.fieldState = fieldState; + this.isunion = isunion; + } + + override void visit(Dsymbol d) {} + + override void visit(Nspace ns) + { + //printf("Nspace::setFieldOffset() %s\n", toChars()); + if (ns._scope) // if fwd reference + dsymbolSemantic(ns, null); // try to resolve it + ns.members.foreachDsymbol( s => s.setFieldOffset(ad, fieldState, isunion) ); + } + + override void visit(VarDeclaration vd) + { + //printf("VarDeclaration::setFieldOffset(ad = %s) %s\n", ad.toChars(), vd.toChars()); + + if (vd.aliasTuple) + { + // If this variable was really a tuple, set the offsets for the tuple fields + vd.aliasTuple.foreachVar((s) { s.setFieldOffset(ad, fieldState, isunion); }); + return; + } + + if (!vd.isField()) + return; + assert(!(vd.storage_class & (STC.static_ | STC.extern_ | STC.parameter))); + + //printf("+VarDeclaration::setFieldOffset(ad = %s) %s\n", ad.toChars(), toChars()); + + /* Fields that are tuples appear both as part of TupleDeclarations and + * as members. That means ignore them if they are already a field. + */ + if (vd.offset) + { + // already a field + fieldState.offset = ad.structsize; // https://issues.dlang.org/show_bug.cgi?id=13613 + return; + } + for (size_t i = 0; i < ad.fields.length; i++) + { + if (ad.fields[i] == vd) + { + // already a field + fieldState.offset = ad.structsize; // https://issues.dlang.org/show_bug.cgi?id=13613 + return; + } + } + + // Check for forward referenced types which will fail the size() call + Type t = vd.type.toBasetype(); + if (vd.storage_class & STC.ref_) + { + // References are the size of a pointer + t = Type.tvoidptr; + } + Type tv = t.baseElemOf(); + if (tv.ty == Tstruct) + { + auto ts = cast(TypeStruct)tv; + assert(ts.sym != ad); // already checked in ad.determineFields() + if (!ts.sym.determineSize(vd.loc)) + { + vd.type = Type.terror; + vd.errors = true; + return; + } + } + + // List in ad.fields. Even if the type is error, it's necessary to avoid + // pointless error diagnostic "more initializers than fields" on struct literal. + ad.fields.push(vd); + + if (t.ty == Terror) + return; + + /* If coming after a bit field in progress, + * advance past the field + */ + fieldState.inFlight = false; + + const sz = t.size(vd.loc); + assert(sz != SIZE_INVALID && sz < uint.max); + uint memsize = cast(uint)sz; // size of member + uint memalignsize = target.fieldalign(t); // size of member for alignment purposes + vd.offset = placeField( + fieldState.offset, + memsize, memalignsize, vd.alignment, + ad.structsize, ad.alignsize, + isunion); + + //printf("\t%s: memalignsize = %d\n", toChars(), memalignsize); + //printf(" addField '%s' to '%s' at offset %d, size = %d\n", toChars(), ad.toChars(), offset, memsize); + } + + override void visit(BitFieldDeclaration bfd) + { + enum log = false; + static if (log) + { + printf("BitFieldDeclaration::setFieldOffset(ad: %s, field: %s)\n", ad.toChars(), bfd.toChars()); + void print(const FieldState* fieldState) + { + fieldState.print(); + printf(" fieldWidth = %d bits\n", bfd.fieldWidth); + } + print(fieldState); + } + + Type t = bfd.type.toBasetype(); + const bool anon = bfd.isAnonymous(); + + // List in ad.fields. Even if the type is error, it's necessary to avoid + // pointless error diagnostic "more initializers than fields" on struct literal. + if (!anon) + ad.fields.push(bfd); + + if (t.ty == Terror) + return; + + const sz = t.size(bfd.loc); + assert(sz != SIZE_INVALID && sz < uint.max); + uint memsize = cast(uint)sz; // size of member + uint memalignsize = target.fieldalign(t); // size of member for alignment purposes + if (log) printf(" memsize: %u memalignsize: %u\n", memsize, memalignsize); + + if (bfd.fieldWidth == 0 && !anon) + error(bfd.loc, "named bit fields cannot have 0 width"); + if (bfd.fieldWidth > memsize * 8) + error(bfd.loc, "bit field width %d is larger than type", bfd.fieldWidth); + + const style = target.c.bitFieldStyle; + + void startNewField() + { + if (log) printf("startNewField()\n"); + uint alignsize; + if (style == TargetC.BitFieldStyle.Gcc_Clang) + { + if (bfd.fieldWidth > 32) + alignsize = memalignsize; + else if (bfd.fieldWidth > 16) + alignsize = 4; + else if (bfd.fieldWidth > 8) + alignsize = 2; + else + alignsize = 1; + } + else + alignsize = memsize; // not memalignsize + + uint dummy; + bfd.offset = placeField( + fieldState.offset, + memsize, alignsize, bfd.alignment, + ad.structsize, + (anon && style == TargetC.BitFieldStyle.Gcc_Clang) ? dummy : ad.alignsize, + isunion); + + fieldState.inFlight = true; + fieldState.fieldOffset = bfd.offset; + fieldState.bitOffset = 0; + fieldState.fieldSize = memsize; + } + + if (style == TargetC.BitFieldStyle.Gcc_Clang) + { + if (bfd.fieldWidth == 0) + { + if (!isunion) + { + // Use type of zero width field to align to next field + fieldState.offset = (fieldState.offset + memalignsize - 1) & ~(memalignsize - 1); + ad.structsize = fieldState.offset; + } + + fieldState.inFlight = false; + return; + } + + if (ad.alignsize == 0) + ad.alignsize = 1; + if (!anon && + ad.alignsize < memalignsize) + ad.alignsize = memalignsize; + } + else if (style == TargetC.BitFieldStyle.MS) + { + if (ad.alignsize == 0) + ad.alignsize = 1; + if (bfd.fieldWidth == 0) + { + if (fieldState.inFlight && !isunion) + { + // documentation says align to next int + //const alsz = cast(uint)Type.tint32.size(); + const alsz = memsize; // but it really does this + fieldState.offset = (fieldState.offset + alsz - 1) & ~(alsz - 1); + ad.structsize = fieldState.offset; + } + + fieldState.inFlight = false; + return; + } + } + else if (style == TargetC.BitFieldStyle.DM) + { + if (anon && bfd.fieldWidth && (!fieldState.inFlight || fieldState.bitOffset == 0)) + return; // this probably should be a bug in DMC + if (ad.alignsize == 0) + ad.alignsize = 1; + if (bfd.fieldWidth == 0) + { + if (fieldState.inFlight && !isunion) + { + const alsz = memsize; + fieldState.offset = (fieldState.offset + alsz - 1) & ~(alsz - 1); + ad.structsize = fieldState.offset; + } + + fieldState.inFlight = false; + return; + } + } + + if (!fieldState.inFlight) + { + //printf("not in flight\n"); + startNewField(); + } + else if (style == TargetC.BitFieldStyle.Gcc_Clang) + { + // If the bit-field spans more units of alignment than its type, + // start a new field at the next alignment boundary. + if (fieldState.bitOffset == fieldState.fieldSize * 8 && + fieldState.bitOffset + bfd.fieldWidth > memalignsize * 8) + { + if (log) printf("more units of alignment than its type\n"); + startNewField(); // the bit field is full + } + else + { + // if alignment boundary is crossed + uint start = fieldState.fieldOffset * 8 + fieldState.bitOffset; + uint end = start + bfd.fieldWidth; + //printf("%s start: %d end: %d memalignsize: %d\n", ad.toChars(), start, end, memalignsize); + if (start / (memalignsize * 8) != (end - 1) / (memalignsize * 8)) + { + if (log) printf("alignment is crossed\n"); + startNewField(); + } + } + } + else if (style == TargetC.BitFieldStyle.DM || + style == TargetC.BitFieldStyle.MS) + { + if (memsize != fieldState.fieldSize || + fieldState.bitOffset + bfd.fieldWidth > fieldState.fieldSize * 8) + { + //printf("new field\n"); + startNewField(); + } + } + else + assert(0); + + bfd.offset = fieldState.fieldOffset; + bfd.bitOffset = fieldState.bitOffset; + + const pastField = bfd.bitOffset + bfd.fieldWidth; + if (style == TargetC.BitFieldStyle.Gcc_Clang) + { + auto size = (pastField + 7) / 8; + fieldState.fieldSize = size; + //printf(" offset: %d, size: %d\n", offset, size); + if (isunion) + { + const newstructsize = bfd.offset + size; + if (newstructsize > ad.structsize) + ad.structsize = newstructsize; + } + else + ad.structsize = bfd.offset + size; + } + else + fieldState.fieldSize = memsize; + //printf("at end: ad.structsize = %d\n", cast(int)ad.structsize); + //print(fieldState); + + if (!isunion) + { + fieldState.offset = bfd.offset + fieldState.fieldSize; + fieldState.bitOffset = pastField; + } + + //printf("\t%s: offset = %d bitOffset = %d fieldWidth = %d memsize = %d\n", toChars(), offset, bitOffset, fieldWidth, memsize); + //printf("\t%s: memalignsize = %d\n", toChars(), memalignsize); + //printf(" addField '%s' to '%s' at offset %d, size = %d\n", toChars(), ad.toChars(), offset, memsize); + } + + override void visit(TemplateMixin tm) + { + //printf("TemplateMixin.setFieldOffset() %s\n", tm.toChars()); + if (tm._scope) // if fwd reference + dsymbolSemantic(tm, null); // try to resolve it + + tm.members.foreachDsymbol( (s) { s.setFieldOffset(ad, fieldState, isunion); } ); + } + + override void visit(AttribDeclaration atd) + { + atd.include(null).foreachDsymbol( s => s.setFieldOffset(ad, fieldState, isunion) ); + } + + override void visit(AnonDeclaration anond) + { + //printf("\tAnonDeclaration::setFieldOffset %s %p\n", isunion ? "union" : "struct", anond); + if (anond.decl) + { + /* This works by treating an AnonDeclaration as an aggregate 'member', + * so in order to place that member we need to compute the member's + * size and alignment. + */ + size_t fieldstart = ad.fields.length; + + /* Hackishly hijack ad's structsize and alignsize fields + * for use in our fake anon aggregate member. + */ + uint savestructsize = ad.structsize; + uint savealignsize = ad.alignsize; + ad.structsize = 0; + ad.alignsize = 0; + + FieldState fs; + anond.decl.foreachDsymbol( (s) + { + s.setFieldOffset(ad, &fs, anond.isunion); + if (anond.isunion) + fs.offset = 0; + }); + + /* https://issues.dlang.org/show_bug.cgi?id=13613 + * If the fields in this.members had been already + * added in ad.fields, just update *poffset for the subsequent + * field offset calculation. + */ + if (fieldstart == ad.fields.length) + { + ad.structsize = savestructsize; + ad.alignsize = savealignsize; + fieldState.offset = ad.structsize; + return; + } + + anond.anonstructsize = ad.structsize; + anond.anonalignsize = ad.alignsize; + ad.structsize = savestructsize; + ad.alignsize = savealignsize; + + // 0 sized structs are set to 1 byte + if (anond.anonstructsize == 0) + { + anond.anonstructsize = 1; + anond.anonalignsize = 1; + } + + assert(anond._scope); + auto alignment = anond._scope.alignment(); + + /* Given the anon 'member's size and alignment, + * go ahead and place it. + */ + anond.anonoffset = placeField( + fieldState.offset, + anond.anonstructsize, anond.anonalignsize, alignment, + ad.structsize, ad.alignsize, + isunion); + + // Add to the anon fields the base offset of this anonymous aggregate + //printf("anon fields, anonoffset = %d\n", anonoffset); + foreach (const i; fieldstart .. ad.fields.length) + { + VarDeclaration v = ad.fields[i]; + //printf("\t[%d] %s %d\n", i, v.toChars(), v.offset); + v.offset += anond.anonoffset; + } + } + } +} diff --git a/gcc/d/dmd/dtemplate.d b/gcc/d/dmd/dtemplate.d index 326d66364b8..e440b9e2eb6 100644 --- a/gcc/d/dmd/dtemplate.d +++ b/gcc/d/dmd/dtemplate.d @@ -6465,7 +6465,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol */ Identifier id = name; Dsymbol scopesym; - Dsymbol s = sc.search(loc, id, &scopesym); + Dsymbol s = sc.search(loc, id, scopesym); if (!s) { s = sc.search_correct(id); @@ -7831,15 +7831,6 @@ extern (C++) final class TemplateMixin : TemplateInstance return members.foreachDsymbol( (s) { return s.hasPointers(); } ) != 0; } - override void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion) - { - //printf("TemplateMixin.setFieldOffset() %s\n", toChars()); - if (_scope) // if fwd reference - dsymbolSemantic(this, null); // try to resolve it - - members.foreachDsymbol( (s) { s.setFieldOffset(ad, fieldState, isunion); } ); - } - override const(char)* toChars() const { OutBuffer buf; diff --git a/gcc/d/dmd/dtoh.d b/gcc/d/dmd/dtoh.d index 9f855743ef9..ed83a8d9fe1 100644 --- a/gcc/d/dmd/dtoh.d +++ b/gcc/d/dmd/dtoh.d @@ -3284,7 +3284,7 @@ ASTCodegen.Dsymbol symbolFromType(ASTCodegen.Type t) @safe */ ASTCodegen.Dsymbol findMember(ASTCodegen.Dsymbol sym, Identifier name) { - if (auto mem = sym.search(Loc.initial, name, ASTCodegen.IgnoreErrors)) + if (auto mem = sym.search(Loc.initial, name, ASTCodegen.SearchOpt.ignoreErrors)) return mem; // search doesn't work for declarations inside of uninstantiated diff --git a/gcc/d/dmd/errorsink.d b/gcc/d/dmd/errorsink.d index ce2351738d6..3811f1d2932 100644 --- a/gcc/d/dmd/errorsink.d +++ b/gcc/d/dmd/errorsink.d @@ -60,6 +60,20 @@ class ErrorSinkNull : ErrorSink void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) { } } +/***************************************** + * Ignores the messages, but sets `sawErrors` for any calls to `error()` + */ +class ErrorSinkLatch : ErrorSinkNull +{ + nothrow: + extern (C++): + override: + + bool sawErrors; + + void error(const ref Loc loc, const(char)* format, ...) { sawErrors = true; } +} + /***************************************** * Simplest implementation, just sends messages to stderr. * See also: ErrorSinkCompiler. diff --git a/gcc/d/dmd/expression.d b/gcc/d/dmd/expression.d index cd93e54932c..f51a8b00891 100644 --- a/gcc/d/dmd/expression.d +++ b/gcc/d/dmd/expression.d @@ -21,7 +21,6 @@ import dmd.aggregate; import dmd.arraytypes; import dmd.astenums; import dmd.ast_node; -import dmd.gluelayer; import dmd.dclass; import dmd.declaration; import dmd.dimport; @@ -2240,7 +2239,7 @@ extern (C++) final class StructLiteralExp : Expression // while `sym` is only used in `e2ir/s2ir/tocsym` which comes after union { - Symbol* sym; /// back end symbol to initialize with literal + void* sym; /// back end symbol to initialize with literal (used as a Symbol*) /// those fields need to prevent a infinite recursion when one field of struct initialized with 'this' pointer. StructLiteralExp inlinecopy; diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d index 1664bf22dca..c21b382c71e 100644 --- a/gcc/d/dmd/expressionsem.d +++ b/gcc/d/dmd/expressionsem.d @@ -875,7 +875,7 @@ private Expression searchUFCS(Scope* sc, UnaExp ue, Identifier ident) Loc loc = ue.loc; // TODO: merge with Scope.search.searchScopes() - Dsymbol searchScopes(int flags) + Dsymbol searchScopes(SearchOptFlags flags) { Dsymbol s = null; for (Scope* scx = sc; scx; scx = scx.enclosing) @@ -883,7 +883,7 @@ private Expression searchUFCS(Scope* sc, UnaExp ue, Identifier ident) if (!scx.scopesym) continue; if (scx.scopesym.isModule()) - flags |= SearchUnqualifiedModule; // tell Module.search() that SearchLocalsOnly is to be obeyed + flags |= SearchOpt.unqualifiedModule; // tell Module.search() that SearchOpt.localsOnly is to be obeyed s = scx.scopesym.search(loc, ident, flags); if (s) { @@ -910,18 +910,18 @@ private Expression searchUFCS(Scope* sc, UnaExp ue, Identifier ident) return s; } - int flags = 0; + SearchOptFlags flags = SearchOpt.all; Dsymbol s; if (sc.flags & SCOPE.ignoresymbolvisibility) - flags |= IgnoreSymbolVisibility; + flags |= SearchOpt.ignoreVisibility; // First look in local scopes - s = searchScopes(flags | SearchLocalsOnly); + s = searchScopes(flags | SearchOpt.localsOnly); if (!s) { // Second look in imported modules - s = searchScopes(flags | SearchImportsOnly); + s = searchScopes(flags | SearchOpt.importsOnly); } if (!s) @@ -3743,7 +3743,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } Dsymbol scopesym; - Dsymbol s = sc.search(exp.loc, exp.ident, &scopesym); + Dsymbol s = sc.search(exp.loc, exp.ident, scopesym); if (s) { if (s.errors) @@ -6744,7 +6744,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!sc.insert(s)) { - auto conflict = sc.search(Loc.initial, s.ident, null); + Dsymbol pscopesym; + auto conflict = sc.search(Loc.initial, s.ident, pscopesym); error(e.loc, "declaration `%s` is already defined", s.toPrettyChars()); errorSupplemental(conflict.loc, "`%s` `%s` is defined here", conflict.kind(), conflict.toChars()); @@ -6986,7 +6987,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ if (!tup && !sc.insert(s)) { - auto conflict = sc.search(Loc.initial, s.ident, null); + Dsymbol pscopesym; + auto conflict = sc.search(Loc.initial, s.ident, pscopesym); error(e.loc, "declaration `%s` is already defined", s.toPrettyChars()); errorSupplemental(conflict.loc, "`%s` `%s` is defined here", conflict.kind(), conflict.toChars()); @@ -7293,7 +7295,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor s.dsymbolSemantic(sc); if (!sc.insert(s)) { - auto conflict = sc.search(Loc.initial, s.ident, null); + Dsymbol pscopesym; + auto conflict = sc.search(Loc.initial, s.ident, pscopesym); error(e.loc, "declaration `%s` is already defined", s.toPrettyChars()); errorSupplemental(conflict.loc, "`%s` `%s` is defined here", conflict.kind(), conflict.toChars()); @@ -14208,15 +14211,15 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) if (auto ie = eright.isScopeExp()) // also used for template alias's { - auto flags = SearchLocalsOnly; + SearchOptFlags flags = SearchOpt.localsOnly; /* Disable access to another module's private imports. * The check for 'is sds our current module' is because * the current module should have access to its own imports. */ if (ie.sds.isModule() && ie.sds != sc._module) - flags |= IgnorePrivateImports; + flags |= SearchOpt.ignorePrivateImports; if (sc.flags & SCOPE.ignoresymbolvisibility) - flags |= IgnoreSymbolVisibility; + flags |= SearchOpt.ignoreVisibility; Dsymbol s = ie.sds.search(exp.loc, exp.ident, flags); /* Check for visibility before resolving aliases because public * aliases to private symbols are public. @@ -16038,7 +16041,8 @@ VarDeclaration makeThis2Argument(const ref Loc loc, Scope* sc, FuncDeclaration f */ bool verifyHookExist(const ref Loc loc, ref Scope sc, Identifier id, string description, Identifier module_ = Id.object) { - auto rootSymbol = sc.search(loc, Id.empty, null); + Dsymbol pscopesym; + auto rootSymbol = sc.search(loc, Id.empty, pscopesym); if (auto moduleSymbol = rootSymbol.search(loc, module_)) if (moduleSymbol.search(loc, id)) return true; diff --git a/gcc/d/dmd/func.d b/gcc/d/dmd/func.d index 351faa471f2..feaa5bb4d91 100644 --- a/gcc/d/dmd/func.d +++ b/gcc/d/dmd/func.d @@ -695,6 +695,7 @@ extern (C++) class FuncDeclaration : Declaration int result = 0; if (fd.ident == ident) { + import dmd.typesem : covariant; const cov = type.covariant(fd.type); if (cov != Covariant.distinct) { @@ -721,6 +722,8 @@ extern (C++) class FuncDeclaration : Declaration final int findVtblIndex(Dsymbols* vtbl, int dim) { //printf("findVtblIndex() %s\n", toChars()); + import dmd.typesem : covariant; + FuncDeclaration mismatch = null; StorageClass mismatchstc = 0; int mismatchvi = -1; @@ -947,6 +950,7 @@ extern (C++) class FuncDeclaration : Declaration */ if (t.ty == Tfunction) { + import dmd.typesem : covariant; auto tf = cast(TypeFunction)f.type; if (tf.covariant(t) == Covariant.yes && tf.nextOf().implicitConvTo(t.nextOf()) >= MATCH.constant) @@ -1157,6 +1161,7 @@ extern (C++) class FuncDeclaration : Declaration args.push(e); } + import dmd.typesem : callMatch; MATCH m = tg.callMatch(null, ArgumentList(&args, names), 1); if (m > MATCH.nomatch) { diff --git a/gcc/d/dmd/globals.d b/gcc/d/dmd/globals.d index 8d88207a566..9257bc464f4 100644 --- a/gcc/d/dmd/globals.d +++ b/gcc/d/dmd/globals.d @@ -167,7 +167,7 @@ extern (C++) struct Param bool cov; // generate code coverage data ubyte covPercent; // 0..100 code coverage percentage required bool ctfe_cov = false; // generate coverage data for ctfe - bool ignoreUnsupportedPragmas; // rather than error on them + bool ignoreUnsupportedPragmas = true; // rather than error on them bool useModuleInfo = true; // generate runtime module information bool useTypeInfo = true; // generate runtime type information bool useExceptions = true; // support exception handling diff --git a/gcc/d/dmd/import.h b/gcc/d/dmd/import.h index 624cd7406a3..2a02f13ba55 100644 --- a/gcc/d/dmd/import.h +++ b/gcc/d/dmd/import.h @@ -41,7 +41,6 @@ public: const char *kind() const override; Visibility visible() override; Import *syntaxCopy(Dsymbol *s) override; // copy only syntax trees - void importAll(Scope *sc) override; Dsymbol *toAlias() override; bool overloadInsert(Dsymbol *s) override; diff --git a/gcc/d/dmd/init.d b/gcc/d/dmd/init.d index e48410082bd..5aefb0048a6 100644 --- a/gcc/d/dmd/init.d +++ b/gcc/d/dmd/init.d @@ -12,21 +12,15 @@ module dmd.init; import core.stdc.stdio; -import core.checkedint; import dmd.arraytypes; import dmd.astenums; import dmd.ast_node; -import dmd.dsymbol; import dmd.expression; -import dmd.globals; -import dmd.hdrgen; import dmd.identifier; import dmd.location; import dmd.mtype; -import dmd.common.outbuffer; import dmd.rootobject; -import dmd.tokens; import dmd.visitor; enum NeedInterpret : int diff --git a/gcc/d/dmd/initsem.d b/gcc/d/dmd/initsem.d index 6d31f956c8e..19d576d4660 100644 --- a/gcc/d/dmd/initsem.d +++ b/gcc/d/dmd/initsem.d @@ -606,7 +606,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ { import dmd.common.outbuffer; OutBuffer buf; - HdrGenStage hgs; + HdrGenState hgs; toCBuffer(ts.sym, buf, hgs); printf("%s\n", buf.peekChars()); } @@ -803,9 +803,6 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ Loop1: for (size_t index = 0; index < ci.initializerList.length; ) { - CInitializer cprev; - size_t indexprev; - L1: DesigInit di = ci.initializerList[index]; Designators* dlist = di.designatorList; if (dlist) @@ -833,15 +830,6 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ continue Loop1; } } - if (cprev) - { - /* The peeling didn't work, so unpeel it - */ - ci = cprev; - index = indexprev; - di = ci.initializerList[index]; - goto L2; - } error(ci.loc, "`.%s` is not a field of `%s`\n", id.toChars(), sd.toChars()); return err(); } @@ -849,18 +837,55 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ { if (fieldi == nfields) break; - if (/*index == 0 && ci.initializerList.length == 1 &&*/ di.initializer.isCInitializer()) + + auto ix = di.initializer; + + /* If a C initializer is wrapped in a C initializer, with no designators, + * peel off the outer one + */ + if (ix.isCInitializer()) + { + CInitializer cix = ix.isCInitializer(); + if (cix.initializerList.length == 1) + { + DesigInit dix = cix.initializerList[0]; + if (!dix.designatorList) + { + Initializer inix = dix.initializer; + if (inix.isCInitializer()) + ix = inix; + } + } + } + + if (auto cix = ix.isCInitializer()) { - /* Try peeling off this set of { } and see if it works + /* ImportC loses the structure from anonymous structs, but this is retained + * by the initializer syntax. if a CInitializer has a Designator, it is probably + * a nested anonymous struct */ - cprev = ci; - ci = di.initializer.isCInitializer(); - indexprev = index; - index = 0; - goto L1; + if (cix.initializerList.length) + { + DesigInit dix = cix.initializerList[0]; + Designators* dlistx = dix.designatorList; + if (dlistx && (*dlistx).length == 1 && (*dlistx)[0].ident) + { + auto id = (*dlistx)[0].ident; + foreach (k, f; sd.fields[]) // linear search for now + { + if (f.ident == id) + { + fieldi = k; + si.addInit(id, dix.initializer); + ++fieldi; + ++index; + continue Loop1; + } + } + } + } } - L2: VarDeclaration field; while (1) // skip field if it overlaps with previously seen fields { @@ -871,10 +896,11 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ if (fieldi == nfields) break; } + auto tn = field.type.toBasetype(); auto tnsa = tn.isTypeSArray(); auto tns = tn.isTypeStruct(); - auto ix = di.initializer; + if (tnsa && ix.isExpInitializer()) { ExpInitializer ei = ix.isExpInitializer(); @@ -1013,7 +1039,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ } else { - error(ci.loc, "unrecognized C initializer `%s`", toChars(ci)); + error(ci.loc, "unrecognized C initializer `%s` for type `%s`", toChars(ci), t.toChars()); return err(); } } @@ -1548,6 +1574,11 @@ Expressions* resolveStructLiteralNamedArgs(StructDeclaration sd, Type t, Scope* cast(int) nfields, nfields != 1 ? "s".ptr : "".ptr); return null; } + if (fieldi >= nfields) + { + error(argLoc, "trying to initialize past the last field `%s` of `%s`", sd.fields[nfields - 1].toChars(), sd.toChars()); + return null; + } VarDeclaration vd = sd.fields[fieldi]; if (elems[fieldi]) diff --git a/gcc/d/dmd/lambdacomp.d b/gcc/d/dmd/lambdacomp.d index d19d435f240..c90c36069fd 100644 --- a/gcc/d/dmd/lambdacomp.d +++ b/gcc/d/dmd/lambdacomp.d @@ -244,7 +244,7 @@ public: { // we must check what the identifier expression is. Dsymbol scopesym; - Dsymbol s = sc.search(exp.loc, exp.ident, &scopesym); + Dsymbol s = sc.search(exp.loc, exp.ident, scopesym); if (s) { auto v = s.isVarDeclaration(); diff --git a/gcc/d/dmd/lexer.d b/gcc/d/dmd/lexer.d index b8faec76d60..2c6a5950569 100644 --- a/gcc/d/dmd/lexer.d +++ b/gcc/d/dmd/lexer.d @@ -2008,23 +2008,17 @@ class Lexer case 'u': dchar d1; size_t idx; - auto msg = utf_decodeChar(str, idx, d1); - dchar d2 = 0; - if (idx < n && !msg) - msg = utf_decodeChar(str, idx, d2); - if (msg) - error(loc, "%.*s", cast(int)msg.length, msg.ptr); - else if (idx < n) - error(loc, "max number of chars in 16 bit character literal is 2, had %d", - cast(int)((n + 1) >> 1)); - else if (d1 > 0x1_0000) - error(loc, "%d does not fit in 16 bits", d1); - else if (d2 > 0x1_0000) - error(loc, "%d does not fit in 16 bits", d2); - u = d1; - if (d2) - u = (d1 << 16) | d2; - break; + while (idx < n) + { + string msg = utf_decodeChar(str, idx, d1); + if (msg) + error(loc, "%.*s", cast(int)msg.length, msg.ptr); + } + if (d1 >= 0x1_0000) + error(loc, "x%x does not fit in 16 bits", d1); + t.unsvalue = d1; + t.value = TOK.wcharLiteral; // C11 6.4.4.4-9 + return; case 'U': dchar d; @@ -2035,8 +2029,9 @@ class Lexer else if (idx < n) error(loc, "max number of chars in 32 bit character literal is 1, had %d", cast(int)((n + 3) >> 2)); - u = d; - break; + t.unsvalue = d; + t.value = TOK.dcharLiteral; // C11 6.4.4.4-9 + return; default: assert(0); @@ -3270,7 +3265,7 @@ class Lexer while (1) { printf("%s ", (*tk).toChars()); - if (tk.value == TOK.endOfFile) + if (tk.value == TOK.endOfFile || tk.value == TOK.endOfLine) break; tk = peek(tk); } diff --git a/gcc/d/dmd/module.h b/gcc/d/dmd/module.h index cab0b0a4c1b..80a6ea25965 100644 --- a/gcc/d/dmd/module.h +++ b/gcc/d/dmd/module.h @@ -88,7 +88,7 @@ public: Identifier *searchCacheIdent; Dsymbol *searchCacheSymbol; // cached value of search - int searchCacheFlags; // cached flags + SearchOptFlags searchCacheFlags; // cached flags d_bool insearch; // module from command line we're imported from, @@ -121,9 +121,8 @@ public: const char *kind() const override; bool read(const Loc &loc); // read file, returns 'true' if succeed, 'false' otherwise. Module *parse(); // syntactic parse - void importAll(Scope *sc) override; int needModuleInfo(); - bool isPackageAccessible(Package *p, Visibility visibility, int flags = 0) override; + bool isPackageAccessible(Package *p, Visibility visibility, SearchOptFlags flags = (SearchOptFlags)SearchOpt::all) override; Dsymbol *symtabInsert(Dsymbol *s) override; static void runDeferredSemantic(); static void runDeferredSemantic2(); diff --git a/gcc/d/dmd/mtype.d b/gcc/d/dmd/mtype.d index 8860f143715..626bf7431f0 100644 --- a/gcc/d/dmd/mtype.d +++ b/gcc/d/dmd/mtype.d @@ -19,7 +19,6 @@ import core.stdc.string; import dmd.aggregate; import dmd.arraytypes; -import dmd.attrib; import dmd.astenums; import dmd.ast_node; import dmd.gluelayer; @@ -35,7 +34,6 @@ import dmd.dsymbolsem; import dmd.dtemplate; import dmd.errors; import dmd.expression; -import dmd.expressionsem; import dmd.func; import dmd.globals; import dmd.hdrgen; @@ -522,262 +520,6 @@ extern (C++) abstract class Type : ASTNode return mcache; } - /******************************* - * Covariant means that 'this' can substitute for 't', - * i.e. a pure function is a match for an impure type. - * Params: - * t = type 'this' is covariant with - * pstc = if not null, store STCxxxx which would make it covariant - * cppCovariant = true if extern(C++) function types should follow C++ covariant rules - * Returns: - * An enum value of either `Covariant.yes` or a reason it's not covariant. - */ - final Covariant covariant(Type t, StorageClass* pstc = null, bool cppCovariant = false) - { - version (none) - { - printf("Type::covariant(t = %s) %s\n", t.toChars(), toChars()); - printf("deco = %p, %p\n", deco, t.deco); - // printf("ty = %d\n", next.ty); - printf("mod = %x, %x\n", mod, t.mod); - } - if (pstc) - *pstc = 0; - StorageClass stc = 0; - - bool notcovariant = false; - - if (equals(t)) - return Covariant.yes; - - TypeFunction t1 = this.isTypeFunction(); - TypeFunction t2 = t.isTypeFunction(); - - if (!t1 || !t2) - goto Ldistinct; - - if (t1.parameterList.varargs != t2.parameterList.varargs) - goto Ldistinct; - - if (t1.parameterList.parameters && t2.parameterList.parameters) - { - if (t1.parameterList.length != t2.parameterList.length) - goto Ldistinct; - - foreach (i, fparam1; t1.parameterList) - { - Parameter fparam2 = t2.parameterList[i]; - Type tp1 = fparam1.type; - Type tp2 = fparam2.type; - - if (!tp1.equals(tp2)) - { - if (tp1.ty == tp2.ty) - { - if (auto tc1 = tp1.isTypeClass()) - { - if (tc1.sym == (cast(TypeClass)tp2).sym && MODimplicitConv(tp2.mod, tp1.mod)) - goto Lcov; - } - else if (auto ts1 = tp1.isTypeStruct()) - { - if (ts1.sym == (cast(TypeStruct)tp2).sym && MODimplicitConv(tp2.mod, tp1.mod)) - goto Lcov; - } - else if (tp1.ty == Tpointer) - { - if (tp2.implicitConvTo(tp1)) - goto Lcov; - } - else if (tp1.ty == Tarray) - { - if (tp2.implicitConvTo(tp1)) - goto Lcov; - } - else if (tp1.ty == Tdelegate) - { - if (tp2.implicitConvTo(tp1)) - goto Lcov; - } - } - goto Ldistinct; - } - Lcov: - notcovariant |= !fparam1.isCovariant(t1.isref, fparam2); - - /* https://issues.dlang.org/show_bug.cgi?id=23135 - * extern(C++) mutable parameters are not covariant with const. - */ - if (t1.linkage == LINK.cpp && cppCovariant) - { - notcovariant |= tp1.isNaked() != tp2.isNaked(); - if (auto tpn1 = tp1.nextOf()) - notcovariant |= tpn1.isNaked() != tp2.nextOf().isNaked(); - } - } - } - else if (t1.parameterList.parameters != t2.parameterList.parameters) - { - if (t1.parameterList.length || t2.parameterList.length) - goto Ldistinct; - } - - // The argument lists match - if (notcovariant) - goto Lnotcovariant; - if (t1.linkage != t2.linkage) - goto Lnotcovariant; - - { - // Return types - Type t1n = t1.next; - Type t2n = t2.next; - - if (!t1n || !t2n) // happens with return type inference - goto Lnotcovariant; - - if (t1n.equals(t2n)) - goto Lcovariant; - if (t1n.ty == Tclass && t2n.ty == Tclass) - { - /* If same class type, but t2n is const, then it's - * covariant. Do this test first because it can work on - * forward references. - */ - if ((cast(TypeClass)t1n).sym == (cast(TypeClass)t2n).sym && MODimplicitConv(t1n.mod, t2n.mod)) - goto Lcovariant; - - // If t1n is forward referenced: - ClassDeclaration cd = (cast(TypeClass)t1n).sym; - if (cd.semanticRun < PASS.semanticdone && !cd.isBaseInfoComplete()) - cd.dsymbolSemantic(null); - if (!cd.isBaseInfoComplete()) - { - return Covariant.fwdref; - } - } - if (t1n.ty == Tstruct && t2n.ty == Tstruct) - { - if ((cast(TypeStruct)t1n).sym == (cast(TypeStruct)t2n).sym && MODimplicitConv(t1n.mod, t2n.mod)) - goto Lcovariant; - } - else if (t1n.ty == t2n.ty && t1n.implicitConvTo(t2n)) - { - if (t1.isref && t2.isref) - { - // Treat like pointers to t1n and t2n - if (t1n.constConv(t2n) < MATCH.constant) - goto Lnotcovariant; - } - goto Lcovariant; - } - else if (t1n.ty == Tnull) - { - // NULL is covariant with any pointer type, but not with any - // dynamic arrays, associative arrays or delegates. - // https://issues.dlang.org/show_bug.cgi?id=8589 - // https://issues.dlang.org/show_bug.cgi?id=19618 - Type t2bn = t2n.toBasetype(); - if (t2bn.ty == Tnull || t2bn.ty == Tpointer || t2bn.ty == Tclass) - goto Lcovariant; - } - // bottom type is covariant to any type - else if (t1n.ty == Tnoreturn) - goto Lcovariant; - } - goto Lnotcovariant; - - Lcovariant: - if (t1.isref != t2.isref) - goto Lnotcovariant; - - if (!t1.isref && (t1.isScopeQual || t2.isScopeQual)) - { - StorageClass stc1 = t1.isScopeQual ? STC.scope_ : 0; - StorageClass stc2 = t2.isScopeQual ? STC.scope_ : 0; - if (t1.isreturn) - { - stc1 |= STC.return_; - if (!t1.isScopeQual) - stc1 |= STC.ref_; - } - if (t2.isreturn) - { - stc2 |= STC.return_; - if (!t2.isScopeQual) - stc2 |= STC.ref_; - } - if (!Parameter.isCovariantScope(t1.isref, stc1, stc2)) - goto Lnotcovariant; - } - - // We can subtract 'return ref' from 'this', but cannot add it - else if (t1.isreturn && !t2.isreturn) - goto Lnotcovariant; - - /* https://issues.dlang.org/show_bug.cgi?id=23135 - * extern(C++) mutable member functions are not covariant with const. - */ - if (t1.linkage == LINK.cpp && cppCovariant && t1.isNaked() != t2.isNaked()) - goto Lnotcovariant; - - /* Can convert mutable to const - */ - if (!MODimplicitConv(t2.mod, t1.mod)) - { - version (none) - { - //stop attribute inference with const - // If adding 'const' will make it covariant - if (MODimplicitConv(t2.mod, MODmerge(t1.mod, MODFlags.const_))) - stc |= STC.const_; - else - goto Lnotcovariant; - } - else - { - goto Ldistinct; - } - } - - /* Can convert pure to impure, nothrow to throw, and nogc to gc - */ - if (!t1.purity && t2.purity) - stc |= STC.pure_; - - if (!t1.isnothrow && t2.isnothrow) - stc |= STC.nothrow_; - - if (!t1.isnogc && t2.isnogc) - stc |= STC.nogc; - - /* Can convert safe/trusted to system - */ - if (t1.trust <= TRUST.system && t2.trust >= TRUST.trusted) - { - // Should we infer trusted or safe? Go with safe. - stc |= STC.safe; - } - - if (stc) - { - if (pstc) - *pstc = stc; - goto Lnotcovariant; - } - - //printf("\tcovaraint: 1\n"); - return Covariant.yes; - - Ldistinct: - //printf("\tcovaraint: 0\n"); - return Covariant.distinct; - - Lnotcovariant: - //printf("\tcovaraint: 2\n"); - return Covariant.no; - } - /******************************** * For pretty-printing a type. */ @@ -1061,17 +803,6 @@ extern (C++) abstract class Type : ASTNode return isscalar(); } - /********************************* - * Check type to see if it is based on a deprecated symbol. - */ - void checkDeprecated(const ref Loc loc, Scope* sc) - { - if (Dsymbol s = toDsymbol(sc)) - { - s.checkDeprecated(loc, sc); - } - } - final bool isConst() const nothrow pure @nogc @safe { return (mod & MODFlags.const_) != 0; @@ -2825,13 +2556,6 @@ extern (C++) abstract class TypeNext : Type this.next = next; } - override final void checkDeprecated(const ref Loc loc, Scope* sc) - { - Type.checkDeprecated(loc, sc); - if (next) // next can be NULL if TypeFunction and auto return type - next.checkDeprecated(loc, sc); - } - override final int hasWild() const { if (ty == Tfunction) @@ -4612,27 +4336,7 @@ extern (C++) final class TypeFunction : TypeNext return t.merge(); } - // arguments get specially formatted - private const(char)* getParamError(Expression arg, Parameter par) - { - if (global.gag && !global.params.v.showGaggedErrors) - return null; - // show qualification when toChars() is the same but types are different - // https://issues.dlang.org/show_bug.cgi?id=19948 - // when comparing the type with strcmp, we need to drop the qualifier - bool qual = !arg.type.mutableOf().equals(par.type.mutableOf()) && - strcmp(arg.type.mutableOf().toChars(), par.type.mutableOf().toChars()) == 0; - auto at = qual ? arg.type.toPrettyChars(true) : arg.type.toChars(); - OutBuffer buf; - // only mention rvalue if it's relevant - const rv = !arg.isLvalue() && par.isReference(); - buf.printf("cannot pass %sargument `%s` of type `%s` to parameter `%s`", - rv ? "rvalue ".ptr : "".ptr, arg.toChars(), at, - parameterToChars(par, this, qual)); - return buf.extractChars(); - } - - private extern(D) const(char)* getMatchError(A...)(const(char)* format, A args) + extern(D) static const(char)* getMatchError(A...)(const(char)* format, A args) { if (global.gag && !global.params.v.showGaggedErrors) return null; @@ -4641,185 +4345,6 @@ extern (C++) final class TypeFunction : TypeNext return buf.extractChars(); } - /******************************** - * 'args' are being matched to function 'this' - * Determine match level. - * Params: - * tthis = type of `this` pointer, null if not member function - * argumentList = arguments to function call - * flag = 1: performing a partial ordering match - * pMessage = address to store error message, or null - * sc = context - * Returns: - * MATCHxxxx - */ - extern (D) MATCH callMatch(Type tthis, ArgumentList argumentList, int flag = 0, const(char)** pMessage = null, Scope* sc = null) - { - //printf("TypeFunction::callMatch() %s\n", toChars()); - MATCH match = MATCH.exact; // assume exact match - ubyte wildmatch = 0; - - if (tthis) - { - Type t = tthis; - if (t.toBasetype().ty == Tpointer) - t = t.toBasetype().nextOf(); // change struct* to struct - if (t.mod != mod) - { - if (MODimplicitConv(t.mod, mod)) - match = MATCH.constant; - else if ((mod & MODFlags.wild) && MODimplicitConv(t.mod, (mod & ~MODFlags.wild) | MODFlags.const_)) - { - match = MATCH.constant; - } - else - return MATCH.nomatch; - } - if (isWild()) - { - if (t.isWild()) - wildmatch |= MODFlags.wild; - else if (t.isConst()) - wildmatch |= MODFlags.const_; - else if (t.isImmutable()) - wildmatch |= MODFlags.immutable_; - else - wildmatch |= MODFlags.mutable; - } - } - - const nparams = parameterList.length; - if (argumentList.length > nparams) - { - if (parameterList.varargs == VarArg.none) - { - // suppress early exit if an error message is wanted, - // so we can check any matching args are valid - if (!pMessage) - return MATCH.nomatch; - } - // too many args; no match - match = MATCH.convert; // match ... with a "conversion" match level - } - - // https://issues.dlang.org/show_bug.cgi?id=22997 - if (parameterList.varargs == VarArg.none && nparams > argumentList.length && !parameterList.hasDefaultArgs) - { - OutBuffer buf; - buf.printf("too few arguments, expected %d, got %d", cast(int)nparams, cast(int)argumentList.length); - if (pMessage) - *pMessage = buf.extractChars(); - return MATCH.nomatch; - } - auto resolvedArgs = resolveNamedArgs(argumentList, pMessage); - Expression[] args; - if (!resolvedArgs) - { - if (!pMessage || *pMessage) - return MATCH.nomatch; - - // if no message was provided, it was because of overflow which will be diagnosed below - match = MATCH.nomatch; - args = argumentList.arguments ? (*argumentList.arguments)[] : null; - } - else - { - args = (*resolvedArgs)[]; - } - - foreach (u, p; parameterList) - { - if (u >= args.length) - break; - - Expression arg = args[u]; - if (!arg) - continue; // default argument - - Type tprm = p.type; - Type targ = arg.type; - - if (!(p.isLazy() && tprm.ty == Tvoid && targ.ty != Tvoid)) - { - const isRef = p.isReference(); - wildmatch |= targ.deduceWild(tprm, isRef); - } - } - if (wildmatch) - { - /* Calculate wild matching modifier - */ - if (wildmatch & MODFlags.const_ || wildmatch & (wildmatch - 1)) - wildmatch = MODFlags.const_; - else if (wildmatch & MODFlags.immutable_) - wildmatch = MODFlags.immutable_; - else if (wildmatch & MODFlags.wild) - wildmatch = MODFlags.wild; - else - { - assert(wildmatch & MODFlags.mutable); - wildmatch = MODFlags.mutable; - } - } - - foreach (u, p; parameterList) - { - MATCH m; - - assert(p); - - // One or more arguments remain - if (u < args.length) - { - Expression arg = args[u]; - if (!arg) - continue; // default argument - m = argumentMatchParameter(this, p, arg, wildmatch, flag, sc, pMessage); - } - else if (p.defaultArg) - continue; - - /* prefer matching the element type rather than the array - * type when more arguments are present with T[]... - */ - if (parameterList.varargs == VarArg.typesafe && u + 1 == nparams && args.length > nparams) - goto L1; - - //printf("\tm = %d\n", m); - if (m == MATCH.nomatch) // if no match - { - L1: - if (parameterList.varargs == VarArg.typesafe && u + 1 == nparams) // if last varargs param - { - auto trailingArgs = args[u .. $]; - if (auto vmatch = matchTypeSafeVarArgs(this, p, trailingArgs, pMessage)) - return vmatch < match ? vmatch : match; - // Error message was already generated in `matchTypeSafeVarArgs` - return MATCH.nomatch; - } - if (pMessage && u >= args.length) - *pMessage = getMatchError("missing argument for parameter #%d: `%s`", - u + 1, parameterToChars(p, this, false)); - // If an error happened previously, `pMessage` was already filled - else if (pMessage && !*pMessage) - *pMessage = getParamError(args[u], p); - - return MATCH.nomatch; - } - if (m < match) - match = m; // pick worst match - } - - if (pMessage && !parameterList.varargs && args.length > nparams) - { - // all parameters had a match, but there are surplus args - *pMessage = getMatchError("expected %d argument(s), not %d", nparams, args.length); - return MATCH.nomatch; - } - //printf("match = %d\n", match); - return match; - } - /******************************** * Convert an `argumentList`, which may contain named arguments, into * a list of arguments in the order of the parameter list. @@ -6935,7 +6460,7 @@ extern (C++) final class Parameter : ASTNode return isCovariantScope(returnByRef, thisSTC, otherSTC); } - extern (D) private static bool isCovariantScope(bool returnByRef, StorageClass from, StorageClass to) pure nothrow @nogc @safe + extern (D) static bool isCovariantScope(bool returnByRef, StorageClass from, StorageClass to) pure nothrow @nogc @safe { // Workaround for failing covariance when finding a common type of delegates, // some of which have parameters with inferred scope @@ -7234,328 +6759,6 @@ const(char)* toChars(ScopeRef sr) pure nothrow @nogc @safe } } -/** - * Used by `callMatch` to check if the copy constructor may be called to - * copy the argument - * - * This is done by seeing if a call to the copy constructor can be made: - * ``` - * typeof(tprm) __copytmp; - * copytmp.__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); - tmp.storage_class = STC.rvalue | STC.temp | STC.ctfe; - tmp.dsymbolSemantic(sc); - Expression ve = new VarExp(arg.loc, tmp); - Expression e = new DotIdExp(arg.loc, ve, Id.ctor); - e = new CallExp(arg.loc, e, arg); - //printf("e = %s\n", e.toChars()); - if (.trySemantic(e, sc)) - return true; - - if (pMessage) - { - /* https://issues.dlang.org/show_bug.cgi?id=22202 - * - * If a function was deduced by semantic on the CallExp, - * it means that resolveFuncCall completed succesfully. - * Therefore, there exists a callable copy constructor, - * however, it cannot be called because scope constraints - * such as purity, safety or nogc. - */ - OutBuffer buf; - auto callExp = e.isCallExp(); - if (auto f = callExp.f) - { - char[] s; - if (!f.isPure && sc.func.setImpure()) - s ~= "pure "; - if (!f.isSafe() && !f.isTrusted() && sc.setUnsafe()) - s ~= "@safe "; - if (!f.isNogc && sc.func.setGC(arg.loc, null)) - s ~= "nogc "; - if (s) - { - s[$-1] = '\0'; - buf.printf("`%s` copy constructor cannot be called from a `%s` context", f.type.toChars(), s.ptr); - } - else if (f.isGenerated() && f.isDisabled()) - { - /* https://issues.dlang.org/show_bug.cgi?id=23097 - * Compiler generated copy constructor failed. - */ - buf.printf("generating a copy constructor for `struct %s` failed, therefore instances of it are uncopyable", - argStruct.toChars()); - } - else - { - /* Although a copy constructor may exist, no suitable match was found. - * i.e: `inout` constructor creates `const` object, not mutable. - * Fallback to using the original generic error before https://issues.dlang.org/show_bug.cgi?id=22202. - */ - goto Lnocpctor; - } - } - else - { - Lnocpctor: - buf.printf("`struct %s` does not define a copy constructor for `%s` to `%s` copies", - argStruct.toChars(), arg.type.toChars(), tprm.toChars()); - } - - *pMessage = buf.extractChars(); - } - return false; -} - -/** - * Match a single parameter to an argument. - * - * 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 - * and some check about value reference. - * - * Params: - * 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 - * (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, - Expression arg, ubyte wildmatch, int flag, Scope* sc, const(char)** pMessage) -{ - //printf("arg: %s, type: %s\n", arg.toChars(), arg.type.toChars()); - MATCH m; - Type targ = arg.type; - Type tprm = wildmatch ? p.type.substWildTo(wildmatch) : p.type; - - if (p.isLazy() && tprm.ty == Tvoid && targ.ty != Tvoid) - m = MATCH.convert; - else if (flag) - { - // for partial ordering, value is an irrelevant mockup, just look at the type - m = targ.implicitConvTo(tprm); - } - else - { - const isRef = p.isReference(); - StructDeclaration argStruct, prmStruct; - - // first look for a copy constructor - if (arg.isLvalue() && !isRef && 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; - } - - // check if the copy constructor may be called to copy the argument - if (argStruct && argStruct == prmStruct && argStruct.hasCopyCtor) - { - if (!isCopyConstructorCallable(argStruct, arg, tprm, sc, pMessage)) - return MATCH.nomatch; - m = MATCH.exact; - } - else - { - import dmd.dcast : cimplicitConvTo; - m = (sc && sc.flags & SCOPE.Cfile) ? arg.cimplicitConvTo(tprm) : arg.implicitConvTo(tprm); - } - } - - // Non-lvalues do not match ref or out parameters - if (p.isReference()) - { - // https://issues.dlang.org/show_bug.cgi?id=13783 - // Don't use toBasetype() to handle enum types. - Type ta = targ; - Type tp = tprm; - //printf("fparam[%d] ta = %s, tp = %s\n", u, ta.toChars(), tp.toChars()); - - if (m && !arg.isLvalue()) - { - if (p.storageClass & STC.out_) - { - if (pMessage) *pMessage = tf.getParamError(arg, p); - return MATCH.nomatch; - } - - if (arg.op == EXP.string_ && tp.ty == Tsarray) - { - if (ta.ty != Tsarray) - { - Type tn = tp.nextOf().castMod(ta.nextOf().mod); - dinteger_t dim = (cast(StringExp)arg).len; - ta = tn.sarrayOf(dim); - } - } - else if (arg.op == EXP.slice && tp.ty == Tsarray) - { - // Allow conversion from T[lwr .. upr] to ref T[upr-lwr] - if (ta.ty != Tsarray) - { - Type tn = ta.nextOf(); - dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger(); - ta = tn.sarrayOf(dim); - } - } - else if ((p.storageClass & STC.in_) && global.params.previewIn) - { - // Allow converting a literal to an `in` which is `ref` - if (arg.op == EXP.arrayLiteral && tp.ty == Tsarray) - { - Type tn = tp.nextOf(); - dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger(); - ta = tn.sarrayOf(dim); - } - - // Need to make this a rvalue through a temporary - m = MATCH.convert; - } - else if (global.params.rvalueRefParam != FeatureState.enabled || - p.storageClass & STC.out_ || - !arg.type.isCopyable()) // can't copy to temp for ref parameter - { - if (pMessage) *pMessage = tf.getParamError(arg, p); - return MATCH.nomatch; - } - else - { - /* in functionParameters() we'll convert this - * rvalue into a temporary - */ - m = MATCH.convert; - } - } - - /* If the match is not already perfect or if the arg - is not a lvalue then try the `alias this` chain - see https://issues.dlang.org/show_bug.cgi?id=15674 - and https://issues.dlang.org/show_bug.cgi?id=21905 - */ - if (ta != tp || !arg.isLvalue()) - { - Type firsttab = ta.toBasetype(); - while (1) - { - Type tab = ta.toBasetype(); - Type tat = tab.aliasthisOf(); - if (!tat || !tat.implicitConvTo(tprm)) - break; - if (tat == tab || tat == firsttab) - break; - ta = tat; - } - } - - /* A ref variable should work like a head-const reference. - * e.g. disallows: - * ref T <- an lvalue of const(T) argument - * ref T[dim] <- an lvalue of const(T[dim]) argument - */ - if (!ta.constConv(tp)) - { - if (pMessage) *pMessage = tf.getParamError(arg, p); - return MATCH.nomatch; - } - } - return m; -} - -/** - * Match the remaining arguments `trailingArgs` with parameter `p`. - * - * Assume we already checked that `p` is the last parameter of `tf`, - * and we want to know whether the arguments would match `p`. - * - * Params: - * tf = The `TypeFunction`, only used for error reporting - * p = The last parameter of `tf` which is variadic - * trailingArgs = The remaining arguments that should match `p` - * pMessage = A buffer to write the error in, or `null` - * - * Returns: Whether `trailingArgs` match `p`. - */ -private extern(D) MATCH matchTypeSafeVarArgs(TypeFunction tf, Parameter p, - Expression[] trailingArgs, const(char)** pMessage) -{ - Type tb = p.type.toBasetype(); - - switch (tb.ty) - { - case Tsarray: - TypeSArray tsa = cast(TypeSArray)tb; - dinteger_t sz = tsa.dim.toInteger(); - if (sz != trailingArgs.length) - { - if (pMessage) - *pMessage = tf.getMatchError("expected %llu variadic argument(s), not %zu", - sz, trailingArgs.length); - return MATCH.nomatch; - } - goto case Tarray; - case Tarray: - { - MATCH match = MATCH.exact; - TypeArray ta = cast(TypeArray)tb; - foreach (arg; trailingArgs) - { - MATCH m; - assert(arg); - - /* If lazy array of delegates, - * convert arg(s) to delegate(s) - */ - Type tret = p.isLazyArray(); - if (tret) - { - if (ta.next.equals(arg.type)) - m = MATCH.exact; - else if (tret.toBasetype().ty == Tvoid) - m = MATCH.convert; - else - { - m = arg.implicitConvTo(tret); - if (m == MATCH.nomatch) - m = arg.implicitConvTo(ta.next); - } - } - else - m = arg.implicitConvTo(ta.next); - - if (m == MATCH.nomatch) - { - if (pMessage) *pMessage = tf.getParamError(arg, p); - return MATCH.nomatch; - } - if (m < match) - match = m; - } - return match; - } - case Tclass: - // We leave it up to the actual constructor call to do the matching. - return MATCH.exact; - - default: - // We can have things as `foo(int[int] wat...)` but they only match - // with an associative array proper. - if (pMessage && trailingArgs.length) *pMessage = tf.getParamError(trailingArgs[0], p); - return MATCH.nomatch; - } -} - /** * Creates an appropriate vector type for `tv` that will hold one boolean * result for each element of the vector type. The result of vector comparisons @@ -7738,3 +6941,36 @@ TypeIdentifier getException() tid.addIdent(Id.Exception); return tid; } + +/************************************** + * Check and set 'att' if 't' is a recursive 'alias this' type + * + * The goal is to prevent endless loops when there is a cycle in the alias this chain. + * Since there is no multiple `alias this`, the chain either ends in a leaf, + * or it loops back on itself as some point. + * + * Example: S0 -> (S1 -> S2 -> S3 -> S1) + * + * `S0` is not a recursive alias this, so this returns `false`, and a rewrite to `S1` can be tried. + * `S1` is a recursive alias this type, but since `att` is initialized to `null`, + * this still returns `false`, but `att1` is set to `S1`. + * A rewrite to `S2` and `S3` can be tried, but when we want to try a rewrite to `S1` again, + * we notice `att == t`, so we're back at the start of the loop, and this returns `true`. + * + * Params: + * att = type reference used to detect recursion. Should be initialized to `null`. + * t = type of 'alias this' rewrite to attempt + * + * Returns: + * `false` if the rewrite is safe, `true` if it would loop back around + */ +bool isRecursiveAliasThis(ref Type att, Type t) +{ + //printf("+isRecursiveAliasThis(att = %s, t = %s)\n", att ? att.toChars() : "null", t.toChars()); + auto tb = t.toBasetype(); + if (att && tb.equivalent(att)) + return true; + else if (!att && tb.checkAliasThisRec()) + att = tb; + return false; +} diff --git a/gcc/d/dmd/mtype.h b/gcc/d/dmd/mtype.h index 675e944050b..ef1ce100640 100644 --- a/gcc/d/dmd/mtype.h +++ b/gcc/d/dmd/mtype.h @@ -225,7 +225,6 @@ public: // kludge for template.isType() DYNCAST dyncast() const override final { return DYNCAST_TYPE; } size_t getUniqueID() const; - Covariant covariant(Type *, StorageClass * = NULL, bool = false); const char *toChars() const override; char *toPrettyChars(bool QualifyTypes = false); static void _init(); @@ -249,7 +248,6 @@ public: virtual bool isString(); virtual bool isAssignable(); virtual bool isBoolean(); - virtual void checkDeprecated(const Loc &loc, Scope *sc); bool isConst() const { return (mod & MODconst) != 0; } bool isImmutable() const { return (mod & MODimmutable) != 0; } bool isMutable() const { return (mod & (MODconst | MODimmutable | MODwild)) == 0; } @@ -364,7 +362,6 @@ class TypeNext : public Type public: Type *next; - void checkDeprecated(const Loc &loc, Scope *sc) override final; int hasWild() const override final; Type *nextOf() override final; Type *makeConst() override final; @@ -929,3 +926,4 @@ public: // If the type is a class or struct, returns the symbol for it, else null. AggregateDeclaration *isAggregate(Type *t); +Covariant covariant(Type *, Type *, StorageClass * = NULL, bool = false); diff --git a/gcc/d/dmd/nspace.d b/gcc/d/dmd/nspace.d index 22c6e63a465..65f7d2964cb 100644 --- a/gcc/d/dmd/nspace.d +++ b/gcc/d/dmd/nspace.d @@ -46,22 +46,14 @@ module dmd.nspace; -import dmd.aggregate; import dmd.arraytypes; -import dmd.astenums; -import dmd.dscope; import dmd.dsymbol; -import dmd.dsymbolsem; -import dmd.errors; import dmd.expression; -import dmd.globals; import dmd.identifier; import dmd.location; import dmd.visitor; import core.stdc.stdio; -private enum LOG = false; - /// Ditto extern (C++) final class Nspace : ScopeDsymbol { @@ -91,14 +83,6 @@ extern (C++) final class Nspace : ScopeDsymbol return members.foreachDsymbol( (s) { return s.hasPointers(); } ) != 0; } - override void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion) - { - //printf("Nspace::setFieldOffset() %s\n", toChars()); - if (_scope) // if fwd reference - dsymbolSemantic(this, null); // try to resolve it - members.foreachDsymbol( s => s.setFieldOffset(ad, fieldState, isunion) ); - } - override const(char)* kind() const { return "namespace"; diff --git a/gcc/d/dmd/nspace.h b/gcc/d/dmd/nspace.h index 701cc935eb5..4a1bd9141ec 100644 --- a/gcc/d/dmd/nspace.h +++ b/gcc/d/dmd/nspace.h @@ -22,7 +22,6 @@ class Nspace final : public ScopeDsymbol Expression *identExp; Nspace *syntaxCopy(Dsymbol *s) override; bool hasPointers() override; - void setFieldOffset(AggregateDeclaration *ad, FieldState& fieldState, bool isunion) override; const char *kind() const override; Nspace *isNspace() override { return this; } void accept(Visitor *v) override { v->visit(this); } diff --git a/gcc/d/dmd/scope.h b/gcc/d/dmd/scope.h index 2cac5f2941b..cceb5a7e3a8 100644 --- a/gcc/d/dmd/scope.h +++ b/gcc/d/dmd/scope.h @@ -130,5 +130,5 @@ struct Scope AliasDeclaration *aliasAsg; // if set, then aliasAsg is being assigned a new value, // do not set wasRead for it - Dsymbol *search(const Loc &loc, Identifier *ident, Dsymbol **pscopesym, int flags = IgnoreNone); + 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 c255701d767..7498eaf4458 100644 --- a/gcc/d/dmd/semantic3.d +++ b/gcc/d/dmd/semantic3.d @@ -520,7 +520,8 @@ private extern(C++) final class Semantic3Visitor : Visitor { Parameter narg = Parameter.getNth(t.arguments, j); assert(narg.ident); - VarDeclaration v = sc2.search(Loc.initial, narg.ident, null).isVarDeclaration(); + Dsymbol pscopesym; + VarDeclaration v = sc2.search(Loc.initial, narg.ident, pscopesym).isVarDeclaration(); assert(v); (*exps)[j] = new VarExp(v.loc, v); } diff --git a/gcc/d/dmd/statement.d b/gcc/d/dmd/statement.d index b5906c8edc3..430454480e1 100644 --- a/gcc/d/dmd/statement.d +++ b/gcc/d/dmd/statement.d @@ -20,19 +20,15 @@ import dmd.arraytypes; import dmd.astenums; import dmd.ast_node; import dmd.errors; -import dmd.gluelayer; import dmd.cond; import dmd.declaration; import dmd.dsymbol; import dmd.expression; import dmd.func; -import dmd.globals; -import dmd.hdrgen; import dmd.id; import dmd.identifier; import dmd.location; import dmd.mtype; -import dmd.common.outbuffer; import dmd.rootobject; import dmd.sapply; import dmd.staticassert; @@ -333,6 +329,8 @@ extern (C++) final class ErrorStatement : Statement extern (D) this() { super(Loc.initial, STMT.Error); + + import dmd.globals; assert(global.gaggedErrors || global.errors); } @@ -1773,7 +1771,7 @@ extern (C++) class AsmStatement : Statement */ extern (C++) final class InlineAsmStatement : AsmStatement { - code* asmcode; + void* asmcode; uint asmalign; // alignment of this statement uint regs; // mask of registers modified (must match regm_t in back end) bool refparam; // true if function parameter is referenced diff --git a/gcc/d/dmd/statement.h b/gcc/d/dmd/statement.h index ef8423f1cf7..1e493f0d188 100644 --- a/gcc/d/dmd/statement.h +++ b/gcc/d/dmd/statement.h @@ -716,7 +716,7 @@ public: class InlineAsmStatement final : public AsmStatement { public: - code *asmcode; + void *asmcode; unsigned asmalign; // alignment of this statement unsigned regs; // mask of registers modified (must match regm_t in back end) d_bool refparam; // true if function parameter is referenced diff --git a/gcc/d/dmd/statementsem.d b/gcc/d/dmd/statementsem.d index 3873adc82ee..fcc606b6dd5 100644 --- a/gcc/d/dmd/statementsem.d +++ b/gcc/d/dmd/statementsem.d @@ -2261,7 +2261,8 @@ Statement statementSemanticVisit(Statement s, Scope* sc) continue; assert(scx.sw == sw); - if (!scx.search(cs.exp.loc, v.ident, null)) + Dsymbol pscopesym; + if (!scx.search(cs.exp.loc, v.ident, pscopesym)) { error(cs.loc, "`case` variable `%s` declared at %s cannot be declared in `switch` body", v.toChars(), v.loc.toChars()); @@ -2525,10 +2526,9 @@ Statement statementSemanticVisit(Statement s, Scope* sc) if (fd.fes) fd = fd.fes.func; // fd is now function enclosing foreach - TypeFunction tf = cast(TypeFunction)fd.type; - assert(tf.ty == Tfunction); + auto tf = fd.type.isTypeFunction(); - if (rs.exp && rs.exp.op == EXP.variable && (cast(VarExp)rs.exp).var == fd.vresult) + if (rs.exp && rs.exp.isVarExp() && rs.exp.isVarExp().var == fd.vresult) { // return vresult; if (sc.fes) @@ -2616,7 +2616,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) rs.exp.checkSharedAccess(sc, returnSharedRef); // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684 - if (rs.exp.op == EXP.type) + if (rs.exp.isTypeExp()) rs.exp = resolveAliasThis(sc, rs.exp); rs.exp = resolveProperties(sc, rs.exp); @@ -2632,14 +2632,14 @@ Statement statementSemanticVisit(Statement s, Scope* sc) // Extract side-effect part rs.exp = Expression.extractLast(rs.exp, e0); - if (rs.exp.op == EXP.call) + if (rs.exp.isCallExp()) rs.exp = valueNoDtor(rs.exp); /* Void-return function can have void / noreturn typed expression * on return statement. */ auto texp = rs.exp.type; - const convToVoid = texp.ty == Tvoid || texp.ty == Tnoreturn; + const convToVoid = texp.ty == Tvoid || texp.isTypeNoreturn(); if (tbret && tbret.ty == Tvoid || convToVoid) { @@ -2688,7 +2688,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) { tf.next = rs.exp.type; } - else if (tret.ty != Terror && !rs.exp.type.equals(tret)) + else if (!tret.isTypeError() && !rs.exp.type.equals(tret)) { int m1 = rs.exp.type.implicitConvTo(tret); int m2 = tret.implicitConvTo(rs.exp.type); @@ -2789,7 +2789,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) // Found an actual return value before else if (tf.next.ty != Tvoid && !resType.toBasetype().isTypeNoreturn()) { - if (tf.next.ty != Terror) + if (!tf.next.isTypeError()) { error(rs.loc, "mismatched function return type inference of `void` and `%s`", tf.next.toChars()); } @@ -2807,7 +2807,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) if (tbret.ty != Tvoid && !resType.isTypeNoreturn()) // if non-void return { - if (tbret.ty != Terror) + if (!tbret.isTypeError()) { if (e0) error(rs.loc, "expected return type of `%s`, not `%s`", tret.toChars(), resType.toChars()); @@ -2901,7 +2901,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) } if (e0) { - if (e0.op == EXP.declaration || e0.op == EXP.comma) + if (e0.isDeclarationExp() || e0.isCommaExp()) { rs.exp = Expression.combine(e0, rs.exp); } diff --git a/gcc/d/dmd/staticassert.d b/gcc/d/dmd/staticassert.d index 7f22c4c993a..760c66a415a 100644 --- a/gcc/d/dmd/staticassert.d +++ b/gcc/d/dmd/staticassert.d @@ -14,14 +14,11 @@ module dmd.staticassert; import dmd.arraytypes; -import dmd.dscope; import dmd.dsymbol; import dmd.expression; -import dmd.globals; import dmd.location; import dmd.id; import dmd.identifier; -import dmd.mtype; import dmd.visitor; /*********************************************************** diff --git a/gcc/d/dmd/template.h b/gcc/d/dmd/template.h index 44f95ec0ec3..4be136102c2 100644 --- a/gcc/d/dmd/template.h +++ b/gcc/d/dmd/template.h @@ -311,7 +311,6 @@ public: const char *kind() const override; bool oneMember(Dsymbol **ps, Identifier *ident) override; bool hasPointers() override; - void setFieldOffset(AggregateDeclaration *ad, FieldState& fieldState, bool isunion) override; const char *toChars() const override; TemplateMixin *isTemplateMixin() override { return this; } diff --git a/gcc/d/dmd/traits.d b/gcc/d/dmd/traits.d index 0acadbb4604..aebc0b5512b 100644 --- a/gcc/d/dmd/traits.d +++ b/gcc/d/dmd/traits.d @@ -1665,12 +1665,12 @@ Expression semanticTraits(TraitsExp e, Scope* sc) } else if (auto ed = sm.isEnumDeclaration()) { - ScopeDsymbol._foreach(null, ed.members, &pushIdentsDg); + _foreach(null, ed.members, &pushIdentsDg); } return 0; } - ScopeDsymbol._foreach(sc, sds.members, &pushIdentsDg); + _foreach(sc, sds.members, &pushIdentsDg); auto cd = sds.isClassDeclaration(); if (cd && e.ident == Id.allMembers) { @@ -1684,7 +1684,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) { auto cb = (*cd.baseclasses)[i].sym; assert(cb); - ScopeDsymbol._foreach(null, cb.members, &pushIdentsDg); + _foreach(null, cb.members, &pushIdentsDg); if (cb.baseclasses.length) pushBaseMembersDg(cb); } diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d index 2063a954b99..b0e45f4c042 100644 --- a/gcc/d/dmd/typesem.d +++ b/gcc/d/dmd/typesem.d @@ -228,7 +228,7 @@ private void resolveHelper(TypeQualified mt, const ref Loc loc, Scope* sc, Dsymb Type t = s.getType(); // type symbol, type alias, or type tuple? uint errorsave = global.errors; - int flags = t is null ? SearchLocalsOnly : IgnorePrivateImports; + SearchOptFlags flags = t is null ? SearchOpt.localsOnly : SearchOpt.ignorePrivateImports; Dsymbol sm = s.searchX(loc, sc, id, flags); if (sm) @@ -380,12 +380,12 @@ private void resolveHelper(TypeQualified mt, const ref Loc loc, Scope* sc, Dsymb * loc = location to print the error messages * sc = the scope where the symbol is located * id = the id of the symbol - * flags = the search flags which can be `SearchLocalsOnly` or `IgnorePrivateImports` + * flags = the search flags which can be `SearchLocalsOnly` or `SearchOpt.ignorePrivateImports` * * Returns: * symbol found, NULL if not */ -private Dsymbol searchX(Dsymbol dsym, const ref Loc loc, Scope* sc, RootObject id, int flags) +private Dsymbol searchX(Dsymbol dsym, const ref Loc loc, Scope* sc, RootObject id, SearchOptFlags flags) { //printf("Dsymbol::searchX(this=%p,%s, ident='%s')\n", this, toChars(), ident.toChars()); Dsymbol s = dsym.toAlias(); @@ -486,6 +486,529 @@ Expression typeToExpression(Type t) } } +/******************************** + * 'args' are being matched to function type 'tf' + * Determine match level. + * Params: + * tf = function type + * tthis = type of `this` pointer, null if not member function + * argumentList = arguments to function call + * flag = 1: performing a partial ordering match + * pMessage = address to store error message, or null + * sc = context + * Returns: + * MATCHxxxx + */ +extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentList, int flag = 0, const(char)** pMessage = null, Scope* sc = null) +{ + //printf("TypeFunction::callMatch() %s\n", tf.toChars()); + MATCH match = MATCH.exact; // assume exact match + ubyte wildmatch = 0; + + if (tthis) + { + Type t = tthis; + if (t.toBasetype().ty == Tpointer) + t = t.toBasetype().nextOf(); // change struct* to struct + if (t.mod != tf.mod) + { + if (MODimplicitConv(t.mod, tf.mod)) + match = MATCH.constant; + else if ((tf.mod & MODFlags.wild) && MODimplicitConv(t.mod, (tf.mod & ~MODFlags.wild) | MODFlags.const_)) + { + match = MATCH.constant; + } + else + return MATCH.nomatch; + } + if (tf.isWild()) + { + if (t.isWild()) + wildmatch |= MODFlags.wild; + else if (t.isConst()) + wildmatch |= MODFlags.const_; + else if (t.isImmutable()) + wildmatch |= MODFlags.immutable_; + else + wildmatch |= MODFlags.mutable; + } + } + + ParameterList* parameterList = &tf.parameterList; + const nparams = parameterList.length; + if (argumentList.length > nparams) + { + if (parameterList.varargs == VarArg.none) + { + // suppress early exit if an error message is wanted, + // so we can check any matching args are valid + if (!pMessage) + return MATCH.nomatch; + } + // too many args; no match + match = MATCH.convert; // match ... with a "conversion" match level + } + + // https://issues.dlang.org/show_bug.cgi?id=22997 + if (parameterList.varargs == VarArg.none && nparams > argumentList.length && !parameterList.hasDefaultArgs) + { + OutBuffer buf; + buf.printf("too few arguments, expected %d, got %d", cast(int)nparams, cast(int)argumentList.length); + if (pMessage) + *pMessage = buf.extractChars(); + return MATCH.nomatch; + } + auto resolvedArgs = tf.resolveNamedArgs(argumentList, pMessage); + Expression[] args; + if (!resolvedArgs) + { + if (!pMessage || *pMessage) + return MATCH.nomatch; + + // if no message was provided, it was because of overflow which will be diagnosed below + match = MATCH.nomatch; + args = argumentList.arguments ? (*argumentList.arguments)[] : null; + } + else + { + args = (*resolvedArgs)[]; + } + + foreach (u, p; *parameterList) + { + if (u >= args.length) + break; + + Expression arg = args[u]; + if (!arg) + continue; // default argument + + Type tprm = p.type; + Type targ = arg.type; + + if (!(p.isLazy() && tprm.ty == Tvoid && targ.ty != Tvoid)) + { + const isRef = p.isReference(); + wildmatch |= targ.deduceWild(tprm, isRef); + } + } + if (wildmatch) + { + /* Calculate wild matching modifier + */ + if (wildmatch & MODFlags.const_ || wildmatch & (wildmatch - 1)) + wildmatch = MODFlags.const_; + else if (wildmatch & MODFlags.immutable_) + wildmatch = MODFlags.immutable_; + else if (wildmatch & MODFlags.wild) + wildmatch = MODFlags.wild; + else + { + assert(wildmatch & MODFlags.mutable); + wildmatch = MODFlags.mutable; + } + } + + foreach (u, p; *parameterList) + { + MATCH m; + + assert(p); + + // One or more arguments remain + if (u < args.length) + { + Expression arg = args[u]; + if (!arg) + continue; // default argument + m = argumentMatchParameter(tf, p, arg, wildmatch, flag, sc, pMessage); + } + else if (p.defaultArg) + continue; + + /* prefer matching the element type rather than the array + * type when more arguments are present with T[]... + */ + if (parameterList.varargs == VarArg.typesafe && u + 1 == nparams && args.length > nparams) + goto L1; + + //printf("\tm = %d\n", m); + if (m == MATCH.nomatch) // if no match + { + L1: + if (parameterList.varargs == VarArg.typesafe && u + 1 == nparams) // if last varargs param + { + auto trailingArgs = args[u .. $]; + if (auto vmatch = matchTypeSafeVarArgs(tf, p, trailingArgs, pMessage)) + return vmatch < match ? vmatch : match; + // Error message was already generated in `matchTypeSafeVarArgs` + return MATCH.nomatch; + } + if (pMessage && u >= args.length) + *pMessage = tf.getMatchError("missing argument for parameter #%d: `%s`", + u + 1, parameterToChars(p, tf, false)); + // If an error happened previously, `pMessage` was already filled + else if (pMessage && !*pMessage) + *pMessage = tf.getParamError(args[u], p); + + return MATCH.nomatch; + } + if (m < match) + match = m; // pick worst match + } + + if (pMessage && !parameterList.varargs && args.length > nparams) + { + // all parameters had a match, but there are surplus args + *pMessage = tf.getMatchError("expected %d argument(s), not %d", nparams, args.length); + return MATCH.nomatch; + } + //printf("match = %d\n", match); + return match; +} + +/** + * Used by `callMatch` to check if the copy constructor may be called to + * copy the argument + * + * This is done by seeing if a call to the copy constructor can be made: + * ``` + * typeof(tprm) __copytmp; + * copytmp.__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); + tmp.storage_class = STC.rvalue | STC.temp | STC.ctfe; + tmp.dsymbolSemantic(sc); + Expression ve = new VarExp(arg.loc, tmp); + Expression e = new DotIdExp(arg.loc, ve, Id.ctor); + e = new CallExp(arg.loc, e, arg); + //printf("e = %s\n", e.toChars()); + if (.trySemantic(e, sc)) + return true; + + if (pMessage) + { + /* https://issues.dlang.org/show_bug.cgi?id=22202 + * + * If a function was deduced by semantic on the CallExp, + * it means that resolveFuncCall completed succesfully. + * Therefore, there exists a callable copy constructor, + * however, it cannot be called because scope constraints + * such as purity, safety or nogc. + */ + OutBuffer buf; + auto callExp = e.isCallExp(); + if (auto f = callExp.f) + { + char[] s; + if (!f.isPure && sc.func.setImpure()) + s ~= "pure "; + if (!f.isSafe() && !f.isTrusted() && sc.setUnsafe()) + s ~= "@safe "; + if (!f.isNogc && sc.func.setGC(arg.loc, null)) + s ~= "nogc "; + if (s) + { + s[$-1] = '\0'; + buf.printf("`%s` copy constructor cannot be called from a `%s` context", f.type.toChars(), s.ptr); + } + else if (f.isGenerated() && f.isDisabled()) + { + /* https://issues.dlang.org/show_bug.cgi?id=23097 + * Compiler generated copy constructor failed. + */ + buf.printf("generating a copy constructor for `struct %s` failed, therefore instances of it are uncopyable", + argStruct.toChars()); + } + else + { + /* Although a copy constructor may exist, no suitable match was found. + * i.e: `inout` constructor creates `const` object, not mutable. + * Fallback to using the original generic error before https://issues.dlang.org/show_bug.cgi?id=22202. + */ + goto Lnocpctor; + } + } + else + { + Lnocpctor: + buf.printf("`struct %s` does not define a copy constructor for `%s` to `%s` copies", + argStruct.toChars(), arg.type.toChars(), tprm.toChars()); + } + + *pMessage = buf.extractChars(); + } + return false; +} + +/** + * Match a single parameter to an argument. + * + * 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 + * and some check about value reference. + * + * Params: + * 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 + * (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, + Expression arg, ubyte wildmatch, int flag, Scope* sc, const(char)** pMessage) +{ + //printf("arg: %s, type: %s\n", arg.toChars(), arg.type.toChars()); + MATCH m; + Type targ = arg.type; + Type tprm = wildmatch ? p.type.substWildTo(wildmatch) : p.type; + + if (p.isLazy() && tprm.ty == Tvoid && targ.ty != Tvoid) + m = MATCH.convert; + else if (flag) + { + // for partial ordering, value is an irrelevant mockup, just look at the type + m = targ.implicitConvTo(tprm); + } + else + { + const isRef = p.isReference(); + StructDeclaration argStruct, prmStruct; + + // first look for a copy constructor + if (arg.isLvalue() && !isRef && 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; + } + + // check if the copy constructor may be called to copy the argument + if (argStruct && argStruct == prmStruct && argStruct.hasCopyCtor) + { + if (!isCopyConstructorCallable(argStruct, arg, tprm, sc, pMessage)) + return MATCH.nomatch; + m = MATCH.exact; + } + else + { + import dmd.dcast : cimplicitConvTo; + m = (sc && sc.flags & SCOPE.Cfile) ? arg.cimplicitConvTo(tprm) : arg.implicitConvTo(tprm); + } + } + + // Non-lvalues do not match ref or out parameters + if (p.isReference()) + { + // https://issues.dlang.org/show_bug.cgi?id=13783 + // Don't use toBasetype() to handle enum types. + Type ta = targ; + Type tp = tprm; + //printf("fparam[%d] ta = %s, tp = %s\n", u, ta.toChars(), tp.toChars()); + + if (m && !arg.isLvalue()) + { + if (p.storageClass & STC.out_) + { + if (pMessage) *pMessage = tf.getParamError(arg, p); + return MATCH.nomatch; + } + + if (arg.op == EXP.string_ && tp.ty == Tsarray) + { + if (ta.ty != Tsarray) + { + Type tn = tp.nextOf().castMod(ta.nextOf().mod); + dinteger_t dim = (cast(StringExp)arg).len; + ta = tn.sarrayOf(dim); + } + } + else if (arg.op == EXP.slice && tp.ty == Tsarray) + { + // Allow conversion from T[lwr .. upr] to ref T[upr-lwr] + if (ta.ty != Tsarray) + { + Type tn = ta.nextOf(); + dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger(); + ta = tn.sarrayOf(dim); + } + } + else if ((p.storageClass & STC.in_) && global.params.previewIn) + { + // Allow converting a literal to an `in` which is `ref` + if (arg.op == EXP.arrayLiteral && tp.ty == Tsarray) + { + Type tn = tp.nextOf(); + dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger(); + ta = tn.sarrayOf(dim); + } + + // Need to make this a rvalue through a temporary + m = MATCH.convert; + } + else if (global.params.rvalueRefParam != FeatureState.enabled || + p.storageClass & STC.out_ || + !arg.type.isCopyable()) // can't copy to temp for ref parameter + { + if (pMessage) *pMessage = tf.getParamError(arg, p); + return MATCH.nomatch; + } + else + { + /* in functionParameters() we'll convert this + * rvalue into a temporary + */ + m = MATCH.convert; + } + } + + /* If the match is not already perfect or if the arg + is not a lvalue then try the `alias this` chain + see https://issues.dlang.org/show_bug.cgi?id=15674 + and https://issues.dlang.org/show_bug.cgi?id=21905 + */ + if (ta != tp || !arg.isLvalue()) + { + Type firsttab = ta.toBasetype(); + while (1) + { + Type tab = ta.toBasetype(); + Type tat = tab.aliasthisOf(); + if (!tat || !tat.implicitConvTo(tprm)) + break; + if (tat == tab || tat == firsttab) + break; + ta = tat; + } + } + + /* A ref variable should work like a head-const reference. + * e.g. disallows: + * ref T <- an lvalue of const(T) argument + * ref T[dim] <- an lvalue of const(T[dim]) argument + */ + if (!ta.constConv(tp)) + { + if (pMessage) *pMessage = tf.getParamError(arg, p); + return MATCH.nomatch; + } + } + return m; +} + +// arguments get specially formatted +private const(char)* getParamError(TypeFunction tf, Expression arg, Parameter par) +{ + if (global.gag && !global.params.v.showGaggedErrors) + return null; + // show qualification when toChars() is the same but types are different + // https://issues.dlang.org/show_bug.cgi?id=19948 + // when comparing the type with strcmp, we need to drop the qualifier + bool qual = !arg.type.mutableOf().equals(par.type.mutableOf()) && + strcmp(arg.type.mutableOf().toChars(), par.type.mutableOf().toChars()) == 0; + auto at = qual ? arg.type.toPrettyChars(true) : arg.type.toChars(); + OutBuffer buf; + // only mention rvalue if it's relevant + const rv = !arg.isLvalue() && par.isReference(); + buf.printf("cannot pass %sargument `%s` of type `%s` to parameter `%s`", + rv ? "rvalue ".ptr : "".ptr, arg.toChars(), at, + parameterToChars(par, tf, qual)); + return buf.extractChars(); +} + +/** + * Match the remaining arguments `trailingArgs` with parameter `p`. + * + * Assume we already checked that `p` is the last parameter of `tf`, + * and we want to know whether the arguments would match `p`. + * + * Params: + * tf = The `TypeFunction`, only used for error reporting + * p = The last parameter of `tf` which is variadic + * trailingArgs = The remaining arguments that should match `p` + * pMessage = A buffer to write the error in, or `null` + * + * Returns: Whether `trailingArgs` match `p`. + */ +private extern(D) MATCH matchTypeSafeVarArgs(TypeFunction tf, Parameter p, + Expression[] trailingArgs, const(char)** pMessage) +{ + Type tb = p.type.toBasetype(); + + switch (tb.ty) + { + case Tsarray: + TypeSArray tsa = cast(TypeSArray)tb; + dinteger_t sz = tsa.dim.toInteger(); + if (sz != trailingArgs.length) + { + if (pMessage) + *pMessage = tf.getMatchError("expected %llu variadic argument(s), not %zu", + sz, trailingArgs.length); + return MATCH.nomatch; + } + goto case Tarray; + case Tarray: + { + MATCH match = MATCH.exact; + TypeArray ta = cast(TypeArray)tb; + foreach (arg; trailingArgs) + { + MATCH m; + assert(arg); + + /* If lazy array of delegates, + * convert arg(s) to delegate(s) + */ + Type tret = p.isLazyArray(); + if (tret) + { + if (ta.next.equals(arg.type)) + m = MATCH.exact; + else if (tret.toBasetype().ty == Tvoid) + m = MATCH.convert; + else + { + m = arg.implicitConvTo(tret); + if (m == MATCH.nomatch) + m = arg.implicitConvTo(ta.next); + } + } + else + m = arg.implicitConvTo(ta.next); + + if (m == MATCH.nomatch) + { + if (pMessage) *pMessage = tf.getParamError(arg, p); + return MATCH.nomatch; + } + if (m < match) + match = m; + } + return match; + } + case Tclass: + // We leave it up to the actual constructor call to do the matching. + return MATCH.exact; + + default: + // We can have things as `foo(int[int] wat...)` but they only match + // with an associative array proper. + if (pMessage && trailingArgs.length) *pMessage = tf.getParamError(trailingArgs[0], p); + return MATCH.nomatch; + } +} + /****************************************** * Perform semantic analysis on a type. * Params: @@ -1878,7 +2401,7 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) /* look for pre-existing declaration */ Dsymbol scopesym; - auto s = sc2.search(mtype.loc, mtype.id, &scopesym, IgnoreErrors | TagNameSpace); + auto s = sc2.search(mtype.loc, mtype.id, scopesym, SearchOpt.ignoreErrors | SearchOpt.tagNameSpace); if (!s || s.isModule()) { // no pre-existing declaration, so declare it @@ -2753,7 +3276,7 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type } Dsymbol scopesym; - Dsymbol s = sc.search(loc, mt.ident, &scopesym); + Dsymbol s = sc.search(loc, mt.ident, scopesym); /* * https://issues.dlang.org/show_bug.cgi?id=1170 * https://issues.dlang.org/show_bug.cgi?id=10739 @@ -2776,7 +3299,7 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type mixinTempl.dsymbolSemantic(sc); } sds.members.foreachDsymbol( s => semanticOnMixin(s) ); - s = sc.search(loc, mt.ident, &scopesym); + s = sc.search(loc, mt.ident, scopesym); } } @@ -3794,8 +4317,8 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag return e; } - immutable flags = sc.flags & SCOPE.ignoresymbolvisibility ? IgnoreSymbolVisibility : 0; - s = mt.sym.search(e.loc, ident, flags | IgnorePrivateImports); + immutable flags = sc.flags & SCOPE.ignoresymbolvisibility ? SearchOpt.ignoreVisibility : 0; + s = mt.sym.search(e.loc, ident, flags | SearchOpt.ignorePrivateImports); L1: if (!s) { @@ -4074,8 +4597,8 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag return e; } - int flags = sc.flags & SCOPE.ignoresymbolvisibility ? IgnoreSymbolVisibility : 0; - s = mt.sym.search(e.loc, ident, flags | IgnorePrivateImports); + SearchOptFlags flags = sc.flags & SCOPE.ignoresymbolvisibility ? SearchOpt.ignoreVisibility : SearchOpt.all; + s = mt.sym.search(e.loc, ident, flags | SearchOpt.ignorePrivateImports); L1: if (!s) @@ -4723,7 +5246,7 @@ Type getComplexLibraryType(const ref Loc loc, Scope* sc, TY ty) return *pt; } - Dsymbol s = mConfig.searchX(Loc.initial, sc, id, IgnorePrivateImports); + Dsymbol s = mConfig.searchX(Loc.initial, sc, id, SearchOpt.ignorePrivateImports); if (!s) { error(loc, "`%s` not found in core.stdc.config", id.toChars()); @@ -4748,6 +5271,263 @@ Type getComplexLibraryType(const ref Loc loc, Scope* sc, TY ty) return *pt; } +/******************************* + * Covariant means that 'src' can substitute for 't', + * i.e. a pure function is a match for an impure type. + * Params: + * src = source type + * t = type 'src' is covariant with + * pstc = if not null, store STCxxxx which would make it covariant + * cppCovariant = true if extern(C++) function types should follow C++ covariant rules + * Returns: + * An enum value of either `Covariant.yes` or a reason it's not covariant. + */ +extern (C++) Covariant covariant(Type src, Type t, StorageClass* pstc = null, bool cppCovariant = false) +{ + version (none) + { + printf("Type::covariant(t = %s) %s\n", t.toChars(), src.toChars()); + printf("deco = %p, %p\n", src.deco, t.deco); + // printf("ty = %d\n", next.ty); + printf("mod = %x, %x\n", src.mod, t.mod); + } + if (pstc) + *pstc = 0; + StorageClass stc = 0; + + bool notcovariant = false; + + if (src.equals(t)) + return Covariant.yes; + + TypeFunction t1 = src.isTypeFunction(); + TypeFunction t2 = t.isTypeFunction(); + + if (!t1 || !t2) + goto Ldistinct; + + if (t1.parameterList.varargs != t2.parameterList.varargs) + goto Ldistinct; + + if (t1.parameterList.parameters && t2.parameterList.parameters) + { + if (t1.parameterList.length != t2.parameterList.length) + goto Ldistinct; + + foreach (i, fparam1; t1.parameterList) + { + Parameter fparam2 = t2.parameterList[i]; + Type tp1 = fparam1.type; + Type tp2 = fparam2.type; + + if (!tp1.equals(tp2)) + { + if (tp1.ty == tp2.ty) + { + if (auto tc1 = tp1.isTypeClass()) + { + if (tc1.sym == (cast(TypeClass)tp2).sym && MODimplicitConv(tp2.mod, tp1.mod)) + goto Lcov; + } + else if (auto ts1 = tp1.isTypeStruct()) + { + if (ts1.sym == (cast(TypeStruct)tp2).sym && MODimplicitConv(tp2.mod, tp1.mod)) + goto Lcov; + } + else if (tp1.ty == Tpointer) + { + if (tp2.implicitConvTo(tp1)) + goto Lcov; + } + else if (tp1.ty == Tarray) + { + if (tp2.implicitConvTo(tp1)) + goto Lcov; + } + else if (tp1.ty == Tdelegate) + { + if (tp2.implicitConvTo(tp1)) + goto Lcov; + } + } + goto Ldistinct; + } + Lcov: + notcovariant |= !fparam1.isCovariant(t1.isref, fparam2); + + /* https://issues.dlang.org/show_bug.cgi?id=23135 + * extern(C++) mutable parameters are not covariant with const. + */ + if (t1.linkage == LINK.cpp && cppCovariant) + { + notcovariant |= tp1.isNaked() != tp2.isNaked(); + if (auto tpn1 = tp1.nextOf()) + notcovariant |= tpn1.isNaked() != tp2.nextOf().isNaked(); + } + } + } + else if (t1.parameterList.parameters != t2.parameterList.parameters) + { + if (t1.parameterList.length || t2.parameterList.length) + goto Ldistinct; + } + + // The argument lists match + if (notcovariant) + goto Lnotcovariant; + if (t1.linkage != t2.linkage) + goto Lnotcovariant; + + { + // Return types + Type t1n = t1.next; + Type t2n = t2.next; + + if (!t1n || !t2n) // happens with return type inference + goto Lnotcovariant; + + if (t1n.equals(t2n)) + goto Lcovariant; + if (t1n.ty == Tclass && t2n.ty == Tclass) + { + /* If same class type, but t2n is const, then it's + * covariant. Do this test first because it can work on + * forward references. + */ + if ((cast(TypeClass)t1n).sym == (cast(TypeClass)t2n).sym && MODimplicitConv(t1n.mod, t2n.mod)) + goto Lcovariant; + + // If t1n is forward referenced: + ClassDeclaration cd = (cast(TypeClass)t1n).sym; + if (cd.semanticRun < PASS.semanticdone && !cd.isBaseInfoComplete()) + cd.dsymbolSemantic(null); + if (!cd.isBaseInfoComplete()) + { + return Covariant.fwdref; + } + } + if (t1n.ty == Tstruct && t2n.ty == Tstruct) + { + if ((cast(TypeStruct)t1n).sym == (cast(TypeStruct)t2n).sym && MODimplicitConv(t1n.mod, t2n.mod)) + goto Lcovariant; + } + else if (t1n.ty == t2n.ty && t1n.implicitConvTo(t2n)) + { + if (t1.isref && t2.isref) + { + // Treat like pointers to t1n and t2n + if (t1n.constConv(t2n) < MATCH.constant) + goto Lnotcovariant; + } + goto Lcovariant; + } + else if (t1n.ty == Tnull) + { + // NULL is covariant with any pointer type, but not with any + // dynamic arrays, associative arrays or delegates. + // https://issues.dlang.org/show_bug.cgi?id=8589 + // https://issues.dlang.org/show_bug.cgi?id=19618 + Type t2bn = t2n.toBasetype(); + if (t2bn.ty == Tnull || t2bn.ty == Tpointer || t2bn.ty == Tclass) + goto Lcovariant; + } + // bottom type is covariant to any type + else if (t1n.ty == Tnoreturn) + goto Lcovariant; + } + goto Lnotcovariant; + +Lcovariant: + if (t1.isref != t2.isref) + goto Lnotcovariant; + + if (!t1.isref && (t1.isScopeQual || t2.isScopeQual)) + { + StorageClass stc1 = t1.isScopeQual ? STC.scope_ : 0; + StorageClass stc2 = t2.isScopeQual ? STC.scope_ : 0; + if (t1.isreturn) + { + stc1 |= STC.return_; + if (!t1.isScopeQual) + stc1 |= STC.ref_; + } + if (t2.isreturn) + { + stc2 |= STC.return_; + if (!t2.isScopeQual) + stc2 |= STC.ref_; + } + if (!Parameter.isCovariantScope(t1.isref, stc1, stc2)) + goto Lnotcovariant; + } + + // We can subtract 'return ref' from 'this', but cannot add it + else if (t1.isreturn && !t2.isreturn) + goto Lnotcovariant; + + /* https://issues.dlang.org/show_bug.cgi?id=23135 + * extern(C++) mutable member functions are not covariant with const. + */ + if (t1.linkage == LINK.cpp && cppCovariant && t1.isNaked() != t2.isNaked()) + goto Lnotcovariant; + + /* Can convert mutable to const + */ + if (!MODimplicitConv(t2.mod, t1.mod)) + { + version (none) + { + //stop attribute inference with const + // If adding 'const' will make it covariant + if (MODimplicitConv(t2.mod, MODmerge(t1.mod, MODFlags.const_))) + stc |= STC.const_; + else + goto Lnotcovariant; + } + else + { + goto Ldistinct; + } + } + + /* Can convert pure to impure, nothrow to throw, and nogc to gc + */ + if (!t1.purity && t2.purity) + stc |= STC.pure_; + + if (!t1.isnothrow && t2.isnothrow) + stc |= STC.nothrow_; + + if (!t1.isnogc && t2.isnogc) + stc |= STC.nogc; + + /* Can convert safe/trusted to system + */ + if (t1.trust <= TRUST.system && t2.trust >= TRUST.trusted) + { + // Should we infer trusted or safe? Go with safe. + stc |= STC.safe; + } + + if (stc) + { + if (pstc) + *pstc = stc; + goto Lnotcovariant; + } + + //printf("\tcovaraint: 1\n"); + return Covariant.yes; + +Ldistinct: + //printf("\tcovaraint: 0\n"); + return Covariant.distinct; + +Lnotcovariant: + //printf("\tcovaraint: 2\n"); + return Covariant.no; +} + /******************************* Private *****************************************/ private: diff --git a/gcc/d/gdc.texi b/gcc/d/gdc.texi index e6a8a906161..fe1c62553cc 100644 --- a/gcc/d/gdc.texi +++ b/gcc/d/gdc.texi @@ -740,8 +740,10 @@ arguments like @code{va_start}. @opindex fignore-unknown-pragmas @opindex fno-ignore-unknown-pragmas -@item -fignore-unknown-pragmas -Turns off errors for unsupported pragmas. +@item -fno-ignore-unknown-pragmas +Do not recognize unsupported pragmas. Any @code{pragma()} encountered that is +not part of the D language will result in an error. This option is now +deprecated and will be removed in a future release. @opindex fmax-errors @item -fmax-errors=@var{n} diff --git a/gcc/d/typeinfo.cc b/gcc/d/typeinfo.cc index f671131413e..257a2abe5d7 100644 --- a/gcc/d/typeinfo.cc +++ b/gcc/d/typeinfo.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "coretypes.h" #include "dmd/aggregate.h" +#include "dmd/dsymbol.h" #include "dmd/enum.h" #include "dmd/errors.h" #include "dmd/expression.h" @@ -205,7 +206,7 @@ make_frontend_typeinfo (Identifier *ident, ClassDeclaration *base = NULL) /* Create object module in order to complete the semantic. */ if (!object_module->_scope) - object_module->importAll (NULL); + importAll (object_module, NULL); /* Object class doesn't exist, create a stub one that will cause an error if used. */ diff --git a/gcc/testsuite/gdc.test/compilable/imports/defines.c b/gcc/testsuite/gdc.test/compilable/imports/defines.c index 6bd07366544..8a5601a2d08 100644 --- a/gcc/testsuite/gdc.test/compilable/imports/defines.c +++ b/gcc/testsuite/gdc.test/compilable/imports/defines.c @@ -26,3 +26,7 @@ _Static_assert(F80 == 9.0L, "9"); #define SSS "hello" _Static_assert(SSS[0] == 'h', "10"); + +#define ABC 12 +#define GHI (size) abbadabba +#define DEF (ABC + 5) diff --git a/gcc/testsuite/gdc.test/compilable/test9565.d b/gcc/testsuite/gdc.test/compilable/test9565.d deleted file mode 100644 index 9e3ee6a8170..00000000000 --- a/gcc/testsuite/gdc.test/compilable/test9565.d +++ /dev/null @@ -1,86 +0,0 @@ -// REQUIRED_ARGS: -o- -// PERMUTE_ARGS: - -template TypeTuple(T...) { alias TypeTuple = T; } - -bool startsWith(string s, string m) { return s[0 .. m.length] == m; } - -void main() -{ - enum string castPrefix = "cast(" ~ size_t.stringof ~ ")"; - - // TypeSArray - static assert((int[10]).stringof == "int[10]", T.stringof); - - int[] arr; - - // IndexExp - { - // index == IntegerExp - static assert((arr[ 4 ]).stringof == "arr[4]"); - static assert((arr[ 4U ]).stringof == "arr[4]"); - static assert((arr[ 4L ]).stringof == "arr[4]"); - static assert((arr[ 4LU]).stringof == "arr[4]"); - - // index == UAddExp - static assert((arr[+4 ]).stringof == "arr[4]"); - static assert((arr[+4U ]).stringof == "arr[4]"); - static assert((arr[+4L ]).stringof == "arr[4]"); - static assert((arr[+4LU]).stringof == "arr[4]"); - - // index == NegExp - static assert((arr[-4 ]).stringof == "arr[" ~ castPrefix ~ "-4]"); - static assert((arr[-4U ]).stringof == "arr[4294967292]"); - static assert((arr[int.min] ).stringof == "arr[" ~ castPrefix ~ "-2147483648]"); - static if (is(size_t == ulong)) - { - static assert((arr[-4L ]).stringof == "arr[" ~ castPrefix ~ "-4L]"); - static assert((arr[-4LU]).stringof == "arr[-4LU]"); - - // IntegerLiteral needs suffix if the value is greater than long.max - static assert((arr[long.max + 0]).stringof == "arr[9223372036854775807]"); - static assert((arr[long.max + 1]).stringof == "arr[" ~ castPrefix ~ "(9223372036854775807L + 1L)]"); - } - - foreach (Int; TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong)) - { - enum Int p4 = +4; - enum string result1 = (arr[p4]).stringof; - static assert(result1 == "arr[4]"); - - enum string result2 = (arr[cast(Int)+4]).stringof; - static assert(result2 == "arr[4]"); - } - foreach (Int; TypeTuple!(byte, short, int, long)) - { - // keep "cast(Type)" in the string representation - - enum Int m4 = -4; - static if (is(typeof({ size_t x = m4; }))) - { - enum string result1 = (arr[m4]).stringof; - static assert(result1.startsWith("arr[" ~ castPrefix)); - } - else - static assert(!__traits(compiles, arr[m4])); - - enum string result2 = (arr[cast(Int)-4]).stringof; - static assert(result2.startsWith("arr[" ~ castPrefix)); - } - } - - // SliceExp - { - // lwr,upr == IntegerExp - static assert((arr[4 .. 8 ]).stringof == "arr[4..8]"); - static assert((arr[4U .. 8U ]).stringof == "arr[4..8]"); - static assert((arr[4L .. 8L ]).stringof == "arr[4..8]"); - static assert((arr[4LU .. 8LU]).stringof == "arr[4..8]"); - - // lwr,upr == UAddExp - static assert((arr[+4 .. +8 ]).stringof == "arr[4..8]"); - static assert((arr[+4U .. +8U ]).stringof == "arr[4..8]"); - static assert((arr[+4L .. +8L ]).stringof == "arr[4..8]"); - static assert((arr[+4LU .. +8LU]).stringof == "arr[4..8]"); - } -} diff --git a/gcc/testsuite/gdc.test/compilable/testdefines.d b/gcc/testsuite/gdc.test/compilable/testdefines.d index 4507266c751..9dd8cf2af8d 100644 --- a/gcc/testsuite/gdc.test/compilable/testdefines.d +++ b/gcc/testsuite/gdc.test/compilable/testdefines.d @@ -12,3 +12,6 @@ static assert(F64 == 8.0); static assert(F80 == 9.0L); static assert(SSS == "hello"); + +static assert(ABC == 12); +static assert(DEF == 17); diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19890a.d b/gcc/testsuite/gdc.test/fail_compilation/fail19890a.d index d4da11604fa..9b984961f33 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail19890a.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail19890a.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail19890a.d(8): Error: `void[$n$$?:64=LU$]` size 1 * $n$ exceeds $?:windows+32=0x1000000|0x7fffffff$ size limit for static array +fail_compilation/fail19890a.d(8): Error: `void[$n$$?:64=LU$]` size 1 * $n$ exceeds $?:windows+32omf=0x1000000|0x7fffffff$ size limit for static array --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19890b.d b/gcc/testsuite/gdc.test/fail_compilation/fail19890b.d index f4a5dada2c8..19081d95871 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail19890b.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail19890b.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail19890b.d(8): Error: `void[$n$$?:64=LU$]` size 1 * $n$ exceeds $?:windows+32=0x1000000|0x7fffffff$ size limit for static array +fail_compilation/fail19890b.d(8): Error: `void[$n$$?:64=LU$]` size 1 * $n$ exceeds $?:windows+32omf=0x1000000|0x7fffffff$ size limit for static array --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4611.d b/gcc/testsuite/gdc.test/fail_compilation/fail4611.d index 030c47c69ca..04adf13f5e7 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail4611.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail4611.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail4611.d(15): Error: `Vec[$n$]` size 4 * $n$ exceeds $?:windows+32=0x1000000|0x7fffffff$ size limit for static array +fail_compilation/fail4611.d(15): Error: `Vec[$n$]` size 4 * $n$ exceeds $?:windows+32omf=0x1000000|0x7fffffff$ size limit for static array --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/pragmas.d b/gcc/testsuite/gdc.test/fail_compilation/pragmas.d index 5a4b5d95d07..d967ab5451b 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/pragmas.d +++ b/gcc/testsuite/gdc.test/fail_compilation/pragmas.d @@ -5,7 +5,6 @@ TEST_OUTPUT: --- fail_compilation/pragmas.d(103): Error: one boolean expression expected for `pragma(inline)`, not 2 fail_compilation/pragmas.d(108): Error: one boolean expression expected for `pragma(inline)`, not 2 -fail_compilation/pragmas.d(118): Error: unrecognized `pragma(unrecognized)` --- */ @@ -28,5 +27,5 @@ void test3() void test4() { - pragma(unrecognized, "string"); + pragma(unrecognized, "string"); // permitted, just ignored } diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE index 5edcee1c84d..fa7004b7a41 100644 --- a/libphobos/libdruntime/MERGE +++ b/libphobos/libdruntime/MERGE @@ -1,4 +1,4 @@ -2bbf64907cbbb483d003e0a8fcf8b502e4883799 +f1a045928e03239b9477f9497f43f2cf0e61e959 The first line of this file holds the git revision number of the last merge done from the dlang/dmd repository. diff --git a/libphobos/libdruntime/Makefile.am b/libphobos/libdruntime/Makefile.am index 5e4c5ac0260..3ef98dcc737 100644 --- a/libphobos/libdruntime/Makefile.am +++ b/libphobos/libdruntime/Makefile.am @@ -202,26 +202,26 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \ core/stdc/ctype.d core/stdc/errno.d core/stdc/fenv.d \ core/stdc/float_.d core/stdc/inttypes.d core/stdc/limits.d \ core/stdc/locale.d core/stdc/math.d core/stdc/signal.d \ - core/stdc/stdarg.d core/stdc/stddef.d core/stdc/stdint.d \ - core/stdc/stdio.d core/stdc/stdlib.d core/stdc/string.d \ - core/stdc/tgmath.d core/stdc/time.d core/stdc/wchar_.d \ - core/stdc/wctype.d core/sync/barrier.d core/sync/condition.d \ - core/sync/config.d core/sync/event.d core/sync/exception.d \ - core/sync/mutex.d core/sync/package.d core/sync/rwmutex.d \ - core/sync/semaphore.d core/thread/context.d core/thread/fiber.d \ - core/thread/osthread.d core/thread/package.d core/thread/threadbase.d \ - core/thread/threadgroup.d core/thread/types.d core/time.d \ - core/vararg.d core/volatile.d etc/valgrind/valgrind.d gcc/attribute.d \ - gcc/attributes.d gcc/backtrace.d gcc/builtins.d gcc/deh.d gcc/emutls.d \ - gcc/gthread.d gcc/sections/common.d gcc/sections/elf.d \ - gcc/sections/macho.d gcc/sections/package.d gcc/sections/pecoff.d \ - gcc/simd.d gcc/unwind/arm.d gcc/unwind/arm_common.d gcc/unwind/c6x.d \ - gcc/unwind/generic.d gcc/unwind/package.d gcc/unwind/pe.d object.d \ - rt/aApply.d rt/aApplyR.d rt/aaA.d rt/adi.d rt/arraycat.d rt/cast_.d \ - rt/config.d rt/critical_.d rt/deh.d rt/dmain2.d rt/ehalloc.d \ - rt/invariant.d rt/lifetime.d rt/memory.d rt/minfo.d rt/monitor_.d \ - rt/profilegc.d rt/sections.d rt/tlsgc.d rt/util/typeinfo.d \ - rt/util/utility.d + core/stdc/stdarg.d core/stdc/stdatomic.d core/stdc/stddef.d \ + core/stdc/stdint.d core/stdc/stdio.d core/stdc/stdlib.d \ + core/stdc/string.d core/stdc/tgmath.d core/stdc/time.d \ + core/stdc/wchar_.d core/stdc/wctype.d core/sync/barrier.d \ + core/sync/condition.d core/sync/config.d core/sync/event.d \ + core/sync/exception.d core/sync/mutex.d core/sync/package.d \ + core/sync/rwmutex.d core/sync/semaphore.d core/thread/context.d \ + core/thread/fiber.d core/thread/osthread.d core/thread/package.d \ + core/thread/threadbase.d core/thread/threadgroup.d core/thread/types.d \ + core/time.d core/vararg.d core/volatile.d etc/valgrind/valgrind.d \ + gcc/attribute.d gcc/attributes.d gcc/backtrace.d gcc/builtins.d \ + gcc/deh.d gcc/emutls.d gcc/gthread.d gcc/sections/common.d \ + gcc/sections/elf.d gcc/sections/macho.d gcc/sections/package.d \ + gcc/sections/pecoff.d gcc/simd.d gcc/unwind/arm.d \ + gcc/unwind/arm_common.d gcc/unwind/c6x.d gcc/unwind/generic.d \ + gcc/unwind/package.d gcc/unwind/pe.d object.d rt/aApply.d rt/aApplyR.d \ + rt/aaA.d rt/adi.d rt/arraycat.d rt/cast_.d rt/config.d rt/critical_.d \ + rt/deh.d rt/dmain2.d rt/ehalloc.d rt/invariant.d rt/lifetime.d \ + rt/memory.d rt/minfo.d rt/monitor_.d rt/profilegc.d rt/sections.d \ + rt/tlsgc.d rt/util/typeinfo.d rt/util/utility.d DRUNTIME_DSOURCES_STDCXX = core/stdcpp/allocator.d core/stdcpp/array.d \ core/stdcpp/exception.d core/stdcpp/memory.d core/stdcpp/new_.d \ diff --git a/libphobos/libdruntime/Makefile.in b/libphobos/libdruntime/Makefile.in index 9c29e203467..1d2bd66817d 100644 --- a/libphobos/libdruntime/Makefile.in +++ b/libphobos/libdruntime/Makefile.in @@ -225,23 +225,24 @@ am__objects_1 = core/atomic.lo core/attribute.lo core/bitop.lo \ core/stdc/config.lo core/stdc/ctype.lo core/stdc/errno.lo \ core/stdc/fenv.lo core/stdc/float_.lo core/stdc/inttypes.lo \ core/stdc/limits.lo core/stdc/locale.lo core/stdc/math.lo \ - core/stdc/signal.lo core/stdc/stdarg.lo core/stdc/stddef.lo \ - core/stdc/stdint.lo core/stdc/stdio.lo core/stdc/stdlib.lo \ - core/stdc/string.lo core/stdc/tgmath.lo core/stdc/time.lo \ - core/stdc/wchar_.lo core/stdc/wctype.lo core/sync/barrier.lo \ - core/sync/condition.lo core/sync/config.lo core/sync/event.lo \ - core/sync/exception.lo core/sync/mutex.lo core/sync/package.lo \ - core/sync/rwmutex.lo core/sync/semaphore.lo \ - core/thread/context.lo core/thread/fiber.lo \ - core/thread/osthread.lo core/thread/package.lo \ - core/thread/threadbase.lo core/thread/threadgroup.lo \ - core/thread/types.lo core/time.lo core/vararg.lo \ - core/volatile.lo etc/valgrind/valgrind.lo gcc/attribute.lo \ - gcc/attributes.lo gcc/backtrace.lo gcc/builtins.lo gcc/deh.lo \ - gcc/emutls.lo gcc/gthread.lo gcc/sections/common.lo \ - gcc/sections/elf.lo gcc/sections/macho.lo \ - gcc/sections/package.lo gcc/sections/pecoff.lo gcc/simd.lo \ - gcc/unwind/arm.lo gcc/unwind/arm_common.lo gcc/unwind/c6x.lo \ + core/stdc/signal.lo core/stdc/stdarg.lo core/stdc/stdatomic.lo \ + core/stdc/stddef.lo core/stdc/stdint.lo core/stdc/stdio.lo \ + core/stdc/stdlib.lo core/stdc/string.lo core/stdc/tgmath.lo \ + core/stdc/time.lo core/stdc/wchar_.lo core/stdc/wctype.lo \ + core/sync/barrier.lo core/sync/condition.lo \ + core/sync/config.lo core/sync/event.lo core/sync/exception.lo \ + core/sync/mutex.lo core/sync/package.lo core/sync/rwmutex.lo \ + core/sync/semaphore.lo core/thread/context.lo \ + core/thread/fiber.lo core/thread/osthread.lo \ + core/thread/package.lo core/thread/threadbase.lo \ + core/thread/threadgroup.lo core/thread/types.lo core/time.lo \ + core/vararg.lo core/volatile.lo etc/valgrind/valgrind.lo \ + gcc/attribute.lo gcc/attributes.lo gcc/backtrace.lo \ + gcc/builtins.lo gcc/deh.lo gcc/emutls.lo gcc/gthread.lo \ + gcc/sections/common.lo gcc/sections/elf.lo \ + gcc/sections/macho.lo gcc/sections/package.lo \ + gcc/sections/pecoff.lo gcc/simd.lo gcc/unwind/arm.lo \ + gcc/unwind/arm_common.lo gcc/unwind/c6x.lo \ gcc/unwind/generic.lo gcc/unwind/package.lo gcc/unwind/pe.lo \ object.lo rt/aApply.lo rt/aApplyR.lo rt/aaA.lo rt/adi.lo \ rt/arraycat.lo rt/cast_.lo rt/config.lo rt/critical_.lo \ @@ -879,26 +880,26 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \ core/stdc/ctype.d core/stdc/errno.d core/stdc/fenv.d \ core/stdc/float_.d core/stdc/inttypes.d core/stdc/limits.d \ core/stdc/locale.d core/stdc/math.d core/stdc/signal.d \ - core/stdc/stdarg.d core/stdc/stddef.d core/stdc/stdint.d \ - core/stdc/stdio.d core/stdc/stdlib.d core/stdc/string.d \ - core/stdc/tgmath.d core/stdc/time.d core/stdc/wchar_.d \ - core/stdc/wctype.d core/sync/barrier.d core/sync/condition.d \ - core/sync/config.d core/sync/event.d core/sync/exception.d \ - core/sync/mutex.d core/sync/package.d core/sync/rwmutex.d \ - core/sync/semaphore.d core/thread/context.d core/thread/fiber.d \ - core/thread/osthread.d core/thread/package.d core/thread/threadbase.d \ - core/thread/threadgroup.d core/thread/types.d core/time.d \ - core/vararg.d core/volatile.d etc/valgrind/valgrind.d gcc/attribute.d \ - gcc/attributes.d gcc/backtrace.d gcc/builtins.d gcc/deh.d gcc/emutls.d \ - gcc/gthread.d gcc/sections/common.d gcc/sections/elf.d \ - gcc/sections/macho.d gcc/sections/package.d gcc/sections/pecoff.d \ - gcc/simd.d gcc/unwind/arm.d gcc/unwind/arm_common.d gcc/unwind/c6x.d \ - gcc/unwind/generic.d gcc/unwind/package.d gcc/unwind/pe.d object.d \ - rt/aApply.d rt/aApplyR.d rt/aaA.d rt/adi.d rt/arraycat.d rt/cast_.d \ - rt/config.d rt/critical_.d rt/deh.d rt/dmain2.d rt/ehalloc.d \ - rt/invariant.d rt/lifetime.d rt/memory.d rt/minfo.d rt/monitor_.d \ - rt/profilegc.d rt/sections.d rt/tlsgc.d rt/util/typeinfo.d \ - rt/util/utility.d + core/stdc/stdarg.d core/stdc/stdatomic.d core/stdc/stddef.d \ + core/stdc/stdint.d core/stdc/stdio.d core/stdc/stdlib.d \ + core/stdc/string.d core/stdc/tgmath.d core/stdc/time.d \ + core/stdc/wchar_.d core/stdc/wctype.d core/sync/barrier.d \ + core/sync/condition.d core/sync/config.d core/sync/event.d \ + core/sync/exception.d core/sync/mutex.d core/sync/package.d \ + core/sync/rwmutex.d core/sync/semaphore.d core/thread/context.d \ + core/thread/fiber.d core/thread/osthread.d core/thread/package.d \ + core/thread/threadbase.d core/thread/threadgroup.d core/thread/types.d \ + core/time.d core/vararg.d core/volatile.d etc/valgrind/valgrind.d \ + gcc/attribute.d gcc/attributes.d gcc/backtrace.d gcc/builtins.d \ + gcc/deh.d gcc/emutls.d gcc/gthread.d gcc/sections/common.d \ + gcc/sections/elf.d gcc/sections/macho.d gcc/sections/package.d \ + gcc/sections/pecoff.d gcc/simd.d gcc/unwind/arm.d \ + gcc/unwind/arm_common.d gcc/unwind/c6x.d gcc/unwind/generic.d \ + gcc/unwind/package.d gcc/unwind/pe.d object.d rt/aApply.d rt/aApplyR.d \ + rt/aaA.d rt/adi.d rt/arraycat.d rt/cast_.d rt/config.d rt/critical_.d \ + rt/deh.d rt/dmain2.d rt/ehalloc.d rt/invariant.d rt/lifetime.d \ + rt/memory.d rt/minfo.d rt/monitor_.d rt/profilegc.d rt/sections.d \ + rt/tlsgc.d rt/util/typeinfo.d rt/util/utility.d DRUNTIME_DSOURCES_STDCXX = core/stdcpp/allocator.d core/stdcpp/array.d \ core/stdcpp/exception.d core/stdcpp/memory.d core/stdcpp/new_.d \ @@ -1316,6 +1317,7 @@ core/stdc/locale.lo: core/stdc/$(am__dirstamp) core/stdc/math.lo: core/stdc/$(am__dirstamp) core/stdc/signal.lo: core/stdc/$(am__dirstamp) core/stdc/stdarg.lo: core/stdc/$(am__dirstamp) +core/stdc/stdatomic.lo: core/stdc/$(am__dirstamp) core/stdc/stddef.lo: core/stdc/$(am__dirstamp) core/stdc/stdint.lo: core/stdc/$(am__dirstamp) core/stdc/stdio.lo: core/stdc/$(am__dirstamp) diff --git a/libphobos/libdruntime/core/internal/array/operations.d b/libphobos/libdruntime/core/internal/array/operations.d index 3e2331484b3..7e5b5f43e9b 100644 --- a/libphobos/libdruntime/core/internal/array/operations.d +++ b/libphobos/libdruntime/core/internal/array/operations.d @@ -33,7 +33,7 @@ version (LDC) version = GNU_OR_LDC; * * Returns: the slice containing the result */ -T[] arrayOp(T : T[], Args...)(T[] res, Filter!(isType, Args) args) @trusted @nogc pure nothrow +T[] arrayOp(T : T[], Args...)(T[] res, Filter!(isType, Args) args) @trusted { alias scalarizedExp = staticMap!(toElementType, Args); alias check = typeCheck!(true, T, scalarizedExp); // must support all scalar ops @@ -541,7 +541,7 @@ unittest } // test handling of v op= exp -unittest +@nogc nothrow pure @safe unittest { uint[32] c; arrayOp!(uint[], uint, "+=")(c[], 2); @@ -556,7 +556,7 @@ unittest } // proper error message for UDT lacking certain ops -unittest +@nogc nothrow pure @safe unittest { static assert(!is(typeof(&arrayOp!(int[4][], int[4], "+=")))); static assert(!is(typeof(&arrayOp!(int[4][], int[4], "u-", "=")))); @@ -585,7 +585,7 @@ unittest } // test mixed type array op -unittest +@nogc nothrow pure @safe unittest { uint[32] a = 0xF; float[32] res = 2.0f; @@ -595,7 +595,7 @@ unittest } // test mixed type array op -unittest +@nogc nothrow pure @safe unittest { static struct S { @@ -613,7 +613,7 @@ unittest } // test scalar after operation argument -unittest +@nogc nothrow pure @safe unittest { float[32] res, a = 2, b = 3; float c = 4; @@ -622,7 +622,7 @@ unittest assert(v == 2 * 3 + 4); } -unittest +@nogc nothrow pure @safe unittest { // https://issues.dlang.org/show_bug.cgi?id=17964 uint bug(){ @@ -635,7 +635,7 @@ unittest } // https://issues.dlang.org/show_bug.cgi?id=19796 -unittest +nothrow pure @safe unittest { double[] data = [0.5]; double[] result; @@ -645,7 +645,7 @@ unittest } // https://issues.dlang.org/show_bug.cgi?id=21110 -unittest +pure unittest { import core.exception; @@ -668,3 +668,20 @@ unittest void func() { dst[] = a[] + b[]; } assertThrown!AssertError(func(), "Array operations with mismatched lengths must throw an error"); } + +// https://issues.dlang.org/show_bug.cgi?id=24272 +unittest +{ + static struct B + { + B opOpAssign(string op)(B other) + { + static int g; + g++; + throw new Exception(""); + } + } + + B[] bArr; + bArr[] += B(); +} diff --git a/libphobos/libdruntime/core/internal/atomic.d b/libphobos/libdruntime/core/internal/atomic.d index eebf94ee29d..3fd5d4a595d 100644 --- a/libphobos/libdruntime/core/internal/atomic.d +++ b/libphobos/libdruntime/core/internal/atomic.d @@ -49,6 +49,8 @@ version (DigitalMars) enum SizedReg(int reg, T = size_t) = registerNames[reg][RegIndex!T]; } + enum IsAtomicLockFree(T) = T.sizeof <= size_t.sizeof * 2; + inout(T) atomicLoad(MemoryOrder order = MemoryOrder.seq, T)(inout(T)* src) pure nothrow @nogc @trusted if (CanCAS!T) { @@ -649,6 +651,11 @@ version (DigitalMars) } } + void atomicSignalFence(MemoryOrder order = MemoryOrder.seq)() pure nothrow @nogc @trusted + { + // no-op, dmd doesn't reorder instructions + } + void pause() pure nothrow @nogc @trusted { version (D_InlineAsm_X86) @@ -681,37 +688,57 @@ else version (GNU) import gcc.builtins; import gcc.config; + // Targets where MemoryOrder.acq_rel is sufficiently cheaper than using + // MemoryOrder.seq, used when the MemoryOrder requested is not valid for + // a given atomic operation. + version (IA64) + private enum PreferAcquireRelease = true; + else version (PPC) + private enum PreferAcquireRelease = true; + else version (PPC64) + private enum PreferAcquireRelease = true; + else + private enum PreferAcquireRelease = false; + + enum IsAtomicLockFree(T) = __atomic_is_lock_free(T.sizeof, null); + inout(T) atomicLoad(MemoryOrder order = MemoryOrder.seq, T)(inout(T)* src) pure nothrow @nogc @trusted if (CanCAS!T) { + // MemoryOrder.rel and MemoryOrder.acq_rel are not valid for load. static assert(order != MemoryOrder.rel, "invalid MemoryOrder for atomicLoad()"); + static if (order == MemoryOrder.acq_rel) + enum smodel = PreferAcquireRelease ? MemoryOrder.acq : MemoryOrder.seq; + else + enum smodel = order; + static if (GNU_Have_Atomics || GNU_Have_LibAtomic) { static if (T.sizeof == ubyte.sizeof) { - ubyte value = __atomic_load_1(cast(shared)src, order); + ubyte value = __atomic_load_1(cast(shared)src, smodel); return *cast(typeof(return)*)&value; } else static if (T.sizeof == ushort.sizeof) { - ushort value = __atomic_load_2(cast(shared)src, order); + ushort value = __atomic_load_2(cast(shared)src, smodel); return *cast(typeof(return)*)&value; } else static if (T.sizeof == uint.sizeof) { - uint value = __atomic_load_4(cast(shared)src, order); + uint value = __atomic_load_4(cast(shared)src, smodel); return *cast(typeof(return)*)&value; } else static if (T.sizeof == ulong.sizeof && GNU_Have_64Bit_Atomics) { - ulong value = __atomic_load_8(cast(shared)src, order); + ulong value = __atomic_load_8(cast(shared)src, smodel); return *cast(typeof(return)*)&value; } else static if (GNU_Have_LibAtomic) { T value; - __atomic_load(T.sizeof, cast(shared)src, &value, order); + __atomic_load(T.sizeof, cast(shared)src, &value, smodel); return *cast(typeof(return)*)&value; } else @@ -728,20 +755,26 @@ else version (GNU) void atomicStore(MemoryOrder order = MemoryOrder.seq, T)(T* dest, T value) pure nothrow @nogc @trusted if (CanCAS!T) { + // MemoryOrder.acq and MemoryOrder.acq_rel are not valid for store. static assert(order != MemoryOrder.acq, "Invalid MemoryOrder for atomicStore()"); + static if (order == MemoryOrder.acq_rel) + enum smodel = PreferAcquireRelease ? MemoryOrder.rel : MemoryOrder.seq; + else + enum smodel = order; + static if (GNU_Have_Atomics || GNU_Have_LibAtomic) { static if (T.sizeof == ubyte.sizeof) - __atomic_store_1(cast(shared)dest, *cast(ubyte*)&value, order); + __atomic_store_1(cast(shared)dest, *cast(ubyte*)&value, smodel); else static if (T.sizeof == ushort.sizeof) - __atomic_store_2(cast(shared)dest, *cast(ushort*)&value, order); + __atomic_store_2(cast(shared)dest, *cast(ushort*)&value, smodel); else static if (T.sizeof == uint.sizeof) - __atomic_store_4(cast(shared)dest, *cast(uint*)&value, order); + __atomic_store_4(cast(shared)dest, *cast(uint*)&value, smodel); else static if (T.sizeof == ulong.sizeof && GNU_Have_64Bit_Atomics) - __atomic_store_8(cast(shared)dest, *cast(ulong*)&value, order); + __atomic_store_8(cast(shared)dest, *cast(ulong*)&value, smodel); else static if (GNU_Have_LibAtomic) - __atomic_store(T.sizeof, cast(shared)dest, cast(void*)&value, order); + __atomic_store(T.sizeof, cast(shared)dest, cast(void*)&value, smodel); else static assert(0, "Invalid template type specified."); } @@ -814,30 +847,36 @@ else version (GNU) { static if (GNU_Have_Atomics || GNU_Have_LibAtomic) { + // MemoryOrder.acq is not valid for exchange. + static if (order == MemoryOrder.acq) + enum smodel = PreferAcquireRelease ? MemoryOrder.acq_rel : MemoryOrder.seq; + else + enum smodel = order; + static if (T.sizeof == byte.sizeof) { - ubyte res = __atomic_exchange_1(cast(shared)dest, *cast(ubyte*)&value, order); + ubyte res = __atomic_exchange_1(cast(shared)dest, *cast(ubyte*)&value, smodel); return *cast(typeof(return)*)&res; } else static if (T.sizeof == short.sizeof) { - ushort res = __atomic_exchange_2(cast(shared)dest, *cast(ushort*)&value, order); + ushort res = __atomic_exchange_2(cast(shared)dest, *cast(ushort*)&value, smodel); return *cast(typeof(return)*)&res; } else static if (T.sizeof == int.sizeof) { - uint res = __atomic_exchange_4(cast(shared)dest, *cast(uint*)&value, order); + uint res = __atomic_exchange_4(cast(shared)dest, *cast(uint*)&value, smodel); return *cast(typeof(return)*)&res; } else static if (T.sizeof == long.sizeof && GNU_Have_64Bit_Atomics) { - ulong res = __atomic_exchange_8(cast(shared)dest, *cast(ulong*)&value, order); + ulong res = __atomic_exchange_8(cast(shared)dest, *cast(ulong*)&value, smodel); return *cast(typeof(return)*)&res; } else static if (GNU_Have_LibAtomic) { T res = void; - __atomic_exchange(T.sizeof, cast(shared)dest, cast(void*)&value, &res, order); + __atomic_exchange(T.sizeof, cast(shared)dest, cast(void*)&value, &res, smodel); return res; } else @@ -885,21 +924,42 @@ else version (GNU) static if (GNU_Have_Atomics || GNU_Have_LibAtomic) { + static if (fail == MemoryOrder.rel || fail == MemoryOrder.acq_rel) + { + // MemoryOrder.rel and MemoryOrder.acq_rel are not valid failure models. + enum smodel = (succ != MemoryOrder.seq && PreferAcquireRelease) + ? MemoryOrder.acq_rel : MemoryOrder.seq; + enum fmodel = (succ != MemoryOrder.seq && PreferAcquireRelease) + ? MemoryOrder.raw : MemoryOrder.seq; + } + else static if (fail > succ) + { + // Failure memory model cannot be stronger than success. + enum smodel = (fail != MemoryOrder.seq && PreferAcquireRelease) + ? MemoryOrder.acq_rel : MemoryOrder.seq; + enum fmodel = fail; + } + else + { + enum smodel = succ; + enum fmodel = fail; + } + static if (T.sizeof == byte.sizeof) res = __atomic_compare_exchange_1(cast(shared)dest, compare, *cast(ubyte*)&value, - weak, succ, fail); + weak, smodel, fmodel); else static if (T.sizeof == short.sizeof) res = __atomic_compare_exchange_2(cast(shared)dest, compare, *cast(ushort*)&value, - weak, succ, fail); + weak, smodel, fmodel); else static if (T.sizeof == int.sizeof) res = __atomic_compare_exchange_4(cast(shared)dest, compare, *cast(uint*)&value, - weak, succ, fail); + weak, smodel, fmodel); else static if (T.sizeof == long.sizeof && GNU_Have_64Bit_Atomics) res = __atomic_compare_exchange_8(cast(shared)dest, compare, *cast(ulong*)&value, - weak, succ, fail); + weak, smodel, fmodel); else static if (GNU_Have_LibAtomic) res = __atomic_compare_exchange(T.sizeof, cast(shared)dest, compare, cast(void*)&value, - succ, fail); + smodel, fmodel); else static assert(0, "Invalid template type specified."); } @@ -945,6 +1005,11 @@ else version (GNU) } } + void atomicSignalFence(MemoryOrder order = MemoryOrder.seq)() pure nothrow @nogc @trusted + { + __atomic_signal_fence(order); + } + void pause() pure nothrow @nogc @trusted { version (X86) diff --git a/libphobos/libdruntime/core/stdc/stdatomic.d b/libphobos/libdruntime/core/stdc/stdatomic.d new file mode 100644 index 00000000000..ae17e040da7 --- /dev/null +++ b/libphobos/libdruntime/core/stdc/stdatomic.d @@ -0,0 +1,1124 @@ +/** + * A D implementation of the C stdatomic.h header. + * + * $(NOTE If it compiles it should produce similar assembly to the system C toolchain + * and should not introduce when optimizing unnecessary behaviors, + * if you do not care about this guarantee use the _impl suffix.) + * + * $(NOTE The D shared type qualifier is the closest to the _Atomic type qualifier from C. It may be changed from shared in the future.) + * + * Copyright: Copyright Richard (Rikki) Andrew Cattermole 2023. + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Authors: Richard (Rikki) Andrew cattermole + * Source: $(DRUNTIMESRC core/stdc/stdatomic.d) + */ +module core.stdc.stdatomic; +import core.atomic : MemoryOrder; +import core.internal.atomic; +import core.stdc.config; +import core.stdc.stdint; + +@safe nothrow @nogc: + +/// +enum memory_order +{ + /// No ordering provided + memory_order_relaxed = MemoryOrder.raw, + /// As per cppreference.com circa 2015 no compiler supports consume memory order and in practice it devolves to acquire. + memory_order_consume = MemoryOrder.acq, + /// Prevent reordering before operation + memory_order_acquire = MemoryOrder.acq, + /// Prevent reordering after operation + memory_order_release = MemoryOrder.rel, + /// Prevent reordering before and after operation + memory_order_acq_rel = MemoryOrder.acq_rel, + /// Prevent reordering before for read operations and after for writes. + memory_order_seq_cst = MemoryOrder.seq +} + +/// +enum +{ + /// + __STDC_VERSION_STDATOMIC_H__ = 202311, + + /// + ATOMIC_BOOL_LOCK_FREE = IsAtomicLockFree!bool ? 2 : 0, + /// + ATOMIC_CHAR_LOCK_FREE = IsAtomicLockFree!char ? 2 : 0, + /// + ATOMIC_CHAR16_T_LOCK_FREE = IsAtomicLockFree!wchar ? 2 : 0, + /// + ATOMIC_CHAR32_T_LOCK_FREE = IsAtomicLockFree!dchar ? 2 : 0, + /// + ATOMIC_WCHAR_T_LOCK_FREE = ATOMIC_CHAR16_T_LOCK_FREE, + /// + ATOMIC_SHORT_LOCK_FREE = IsAtomicLockFree!short ? 2 : 0, + /// + ATOMIC_INT_LOCK_FREE = IsAtomicLockFree!int ? 2 : 0, + /// + ATOMIC_LONG_LOCK_FREE = IsAtomicLockFree!c_long ? 2 : 0, + /// + ATOMIC_LLONG_LOCK_FREE = IsAtomicLockFree!ulong ? 2 : 0, + /// + ATOMIC_POINTER_LOCK_FREE = IsAtomicLockFree!(void*) ? 2 : 0, + /// + ATOMIC_CHAR8_T_LOCK_FREE = ATOMIC_CHAR_LOCK_FREE, +} + +version (DigitalMars) +{ + alias atomic_signal_fence = atomic_signal_fence_impl; /// + + // these all use inline assembly, so will unlikely produce the codegen a user will expect + version(none) + { + alias atomic_flag_clear = atomic_flag_clear_impl; /// + alias atomic_flag_clear_explicit = atomic_flag_clear_explicit_impl; /// + alias atomic_flag_test_and_set = atomic_flag_test_and_set_impl; /// + alias atomic_flag_test_and_set_explicit = atomic_flag_test_and_set_explicit_impl; /// + alias atomic_thread_fence = atomic_thread_fence_impl; /// + alias atomic_store = atomic_store_impl; /// + alias atomic_store_explicit = atomic_store_explicit_impl; /// + alias atomic_load = atomic_load_impl; /// + alias atomic_load_explicit = atomic_load_explicit_impl; /// + alias atomic_exchange = atomic_exchange_impl; /// + alias atomic_exchange_explicit = atomic_exchange_explicit_impl; /// + alias atomic_compare_exchange_strong = atomic_compare_exchange_strong_impl; /// + alias atomic_compare_exchange_weak = atomic_compare_exchange_weak_impl; /// + alias atomic_compare_exchange_strong_explicit = atomic_compare_exchange_strong_explicit_impl; /// + alias atomic_compare_exchange_weak_explicit = atomic_compare_exchange_weak_explicit_impl; /// + alias atomic_fetch_add = atomic_fetch_add_impl; /// + alias atomic_fetch_add_explicit = atomic_fetch_add_explicit_impl; /// + alias atomic_fetch_sub = atomic_fetch_sub_impl; /// + alias atomic_fetch_sub_explicit = atomic_fetch_sub_explicit_impl; /// + alias atomic_fetch_or = atomic_fetch_or_impl; /// + alias atomic_fetch_or_explicit = atomic_fetch_or_explicit_impl; /// + alias atomic_fetch_xor = atomic_fetch_xor_impl; /// + alias atomic_fetch_xor_explicit = atomic_fetch_xor_explicit_impl; /// + alias atomic_fetch_and = atomic_fetch_and_impl; /// + alias atomic_fetch_and_explicit = atomic_fetch_and_explicit_impl; /// + } +} +else version(GNU) +{ + alias atomic_flag_clear = atomic_flag_clear_impl; /// + alias atomic_flag_clear_explicit = atomic_flag_clear_explicit_impl; /// + alias atomic_flag_test_and_set = atomic_flag_test_and_set_impl; /// + alias atomic_flag_test_and_set_explicit = atomic_flag_test_and_set_explicit_impl; /// + alias atomic_signal_fence = atomic_signal_fence_impl; /// + alias atomic_thread_fence = atomic_thread_fence_impl; /// + alias atomic_store = atomic_store_impl; /// + alias atomic_store_explicit = atomic_store_explicit_impl; /// + alias atomic_load = atomic_load_impl; /// + alias atomic_load_explicit = atomic_load_explicit_impl; /// + alias atomic_exchange = atomic_exchange_impl; /// + alias atomic_exchange_explicit = atomic_exchange_explicit_impl; /// + alias atomic_compare_exchange_strong = atomic_compare_exchange_strong_impl; /// + alias atomic_compare_exchange_weak = atomic_compare_exchange_weak_impl; /// + alias atomic_compare_exchange_strong_explicit = atomic_compare_exchange_strong_explicit_impl; /// + alias atomic_compare_exchange_weak_explicit = atomic_compare_exchange_weak_explicit_impl; /// + alias atomic_fetch_add = atomic_fetch_add_impl; /// + alias atomic_fetch_add_explicit = atomic_fetch_add_explicit_impl; /// + alias atomic_fetch_sub = atomic_fetch_sub_impl; /// + alias atomic_fetch_sub_explicit = atomic_fetch_sub_explicit_impl; /// + alias atomic_fetch_or = atomic_fetch_or_impl; /// + alias atomic_fetch_or_explicit = atomic_fetch_or_explicit_impl; /// + alias atomic_fetch_xor = atomic_fetch_xor_impl; /// + alias atomic_fetch_xor_explicit = atomic_fetch_xor_explicit_impl; /// + alias atomic_fetch_and = atomic_fetch_and_impl; /// + alias atomic_fetch_and_explicit = atomic_fetch_and_explicit_impl; /// +} + +/// +pragma(inline, true) +bool atomic_is_lock_free(A)(const shared(A)* obj) +{ + return IsAtomicLockFree!A; +} + +/// Guaranteed to be a atomic boolean type +struct atomic_flag +{ + private bool b; +} + +/// +enum ATOMIC_FLAG_INIT = atomic_flag.init; + +/// +pragma(inline, true) +void atomic_flag_clear_impl()(atomic_flag* obj) +{ + assert(obj !is null); + + atomicStore(&obj.b, false); +} + +/// +pragma(inline, true) +void atomic_flag_clear_explicit_impl()(atomic_flag* obj, memory_order order) +{ + assert(obj !is null); + + final switch (order) + { + case memory_order.memory_order_relaxed: + atomicStore!(memory_order.memory_order_relaxed)(&obj.b, false); + break; + + case memory_order.memory_order_acquire: + // Ideally this would error at compile time but alas it is not an intrinsic. + // Note: this is not a valid memory order for this operation. + atomicStore!(memory_order.memory_order_seq_cst)(&obj.b, false); + break; + + case memory_order.memory_order_release: + atomicStore!(memory_order.memory_order_release)(&obj.b, false); + break; + + case memory_order.memory_order_acq_rel: + atomicStore!(memory_order.memory_order_acq_rel)(&obj.b, false); + break; + + case memory_order.memory_order_seq_cst: + atomicStore(&obj.b, false); + break; + } +} + +/// +pragma(inline, true) +bool atomic_flag_test_and_set_impl()(atomic_flag* obj) +{ + assert(obj !is null); + return atomicExchange(&obj.b, true); +} + +/// +unittest +{ + atomic_flag flag; + assert(!atomic_flag_test_and_set_impl(&flag)); + atomic_flag_clear_impl(&flag); +} + +/// +pragma(inline, true) +bool atomic_flag_test_and_set_explicit_impl()(atomic_flag* obj, memory_order order) +{ + assert(obj !is null); + + final switch (order) + { + case memory_order.memory_order_relaxed: + return atomicExchange!(memory_order.memory_order_relaxed)(&obj.b, true); + + case memory_order.memory_order_acquire: + return atomicExchange!(memory_order.memory_order_acquire)(&obj.b, true); + + case memory_order.memory_order_release: + return atomicExchange!(memory_order.memory_order_release)(&obj.b, true); + + case memory_order.memory_order_acq_rel: + return atomicExchange!(memory_order.memory_order_acq_rel)(&obj.b, true); + + case memory_order.memory_order_seq_cst: + return atomicExchange(&obj.b, true); + } +} + +/// +unittest +{ + atomic_flag flag; + assert(!atomic_flag_test_and_set_explicit_impl(&flag, memory_order.memory_order_seq_cst)); + atomic_flag_clear_explicit_impl(&flag, memory_order.memory_order_seq_cst); +} + +/** + * Initializes an atomic variable, the destination should not have any expression associated with it prior to this call. + * + * We use an out parameter instead of a pointer for destination in an attempt to communicate to the compiler that it initializers. + */ +pragma(inline, true) +void atomic_init(A, C)(out shared(A) obj, C desired) @trusted +{ + obj = cast(shared) desired; +} + +/// +unittest +{ + shared int val; + atomic_init(val, 2); +} + +/// No-op function, doesn't apply to D +pragma(inline, true) +A kill_dependency(A)(A y) @trusted +{ + return y; +} + +/// Don't allow reordering, does not emit any instructions. +pragma(inline, true) +void atomic_signal_fence_impl()(memory_order order) +{ + final switch (order) + { + case memory_order.memory_order_relaxed: + atomicSignalFence!(memory_order.memory_order_relaxed); + break; + + case memory_order.memory_order_acquire: + atomicSignalFence!(memory_order.memory_order_acquire); + break; + + case memory_order.memory_order_release: + atomicSignalFence!(memory_order.memory_order_release); + break; + + case memory_order.memory_order_acq_rel: + atomicSignalFence!(memory_order.memory_order_acq_rel); + break; + + case memory_order.memory_order_seq_cst: + atomicSignalFence!(memory_order.memory_order_seq_cst); + break; + } +} + +/// +unittest +{ + atomic_signal_fence_impl(memory_order.memory_order_seq_cst); +} + +/// Don't allow reordering, and emit a fence instruction. +pragma(inline, true) +void atomic_thread_fence_impl()(memory_order order) +{ + final switch (order) + { + case memory_order.memory_order_relaxed: + atomicFence!(memory_order.memory_order_relaxed); + break; + + case memory_order.memory_order_acquire: + atomicFence!(memory_order.memory_order_acquire); + break; + + case memory_order.memory_order_release: + atomicFence!(memory_order.memory_order_release); + break; + + case memory_order.memory_order_acq_rel: + atomicFence!(memory_order.memory_order_acq_rel); + break; + + case memory_order.memory_order_seq_cst: + atomicFence!(memory_order.memory_order_seq_cst); + break; + } +} + +/// +unittest +{ + atomic_thread_fence_impl(memory_order.memory_order_seq_cst); +} + +/// +alias atomic_bool = shared(bool); +/// +alias atomic_char = shared(char); +/// +alias atomic_schar = shared(byte); +/// +alias atomic_uchar = shared(ubyte); +/// +alias atomic_short = shared(short); +/// +alias atomic_ushort = shared(ushort); +/// +alias atomic_int = shared(int); +/// +alias atomic_uint = shared(uint); +/// +alias atomic_long = shared(c_long); +/// +alias atomic_ulong = shared(c_ulong); +/// +alias atomic_llong = shared(long); +/// +alias atomic_ullong = shared(ulong); +/// +alias atomic_char8_t = shared(char); +/// +alias atomic_char16_t = shared(wchar); +/// +alias atomic_char32_t = shared(dchar); +/// +alias atomic_wchar_t = shared(wchar); + +/// +alias atomic_int_least8_t = shared(int_least8_t); +/// +alias atomic_uint_least8_t = shared(uint_least8_t); +/// +alias atomic_int_least16_t = shared(int_least16_t); +/// +alias atomic_uint_least16_t = shared(uint_least16_t); +/// +alias atomic_int_least32_t = shared(int_least32_t); +/// +alias atomic_uint_least32_t = shared(uint_least32_t); +/// +alias atomic_int_least64_t = shared(int_least64_t); +/// +alias atomic_uint_least64_t = shared(uint_least64_t); +/// +alias atomic_int_fast8_t = shared(int_fast8_t); +/// +alias atomic_uint_fast8_t = shared(uint_fast8_t); +/// +alias atomic_int_fast16_t = shared(int_fast16_t); +/// +alias atomic_uint_fast16_t = shared(uint_fast16_t); +/// +alias atomic_int_fast32_t = shared(int_fast32_t); +/// +alias atomic_uint_fast32_t = shared(uint_fast32_t); +/// +alias atomic_int_fast64_t = shared(int_fast64_t); +/// +alias atomic_uint_fast64_t = shared(uint_fast64_t); +/// +alias atomic_intptr_t = shared(intptr_t); +/// +alias atomic_uintptr_t = shared(uintptr_t); +/// +alias atomic_size_t = shared(size_t); +/// +alias atomic_ptrdiff_t = shared(ptrdiff_t); +/// +alias atomic_intmax_t = shared(intmax_t); +/// +alias atomic_uintmax_t = shared(uintmax_t); + +/// +pragma(inline, true) +void atomic_store_impl(A, C)(shared(A)* obj, C desired) @trusted +{ + assert(obj !is null); + atomicStore(obj, cast(A)desired); +} + +/// +unittest +{ + shared(int) obj; + atomic_store_impl(&obj, 3); +} + +/// +pragma(inline, true) +void atomic_store_explicit_impl(A, C)(shared(A)* obj, C desired, memory_order order) @trusted +{ + assert(obj !is null); + + final switch (order) + { + case memory_order.memory_order_relaxed: + atomicStore!(memory_order.memory_order_relaxed)(obj, cast(A)desired); + break; + + case memory_order.memory_order_acquire: + // Ideally this would error at compile time but alas it is not an intrinsic. + // Note: this is not a valid memory order for this operation. + atomicStore!(memory_order.memory_order_release)(obj, cast(A)desired); + break; + + case memory_order.memory_order_release: + atomicStore!(memory_order.memory_order_release)(obj, cast(A)desired); + break; + + case memory_order.memory_order_acq_rel: + atomicStore!(memory_order.memory_order_acq_rel)(obj, cast(A)desired); + break; + + case memory_order.memory_order_seq_cst: + atomicStore!(memory_order.memory_order_seq_cst)(obj, cast(A)desired); + break; + } +} + +/// +unittest +{ + shared(int) obj; + atomic_store_explicit_impl(&obj, 3, memory_order.memory_order_seq_cst); +} + +/// +pragma(inline, true) +A atomic_load_impl(A)(const shared(A)* obj) @trusted +{ + assert(obj !is null); + return atomicLoad(cast(shared(A)*)obj); +} + +/// +unittest +{ + shared(int) obj = 3; + assert(atomic_load_impl(&obj) == 3); +} + +/// +pragma(inline, true) +A atomic_load_explicit_impl(A)(const shared(A)* obj, memory_order order) @trusted +{ + assert(obj !is null); + + final switch (order) + { + case memory_order.memory_order_relaxed: + return atomicLoad!(memory_order.memory_order_relaxed)(obj); + + case memory_order.memory_order_acquire: + return atomicLoad!(memory_order.memory_order_acquire)(obj); + + case memory_order.memory_order_release: + // Ideally this would error at compile time but alas it is not an intrinsic. + // Note: this is not a valid memory order for this operation. + return atomicLoad!(memory_order.memory_order_acquire)(obj); + + case memory_order.memory_order_acq_rel: + return atomicLoad!(memory_order.memory_order_acq_rel)(obj); + + case memory_order.memory_order_seq_cst: + return atomicLoad!(memory_order.memory_order_seq_cst)(obj); + } +} + +/// +unittest +{ + shared(int) obj = 3; + assert(atomic_load_explicit_impl(&obj, memory_order.memory_order_seq_cst) == 3); +} + +/// +pragma(inline, true) +A atomic_exchange_impl(A, C)(shared(A)* obj, C desired) @trusted +{ + assert(obj !is null); + return atomicExchange(cast(shared(A)*)obj, cast(A)desired); +} + +/// +unittest +{ + shared(int) obj = 3; + assert(atomic_exchange_impl(&obj, 2) == 3); +} + +/// +pragma(inline, true) +A atomic_exchange_explicit_impl(A, C)(shared(A)* obj, C desired, memory_order order) @trusted +{ + assert(obj !is null); + + final switch (order) + { + case memory_order.memory_order_relaxed: + return atomicExchange!(memory_order.memory_order_relaxed)(obj, cast(A)desired); + + case memory_order.memory_order_acquire: + return atomicExchange!(memory_order.memory_order_acquire)(obj, cast(A)desired); + + case memory_order.memory_order_release: + return atomicExchange!(memory_order.memory_order_release)(obj, cast(A)desired); + + case memory_order.memory_order_acq_rel: + return atomicExchange!(memory_order.memory_order_acq_rel)(obj, cast(A)desired); + + case memory_order.memory_order_seq_cst: + return atomicExchange!(memory_order.memory_order_seq_cst)(obj, cast(A)desired); + } +} + +/// +unittest +{ + shared(int) obj = 3; + assert(atomic_exchange_explicit_impl(&obj, 2, memory_order.memory_order_seq_cst) == 3); +} + +/// +pragma(inline, true) +bool atomic_compare_exchange_strong_impl(A, C)(shared(A)* obj, A* expected, C desired) @trusted +{ + return atomicCompareExchangeStrong(cast(A*)obj, expected, cast(A)desired); +} + +/// +unittest +{ + shared(int) obj = 3; + int expected = 3; + assert(atomic_compare_exchange_strong_impl(&obj, &expected, 2)); +} + +/// +pragma(inline, true) +bool atomic_compare_exchange_weak_impl(A, C)(shared(A)* obj, A* expected, C desired) @trusted +{ + return atomicCompareExchangeStrong(cast(A*)obj, expected, cast(A)desired); +} + +/// +unittest +{ + shared(int) obj = 3; + int expected = 3; + static assert(__traits(compiles, {atomic_compare_exchange_weak_impl(&obj, &expected, 2);})); +} + +/// +pragma(inline, true) +bool atomic_compare_exchange_strong_explicit_impl(A, C)(shared(A)* obj, A* expected, C desired, memory_order succ, memory_order fail) @trusted +{ + assert(obj !is null); + // We use these giant switch inside switch statements + // because as of 2023 they are capable of being for the most part inlined by gdc & ldc when using literal arguments for memory_order. + + final switch(succ) + { + case memory_order.memory_order_relaxed: + final switch(fail) + { + case memory_order.memory_order_relaxed: + return atomicCompareExchangeStrong!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_acquire: + return atomicCompareExchangeStrong!(memory_order.memory_order_relaxed, memory_order.memory_order_acquire)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_release: + return atomicCompareExchangeStrong!(memory_order.memory_order_relaxed, memory_order.memory_order_release)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_acq_rel: + return atomicCompareExchangeStrong!(memory_order.memory_order_relaxed, memory_order.memory_order_acq_rel)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_seq_cst: + return atomicCompareExchangeStrong!(memory_order.memory_order_relaxed, memory_order.memory_order_seq_cst)(cast(A*)obj, expected, cast(A)desired); + } + case memory_order.memory_order_acquire: + final switch(fail) + { + case memory_order.memory_order_relaxed: + return atomicCompareExchangeStrong!(memory_order.memory_order_acquire, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_acquire: + return atomicCompareExchangeStrong!(memory_order.memory_order_acquire, memory_order.memory_order_acquire)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_release: + return atomicCompareExchangeStrong!(memory_order.memory_order_acquire, memory_order.memory_order_release)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_acq_rel: + return atomicCompareExchangeStrong!(memory_order.memory_order_acquire, memory_order.memory_order_acq_rel)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_seq_cst: + return atomicCompareExchangeStrong!(memory_order.memory_order_acquire, memory_order.memory_order_seq_cst)(cast(A*)obj, expected, cast(A)desired); + } + case memory_order.memory_order_release: + final switch(fail) + { + case memory_order.memory_order_relaxed: + return atomicCompareExchangeStrong!(memory_order.memory_order_release, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_acquire: + return atomicCompareExchangeStrong!(memory_order.memory_order_release, memory_order.memory_order_acquire)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_release: + return atomicCompareExchangeStrong!(memory_order.memory_order_release, memory_order.memory_order_release)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_acq_rel: + return atomicCompareExchangeStrong!(memory_order.memory_order_release, memory_order.memory_order_acq_rel)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_seq_cst: + return atomicCompareExchangeStrong!(memory_order.memory_order_release, memory_order.memory_order_seq_cst)(cast(A*)obj, expected, cast(A)desired); + } + case memory_order.memory_order_acq_rel: + final switch(fail) + { + case memory_order.memory_order_relaxed: + return atomicCompareExchangeStrong!(memory_order.memory_order_acq_rel, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_acquire: + return atomicCompareExchangeStrong!(memory_order.memory_order_acq_rel, memory_order.memory_order_acquire)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_release: + return atomicCompareExchangeStrong!(memory_order.memory_order_acq_rel, memory_order.memory_order_release)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_acq_rel: + return atomicCompareExchangeStrong!(memory_order.memory_order_acq_rel, memory_order.memory_order_acq_rel)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_seq_cst: + return atomicCompareExchangeStrong!(memory_order.memory_order_acq_rel, memory_order.memory_order_seq_cst)(cast(A*)obj, expected, cast(A)desired); + } + case memory_order.memory_order_seq_cst: + final switch(fail) + { + case memory_order.memory_order_relaxed: + return atomicCompareExchangeStrong!(memory_order.memory_order_seq_cst, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_acquire: + return atomicCompareExchangeStrong!(memory_order.memory_order_seq_cst, memory_order.memory_order_acquire)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_release: + return atomicCompareExchangeStrong!(memory_order.memory_order_seq_cst, memory_order.memory_order_release)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_acq_rel: + return atomicCompareExchangeStrong!(memory_order.memory_order_seq_cst, memory_order.memory_order_acq_rel)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_seq_cst: + return atomicCompareExchangeStrong!(memory_order.memory_order_seq_cst, memory_order.memory_order_seq_cst)(cast(A*)obj, expected, cast(A)desired); + } + } +} + +/// +unittest +{ + shared(int) obj = 3; + int expected = 3; + assert(atomic_compare_exchange_strong_explicit_impl(&obj, &expected, 2, memory_order.memory_order_seq_cst, memory_order.memory_order_seq_cst)); +} + +/// +pragma(inline, true) +bool atomic_compare_exchange_weak_explicit_impl(A, C)(shared(A)* obj, A* expected, C desired, memory_order succ, memory_order fail) @trusted +{ + assert(obj !is null); + // We use these giant switch inside switch statements + // because as of 2023 they are capable of being for the most part inlined by gdc & ldc when using literal arguments for memory_order. + + final switch(succ) + { + case memory_order.memory_order_relaxed: + final switch(fail) + { + case memory_order.memory_order_relaxed: + return atomicCompareExchangeWeak!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_acquire: + return atomicCompareExchangeWeak!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_release: + return atomicCompareExchangeWeak!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_acq_rel: + return atomicCompareExchangeWeak!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_seq_cst: + return atomicCompareExchangeWeak!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired); + } + case memory_order.memory_order_acquire: + final switch(fail) + { + case memory_order.memory_order_relaxed: + return atomicCompareExchangeWeak!(memory_order.memory_order_acquire, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_acquire: + return atomicCompareExchangeWeak!(memory_order.memory_order_acquire, memory_order.memory_order_acquire)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_release: + return atomicCompareExchangeWeak!(memory_order.memory_order_acquire, memory_order.memory_order_release)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_acq_rel: + return atomicCompareExchangeWeak!(memory_order.memory_order_acquire, memory_order.memory_order_acq_rel)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_seq_cst: + return atomicCompareExchangeWeak!(memory_order.memory_order_acquire, memory_order.memory_order_seq_cst)(cast(A*)obj, expected, cast(A)desired); + } + case memory_order.memory_order_release: + final switch(fail) + { + case memory_order.memory_order_relaxed: + return atomicCompareExchangeWeak!(memory_order.memory_order_release, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_acquire: + return atomicCompareExchangeWeak!(memory_order.memory_order_release, memory_order.memory_order_acquire)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_release: + return atomicCompareExchangeWeak!(memory_order.memory_order_release, memory_order.memory_order_release)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_acq_rel: + return atomicCompareExchangeWeak!(memory_order.memory_order_release, memory_order.memory_order_acq_rel)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_seq_cst: + return atomicCompareExchangeWeak!(memory_order.memory_order_release, memory_order.memory_order_seq_cst)(cast(A*)obj, expected, cast(A)desired); + } + case memory_order.memory_order_acq_rel: + final switch(fail) + { + case memory_order.memory_order_relaxed: + return atomicCompareExchangeWeak!(memory_order.memory_order_acq_rel, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_acquire: + return atomicCompareExchangeWeak!(memory_order.memory_order_acq_rel, memory_order.memory_order_acquire)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_release: + return atomicCompareExchangeWeak!(memory_order.memory_order_acq_rel, memory_order.memory_order_release)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_acq_rel: + return atomicCompareExchangeWeak!(memory_order.memory_order_acq_rel, memory_order.memory_order_acq_rel)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_seq_cst: + return atomicCompareExchangeWeak!(memory_order.memory_order_acq_rel, memory_order.memory_order_seq_cst)(cast(A*)obj, expected, cast(A)desired); + } + case memory_order.memory_order_seq_cst: + final switch(fail) + { + case memory_order.memory_order_relaxed: + return atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_acquire: + return atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_acquire)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_release: + return atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_release)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_acq_rel: + return atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_acq_rel)(cast(A*)obj, expected, cast(A)desired); + case memory_order.memory_order_seq_cst: + return atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_seq_cst)(cast(A*)obj, expected, cast(A)desired); + } + } +} + +/// +unittest +{ + shared(int) obj = 3; + int expected = 3; + atomic_compare_exchange_weak_explicit_impl(&obj, &expected, 2, memory_order.memory_order_seq_cst, memory_order.memory_order_seq_cst); +} + +/// +pragma(inline, true) +A atomic_fetch_add_impl(A, M)(shared(A)* obj, M arg) @trusted +{ + assert(obj !is null); + return atomicFetchAdd(cast(A*)obj, arg); +} + +/// +unittest +{ + shared(int) val; + atomic_fetch_add_impl(&val, 3); + assert(atomic_load_impl(&val) == 3); +} + +pragma(inline, true) +A atomic_fetch_sub_impl(A, M)(shared(A)* obj, M arg) @trusted +{ + assert(obj !is null); + return atomicFetchSub(cast(A*)obj, arg); +} + +/// +unittest +{ + shared(int) val = 3; + atomic_fetch_sub_impl(&val, 3); + assert(atomic_load_impl(&val) == 0); +} + +/// +pragma(inline, true) +A atomic_fetch_add_explicit_impl(A, M)(shared(A)* obj, M arg, memory_order order) @trusted +{ + assert(obj !is null); + + final switch(order) + { + case memory_order.memory_order_relaxed: + return atomicFetchAdd!(memory_order.memory_order_relaxed)(cast(A*)obj, arg); + case memory_order.memory_order_acquire: + return atomicFetchAdd!(memory_order.memory_order_acquire)(cast(A*)obj, arg); + case memory_order.memory_order_release: + return atomicFetchAdd!(memory_order.memory_order_release)(cast(A*)obj, arg); + case memory_order.memory_order_acq_rel: + return atomicFetchAdd!(memory_order.memory_order_acq_rel)(cast(A*)obj, arg); + case memory_order.memory_order_seq_cst: + return atomicFetchAdd!(memory_order.memory_order_seq_cst)(cast(A*)obj, arg); + } +} + +/// +unittest +{ + shared(int) val; + atomic_fetch_add_explicit_impl(&val, 3, memory_order.memory_order_seq_cst); + assert(atomic_load_impl(&val) == 3); +} + +/// +pragma(inline, true) +A atomic_fetch_sub_explicit_impl(A, M)(shared(A)* obj, M arg, memory_order order) @trusted +{ + assert(obj !is null); + + final switch(order) + { + case memory_order.memory_order_relaxed: + return atomicFetchSub!(memory_order.memory_order_relaxed)(cast(A*)obj, arg); + case memory_order.memory_order_acquire: + return atomicFetchSub!(memory_order.memory_order_acquire)(cast(A*)obj, arg); + case memory_order.memory_order_release: + return atomicFetchSub!(memory_order.memory_order_release)(cast(A*)obj, arg); + case memory_order.memory_order_acq_rel: + return atomicFetchSub!(memory_order.memory_order_acq_rel)(cast(A*)obj, arg); + case memory_order.memory_order_seq_cst: + return atomicFetchSub!(memory_order.memory_order_seq_cst)(cast(A*)obj, arg); + } +} + +/// +unittest +{ + shared(int) val = 3; + atomic_fetch_sub_explicit_impl(&val, 3, memory_order.memory_order_seq_cst); + assert(atomic_load_impl(&val) == 0); +} + +/// +pragma(inline, true) +A atomic_fetch_or_impl(A, M)(shared(A)* obj, M arg) @trusted +{ + assert(obj !is null); + + // copied from atomicOp + + A set, get = atomicLoad(cast(A*)obj); + + do + { + set = get | arg; + } while (!atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_seq_cst)(cast(A*)obj, &get, cast(A)set)); + + return get; +} + +/// +unittest +{ + shared(int) val = 5; + atomic_fetch_or_impl(&val, 3); + assert(atomic_load_impl(&val) == 7); +} + +/// +pragma(inline, true) +A atomic_fetch_or_explicit_impl(A, M)(shared(A)* obj, M arg, memory_order order) @trusted +{ + assert(obj !is null); + + A set, get; + + final switch(order) + { + case memory_order.memory_order_relaxed: + get = atomicLoad!(memory_order.memory_order_relaxed)(cast(A*)obj); + do + { + set = get | arg; + } while (!atomicCompareExchangeWeak!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(A*)obj, &get, cast(A)set)); + break; + + case memory_order.memory_order_acquire: + get = atomicLoad!(memory_order.memory_order_acquire)(cast(A*)obj); + do + { + set = get | arg; + } while (!atomicCompareExchangeWeak!(memory_order.memory_order_acquire, memory_order.memory_order_acquire)(cast(A*)obj, &get, cast(A)set)); + break; + + case memory_order.memory_order_release: + get = atomicLoad!(memory_order.memory_order_relaxed)(cast(A*)obj); + do + { + set = get | arg; + } while (!atomicCompareExchangeWeak!(memory_order.memory_order_release, memory_order.memory_order_release)(cast(A*)obj, &get, cast(A)set)); + break; + + case memory_order.memory_order_acq_rel: + get = atomicLoad!(memory_order.memory_order_acq_rel)(cast(A*)obj); + do + { + set = get | arg; + } while (!atomicCompareExchangeWeak!(memory_order.memory_order_acq_rel, memory_order.memory_order_acq_rel)(cast(A*)obj, &get, cast(A)set)); + break; + + case memory_order.memory_order_seq_cst: + get = atomicLoad!(memory_order.memory_order_relaxed)(cast(A*)obj); + do + { + set = get | arg; + } while (!atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_seq_cst)(cast(A*)obj, &get, cast(A)set)); + break; + } + + return get; +} + +/// +unittest +{ + shared(int) val = 5; + atomic_fetch_or_explicit_impl(&val, 3, memory_order.memory_order_seq_cst); + assert(atomic_load_impl(&val) == 7); +} + +/// +pragma(inline, true) +A atomic_fetch_xor_impl(A, M)(shared(A)* obj, M arg) @trusted +{ + assert(obj !is null); + + // copied from atomicOp + + A set, get = atomicLoad(cast(A*)obj); + + do + { + set = get ^ arg; + } while (!atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_seq_cst)(cast(A*)obj, &get, cast(A)set)); + + return get; +} + +/// +unittest +{ + shared(int) val = 5; + atomic_fetch_xor_impl(&val, 3); + assert(atomic_load_impl(&val) == 6); +} + +/// +pragma(inline, true) +A atomic_fetch_xor_explicit_impl(A, M)(shared(A)* obj, M arg, memory_order order) @trusted +{ + assert(obj !is null); + + A set, get; + + final switch(order) + { + case memory_order.memory_order_relaxed: + get = atomicLoad!(memory_order.memory_order_relaxed)(cast(A*)obj); + do + { + set = get ^ arg; + } while (!atomicCompareExchangeWeak!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(A*)obj, &get, cast(A)set)); + break; + + case memory_order.memory_order_acquire: + get = atomicLoad!(memory_order.memory_order_acquire)(cast(A*)obj); + do + { + set = get ^ arg; + } while (!atomicCompareExchangeWeak!(memory_order.memory_order_acquire, memory_order.memory_order_acquire)(cast(A*)obj, &get, cast(A)set)); + break; + + case memory_order.memory_order_release: + get = atomicLoad!(memory_order.memory_order_relaxed)(cast(A*)obj); + do + { + set = get ^ arg; + } while (!atomicCompareExchangeWeak!(memory_order.memory_order_release, memory_order.memory_order_release)(cast(A*)obj, &get, cast(A)set)); + break; + + case memory_order.memory_order_acq_rel: + get = atomicLoad!(memory_order.memory_order_acq_rel)(cast(A*)obj); + do + { + set = get ^ arg; + } while (!atomicCompareExchangeWeak!(memory_order.memory_order_acq_rel, memory_order.memory_order_acq_rel)(cast(A*)obj, &get, cast(A)set)); + break; + + case memory_order.memory_order_seq_cst: + get = atomicLoad!(memory_order.memory_order_relaxed)(cast(A*)obj); + do + { + set = get ^ arg; + } while (!atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_seq_cst)(cast(A*)obj, &get, cast(A)set)); + break; + } + + return get; +} + +/// +unittest +{ + shared(int) val = 5; + atomic_fetch_xor_explicit_impl(&val, 3, memory_order.memory_order_seq_cst); + assert(atomic_load_impl(&val) == 6); +} + +/// +pragma(inline, true) +A atomic_fetch_and_impl(A, M)(shared(A)* obj, M arg) @trusted +{ + assert(obj !is null); + + // copied from atomicOp + + A set, get = atomicLoad(cast(A*)obj); + + do + { + set = get & arg; + } while (!atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_seq_cst)(cast(A*)obj, &get, cast(A)set)); + + return get; +} + +/// +unittest +{ + shared(int) val = 5; + atomic_fetch_and_impl(&val, 3); + assert(atomic_load_impl(&val) == 1); +} + +/// +pragma(inline, true) +A atomic_fetch_and_explicit_impl(A, M)(shared(A)* obj, M arg, memory_order order) @trusted +{ + assert(obj !is null); + + A set, get; + + final switch(order) + { + case memory_order.memory_order_relaxed: + get = atomicLoad!(memory_order.memory_order_relaxed)(cast(A*)obj); + do + { + set = get & arg; + } while (!atomicCompareExchangeWeak!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(A*)obj, &get, cast(A)set)); + break; + + case memory_order.memory_order_acquire: + get = atomicLoad!(memory_order.memory_order_acquire)(cast(A*)obj); + do + { + set = get & arg; + } while (!atomicCompareExchangeWeak!(memory_order.memory_order_acquire, memory_order.memory_order_acquire)(cast(A*)obj, &get, cast(A)set)); + break; + + case memory_order.memory_order_release: + get = atomicLoad!(memory_order.memory_order_relaxed)(cast(A*)obj); + do + { + set = get & arg; + } while (!atomicCompareExchangeWeak!(memory_order.memory_order_release, memory_order.memory_order_release)(cast(A*)obj, &get, cast(A)set)); + break; + + case memory_order.memory_order_acq_rel: + get = atomicLoad!(memory_order.memory_order_acq_rel)(cast(A*)obj); + do + { + set = get & arg; + } while (!atomicCompareExchangeWeak!(memory_order.memory_order_acq_rel, memory_order.memory_order_acq_rel)(cast(A*)obj, &get, cast(A)set)); + break; + + case memory_order.memory_order_seq_cst: + get = atomicLoad!(memory_order.memory_order_relaxed)(cast(A*)obj); + do + { + set = get & arg; + } while (!atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_seq_cst)(cast(A*)obj, &get, cast(A)set)); + break; + } + + return get; +} + +/// +unittest +{ + shared(int) val = 5; + atomic_fetch_and_explicit_impl(&val, 3, memory_order.memory_order_seq_cst); + assert(atomic_load_impl(&val) == 1); +} diff --git a/libphobos/libdruntime/core/thread/osthread.d b/libphobos/libdruntime/core/thread/osthread.d index 066f39e39c7..9ddc187b374 100644 --- a/libphobos/libdruntime/core/thread/osthread.d +++ b/libphobos/libdruntime/core/thread/osthread.d @@ -2129,6 +2129,13 @@ extern (C) void thread_init() @nogc nothrow static extern(C) void initChildAfterFork() { auto thisThread = Thread.getThis(); + if (!thisThread) + { + // It is possible that runtime was not properly initialized in the current process or thread - + // it may happen after `fork` call when using a dynamically loaded shared library written in D from a multithreaded non-D program. + // In such case getThis will return null. + return; + } thisThread.m_addr = pthread_self(); assert( thisThread.m_addr != thisThread.m_addr.init ); thisThread.m_tmach = pthread_mach_thread_np( thisThread.m_addr ); diff --git a/libphobos/libdruntime/object.d b/libphobos/libdruntime/object.d index 5589c0a884f..1b39a27c102 100644 --- a/libphobos/libdruntime/object.d +++ b/libphobos/libdruntime/object.d @@ -526,6 +526,12 @@ unittest private extern(C) void _d_setSameMutex(shared Object ownee, shared Object owner) nothrow; +/** Makes ownee use owner's mutex. + * This will initialize owner's mutex if it hasn't been set yet. + * Params: + * ownee = object to change + * owner = source object + */ void setSameMutex(shared Object ownee, shared Object owner) { import core.atomic : atomicLoad; -- 2.40.1