diff options
Diffstat (limited to 'gcc/d')
64 files changed, 1418 insertions, 689 deletions
diff --git a/gcc/d/ChangeLog b/gcc/d/ChangeLog index f1afaf20331..bb179bcf7e9 100644 --- a/gcc/d/ChangeLog +++ b/gcc/d/ChangeLog @@ -1,3 +1,19 @@ +2022-04-21 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd eb7bee331. + * dmd/VERSION: Update version to v2.100.0-beta.1. + * d-lang.cc (d_handle_option): Handle OPT_frevert_dip1000. + * lang.opt (frevert=dip1000): New option. + +2022-04-13 Iain Buclaw <ibuclaw@gdcproject.org> + + * Make-lang.in (D_FRONTEND_OBJS): Add d/common-bitfields.o, + d/mustuse.o. + * d-ctfloat.cc (CTFloat::isIdentical): Don't treat NaN values as + identical. + * dmd/MERGE: Merge upstream dmd 4d1bfcf14. + * expr.cc (ExprVisitor::visit (VoidInitExp *)): New. + 2022-04-03 Iain Buclaw <ibuclaw@gdcproject.org> * d-lang.cc: Include dmd/template.h. diff --git a/gcc/d/Make-lang.in b/gcc/d/Make-lang.in index 6c90657d65f..f3e34c54015 100644 --- a/gcc/d/Make-lang.in +++ b/gcc/d/Make-lang.in @@ -89,6 +89,7 @@ D_FRONTEND_OBJS = \ d/canthrow.o \ d/chkformat.o \ d/clone.o \ + d/common-bitfields.o \ d/common-file.o \ d/common-outbuffer.o \ d/common-string.o \ @@ -143,6 +144,7 @@ D_FRONTEND_OBJS = \ d/lambdacomp.o \ d/lexer.o \ d/mtype.o \ + d/mustuse.o \ d/nogc.o \ d/nspace.o \ d/ob.o \ diff --git a/gcc/d/d-ctfloat.cc b/gcc/d/d-ctfloat.cc index 6e6d10ff0a4..c4d9a44c59b 100644 --- a/gcc/d/d-ctfloat.cc +++ b/gcc/d/d-ctfloat.cc @@ -55,8 +55,7 @@ CTFloat::isIdentical (real_t x, real_t y) { real_value rx = x.rv (); real_value ry = y.rv (); - return (REAL_VALUE_ISNAN (rx) && REAL_VALUE_ISNAN (ry)) - || real_identical (&rx, &ry); + return real_identical (&rx, &ry); } /* Return true if real_t value R is NaN. */ diff --git a/gcc/d/d-lang.cc b/gcc/d/d-lang.cc index 69571628336..9adcabdf7cf 100644 --- a/gcc/d/d-lang.cc +++ b/gcc/d/d-lang.cc @@ -637,12 +637,17 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value, break; case OPT_frevert_all: + global.params.useDIP1000 = FeatureState::disabled; global.params.useDIP25 = FeatureState::disabled; global.params.dtorFields = FeatureState::disabled; global.params.fix16997 = !value; global.params.markdown = !value; break; + case OPT_frevert_dip1000: + global.params.useDIP1000 = FeatureState::disabled; + break; + case OPT_frevert_dip25: global.params.useDIP25 = FeatureState::disabled; break; diff --git a/gcc/d/d-port.cc b/gcc/d/d-port.cc index a0e06b37683..a908cc8dbb0 100644 --- a/gcc/d/d-port.cc +++ b/gcc/d/d-port.cc @@ -31,11 +31,11 @@ along with GCC; see the file COPYING3. If not see /* Compare the first N bytes of S1 and S2 without regard to the case. */ int -Port::memicmp (const char *s1, const char *s2, size_t n) +Port::memicmp (const char *s1, const char *s2, d_size_t n) { int result = 0; - for (size_t i = 0; i < n; i++) + for (d_size_t i = 0; i < n; i++) { char c1 = s1[i]; char c2 = s2[i]; @@ -143,9 +143,9 @@ Port::readlongBE (const void *buffer) /* Write an SZ-byte sized VALUE to BUFFER, ignoring endian-ness. */ void -Port::valcpy (void *buffer, uint64_t value, size_t sz) +Port::valcpy (void *buffer, uint64_t value, d_size_t sz) { - gcc_assert (((size_t) buffer) % sz == 0); + gcc_assert (((d_size_t) buffer) % sz == 0); switch (sz) { diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE index ca409df346d..d18119193d4 100644 --- a/gcc/d/dmd/MERGE +++ b/gcc/d/dmd/MERGE @@ -1,4 +1,4 @@ -47871363d804f54b29ccfd444b082c19716c2301 +313d28b3db7523e67880ae3baf8ef28ce9abe9bd The first line of this file holds the git revision number of the last merge done from the dlang/dmd repository. diff --git a/gcc/d/dmd/README.md b/gcc/d/dmd/README.md index f613ab28577..b14310312cd 100644 --- a/gcc/d/dmd/README.md +++ b/gcc/d/dmd/README.md @@ -130,6 +130,8 @@ Note that these groups have no strict meaning, the category assignments are a bi | [impcnvtab.d](https://github.com/dlang/dmd/blob/master/src/dmd/impcnvtab.d) | Define an implicit conversion table for basic types | | [importc.d](https://github.com/dlang/dmd/blob/master/src/dmd/importc.d) | Helpers specific to ImportC | | [sideeffect.d](https://github.com/dlang/dmd/blob/master/src/dmd/sideeffect.d) | Extract side-effects of expressions for certain lowerings. | +| [mustuse.d](https://github.com/dlang/dmd/blob/master/src/dmd/mustuse.d) | Helpers related to the `@mustuse` attribute | + **Compile Time Function Execution (CTFE)** diff --git a/gcc/d/dmd/VERSION b/gcc/d/dmd/VERSION index 16f49a73f4d..2450fd55ef8 100644 --- a/gcc/d/dmd/VERSION +++ b/gcc/d/dmd/VERSION @@ -1 +1 @@ -v2.099.1-beta.1 +v2.100.0-beta.1 diff --git a/gcc/d/dmd/aggregate.d b/gcc/d/dmd/aggregate.d index 8895aa5e2bd..f4b5e8af7ff 100644 --- a/gcc/d/dmd/aggregate.d +++ b/gcc/d/dmd/aggregate.d @@ -58,6 +58,28 @@ enum ClassKind : ubyte } /** + * Give a nice string for a class kind for error messages + * Params: + * c = class kind + * Returns: + * 0-terminated string for `c` + */ +const(char)* toChars(ClassKind c) +{ + final switch (c) + { + case ClassKind.d: + return "D"; + case ClassKind.cpp: + return "C++"; + case ClassKind.objc: + return "Objective-C"; + case ClassKind.c: + return "C"; + } +} + +/** * If an aggregate has a pargma(mangle, ...) this holds the information * to mangle. */ diff --git a/gcc/d/dmd/arrayop.d b/gcc/d/dmd/arrayop.d index 52f39d34d0f..16cbe620bc5 100644 --- a/gcc/d/dmd/arrayop.d +++ b/gcc/d/dmd/arrayop.d @@ -158,7 +158,7 @@ Expression arrayOp(BinExp e, Scope* sc) /// ditto Expression arrayOp(BinAssignExp e, Scope* sc) { - //printf("BinAssignExp.arrayOp() %s\n", toChars()); + //printf("BinAssignExp.arrayOp() %s\n", e.toChars()); /* Check that the elements of e1 can be assigned to */ diff --git a/gcc/d/dmd/astenums.d b/gcc/d/dmd/astenums.d index 1a2cf8b0641..bf196934aed 100644 --- a/gcc/d/dmd/astenums.d +++ b/gcc/d/dmd/astenums.d @@ -28,6 +28,7 @@ enum Baseok : ubyte enum MODFlags : int { + none = 0, // default (mutable) const_ = 1, // type is const immutable_ = 4, // type is immutable shared_ = 2, // type is shared diff --git a/gcc/d/dmd/attrib.d b/gcc/d/dmd/attrib.d index 9c2bbd672a7..1e84b55d181 100644 --- a/gcc/d/dmd/attrib.d +++ b/gcc/d/dmd/attrib.d @@ -44,7 +44,6 @@ import dmd.mtype; import dmd.objc; // for objc.addSymbols import dmd.common.outbuffer; import dmd.root.array; // for each -import dmd.target; // for target.systemLinkage import dmd.tokens; import dmd.visitor; @@ -399,7 +398,7 @@ extern (C++) final class LinkDeclaration : AttribDeclaration { super(loc, null, decl); //printf("LinkDeclaration(linkage = %d, decl = %p)\n", linkage, decl); - this.linkage = (linkage == LINK.system) ? target.systemLinkage() : linkage; + this.linkage = linkage; } static LinkDeclaration create(const ref Loc loc, LINK p, Dsymbols* decl) @@ -994,7 +993,7 @@ extern (C++) class ConditionalDeclaration : AttribDeclaration // Decide if 'then' or 'else' code should be included override Dsymbols* include(Scope* sc) { - //printf("ConditionalDeclaration::include(sc = %p) scope = %p\n", sc, scope); + //printf("ConditionalDeclaration::include(sc = %p) scope = %p\n", sc, _scope); if (errors) return null; @@ -1057,7 +1056,7 @@ extern (C++) final class StaticIfDeclaration : ConditionalDeclaration */ override Dsymbols* include(Scope* sc) { - //printf("StaticIfDeclaration::include(sc = %p) scope = %p\n", sc, scope); + //printf("StaticIfDeclaration::include(sc = %p) scope = %p\n", sc, _scope); if (errors || onStack) return null; @@ -1496,12 +1495,7 @@ extern (C++) final class UserAttributeDeclaration : AttribDeclaration if (global.params.cplusplus < CppStdRevision.cpp11) return; - // Avoid `if` at the call site - if (sym.userAttribDecl is null || sym.userAttribDecl.atts is null) - return; - - foreach (exp; *sym.userAttribDecl.atts) - { + foreachUdaNoSemantic(sym, (exp) { if (isGNUABITag(exp)) { if (sym.isCPPNamespaceDeclaration() || sym.isNspace()) @@ -1515,9 +1509,10 @@ extern (C++) final class UserAttributeDeclaration : AttribDeclaration sym.errors = true; } // Only one `@gnuAbiTag` is allowed by semantic2 - return; + return 1; // break } - } + return 0; // continue + }); } } @@ -1544,14 +1539,14 @@ bool isCoreUda(Dsymbol sym, Identifier ident) /** * Iterates the UDAs attached to the given symbol. * - * If `dg` returns `!= 0`, it will stop the iteration and return that - * value, otherwise it will return 0. - * * Params: * sym = the symbol to get the UDAs from * sc = scope to use for semantic analysis of UDAs - * dg = called once for each UDA. If `dg` returns `!= 0`, it will stop the - * iteration and return that value, otherwise it will return `0`. + * dg = called once for each UDA + * + * Returns: + * If `dg` returns `!= 0`, stops the iteration and returns that value. + * Otherwise, returns 0. */ int foreachUda(Dsymbol sym, Scope* sc, int delegate(Expression) dg) { @@ -1577,3 +1572,32 @@ int foreachUda(Dsymbol sym, Scope* sc, int delegate(Expression) dg) }); }); } + +/** + * Iterates the UDAs attached to the given symbol, without performing semantic + * analysis. + * + * Use this function instead of `foreachUda` if semantic analysis of `sym` is + * still in progress. + * + * Params: + * sym = the symbol to get the UDAs from + * dg = called once for each UDA + * + * Returns: + * If `dg` returns `!= 0`, stops the iteration and returns that value. + * Otherwise, returns 0. + */ +int foreachUdaNoSemantic(Dsymbol sym, int delegate(Expression) dg) +{ + if (sym.userAttribDecl is null || sym.userAttribDecl.atts is null) + return 0; + + foreach (exp; *sym.userAttribDecl.atts) + { + if (auto result = dg(exp)) + return result; + } + + return 0; +} diff --git a/gcc/d/dmd/blockexit.d b/gcc/d/dmd/blockexit.d index afd7ac052ad..22c9dde5f7e 100644 --- a/gcc/d/dmd/blockexit.d +++ b/gcc/d/dmd/blockexit.d @@ -139,16 +139,21 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) // Allow if last case/default was empty CaseStatement sc = slast.isCaseStatement(); DefaultStatement sd = slast.isDefaultStatement(); - if (sc && (!sc.statement.hasCode() || sc.statement.isCaseStatement() || sc.statement.isErrorStatement())) - { - } - else if (sd && (!sd.statement.hasCode() || sd.statement.isCaseStatement() || sd.statement.isErrorStatement())) + auto sl = (sc ? sc.statement : (sd ? sd.statement : null)); + + if (sl && (!sl.hasCode() || sl.isErrorStatement())) { } else if (func.getModule().filetype != FileType.c) { const(char)* gototype = s.isCaseStatement() ? "case" : "default"; - s.error("switch case fallthrough - use 'goto %s;' if intended", gototype); + // @@@DEPRECATED_2.110@@@ https://issues.dlang.org/show_bug.cgi?id=22999 + // Deprecated in 2.100 + // Make an error in 2.110 + if (sl && sl.isCaseStatement()) + s.deprecation("switch case fallthrough - use 'goto %s;' if intended", gototype); + else + s.error("switch case fallthrough - use 'goto %s;' if intended", gototype); } } } diff --git a/gcc/d/dmd/clone.d b/gcc/d/dmd/clone.d index 2ed0dc7bdd1..9c8c1c316bf 100644 --- a/gcc/d/dmd/clone.d +++ b/gcc/d/dmd/clone.d @@ -912,8 +912,8 @@ void buildDtors(AggregateDeclaration ad, Scope* sc) ex = new DotVarExp(loc, ex, v); // This is a hack so we can call destructors on const/immutable objects. - // Do it as a type 'paint'. - ex = new CastExp(loc, ex, v.type.mutableOf()); + // Do it as a type 'paint', `cast()` + ex = new CastExp(loc, ex, MODFlags.none); if (stc & STC.safe) stc = (stc & ~STC.safe) | STC.trusted; @@ -1588,7 +1588,7 @@ private bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor) auto tf = ctorDecl.type.toTypeFunction(); const dim = tf.parameterList.length; - if (dim == 1) + if (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg)) { auto param = tf.parameterList[0]; if (param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf()) diff --git a/gcc/d/dmd/common/README.md b/gcc/d/dmd/common/README.md index a9b65c3dd8d..fb282dcf0a1 100644 --- a/gcc/d/dmd/common/README.md +++ b/gcc/d/dmd/common/README.md @@ -2,6 +2,7 @@ | File | Purpose | |------------------------------------------------------------------------------------|-----------------------------------------------------------------| +| [bitfields.d](https://github.com/dlang/dmd/blob/master/src/dmd/common/bitfields.d) | Pack multiple boolean fields into bit fields | | [file.d](https://github.com/dlang/dmd/blob/master/src/dmd/common/file.d) | Functions and objects dedicated to file I/O and management | | [outbuffer.d](https://github.com/dlang/dmd/blob/master/src/dmd/common/outbuffer.d) | An expandable buffer in which you can write text or binary data | | [string.d](https://github.com/dlang/dmd/blob/master/src/dmd/common/string.d) | Common string functions including filename manipulation | diff --git a/gcc/d/dmd/common/bitfields.d b/gcc/d/dmd/common/bitfields.d new file mode 100644 index 00000000000..d17983d66b4 --- /dev/null +++ b/gcc/d/dmd/common/bitfields.d @@ -0,0 +1,70 @@ +/** + * A library bitfields utility + * + * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved + * Authors: Dennis Korpel + * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/common/bitfields.d, common/bitfields.d) + * Documentation: https://dlang.org/phobos/dmd_common_bitfields.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/common/bitfields.d + */ +module dmd.common.bitfields; + +/** + * Generate code for bit fields inside a struct/class body + * Params: + * S = type of a struct with only boolean fields, which should become bit fields + * T = type of bit fields variable, must have enough bits to store all booleans + * Returns: D code with a bit fields variable and getter / setter functions + */ +extern (D) string generateBitFields(S, T)() +if (__traits(isUnsigned, T)) +{ + string result = "extern (C++) pure nothrow @nogc @safe final {"; + enum structName = __traits(identifier, S); + + foreach (size_t i, mem; __traits(allMembers, S)) + { + static assert(is(typeof(__traits(getMember, S, mem)) == bool)); + static assert(i < T.sizeof * 8, "too many fields for bit field storage of type `"~T.stringof~"`"); + enum mask = "(1 << "~i.stringof~")"; + result ~= " + /// set or get the corresponding "~structName~" member + bool "~mem~"() const { return !!(bitFields & "~mask~"); } + /// ditto + bool "~mem~"(bool v) + { + v ? (bitFields |= "~mask~") : (bitFields &= ~"~mask~"); + return v; + }"; + } + return result ~ "}\n private "~T.stringof~" bitFields;\n"; +} + +/// +unittest +{ + static struct B + { + bool x; + bool y; + bool z; + } + + static struct S + { + mixin(generateBitFields!(B, ubyte)); + } + + S s; + assert(!s.x); + s.x = true; + assert(s.x); + s.x = false; + assert(!s.x); + + s.y = true; + assert(s.y); + assert(!s.x); + assert(!s.z); +} diff --git a/gcc/d/dmd/common/file.d b/gcc/d/dmd/common/file.d index e4483d5f947..8f34b5319a9 100644 --- a/gcc/d/dmd/common/file.d +++ b/gcc/d/dmd/common/file.d @@ -25,6 +25,8 @@ import core.sys.posix.unistd; import dmd.common.string; +nothrow: + /** Encapsulated management of a memory-mapped file. @@ -52,6 +54,8 @@ struct FileMapping(Datum) private const(char)* name; // state } + nothrow: + /** Open `filename` and map it in memory. If `Datum` is `const`, opens for read-only and maps the content in memory; no error is issued if the file diff --git a/gcc/d/dmd/common/outbuffer.d b/gcc/d/dmd/common/outbuffer.d index 0705c1880c0..7e46d294f3d 100644 --- a/gcc/d/dmd/common/outbuffer.d +++ b/gcc/d/dmd/common/outbuffer.d @@ -16,6 +16,8 @@ import core.stdc.stdio; import core.stdc.string; import core.stdc.stdlib; +nothrow: + // In theory these functions should also restore errno, but we don't care because // we abort application on error anyway. extern (C) private pure @system @nogc nothrow @@ -54,6 +56,8 @@ struct OutBuffer int level; // state } + nothrow: + /** Construct given size. */ diff --git a/gcc/d/dmd/common/string.d b/gcc/d/dmd/common/string.d index d3bc24f2667..48bf9bb5b55 100644 --- a/gcc/d/dmd/common/string.d +++ b/gcc/d/dmd/common/string.d @@ -10,6 +10,8 @@ */ module dmd.common.string; +nothrow: + /** Defines a temporary array using a fixed-length buffer as back store. If the length of the buffer suffices, it is readily used. Otherwise, `malloc` is used to @@ -26,6 +28,8 @@ struct SmallBuffer(T) private T[] _extent; private bool needsFree; + nothrow: + @disable this(); // no default ctor @disable this(ref const SmallBuffer!T); // noncopyable, nonassignable diff --git a/gcc/d/dmd/constfold.d b/gcc/d/dmd/constfold.d index 75ba9ea1e0a..bf66408f1d4 100644 --- a/gcc/d/dmd/constfold.d +++ b/gcc/d/dmd/constfold.d @@ -936,7 +936,7 @@ UnionExp Identity(EXP op, const ref Loc loc, Type type, Expression e1, Expressio { if (e1.type.isreal()) { - cmp = RealIdentical(e1.toReal(), e2.toReal()); + cmp = CTFloat.isIdentical(e1.toReal(), e2.toReal()); } else if (e1.type.isimaginary()) { diff --git a/gcc/d/dmd/cparse.d b/gcc/d/dmd/cparse.d index bb12aa7c995..2b2046f3da3 100644 --- a/gcc/d/dmd/cparse.d +++ b/gcc/d/dmd/cparse.d @@ -213,16 +213,12 @@ final class CParser(AST) : Parser!AST goto Lexp; case TOK.leftParenthesis: - { - /* If tokens look like a function call, assume it is one, - * As any type-name won't be resolved until semantic, this - * could be rewritten later. - */ - auto tk = &token; - if (isFunctionCall(tk)) - goto Lexp; - goto default; - } + if (auto pt = lookupTypedef(token.ident)) + { + if (*pt) + goto Ldeclaration; + } + goto Lexp; // function call default: { @@ -259,6 +255,7 @@ final class CParser(AST) : Parser!AST case TOK.plusPlus: case TOK.minusMinus: case TOK.sizeof_: + case TOK._Generic: Lexp: auto exp = cparseExpression(); if (token.value == TOK.identifier && exp.op == EXP.identifier) @@ -1625,10 +1622,21 @@ final class CParser(AST) : Parser!AST */ if (token.value == TOK.semicolon) { - nextToken(); if (!tspec) + { + nextToken(); return; // accept empty declaration as an extension + } + if (auto ti = tspec.isTypeIdentifier()) + { + // C11 6.7.2-2 + error("type-specifier missing for declaration of `%s`", ti.ident.toChars()); + nextToken(); + return; + } + + nextToken(); auto tt = tspec.isTypeTag(); if (!tt || !tt.id && (tt.tok == TOK.struct_ || tt.tok == TOK.union_)) @@ -1660,6 +1668,22 @@ final class CParser(AST) : Parser!AST specifier.mod &= ~MOD.xnone; // 'used' it } + void scanPastSemicolon() + { + while (token.value != TOK.semicolon && token.value != TOK.endOfFile) + nextToken(); + nextToken(); + } + + if (token.value == TOK.assign && tspec && tspec.isTypeIdentifier()) + { + /* C11 6.7.2-2 + * Special check for `const b = 1;` because some compilers allow it + */ + error("type-specifier omitted for declaration of `%s`", tspec.isTypeIdentifier().ident.toChars()); + return scanPastSemicolon(); + } + bool first = true; while (1) { @@ -1879,10 +1903,7 @@ final class CParser(AST) : Parser!AST default: error("`=`, `;` or `,` expected to end declaration instead of `%s`", token.toChars()); Lend: - while (token.value != TOK.semicolon && token.value != TOK.endOfFile) - nextToken(); - nextToken(); - return; + return scanPastSemicolon(); } } } @@ -2399,15 +2420,13 @@ final class CParser(AST) : Parser!AST if (idx.length > 2 && idx[0] == '_' && idx[1] == '_') // leading double underscore importBuiltins = true; // probably one of those compiler extensions t = null; - if (scw & SCW.xtypedef) - { - /* Punch through to what the typedef is, to support things like: - * typedef T* T; - */ - auto pt = lookupTypedef(previd); - if (pt && *pt) // if previd is a known typedef - t = *pt; - } + + /* Punch through to what the typedef is, to support things like: + * typedef T* T; + */ + auto pt = lookupTypedef(previd); + if (pt && *pt) // if previd is a known typedef + t = *pt; if (!t) t = new AST.TypeIdentifier(loc, previd); @@ -2529,7 +2548,14 @@ final class CParser(AST) : Parser!AST default: if (declarator == DTR.xdirect) { - error("identifier or `(` expected"); // ) + if (!t || t.isTypeIdentifier()) + { + // const arr[1]; + error("no type-specifier for declarator"); + t = AST.Type.tint32; + } + else + error("identifier or `(` expected"); // ) panic(); } ts = t; @@ -2745,6 +2771,16 @@ final class CParser(AST) : Parser!AST Specifier specifier; specifier.packalign.setDefault(); auto tspec = cparseSpecifierQualifierList(LVL.global, specifier); + if (!tspec) + { + error("type-specifier is missing"); + tspec = AST.Type.tint32; + } + if (tspec && specifier.mod & MOD.xconst) + { + tspec = toConst(tspec); + specifier.mod = MOD.xnone; // 'used' it + } Identifier id; return cparseDeclarator(DTR.xabstract, tspec, id, specifier); } @@ -2825,8 +2861,18 @@ final class CParser(AST) : Parser!AST Specifier specifier; specifier.packalign.setDefault(); auto tspec = cparseDeclarationSpecifiers(LVL.prototype, specifier); - if (tspec && specifier.mod & MOD.xconst) + if (!tspec) { + error("no type-specifier for parameter"); + tspec = AST.Type.tint32; + } + + if (specifier.mod & MOD.xconst) + { + if ((token.value == TOK.rightParenthesis || token.value == TOK.comma) && + tspec.isTypeIdentifier()) + error("type-specifier omitted for parameter `%s`", tspec.isTypeIdentifier().ident.toChars()); + tspec = toConst(tspec); specifier.mod = MOD.xnone; // 'used' it } @@ -3396,7 +3442,12 @@ final class CParser(AST) : Parser!AST Specifier specifier; specifier.packalign = this.packalign; auto tspec = cparseSpecifierQualifierList(LVL.member, specifier); - if (tspec && specifier.mod & MOD.xconst) + if (!tspec) + { + error("no type-specifier for struct member"); + tspec = AST.Type.tint32; + } + if (specifier.mod & MOD.xconst) { tspec = toConst(tspec); specifier.mod = MOD.xnone; // 'used' it @@ -3409,7 +3460,13 @@ final class CParser(AST) : Parser!AST nextToken(); auto tt = tspec.isTypeTag(); if (!tt) + { + if (auto ti = tspec.isTypeIdentifier()) + { + error("type-specifier omitted before declaration of `%s`", ti.ident.toChars()); + } return; // legal but meaningless empty declaration + } /* If anonymous struct declaration * struct { ... members ... }; @@ -3449,6 +3506,12 @@ final class CParser(AST) : Parser!AST AST.Type dt; if (token.value == TOK.colon) { + if (auto ti = tspec.isTypeIdentifier()) + { + error("type-specifier omitted before bit field declaration of `%s`", ti.ident.toChars()); + tspec = AST.Type.tint32; + } + // C11 6.7.2.1-12 unnamed bit-field id = Identifier.generateAnonymousId("BitField"); dt = tspec; diff --git a/gcc/d/dmd/ctfeexpr.d b/gcc/d/dmd/ctfeexpr.d index 32aed16e796..11229d4bd4f 100644 --- a/gcc/d/dmd/ctfeexpr.d +++ b/gcc/d/dmd/ctfeexpr.d @@ -1269,7 +1269,7 @@ private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool ide real_t r1 = e1.type.isreal() ? e1.toReal() : e1.toImaginary(); real_t r2 = e1.type.isreal() ? e2.toReal() : e2.toImaginary(); if (identity) - return !RealIdentical(r1, r2); + return !CTFloat.isIdentical(r1, r2); if (CTFloat.isNaN(r1) || CTFloat.isNaN(r2)) // if unordered { return 1; // they are not equal @@ -1399,7 +1399,7 @@ bool ctfeIdentity(const ref Loc loc, EXP op, Expression e1, Expression e2) cmp = (es1.var == es2.var && es1.offset == es2.offset); } else if (e1.type.isreal()) - cmp = RealIdentical(e1.toReal(), e2.toReal()); + cmp = CTFloat.isIdentical(e1.toReal(), e2.toReal()); else if (e1.type.isimaginary()) cmp = RealIdentical(e1.toImaginary(), e2.toImaginary()); else if (e1.type.iscomplex()) diff --git a/gcc/d/dmd/dcast.d b/gcc/d/dmd/dcast.d index 685987b734d..83978391e42 100644 --- a/gcc/d/dmd/dcast.d +++ b/gcc/d/dmd/dcast.d @@ -432,7 +432,7 @@ MATCH implicitConvTo(Expression e, Type t) return MATCH.nomatch; goto case Tuns8; case Tuns8: - //printf("value = %llu %llu\n", (dinteger_t)(unsigned char)value, value); + //printf("value = %llu %llu\n", cast(dinteger_t)cast(ubyte)value, value); if (cast(ubyte)value != value) return MATCH.nomatch; break; @@ -492,8 +492,8 @@ MATCH implicitConvTo(Expression e, Type t) break; case Tpointer: - //printf("type = %s\n", type.toBasetype()->toChars()); - //printf("t = %s\n", t.toBasetype()->toChars()); + //printf("type = %s\n", type.toBasetype().toChars()); + //printf("t = %s\n", t.toBasetype().toChars()); if (ty == Tpointer && e.type.toBasetype().nextOf().ty == t.toBasetype().nextOf().ty) { /* Allow things like: @@ -1107,6 +1107,10 @@ MATCH implicitConvTo(Expression e, Type t) MATCH visitCond(CondExp e) { + auto result = visit(e); + if (result != MATCH.nomatch) + return result; + MATCH m1 = e.e1.implicitConvTo(t); MATCH m2 = e.e2.implicitConvTo(t); //printf("CondExp: m1 %d m2 %d\n", m1, m2); @@ -2077,7 +2081,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) if (auto tsa = tb.isTypeSArray()) { size_t dim2 = cast(size_t)tsa.dim.toInteger(); - //printf("dim from = %d, to = %d\n", (int)se.len, (int)dim2); + //printf("dim from = %d, to = %d\n", cast(int)se.len, cast(int)dim2); // Changing dimensions if (dim2 != se.len) diff --git a/gcc/d/dmd/declaration.d b/gcc/d/dmd/declaration.d index c0e40a5263a..a533d30ca80 100644 --- a/gcc/d/dmd/declaration.d +++ b/gcc/d/dmd/declaration.d @@ -379,7 +379,7 @@ extern (C++) abstract class Declaration : Dsymbol if (e1 && e1.op == EXP.this_ && isField()) { - VarDeclaration vthis = (cast(ThisExp)e1).var; + VarDeclaration vthis = e1.isThisExp().var; for (Scope* scx = sc; scx; scx = scx.enclosing) { if (scx.func == vthis.parent && (scx.flags & SCOPE.contract)) @@ -656,9 +656,8 @@ extern (C++) final class TupleDeclaration : Declaration if (o.dyncast() == DYNCAST.expression) { Expression e = cast(Expression)o; - if (e.op == EXP.dSymbol) + if (DsymbolExp ve = e.isDsymbolExp()) { - DsymbolExp ve = cast(DsymbolExp)e; Declaration d = ve.s.isDeclaration(); if (d && d.needThis()) { @@ -1083,29 +1082,12 @@ extern (C++) class VarDeclaration : Declaration bool isArgDtorVar; /// temporary created to handle scope destruction of a function argument } - private ushort bitFields; // stores multiple booleans for BitFields + import dmd.common.bitfields : generateBitFields; + mixin(generateBitFields!(BitFields, ushort)); + byte canassign; // it can be assigned to ubyte isdataseg; // private data for isDataseg 0 unset, 1 true, 2 false - // Generate getter and setter functions for `bitFields` - extern (D) mixin(() { - string result = "extern (C++) pure nothrow @nogc @safe final {"; - foreach (size_t i, mem; __traits(allMembers, BitFields)) - { - result ~= " - /// set or get the corresponding BitFields member - bool "~mem~"() const { return !!(bitFields & (1 << "~i.stringof~")); } - /// ditto - bool "~mem~"(bool v) - { - v ? (bitFields |= (1 << "~i.stringof~")) : (bitFields &= ~(1 << "~i.stringof~")); - return v; - }"; - } - return result ~ "}"; - }()); - - final extern (D) this(const ref Loc loc, Type type, Identifier ident, Initializer _init, StorageClass storage_class = STC.undefined_) in { @@ -1160,7 +1142,7 @@ extern (C++) class VarDeclaration : Declaration assert(o.dyncast() == DYNCAST.expression); Expression e = cast(Expression)o; assert(e.op == EXP.dSymbol); - DsymbolExp se = cast(DsymbolExp)e; + DsymbolExp se = e.isDsymbolExp(); se.s.setFieldOffset(ad, fieldState, isunion); } return; @@ -1458,16 +1440,17 @@ extern (C++) class VarDeclaration : Declaration const sdsz = sd.type.size(); assert(sdsz != SIZE_INVALID && sdsz != 0); const n = sz / sdsz; - e = new SliceExp(loc, e, new IntegerExp(loc, 0, Type.tsize_t), new IntegerExp(loc, n, Type.tsize_t)); + SliceExp se = new SliceExp(loc, e, new IntegerExp(loc, 0, Type.tsize_t), + new IntegerExp(loc, n, Type.tsize_t)); // Prevent redundant bounds check - (cast(SliceExp)e).upperIsInBounds = true; - (cast(SliceExp)e).lowerIsLessThanUpper = true; + se.upperIsInBounds = true; + se.lowerIsLessThanUpper = true; // This is a hack so we can call destructors on const/immutable objects. - e.type = sd.type.arrayOf(); + se.type = sd.type.arrayOf(); - e = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayDtor), e); + e = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayDtor), se); } return e; } diff --git a/gcc/d/dmd/dmangle.d b/gcc/d/dmd/dmangle.d index c3662a734d8..1f895e03af0 100644 --- a/gcc/d/dmd/dmangle.d +++ b/gcc/d/dmd/dmangle.d @@ -340,7 +340,6 @@ public: final switch (t.linkage) { case LINK.default_: - case LINK.system: case LINK.d: mc = 'F'; break; @@ -356,6 +355,8 @@ public: case LINK.objc: mc = 'Y'; break; + case LINK.system: + assert(0); } buf.writeByte(mc); @@ -1340,7 +1341,9 @@ extern (D) const(char)[] externallyMangledIdentifier(Declaration d) { if (d.linkage != LINK.d && d.localNum) d.error("the same declaration cannot be in multiple scopes with non-D linkage"); - final switch (d.linkage) + + const l = d.linkage == LINK.system ? target.systemLinkage() : d.linkage; + final switch (l) { case LINK.d: break; @@ -1354,9 +1357,10 @@ extern (D) const(char)[] externallyMangledIdentifier(Declaration d) return p.toDString(); } case LINK.default_: - case LINK.system: d.error("forward declaration"); return d.ident.toString(); + case LINK.system: + assert(0); } } return null; diff --git a/gcc/d/dmd/dmodule.d b/gcc/d/dmd/dmodule.d index c7e6418bf46..2d9f6510ba5 100644 --- a/gcc/d/dmd/dmodule.d +++ b/gcc/d/dmd/dmodule.d @@ -69,7 +69,7 @@ void semantic3OnDependencies(Module m) /** * Remove generated .di files on error and exit */ -void removeHdrFilesAndFail(ref Param params, ref Modules modules) +void removeHdrFilesAndFail(ref Param params, ref Modules modules) nothrow { if (params.doHdrGeneration) { @@ -94,7 +94,7 @@ void removeHdrFilesAndFail(ref Param params, ref Modules modules) * Returns: * the filename of the child package or module */ -private const(char)[] getFilename(Identifier[] packages, Identifier ident) +private const(char)[] getFilename(Identifier[] packages, Identifier ident) nothrow { const(char)[] filename = ident.toString(); @@ -157,14 +157,14 @@ extern (C++) class Package : ScopeDsymbol uint tag; // auto incremented tag, used to mask package tree in scopes Module mod; // !=null if isPkgMod == PKG.module_ - final extern (D) this(const ref Loc loc, Identifier ident) + final extern (D) this(const ref Loc loc, Identifier ident) nothrow { super(loc, ident); __gshared uint packageTag; this.tag = packageTag++; } - override const(char)* kind() const + override const(char)* kind() const nothrow { return "package"; } @@ -664,7 +664,7 @@ extern (C++) final class Module : Package //printf("Module::read('%s') file '%s'\n", toChars(), srcfile.toChars()); if (auto result = global.fileManager.lookup(srcfile)) { - this.src = result.data; + this.src = result; if (global.params.emitMakeDeps) global.params.makeDeps.push(srcfile.toChars()); return true; @@ -1133,8 +1133,10 @@ extern (C++) final class Module : Package // If it isn't there, some compiler rewrites, like // classinst == classinst -> .object.opEquals(classinst, classinst) // would fail inside object.d. - if (members.dim == 0 || (*members)[0].ident != Id.object || - (*members)[0].isImport() is null) + if (filetype != FileType.c && + (members.dim == 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); @@ -1380,7 +1382,7 @@ extern (C++) final class Module : Package a.setDim(0); } - extern (D) static void clearCache() + extern (D) static void clearCache() nothrow { foreach (Module m; amodules) m.searchCacheIdent = null; @@ -1391,7 +1393,7 @@ extern (C++) final class Module : Package * return true if it imports m. * Can be used to detect circular imports. */ - int imports(Module m) + int imports(Module m) nothrow { //printf("%s Module::imports(%s)\n", toChars(), m.toChars()); version (none) @@ -1414,14 +1416,14 @@ extern (C++) final class Module : Package return false; } - bool isRoot() + bool isRoot() nothrow { return this.importedFrom == this; } // true if the module source file is directly // listed in command line. - bool isCoreModule(Identifier ident) + bool isCoreModule(Identifier ident) nothrow { return this.ident == ident && parent && parent.ident == Id.core && !parent.parent; } @@ -1440,7 +1442,7 @@ extern (C++) final class Module : Package uint[uint] ctfe_cov; /// coverage information from ctfe execution_count[line] - override inout(Module) isModule() inout + override inout(Module) isModule() inout nothrow { return this; } @@ -1455,7 +1457,7 @@ extern (C++) final class Module : Package * Params: * buf = The buffer to write to */ - void fullyQualifiedName(ref OutBuffer buf) + void fullyQualifiedName(ref OutBuffer buf) nothrow { buf.writestring(ident.toString()); @@ -1469,7 +1471,7 @@ extern (C++) final class Module : Package /** Lazily initializes and returns the escape table. Turns out it eats a lot of memory. */ - extern(D) Escape* escapetable() + extern(D) Escape* escapetable() nothrow { if (!_escapetable) _escapetable = new Escape(); diff --git a/gcc/d/dmd/dscope.d b/gcc/d/dmd/dscope.d index c3a1d05cd7b..6339a9e36ba 100644 --- a/gcc/d/dmd/dscope.d +++ b/gcc/d/dmd/dscope.d @@ -457,6 +457,8 @@ struct Scope if (sc.scopesym.isModule()) flags |= SearchUnqualifiedModule; // tell Module.search() that SearchLocalsOnly 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)) { diff --git a/gcc/d/dmd/dsymbol.d b/gcc/d/dmd/dsymbol.d index cb6c278fc44..74eaa1d8d7a 100644 --- a/gcc/d/dmd/dsymbol.d +++ b/gcc/d/dmd/dsymbol.d @@ -112,12 +112,12 @@ struct Ungag { uint oldgag; - extern (D) this(uint old) + extern (D) this(uint old) nothrow { this.oldgag = old; } - extern (C++) ~this() + extern (C++) ~this() nothrow { global.gag = oldgag; } @@ -255,27 +255,27 @@ extern (C++) class Dsymbol : ASTNode DeprecatedDeclaration depdecl; // customized deprecation message UserAttributeDeclaration userAttribDecl; // user defined attributes - final extern (D) this() + final extern (D) this() nothrow { //printf("Dsymbol::Dsymbol(%p)\n", this); loc = Loc(null, 0, 0); } - final extern (D) this(Identifier ident) + final extern (D) this(Identifier ident) nothrow { //printf("Dsymbol::Dsymbol(%p, ident)\n", this); this.loc = Loc(null, 0, 0); this.ident = ident; } - final extern (D) this(const ref Loc loc, Identifier ident) + final extern (D) this(const ref Loc loc, Identifier ident) nothrow { //printf("Dsymbol::Dsymbol(%p, ident)\n", this); this.loc = loc; this.ident = ident; } - static Dsymbol create(Identifier ident) + static Dsymbol create(Identifier ident) nothrow { return new Dsymbol(ident); } @@ -800,6 +800,22 @@ extern (C++) class Dsymbol : ASTNode if (isAliasDeclaration() && !_scope) setScope(sc); Dsymbol s2 = sds.symtabLookup(this,ident); + /* https://issues.dlang.org/show_bug.cgi?id=17434 + * + * If we are trying to add an import to the symbol table + * that has already been introduced, then keep the one with + * larger visibility. This is fine for imports because if + * we have multiple imports of the same file, if a single one + * is public then the symbol is reachable. + */ + if (auto i1 = isImport()) + { + if (auto i2 = s2.isImport()) + { + if (sc.explicitVisibility && sc.visibility > i2.visibility) + sds.symtab.update(this); + } + } // If using C tag/prototype/forward declaration rules if (sc.flags & SCOPE.Cfile && !this.isImport()) @@ -822,7 +838,8 @@ extern (C++) class Dsymbol : ASTNode } if (sds.isAggregateDeclaration() || sds.isEnumDeclaration()) { - if (ident == Id.__sizeof || ident == Id.__xalignof || ident == Id._mangleof) + if (ident == Id.__sizeof || + !(sc && sc.flags & SCOPE.Cfile) && (ident == Id.__xalignof || ident == Id._mangleof)) { error("`.%s` property cannot be redefined", ident.toChars()); errors = true; @@ -933,14 +950,7 @@ extern (C++) class Dsymbol : ASTNode TemplateInstance ti = st.isTemplateInstance(); sm = s.search(loc, ti.name); if (!sm) - { - sm = s.search_correct(ti.name); - if (sm) - .error(loc, "template identifier `%s` is not a member of %s `%s`, did you mean %s `%s`?", ti.name.toChars(), s.kind(), s.toPrettyChars(), sm.kind(), sm.toChars()); - else - .error(loc, "template identifier `%s` is not a member of %s `%s`", ti.name.toChars(), s.kind(), s.toPrettyChars()); return null; - } sm = sm.toAlias(); TemplateDeclaration td = sm.isTemplateDeclaration(); if (!td) @@ -1381,16 +1391,16 @@ private: BitArray accessiblePackages, privateAccessiblePackages;// whitelists of accessible (imported) packages public: - final extern (D) this() + final extern (D) this() nothrow { } - final extern (D) this(Identifier ident) + final extern (D) this(Identifier ident) nothrow { super(ident); } - final extern (D) this(const ref Loc loc, Identifier ident) + final extern (D) this(const ref Loc loc, Identifier ident) nothrow { super(loc, ident); } @@ -1604,7 +1614,7 @@ public: return os; } - void importScope(Dsymbol s, Visibility visibility) + void importScope(Dsymbol s, Visibility visibility) nothrow { //printf("%s.ScopeDsymbol::importScope(%s, %d)\n", toChars(), s.toChars(), visibility); // No circular or redundant import's @@ -1631,7 +1641,7 @@ public: } } - extern (D) final void addAccessiblePackage(Package p, Visibility visibility) + extern (D) final void addAccessiblePackage(Package p, Visibility visibility) nothrow { auto pary = visibility.kind == Visibility.Kind.private_ ? &privateAccessiblePackages : &accessiblePackages; if (pary.length <= p.tag) @@ -1639,7 +1649,7 @@ public: (*pary)[p.tag] = true; } - bool isPackageAccessible(Package p, Visibility visibility, int flags = 0) + bool isPackageAccessible(Package p, Visibility visibility, int flags = 0) nothrow { if (p.tag < accessiblePackages.length && accessiblePackages[p.tag] || visibility.kind == Visibility.Kind.private_ && p.tag < privateAccessiblePackages.length && privateAccessiblePackages[p.tag]) @@ -1654,7 +1664,7 @@ public: return false; } - override final bool isforwardRef() + override final bool isforwardRef() nothrow { return (members is null); } @@ -1742,7 +1752,7 @@ public: * Returns: * null if already in table, `s` if inserted */ - Dsymbol symtabInsert(Dsymbol s) + Dsymbol symtabInsert(Dsymbol s) nothrow { return symtab.insert(s); } @@ -1755,7 +1765,7 @@ public: * Returns: * Dsymbol if found, null if not */ - Dsymbol symtabLookup(Dsymbol s, Identifier id) + Dsymbol symtabLookup(Dsymbol s, Identifier id) nothrow { return symtab.lookup(id); } @@ -1838,7 +1848,7 @@ extern (C++) final class WithScopeSymbol : ScopeDsymbol { WithStatement withstate; - extern (D) this(WithStatement withstate) + extern (D) this(WithStatement withstate) nothrow { this.withstate = withstate; } @@ -1898,7 +1908,7 @@ extern (C++) final class ArrayScopeSymbol : ScopeDsymbol private RootObject arrayContent; Scope* sc; - extern (D) this(Scope* sc, Expression exp) + extern (D) this(Scope* sc, Expression exp) nothrow { super(exp.loc, null); assert(exp.op == EXP.index || exp.op == EXP.slice || exp.op == EXP.array); @@ -1906,13 +1916,13 @@ extern (C++) final class ArrayScopeSymbol : ScopeDsymbol this.arrayContent = exp; } - extern (D) this(Scope* sc, TypeTuple type) + extern (D) this(Scope* sc, TypeTuple type) nothrow { this.sc = sc; this.arrayContent = type; } - extern (D) this(Scope* sc, TupleDeclaration td) + extern (D) this(Scope* sc, TupleDeclaration td) nothrow { this.sc = sc; this.arrayContent = td; @@ -2109,7 +2119,7 @@ extern (C++) final class OverloadSet : Dsymbol { Dsymbols a; // array of Dsymbols - extern (D) this(Identifier ident, OverloadSet os = null) + extern (D) this(Identifier ident, OverloadSet os = null) nothrow { super(ident); if (os) @@ -2118,7 +2128,7 @@ extern (C++) final class OverloadSet : Dsymbol } } - void push(Dsymbol s) + void push(Dsymbol s) nothrow { a.push(s); } @@ -2152,12 +2162,13 @@ extern (C++) final class ForwardingScopeDsymbol : ScopeDsymbol * Can be `null` before being lazily initialized. */ ScopeDsymbol forward; - extern (D) this(ScopeDsymbol forward) + extern (D) this(ScopeDsymbol forward) nothrow { super(null); this.forward = forward; } - override Dsymbol symtabInsert(Dsymbol s) + + override Dsymbol symtabInsert(Dsymbol s) nothrow { assert(forward); if (auto d = s.isDeclaration()) @@ -2188,7 +2199,7 @@ extern (C++) final class ForwardingScopeDsymbol : ScopeDsymbol * and * static foreach (i; [0]) { enum i = 2; } */ - override Dsymbol symtabLookup(Dsymbol s, Identifier id) + override Dsymbol symtabLookup(Dsymbol s, Identifier id) nothrow { assert(forward); // correctly diagnose clashing foreach loop variables. @@ -2219,7 +2230,7 @@ extern (C++) final class ForwardingScopeDsymbol : ScopeDsymbol override const(char)* kind()const{ return "local scope"; } - override inout(ForwardingScopeDsymbol) isForwardingScopeDsymbol() inout + override inout(ForwardingScopeDsymbol) isForwardingScopeDsymbol() inout nothrow { return this; } @@ -2234,13 +2245,13 @@ extern (C++) final class ForwardingScopeDsymbol : ScopeDsymbol extern (C++) final class ExpressionDsymbol : Dsymbol { Expression exp; - this(Expression exp) + this(Expression exp) nothrow { super(); this.exp = exp; } - override inout(ExpressionDsymbol) isExpressionDsymbol() inout + override inout(ExpressionDsymbol) isExpressionDsymbol() inout nothrow { return this; } @@ -2259,7 +2270,7 @@ extern (C++) final class AliasAssign : Dsymbol Dsymbol aliassym; /// replace previous RHS of AliasDeclaration with `aliassym` /// only one of type and aliassym can be != null - extern (D) this(const ref Loc loc, Identifier ident, Type type, Dsymbol aliassym) + extern (D) this(const ref Loc loc, Identifier ident, Type type, Dsymbol aliassym) nothrow { super(loc, null); this.ident = ident; @@ -2299,6 +2310,8 @@ extern (C++) final class DsymbolTable : RootObject { AssocArray!(Identifier, Dsymbol) tab; + nothrow: + /*************************** * Look up Identifier in symbol table * Params: @@ -2511,6 +2524,7 @@ Dsymbol handleSymbolRedeclarations(ref Scope sc, Dsymbol s, Dsymbol s2, ScopeDsy if (i1) // vd is the definition { + vd2.storage_class |= STC.extern_; // so toObjFile() won't emit it sds.symtab.update(vd); // replace vd2 with the definition return vd; } diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d index c9906363cc8..5415401e0e4 100644 --- a/gcc/d/dmd/dsymbolsem.d +++ b/gcc/d/dmd/dsymbolsem.d @@ -51,6 +51,7 @@ import dmd.init; import dmd.initsem; import dmd.hdrgen; import dmd.mtype; +import dmd.mustuse; import dmd.nogc; import dmd.nspace; import dmd.objc; @@ -542,7 +543,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor Parameter arg = Parameter.getNth(tt.arguments, pos); arg.type = arg.type.typeSemantic(dsym.loc, sc); //printf("[%d] iexps.dim = %d, ", pos, iexps.dim); - //printf("e = (%s %s, %s), ", Token::tochars[e.op], e.toChars(), e.type.toChars()); + //printf("e = (%s %s, %s), ", Token.tochars[e.op], e.toChars(), e.type.toChars()); //printf("arg = (%s, %s)\n", arg.toChars(), arg.type.toChars()); if (e != ie) @@ -581,7 +582,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor arg = Parameter.getNth(tt.arguments, pos + u); arg.type = arg.type.typeSemantic(dsym.loc, sc); //printf("[%d+%d] exps.dim = %d, ", pos, u, exps.dim); - //printf("ee = (%s %s, %s), ", Token::tochars[ee.op], ee.toChars(), ee.type.toChars()); + //printf("ee = (%s %s, %s), ", Token.tochars[ee.op], ee.toChars(), ee.type.toChars()); //printf("arg = (%s, %s)\n", arg.toChars(), arg.type.toChars()); size_t iexps_dim = iexps.dim - 1 + exps.dim; @@ -2046,6 +2047,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor ed.semanticRun = PASS.semantic; UserAttributeDeclaration.checkGNUABITag(ed, sc.linkage); + checkMustUseReserved(ed); if (!ed.members && !ed.memtype) // enum ident; { @@ -2872,7 +2874,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor sc = sc.endCTFE(); resolved = resolved.ctfeInterpret(); StringExp name = resolved.toStringExp(); - TupleExp tup = name ? null : resolved.toTupleExp(); + TupleExp tup = name ? null : resolved.isTupleExp(); if (!tup && !name) { error(ns.loc, "expected string expression for namespace name, got `%s`", ns.identExp.toChars()); @@ -3017,7 +3019,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if ((funcdecl.storage_class & STC.TYPECTOR) && !(ad || funcdecl.isNested())) funcdecl.storage_class &= ~STC.TYPECTOR; - //printf("function storage_class = x%llx, sc.stc = x%llx, %x\n", storage_class, sc.stc, Declaration::isFinal()); + //printf("function storage_class = x%llx, sc.stc = x%llx, %x\n", storage_class, sc.stc, Declaration.isFinal()); if (sc.flags & SCOPE.compile) funcdecl.flags |= FUNCFLAG.compileTimeOnly; // don't emit code for this function @@ -3056,6 +3058,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor funcdecl.visibility = sc.visibility; funcdecl.userAttribDecl = sc.userAttribDecl; UserAttributeDeclaration.checkGNUABITag(funcdecl, funcdecl.linkage); + checkMustUseReserved(funcdecl); if (!funcdecl.originalType) funcdecl.originalType = funcdecl.type.syntaxCopy(); @@ -4092,7 +4095,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor override void visit(PostBlitDeclaration pbd) { //printf("PostBlitDeclaration::semantic() %s\n", toChars()); - //printf("ident: %s, %s, %p, %p\n", ident.toChars(), Id::dtor.toChars(), ident, Id::dtor); + //printf("ident: %s, %s, %p, %p\n", ident.toChars(), Id.dtor.toChars(), ident, Id.dtor); //printf("stc = x%llx\n", sc.stc); if (pbd.semanticRun >= PASS.semanticdone) return; @@ -4129,7 +4132,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor override void visit(DtorDeclaration dd) { //printf("DtorDeclaration::semantic() %s\n", toChars()); - //printf("ident: %s, %s, %p, %p\n", ident.toChars(), Id::dtor.toChars(), ident, Id::dtor); + //printf("ident: %s, %s, %p, %p\n", ident.toChars(), Id.dtor.toChars(), ident, Id.dtor); if (dd.semanticRun >= PASS.semanticdone) return; if (dd._scope) @@ -4618,7 +4621,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor buildOpAssign(sd, sc2); buildOpEquals(sd, sc2); - if (global.params.useTypeInfo && Type.dtypeinfo) // these functions are used for TypeInfo + if (!(sc2.flags & SCOPE.Cfile) && + global.params.useTypeInfo && Type.dtypeinfo) // these functions are used for TypeInfo { sd.xeq = buildXopEquals(sd, sc2); sd.xcmp = buildXopCmp(sd, sc2); @@ -4770,6 +4774,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } cldec.semanticRun = PASS.semantic; UserAttributeDeclaration.checkGNUABITag(cldec, sc.linkage); + checkMustUseReserved(cldec); if (cldec.baseok < Baseok.done) { @@ -5019,6 +5024,10 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor cldec.com = true; if (cldec.baseClass.isCPPclass()) cldec.classKind = ClassKind.cpp; + if (cldec.classKind != cldec.baseClass.classKind) + cldec.error("with %s linkage cannot inherit from class `%s` with %s linkage", + cldec.classKind.toChars(), cldec.baseClass.toChars(), cldec.baseClass.classKind.toChars()); + if (cldec.baseClass.stack) cldec.stack = true; cldec.enclosing = cldec.baseClass.enclosing; @@ -5475,6 +5484,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor idec.classKind = ClassKind.cpp; idec.cppnamespace = sc.namespace; UserAttributeDeclaration.checkGNUABITag(idec, sc.linkage); + checkMustUseReserved(idec); if (sc.linkage == LINK.objc) objc.setObjc(idec); diff --git a/gcc/d/dmd/dtemplate.d b/gcc/d/dmd/dtemplate.d index a5ec63ca763..fb41e2bd05f 100644 --- a/gcc/d/dmd/dtemplate.d +++ b/gcc/d/dmd/dtemplate.d @@ -2626,7 +2626,7 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, printf("\t%s %s\n", arg.type.toChars(), arg.toChars()); //printf("\tty = %d\n", arg.type.ty); } - //printf("stc = %llx\n", dstart.scope.stc); + //printf("stc = %llx\n", dstart._scope.stc); //printf("match:t/f = %d/%d\n", ta_last, m.last); } @@ -4332,7 +4332,7 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, TemplateParameters* param { TypeStruct tp = cast(TypeStruct)tparam; - //printf("\t%d\n", (MATCH) t.implicitConvTo(tp)); + //printf("\t%d\n", cast(MATCH) t.implicitConvTo(tp)); if (wm && t.deduceWild(tparam, false)) { result = MATCH.constant; @@ -4513,7 +4513,7 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, TemplateParameters* param { TypeClass tp = cast(TypeClass)tparam; - //printf("\t%d\n", (MATCH) t.implicitConvTo(tp)); + //printf("\t%d\n", cast(MATCH) t.implicitConvTo(tp)); if (wm && t.deduceWild(tparam, false)) { result = MATCH.constant; diff --git a/gcc/d/dmd/errors.d b/gcc/d/dmd/errors.d index 114bd44e32c..62e86a1163c 100644 --- a/gcc/d/dmd/errors.d +++ b/gcc/d/dmd/errors.d @@ -434,8 +434,20 @@ else pragma(printf) extern (C++) void vdeprecationSupplemental(const ref Loc loc, const(char)* format, va_list ap); /** - * Call this after printing out fatal error messages to clean up and exit - * the compiler. + * The type of the fatal error handler + * Returns: true if error handling is done, false to do exit(EXIT_FAILURE) + */ +alias FatalErrorHandler = bool delegate(); + +/** + * The fatal error handler. + * If non-null it will be called for every fatal() call issued by the compiler. + */ +__gshared FatalErrorHandler fatalErrorHandler; + +/** + * Call this after printing out fatal error messages to clean up and exit the + * compiler. You can also set a fatalErrorHandler to override this behaviour. */ extern (C++) void fatal(); diff --git a/gcc/d/dmd/escape.d b/gcc/d/dmd/escape.d index 4196c05489d..44c3757248b 100644 --- a/gcc/d/dmd/escape.d +++ b/gcc/d/dmd/escape.d @@ -316,24 +316,27 @@ bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Parameter par, Exp */ void unsafeAssign(VarDeclaration v, const char* desc) { - if (global.params.useDIP1000 == FeatureState.enabled && sc.func.setUnsafe()) + if (setUnsafeDIP1000(sc.func)) { if (!gag) { if (assertmsg) { - error(arg.loc, "%s `%s` assigned to non-scope parameter calling `assert()`", + previewErrorFunc(sc.isDeprecated(), global.params.useDIP1000) + (arg.loc, "%s `%s` assigned to non-scope parameter calling `assert()`", desc, v.toChars()); } else { - error(arg.loc, "%s `%s` assigned to non-scope parameter `%s` calling %s", + previewErrorFunc(sc.isDeprecated(), global.params.useDIP1000) + (arg.loc, "%s `%s` assigned to non-scope parameter `%s` calling %s", desc, v.toChars(), par ? par.toChars() : "this", fdc ? fdc.toPrettyChars() : "indirectly"); } } - result = true; + if (global.params.useDIP1000 == FeatureState.enabled) + result = true; } } @@ -440,22 +443,33 @@ bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Parameter par, Exp * sc = used to determine current function and module * firstArg = `ref` argument through which `arg` may be assigned * arg = initializer for parameter + * param = parameter declaration corresponding to `arg` * gag = do not print error messages * Returns: * `true` if assignment to `firstArg` would cause an error */ -bool checkParamArgumentReturn(Scope* sc, Expression firstArg, Expression arg, bool gag) +bool checkParamArgumentReturn(Scope* sc, Expression firstArg, Expression arg, Parameter param, bool gag) { enum log = false; if (log) printf("checkParamArgumentReturn(firstArg: %s arg: %s)\n", firstArg.toChars(), arg.toChars()); //printf("type = %s, %d\n", arg.type.toChars(), arg.type.hasPointers()); - if (!arg.type.hasPointers()) + if (!(param.storageClass & STC.return_)) return false; + if (!arg.type.hasPointers() && !param.isReference()) + return false; + + // `byRef` needed for `assign(ref int* x, ref int i) {x = &i};` + // Note: taking address of scope pointer is not allowed + // `assign(ref int** x, return ref scope int* i) {x = &i};` + // Thus no return ref/return scope ambiguity here + const byRef = param.isReference() && !(param.storageClass & STC.scope_) + && !(param.storageClass & STC.returnScope); // fixme: it's possible to infer returnScope without scope with vaIsFirstRef + scope e = new AssignExp(arg.loc, firstArg, arg); - return checkAssignEscape(sc, e, gag); + return checkAssignEscape(sc, e, gag, byRef); } /***************************************************** @@ -496,23 +510,13 @@ bool checkConstructorEscape(Scope* sc, CallExp ce, bool gag) foreach (const i; 0 .. n) { Expression arg = (*ce.arguments)[i]; - if (!arg.type.hasPointers()) - continue; - //printf("\targ[%d]: %s\n", i, arg.toChars()); if (i - j < nparams && i >= j) { Parameter p = tf.parameterList[i - j]; - - if (p.storageClass & STC.return_) - { - /* Fake `dve.e1 = arg;` and look for scope violations - */ - scope e = new AssignExp(arg.loc, dve.e1, arg); - if (checkAssignEscape(sc, e, gag)) - return true; - } + if (checkParamArgumentReturn(sc, dve.e1, arg, p, gag)) + return true; } } @@ -529,13 +533,14 @@ bool checkConstructorEscape(Scope* sc, CallExp ce, bool gag) * sc = used to determine current function and module * e = `AssignExp` or `CatAssignExp` to check for any pointers to the stack * gag = do not print error messages + * byRef = set to `true` if `e1` of `e` gets assigned a reference to `e2` * Returns: * `true` if pointers to the stack can escape via assignment */ -bool checkAssignEscape(Scope* sc, Expression e, bool gag) +bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) { enum log = false; - if (log) printf("checkAssignEscape(e: %s)\n", e.toChars()); + if (log) printf("checkAssignEscape(e: %s, byRef: %d)\n", e.toChars(), byRef); if (e.op != EXP.assign && e.op != EXP.blit && e.op != EXP.construct && e.op != EXP.concatenateAssign && e.op != EXP.concatenateElemAssign && e.op != EXP.concatenateDcharAssign) return false; @@ -561,7 +566,10 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag) EscapeByResults er; - escapeByValue(e2, &er); + if (byRef) + escapeByRef(e2, &er); + else + escapeByValue(e2, &er); if (!er.byref.dim && !er.byvalue.dim && !er.byfunc.dim && !er.byexp.dim) return false; @@ -769,7 +777,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag) if (v.isDataseg()) continue; - if (global.params.useDIP1000 == FeatureState.enabled) + if (global.params.useDIP1000 != FeatureState.disabled) { if (va && va.isScope() && !v.isReference()) { @@ -777,20 +785,36 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag) { va.doNotInferReturn = true; } - else if (fd.setUnsafe()) + else if (setUnsafeDIP1000(fd)) { if (!gag) - error(ae.loc, "address of local variable `%s` assigned to return scope `%s`", v.toChars(), va.toChars()); - result = true; - continue; + previewErrorFunc(sc.isDeprecated(), global.params.useDIP1000) + (ae.loc, "address of local variable `%s` assigned to return scope `%s`", v.toChars(), va.toChars()); + + + if (global.params.useDIP1000 == FeatureState.enabled) + { + result = true; + continue; + } } } } Dsymbol p = v.toParent2(); + if (vaIsFirstRef && v.isParameter() && + !(v.storage_class & STC.return_) && + fd.flags & FUNCFLAG.returnInprocess && + p == fd) + { + //if (log) printf("inferring 'return' for parameter %s in function %s\n", v.toChars(), fd.toChars()); + inferReturn(fd, v, /*returnScope:*/ false); + } + // If va's lifetime encloses v's, then error if (va && + !(vaIsFirstRef && (v.storage_class & STC.return_)) && (va.enclosesLifetimeOf(v) || (va.isReference() && !(va.storage_class & STC.temp)) || va.isDataseg()) && fd.setUnsafe()) { @@ -961,12 +985,11 @@ bool checkThrowEscape(Scope* sc, Expression e, bool gag) if (v.isScope() && !v.iscatchvar) // special case: allow catch var to be rethrown // despite being `scope` { + if (!gag) + previewErrorFunc(sc.isDeprecated(), global.params.useDIP1000) + (e.loc, "scope variable `%s` may not be thrown", v.toChars()); if (global.params.useDIP1000 == FeatureState.enabled) // https://issues.dlang.org/show_bug.cgi?id=17029 - { - if (!gag) - error(e.loc, "scope variable `%s` may not be thrown", v.toChars()); result = true; - } continue; } else @@ -1027,13 +1050,16 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag) */ !(p.parent == sc.func)) { - if (global.params.useDIP1000 == FeatureState.enabled // https://issues.dlang.org/show_bug.cgi?id=17029 - && sc.func.setUnsafe()) // https://issues.dlang.org/show_bug.cgi?id=20868 + if (setUnsafeDIP1000(sc.func)) // https://issues.dlang.org/show_bug.cgi?id=20868 { + // Only look for errors if in module listed on command line if (!gag) - error(e.loc, "scope variable `%s` may not be copied into allocated memory", v.toChars()); - result = true; + previewErrorFunc(sc.isDeprecated(), global.params.useDIP1000) + (e.loc, "scope variable `%s` may not be copied into allocated memory", v.toChars()); + if (global.params.useDIP1000 == FeatureState.enabled) + result = true; } + continue; } } @@ -1243,11 +1269,13 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) ) { // https://issues.dlang.org/show_bug.cgi?id=17029 - if (global.params.useDIP1000 == FeatureState.enabled && sc.func.setUnsafe()) + if (setUnsafeDIP1000(sc.func)) { if (!gag) - error(e.loc, "scope variable `%s` may not be returned", v.toChars()); - result = true; + previewErrorFunc(sc.isDeprecated(), global.params.useDIP1000) + (e.loc, "scope variable `%s` may not be returned", v.toChars()); + if (global.params.useDIP1000 == FeatureState.enabled) + result = true; } continue; } @@ -2326,3 +2354,11 @@ private void addMaybe(VarDeclaration va, VarDeclaration v) va.maybes = new VarDeclarations(); va.maybes.push(v); } + + +private bool setUnsafeDIP1000(FuncDeclaration f) +{ + return global.params.useDIP1000 == FeatureState.enabled + ? f.setUnsafe() + : f.isSafeBypassingInference(); +} diff --git a/gcc/d/dmd/expression.d b/gcc/d/dmd/expression.d index 07d885b0bee..107e85b0793 100644 --- a/gcc/d/dmd/expression.d +++ b/gcc/d/dmd/expression.d @@ -967,11 +967,6 @@ extern (C++) abstract class Expression : ASTNode return null; } - TupleExp toTupleExp() - { - return null; - } - /*************************************** * Return !=0 if expression is an lvalue. */ @@ -1660,6 +1655,7 @@ extern (C++) abstract class Expression : ASTNode inout(MixinExp) isMixinExp() { return op == EXP.mixin_ ? cast(typeof(return))this : null; } inout(ImportExp) isImportExp() { return op == EXP.import_ ? cast(typeof(return))this : null; } inout(AssertExp) isAssertExp() { return op == EXP.assert_ ? cast(typeof(return))this : null; } + inout(ThrowExp) isThrowExp() { return op == EXP.throw_ ? cast(typeof(return))this : null; } inout(DotIdExp) isDotIdExp() { return op == EXP.dotIdentifier ? cast(typeof(return))this : null; } inout(DotTemplateExp) isDotTemplateExp() { return op == EXP.dotTemplateDeclaration ? cast(typeof(return))this : null; } inout(DotVarExp) isDotVarExp() { return op == EXP.dotVariable ? cast(typeof(return))this : null; } @@ -1745,6 +1741,7 @@ extern (C++) abstract class Expression : ASTNode inout(ModuleInitExp) isModuleInitExp() { return op == EXP.moduleString ? cast(typeof(return))this : null; } inout(FuncInitExp) isFuncInitExp() { return op == EXP.functionString ? cast(typeof(return))this : null; } inout(PrettyFuncInitExp) isPrettyFuncInitExp() { return op == EXP.prettyFunction ? cast(typeof(return))this : null; } + inout(ObjcClassReferenceExp) isObjcClassReferenceExp() { return op == EXP.objcClassReference ? cast(typeof(return))this : null; } inout(ClassReferenceExp) isClassReferenceExp() { return op == EXP.classReference ? cast(typeof(return))this : null; } inout(ThrownExceptionExp) isThrownExceptionExp() { return op == EXP.thrownException ? cast(typeof(return))this : null; } @@ -2684,7 +2681,7 @@ extern (C++) final class StringExp : Expression const len2 = se2.len; assert(this.sz == se2.sz, "Comparing string expressions of different sizes"); - //printf("sz = %d, len1 = %d, len2 = %d\n", sz, (int)len1, (int)len2); + //printf("sz = %d, len1 = %d, len2 = %d\n", sz, cast(int)len1, cast(int)len2); if (len1 == len2) { switch (sz) @@ -2887,11 +2884,6 @@ extern (C++) final class TupleExp : Expression return new TupleExp(loc, exps); } - override TupleExp toTupleExp() - { - return this; - } - override TupleExp syntaxCopy() { return new TupleExp(loc, e0 ? e0.syntaxCopy() : null, arraySyntaxCopy(exps)); diff --git a/gcc/d/dmd/expression.h b/gcc/d/dmd/expression.h index 9522b23a3b1..330dcdb77ec 100644 --- a/gcc/d/dmd/expression.h +++ b/gcc/d/dmd/expression.h @@ -103,7 +103,6 @@ public: virtual real_t toImaginary(); virtual complex_t toComplex(); virtual StringExp *toStringExp(); - virtual TupleExp *toTupleExp(); virtual bool isLvalue(); virtual Expression *toLvalue(Scope *sc, Expression *e); virtual Expression *modifiableLvalue(Scope *sc, Expression *e); @@ -374,11 +373,11 @@ public: OwnedBy ownedByCtfe; static StringExp *create(const Loc &loc, const char *s); - static StringExp *create(const Loc &loc, const void *s, size_t len); + static StringExp *create(const Loc &loc, const void *s, d_size_t len); static void emplace(UnionExp *pue, const Loc &loc, const char *s); bool equals(const RootObject *o) const; - char32_t getCodeUnit(size_t i) const; - void setCodeUnit(size_t i, char32_t c); + char32_t getCodeUnit(d_size_t i) const; + void setCodeUnit(d_size_t i, char32_t c); StringExp *toStringExp(); StringExp *toUTF8(Scope *sc); Optional<bool> toBool(); @@ -406,7 +405,6 @@ public: Expressions *exps; static TupleExp *create(const Loc &loc, Expressions *exps); - TupleExp *toTupleExp(); TupleExp *syntaxCopy(); bool equals(const RootObject *o) const; diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d index 7b7c489295a..d4e96bb0f09 100644 --- a/gcc/d/dmd/expressionsem.d +++ b/gcc/d/dmd/expressionsem.d @@ -56,6 +56,7 @@ import dmd.initsem; import dmd.inline; import dmd.intrange; import dmd.mtype; +import dmd.mustuse; import dmd.nspace; import dmd.opover; import dmd.optimize; @@ -713,7 +714,7 @@ private Expression resolveUFCSProperties(Scope* sc, Expression e1, Expression e2 */ Expression resolvePropertiesOnly(Scope* sc, Expression e1) { - //printf("e1 = %s %s\n", Token::toChars(e1.op), e1.toChars()); + //printf("e1 = %s %s\n", Token.toChars(e1.op), e1.toChars()); Expression handleOverloadSet(OverloadSet os) { @@ -2027,15 +2028,19 @@ private bool functionParameters(const ref Loc loc, Scope* sc, //printf("type: %s\n", arg.type.toChars()); //printf("param: %s\n", p.toChars()); - if (firstArg && p.storageClass & STC.return_) + const pStc = tf.parameterStorageClass(tthis, p); + + if (firstArg && (pStc & STC.return_)) { /* Argument value can be assigned to firstArg. * Check arg to see if it matters. */ if (global.params.useDIP1000 == FeatureState.enabled) - err |= checkParamArgumentReturn(sc, firstArg, arg, false); + err |= checkParamArgumentReturn(sc, firstArg, arg, p, false); } - else if (tf.parameterEscapes(tthis, p)) + // Allow 'lazy' to imply 'scope' - lazy parameters can be passed along + // as lazy parameters to the next function, but that isn't escaping. + else if (!(pStc & (STC.scope_ | STC.lazy_))) { /* Argument value can escape from the called function. * Check arg to see if it matters. @@ -2043,7 +2048,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, if (global.params.useDIP1000 == FeatureState.enabled) err |= checkParamArgumentEscape(sc, fd, p, arg, false, false); } - else if (!(p.storageClass & STC.return_)) + else if (!(pStc & STC.return_)) { /* Argument value cannot escape from the called function. */ @@ -3228,7 +3233,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } Type t = cle.type.typeSemantic(cle.loc, sc); auto init = initializerSemantic(cle.initializer, sc, t, INITnointerpret); - auto e = initializerToExpression(init, t); + auto e = initializerToExpression(init, t, (sc.flags & SCOPE.Cfile) != 0); if (!e) { error(cle.loc, "cannot convert initializer `%s` to expression", init.toChars()); @@ -3261,7 +3266,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // printf("apply fix for issue 9490: add `this.` to `%s`...\n", e.toChars()); e = new DotVarExp(exp.loc, new ThisExp(exp.loc), ve.var, false); } - //printf("e = %s %s\n", Token::toChars(e.op), e.toChars()); + //printf("e = %s %s\n", Token.toChars(e.op), e.toChars()); e = e.expressionSemantic(sc); } else if (t) @@ -5211,13 +5216,30 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (s.ident) { VarDeclaration v = s.isVarDeclaration(); - if (v && !(sc.flags & SCOPE.Cfile)) + if (v) { - /* Do semantic() on initializer first so this will be illegal: - * int a = a; - */ - e.declaration.dsymbolSemantic(sc); - s.parent = sc.parent; + if (sc.flags & SCOPE.Cfile) + { + /* Do semantic() on the type before inserting v into the symbol table + */ + if (!v.originalType) + v.originalType = v.type.syntaxCopy(); + Scope* sc2 = sc.push(); + sc2.stc |= v.storage_class & STC.FUNCATTR; + sc2.linkage = LINK.c; // account for the extern(C) in front of the declaration + v.inuse++; + v.type = v.type.typeSemantic(v.loc, sc2); + v.inuse--; + sc2.pop(); + } + else + { + /* Do semantic() on initializer first so this will be illegal: + * int a = a; + */ + e.declaration.dsymbolSemantic(sc); + s.parent = sc.parent; + } } if (!sc.insert(s)) @@ -6032,7 +6054,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor auto fileName = FileName(name.toDString); if (auto fmResult = global.fileManager.lookup(fileName)) { - se = new StringExp(e.loc, fmResult.data); + se = new StringExp(e.loc, fmResult); } else { @@ -6047,9 +6069,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // take ownership of buffer (probably leaking) auto data = readResult.extractSlice(); se = new StringExp(e.loc, data); - - FileBuffer* fileBuffer = new FileBuffer(data); - global.fileManager.add(fileName, fileBuffer); + global.fileManager.add(fileName, data); } } } @@ -6388,7 +6408,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor static if (LOGSEMANTIC) { printf("DotIdExp::semantic(this = %p, '%s')\n", exp, exp.toChars()); - //printf("e1.op = %d, '%s'\n", e1.op, Token::toChars(e1.op)); + //printf("e1.op = %d, '%s'\n", e1.op, Token.toChars(e1.op)); } if (sc.flags & SCOPE.Cfile) @@ -8177,7 +8197,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; if (e.type is Type.tvoid) + { + checkMustUse(e.e1, sc); discardValue(e.e1); + } else if (!e.allowCommaExp && !e.isGenerated) e.error("Using the result of a comma expression is not allowed"); } @@ -8956,7 +8979,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Parameter arg = Parameter.getNth(tt.arguments, u); //printf("[%d] iexps.dim = %d, ", u, iexps.dim); - //printf("e = (%s %s, %s), ", Token::tochars[e.op], e.toChars(), e.type.toChars()); + //printf("e = (%s %s, %s), ", Token.toChars[e.op], e.toChars(), e.type.toChars()); //printf("arg = (%s, %s)\n", arg.toChars(), arg.type.toChars()); if (!arg || !e.type.implicitConvTo(arg.type)) @@ -9849,7 +9872,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor * `reorderSettingAAElem` creates a tree of comma expressions, however, * `checkAssignExp` expects only AssignExps. */ - checkAssignEscape(sc, Expression.extractLast(res, tmp), false); + checkAssignEscape(sc, Expression.extractLast(res, tmp), false, false); if (auto ae = res.isConstructExp()) { @@ -10124,7 +10147,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ if (isRecursiveAliasThis(exp.att1, exp.e1.type)) return null; - //printf("att %s e1 = %s\n", Token::toChars(e.op), e.e1.type.toChars()); + //printf("att %s e1 = %s\n", Token.toChars(e.op), e.e1.type.toChars()); Expression e1 = new DotIdExp(exp.loc, exp.e1, ad1.aliasthis.ident); BinExp be = cast(BinExp)exp.copy(); be.e1 = e1; @@ -10142,7 +10165,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ if (isRecursiveAliasThis(exp.att2, exp.e2.type)) return null; - //printf("att %s e2 = %s\n", Token::toChars(e.op), e.e2.type.toChars()); + //printf("att %s e2 = %s\n", Token.toChars(e.op), e.e2.type.toChars()); Expression e2 = new DotIdExp(exp.loc, exp.e2, ad2.aliasthis.ident); BinExp be = cast(BinExp)exp.copy(); be.e2 = e2; @@ -10169,7 +10192,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor auto res = exp.reorderSettingAAElem(sc); if ((exp.op == EXP.concatenateElemAssign || exp.op == EXP.concatenateDcharAssign) && global.params.useDIP1000 == FeatureState.enabled) - checkAssignEscape(sc, res, false); + checkAssignEscape(sc, res, false, false); result = res; } @@ -12193,7 +12216,7 @@ Expression semanticX(DotIdExp exp, Scope* sc) if (Expression ex = unaSemantic(exp, sc)) return ex; - if (exp.ident == Id._mangleof) + if (!(sc.flags & SCOPE.Cfile) && exp.ident == Id._mangleof) { // symbol.mangleof @@ -12302,6 +12325,8 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag) //{ static int z; fflush(stdout); if (++z == 10) *(char*)0=0; } + const cfile = (sc.flags & SCOPE.Cfile) != 0; + /* Special case: rewrite this.id and super.id * to be classtype.id and baseclasstype.id * if we have no this pointer. @@ -12553,7 +12578,16 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag) exp.error("undefined identifier `%s` in %s `%s`", exp.ident.toChars(), ie.sds.kind(), ie.sds.toPrettyChars()); return ErrorExp.get(); } - else if (t1b.ty == Tpointer && exp.e1.type.ty != Tenum && exp.ident != Id._init && exp.ident != Id.__sizeof && exp.ident != Id.__xalignof && exp.ident != Id.offsetof && exp.ident != Id._mangleof && exp.ident != Id.stringof) + else if (t1b.ty == Tpointer && exp.e1.type.ty != Tenum && + !( + exp.ident == Id.__sizeof || + exp.ident == Id.__xalignof || + !cfile && + (exp.ident == Id._mangleof || + exp.ident == Id.offsetof || + exp.ident == Id._init || + exp.ident == Id.stringof) + )) { Type t1bn = t1b.nextOf(); if (flag) @@ -12588,7 +12622,7 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag) Expression e = new IntegerExp(exp.loc, actualAlignment, Type.tsize_t); return e; } - else if (sc.flags & SCOPE.Cfile && exp.ident == Id.__sizeof && exp.e1.isStringExp()) + else if (cfile && exp.ident == Id.__sizeof && exp.e1.isStringExp()) { // Sizeof string literal includes the terminating 0 auto se = exp.e1.isStringExp(); @@ -12803,129 +12837,125 @@ bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false) return false; } - //printf("checkSharedAccess() %s\n", e.toChars()); - - static extern(C++) final class SharedCheckVisitor : SemanticTimeTransitiveVisitor - { - /// In case we don't know which expression triggered it, - /// e.g. for `visit(Type)` overload - Expression original; - /// Where the result is stored (`true` == error) - bool result; - /// Whether we should allow one level of dereferencing - bool allowRef; + //printf("checkSharedAccess() `%s` returnRef: %d\n", e.toChars(), returnRef); - /// Ctor - this(Expression oe, bool allowRef_) - { - this.original = oe; - this.allowRef = allowRef_; - } + /* In case we don't know which expression triggered it, + * e.g. for `visit(Type)` overload + */ + Expression original = e; - void sharedError(Expression e) + bool check(Expression e, bool allowRef) + { + bool sharedError(Expression e) { // https://dlang.org/phobos/core_atomic.html e.error("direct access to shared `%s` is not allowed, see `core.atomic`", e.toChars()); - this.result = true; + return true; } - /// Introduce base class overrides - alias visit = SemanticTimeTransitiveVisitor.visit; - // Error by default - override void visit(Expression e) + bool visit(Expression e) { if (e.type.isShared()) - this.sharedError(e); + return sharedError(e); + return false; } - /// Ditto - override void visit(Type t) + bool visitNew(NewExp e) { + if (e.thisexp) + check(e.thisexp, false); // Note: This handles things like `new shared(Throwable).msg`, // where accessing `msg` would violate `shared`. - if (t.isShared()) - this.sharedError(this.original); + if (e.newtype.isShared()) + return sharedError(original); + return false; } - // Those have no indirections / can be ignored - override void visit(ErrorExp e) {} - override void visit(ComplexExp e) {} - override void visit(IntegerExp e) {} - override void visit(NullExp e) {} - - override void visit(VarExp e) + bool visitVar(VarExp e) { - if (!this.allowRef && e.var.type.isShared()) - this.sharedError(e); + if (!allowRef && e.var.type.isShared()) + return sharedError(e); + return false; } - override void visit(AddrExp e) + bool visitAddr(AddrExp e) { - this.allowRef = true; - e.e1.accept(this); + return check(e.e1, true); } - override void visit(PtrExp e) + bool visitPtr(PtrExp e) { - if (!this.allowRef && e.type.isShared()) - return this.sharedError(e); + if (!allowRef && e.type.isShared()) + return sharedError(e); if (e.e1.type.isShared()) - return this.sharedError(e); + return sharedError(e); - this.allowRef = false; - e.e1.accept(this); + return check(e.e1, false); } - override void visit(DotVarExp e) + bool visitDotVar(DotVarExp e) { auto fd = e.var.isFuncDeclaration(); const sharedFunc = fd && fd.type.isShared; - if (!this.allowRef && e.type.isShared() && !sharedFunc) - return this.sharedError(e); + if (!allowRef && e.type.isShared() && !sharedFunc) + return sharedError(e); - // Allow to use `DotVarExp` within value types - if (e.e1.type.ty == Tsarray || e.e1.type.ty == Tstruct) - return e.e1.accept(this); + // Allow using `DotVarExp` within value types + if (e.e1.type.isTypeSArray() || e.e1.type.isTypeStruct()) + return check(e.e1, allowRef); // If we end up with a single `VarExp`, it might be a `ref` param // `shared ref T` param == `shared(T)*`. if (auto ve = e.e1.isVarExp()) { - this.allowRef = this.allowRef && (ve.var.storage_class & STC.ref_); - return e.e1.accept(this); + return check(e.e1, allowRef && (ve.var.storage_class & STC.ref_)); } - this.allowRef = false; - return e.e1.accept(this); + return check(e.e1, false); } - override void visit(IndexExp e) + bool visitIndex(IndexExp e) { - if (!this.allowRef && e.type.isShared()) - return this.sharedError(e); + if (!allowRef && e.type.isShared()) + return sharedError(e); if (e.e1.type.isShared()) - return this.sharedError(e); + return sharedError(e); - this.allowRef = false; - e.e1.accept(this); + return check(e.e1, false); } - override void visit(CommaExp e) + bool visitComma(CommaExp e) { // Cannot be `return ref` since we can't use the return, // but it's better to show that error than an unrelated `shared` one - this.allowRef = true; - e.e2.accept(this); + return check(e.e2, true); + } + + switch (e.op) + { + default: return visit(e); + + // Those have no indirections / can be ignored + case EXP.call: + case EXP.error: + case EXP.complex80: + case EXP.int64: + case EXP.null_: return false; + + case EXP.variable: return visitVar(e.isVarExp()); + case EXP.new_: return visitNew(e.isNewExp()); + case EXP.address: return visitAddr(e.isAddrExp()); + case EXP.star: return visitPtr(e.isPtrExp()); + case EXP.dotVariable: return visitDotVar(e.isDotVarExp()); + case EXP.index: return visitIndex(e.isIndexExp()); } } - scope visitor = new SharedCheckVisitor(e, returnRef); - e.accept(visitor); - return visitor.result; + return check(e, returnRef); } diff --git a/gcc/d/dmd/file_manager.d b/gcc/d/dmd/file_manager.d index b86c7995562..9fe40bf0603 100644 --- a/gcc/d/dmd/file_manager.d +++ b/gcc/d/dmd/file_manager.d @@ -11,7 +11,7 @@ module dmd.file_manager; import dmd.root.stringtable : StringTable; -import dmd.root.file : File, FileBuffer; +import dmd.root.file : File, Buffer; import dmd.root.filename : FileName; import dmd.root.string : toDString; import dmd.globals; @@ -22,7 +22,7 @@ enum package_di = "package." ~ hdr_ext; final class FileManager { - private StringTable!(FileBuffer*) files; + private StringTable!(const(ubyte)[]) files; /// public this () nothrow @@ -146,7 +146,7 @@ nothrow: * Returns: the loaded source file if it was found in memory, * otherwise `null` */ - const(FileBuffer)* lookup(FileName filename) + const(ubyte)[] lookup(FileName filename) { const name = filename.toString; if (auto val = files.lookup(name)) @@ -154,7 +154,7 @@ nothrow: if (name == "__stdin.d") { - auto buffer = new FileBuffer(readFromStdin().extractSlice()); + auto buffer = readFromStdin().extractSlice(); if (this.files.insert(name, buffer) is null) assert(0, "stdin: Insert after lookup failure should never return `null`"); return buffer; @@ -167,7 +167,7 @@ nothrow: if (!readResult.success) return null; - FileBuffer* fb = new FileBuffer(readResult.extractSlice()); + auto fb = readResult.extractSlice(); if (files.insert(name, fb) is null) assert(0, "Insert after lookup failure should never return `null`"); @@ -187,17 +187,17 @@ nothrow: const(char)[][] lines; if (const buffer = lookup(file)) { - const slice = buffer.data[0 .. buffer.data.length]; + const slice = buffer; size_t start, end; - ubyte c; for (auto i = 0; i < slice.length; i++) { - c = slice[i]; + const c = slice[i]; if (c == '\n' || c == '\r') { if (i != 0) { end = i; + // Appending lines one at a time will certainly be slow lines ~= cast(const(char)[])slice[start .. end]; } // Check for Windows-style CRLF newlines @@ -234,18 +234,21 @@ nothrow: } /** - * Adds a FileBuffer to the table. - * - * Returns: The FileBuffer added, or null + * Adds the contents of a file to the table. + * Params: + * filename = name of the file + * buffer = contents of the file + * Returns: + * the buffer added, or null */ - FileBuffer* add(FileName filename, FileBuffer* filebuffer) + const(ubyte)[] add(FileName filename, const(ubyte)[] buffer) { - auto val = files.insert(filename.toString, filebuffer); + auto val = files.insert(filename.toString, buffer); return val == null ? null : val.value; } } -private FileBuffer readFromStdin() nothrow +private Buffer readFromStdin() nothrow { import core.stdc.stdio; import dmd.errors; @@ -277,7 +280,7 @@ private FileBuffer readFromStdin() nothrow // We're done assert(pos < sz + 2); buffer[pos .. pos + 4] = '\0'; - return FileBuffer(buffer[0 .. pos]); + return Buffer(buffer[0 .. pos]); } } while (pos < sz); diff --git a/gcc/d/dmd/hdrgen.d b/gcc/d/dmd/hdrgen.d index a3afbe50796..69fdf27e1db 100644 --- a/gcc/d/dmd/hdrgen.d +++ b/gcc/d/dmd/hdrgen.d @@ -1538,6 +1538,13 @@ public: } else if (hgs.tpltMember == 0 && global.params.hdrStripPlainFunctions) { + if (!f.fbody) + { + // this can happen on interfaces / abstract functions, see `allowsContractWithoutBody` + if (f.fensures || f.frequires) + buf.writenl(); + contractsToBuffer(f); + } buf.writeByte(';'); buf.writenl(); } @@ -1548,19 +1555,9 @@ public: bodyToBuffer(f); } - void bodyToBuffer(FuncDeclaration f) + /// Returns: whether `do` is needed to write the function body + bool contractsToBuffer(FuncDeclaration f) { - if (!f.fbody || (hgs.hdrgen && global.params.hdrStripPlainFunctions && !hgs.autoMember && !hgs.tpltMember)) - { - buf.writeByte(';'); - buf.writenl(); - return; - } - const savetlpt = hgs.tpltMember; - const saveauto = hgs.autoMember; - hgs.tpltMember = 0; - hgs.autoMember = 0; - buf.writenl(); bool requireDo = false; // in{} if (f.frequires) @@ -1619,6 +1616,29 @@ public: } } } + return requireDo; + } + + void bodyToBuffer(FuncDeclaration f) + { + if (!f.fbody || (hgs.hdrgen && global.params.hdrStripPlainFunctions && !hgs.autoMember && !hgs.tpltMember)) + { + if (!f.fbody && (f.fensures || f.frequires)) + { + buf.writenl(); + contractsToBuffer(f); + } + buf.writeByte(';'); + buf.writenl(); + return; + } + const savetlpt = hgs.tpltMember; + const saveauto = hgs.autoMember; + hgs.tpltMember = 0; + hgs.autoMember = 0; + buf.writenl(); + bool requireDo = contractsToBuffer(f); + if (requireDo) { buf.writestring("do"); @@ -1788,26 +1808,17 @@ public: } } -private extern (C++) final class ExpressionPrettyPrintVisitor : Visitor +/********************************************* + * Print expression to buffer. + */ +private void expressionPrettyPrint(Expression e, OutBuffer* buf, HdrGenState* hgs) { - alias visit = Visitor.visit; -public: - OutBuffer* buf; - HdrGenState* hgs; - - extern (D) this(OutBuffer* buf, HdrGenState* hgs) - { - this.buf = buf; - this.hgs = hgs; - } - - //////////////////////////////////////////////////////////////////////////// - override void visit(Expression e) + void visit(Expression e) { buf.writestring(EXPtoString(e.op)); } - override void visit(IntegerExp e) + void visitInteger(IntegerExp e) { const dinteger_t v = e.toInteger(); if (e.type) @@ -1907,12 +1918,12 @@ public: buf.print(v); } - override void visit(ErrorExp e) + void visitError(ErrorExp e) { buf.writestring("__error"); } - override void visit(VoidInitExp e) + void visitVoidInit(VoidInitExp e) { buf.writestring("__void"); } @@ -1922,12 +1933,12 @@ public: .floatToBuffer(type, value, buf, hgs.hdrgen); } - override void visit(RealExp e) + void visitReal(RealExp e) { floatToBuffer(e.type, e.value); } - override void visit(ComplexExp e) + void visitComplex(ComplexExp e) { /* Print as: * (re+imi) @@ -1939,7 +1950,7 @@ public: buf.writestring("i)"); } - override void visit(IdentifierExp e) + void visitIdentifier(IdentifierExp e) { if (hgs.hdrgen || hgs.ddoc) buf.writestring(e.ident.toHChars2()); @@ -1947,27 +1958,27 @@ public: buf.writestring(e.ident.toString()); } - override void visit(DsymbolExp e) + void visitDsymbol(DsymbolExp e) { buf.writestring(e.s.toChars()); } - override void visit(ThisExp e) + void visitThis(ThisExp e) { buf.writestring("this"); } - override void visit(SuperExp e) + void visitSuper(SuperExp e) { buf.writestring("super"); } - override void visit(NullExp e) + void visitNull(NullExp e) { buf.writestring("null"); } - override void visit(StringExp e) + void visitString(StringExp e) { buf.writeByte('"'); const o = buf.length; @@ -1982,14 +1993,14 @@ public: buf.writeByte(e.postfix); } - override void visit(ArrayLiteralExp e) + void visitArrayLiteral(ArrayLiteralExp e) { buf.writeByte('['); argsToBuffer(e.elements, buf, hgs, e.basis); buf.writeByte(']'); } - override void visit(AssocArrayLiteralExp e) + void visitAssocArrayLiteral(AssocArrayLiteralExp e) { buf.writeByte('['); foreach (i, key; *e.keys) @@ -2004,7 +2015,7 @@ public: buf.writeByte(']'); } - override void visit(StructLiteralExp e) + void visitStructLiteral(StructLiteralExp e) { buf.writestring(e.sd.toChars()); buf.writeByte('('); @@ -2024,7 +2035,7 @@ public: buf.writeByte(')'); } - override void visit(CompoundLiteralExp e) + void visitCompoundLiteral(CompoundLiteralExp e) { buf.writeByte('('); typeToBuffer(e.type, null, buf, hgs); @@ -2032,12 +2043,12 @@ public: e.initializer.initializerToBuffer(buf, hgs); } - override void visit(TypeExp e) + void visitType(TypeExp e) { typeToBuffer(e.type, null, buf, hgs); } - override void visit(ScopeExp e) + void visitScope(ScopeExp e) { if (e.sds.isTemplateInstance()) { @@ -2059,12 +2070,12 @@ public: } } - override void visit(TemplateExp e) + void visitTemplate(TemplateExp e) { buf.writestring(e.td.toChars()); } - override void visit(NewExp e) + void visitNew(NewExp e) { if (e.thisexp) { @@ -2081,7 +2092,7 @@ public: } } - override void visit(NewAnonClassExp e) + void visitNewAnonClass(NewAnonClassExp e) { if (e.thisexp) { @@ -2100,7 +2111,7 @@ public: e.cd.dsymbolToBuffer(buf, hgs); } - override void visit(SymOffExp e) + void visitSymOff(SymOffExp e) { if (e.offset) buf.printf("(& %s%+lld)", e.var.toChars(), e.offset); @@ -2110,22 +2121,22 @@ public: buf.printf("& %s", e.var.toChars()); } - override void visit(VarExp e) + void visitVar(VarExp e) { buf.writestring(e.var.toChars()); } - override void visit(OverExp e) + void visitOver(OverExp e) { buf.writestring(e.vars.ident.toString()); } - override void visit(TupleExp e) + void visitTuple(TupleExp e) { if (e.e0) { buf.writeByte('('); - e.e0.accept(this); + e.e0.expressionPrettyPrint(buf, hgs); buf.writestring(", tuple("); argsToBuffer(e.exps, buf, hgs); buf.writestring("))"); @@ -2138,13 +2149,13 @@ public: } } - override void visit(FuncExp e) + void visitFunc(FuncExp e) { e.fd.dsymbolToBuffer(buf, hgs); //buf.writestring(e.fd.toChars()); } - override void visit(DeclarationExp e) + void visitDeclaration(DeclarationExp e) { /* Normal dmd execution won't reach here - regular variable declarations * are handled in visit(ExpStatement), so here would be used only when @@ -2170,14 +2181,14 @@ public: } } - override void visit(TypeidExp e) + void visitTypeid(TypeidExp e) { buf.writestring("typeid("); objectToBuffer(e.obj, buf, hgs); buf.writeByte(')'); } - override void visit(TraitsExp e) + void visitTraits(TraitsExp e) { buf.writestring("__traits("); if (e.ident) @@ -2193,12 +2204,12 @@ public: buf.writeByte(')'); } - override void visit(HaltExp e) + void visitHalt(HaltExp e) { buf.writestring("halt"); } - override void visit(IsExp e) + void visitIs(IsExp e) { buf.writestring("is("); typeToBuffer(e.targ, e.id, buf, hgs); @@ -2223,13 +2234,13 @@ public: buf.writeByte(')'); } - override void visit(UnaExp e) + void visitUna(UnaExp e) { buf.writestring(EXPtoString(e.op)); expToBuffer(e.e1, precedence[e.op], buf, hgs); } - override void visit(BinExp e) + void visitBin(BinExp e) { expToBuffer(e.e1, precedence[e.op], buf, hgs); buf.writeByte(' '); @@ -2238,7 +2249,7 @@ public: expToBuffer(e.e2, cast(PREC)(precedence[e.op] + 1), buf, hgs); } - override void visit(CommaExp e) + void visitComma(CommaExp e) { // CommaExp is generated by the compiler so it shouldn't // appear in error messages or header files. @@ -2251,7 +2262,7 @@ public: // the old path if (!ve || !(ve.var.storage_class & STC.temp)) { - visit(cast(BinExp)e); + visitBin(cast(BinExp)e); return; } @@ -2281,25 +2292,25 @@ public: } // not one of the known cases, go on the old path - visit(cast(BinExp)e); + visitBin(cast(BinExp)e); return; } - override void visit(MixinExp e) + void visitMixin(MixinExp e) { buf.writestring("mixin("); argsToBuffer(e.exps, buf, hgs, null); buf.writeByte(')'); } - override void visit(ImportExp e) + void visitImport(ImportExp e) { buf.writestring("import("); expToBuffer(e.e1, PREC.assign, buf, hgs); buf.writeByte(')'); } - override void visit(AssertExp e) + void visitAssert(AssertExp e) { buf.writestring("assert("); expToBuffer(e.e1, PREC.assign, buf, hgs); @@ -2311,13 +2322,13 @@ public: buf.writeByte(')'); } - override void visit(ThrowExp e) + void visitThrow(ThrowExp e) { buf.writestring("throw "); expToBuffer(e.e1, PREC.unary, buf, hgs); } - override void visit(DotIdExp e) + void visitDotId(DotIdExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); if (e.arrow) @@ -2327,28 +2338,28 @@ public: buf.writestring(e.ident.toString()); } - override void visit(DotTemplateExp e) + void visitDotTemplate(DotTemplateExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); buf.writeByte('.'); buf.writestring(e.td.toChars()); } - override void visit(DotVarExp e) + void visitDotVar(DotVarExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); buf.writeByte('.'); buf.writestring(e.var.toChars()); } - override void visit(DotTemplateInstanceExp e) + void visitDotTemplateInstance(DotTemplateInstanceExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); buf.writeByte('.'); e.ti.dsymbolToBuffer(buf, hgs); } - override void visit(DelegateExp e) + void visitDelegate(DelegateExp e) { buf.writeByte('&'); if (!e.func.isNested() || e.func.needThis()) @@ -2359,14 +2370,14 @@ public: buf.writestring(e.func.toChars()); } - override void visit(DotTypeExp e) + void visitDotType(DotTypeExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); buf.writeByte('.'); buf.writestring(e.sym.toChars()); } - override void visit(CallExp e) + void visitCall(CallExp e) { if (e.e1.op == EXP.type) { @@ -2375,7 +2386,7 @@ public: * This is ok since types in constructor calls * can never depend on parens anyway */ - e.e1.accept(this); + e.e1.expressionPrettyPrint(buf, hgs); } else expToBuffer(e.e1, precedence[e.op], buf, hgs); @@ -2384,19 +2395,19 @@ public: buf.writeByte(')'); } - override void visit(PtrExp e) + void visitPtr(PtrExp e) { buf.writeByte('*'); expToBuffer(e.e1, precedence[e.op], buf, hgs); } - override void visit(DeleteExp e) + void visitDelete(DeleteExp e) { buf.writestring("delete "); expToBuffer(e.e1, precedence[e.op], buf, hgs); } - override void visit(CastExp e) + void visitCast(CastExp e) { buf.writestring("cast("); if (e.to) @@ -2409,7 +2420,7 @@ public: expToBuffer(e.e1, precedence[e.op], buf, hgs); } - override void visit(VectorExp e) + void visitVector(VectorExp e) { buf.writestring("cast("); typeToBuffer(e.to, null, buf, hgs); @@ -2417,13 +2428,13 @@ public: expToBuffer(e.e1, precedence[e.op], buf, hgs); } - override void visit(VectorArrayExp e) + void visitVectorArray(VectorArrayExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); buf.writestring(".array"); } - override void visit(SliceExp e) + void visitSlice(SliceExp e) { expToBuffer(e.e1, precedence[e.op], buf, hgs); buf.writeByte('['); @@ -2442,32 +2453,32 @@ public: buf.writeByte(']'); } - override void visit(ArrayLengthExp e) + void visitArrayLength(ArrayLengthExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); buf.writestring(".length"); } - override void visit(IntervalExp e) + void visitInterval(IntervalExp e) { expToBuffer(e.lwr, PREC.assign, buf, hgs); buf.writestring(".."); expToBuffer(e.upr, PREC.assign, buf, hgs); } - override void visit(DelegatePtrExp e) + void visitDelegatePtr(DelegatePtrExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); buf.writestring(".ptr"); } - override void visit(DelegateFuncptrExp e) + void visitDelegateFuncptr(DelegateFuncptrExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); buf.writestring(".funcptr"); } - override void visit(ArrayExp e) + void visitArray(ArrayExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); buf.writeByte('['); @@ -2475,14 +2486,14 @@ public: buf.writeByte(']'); } - override void visit(DotExp e) + void visitDot(DotExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); buf.writeByte('.'); expToBuffer(e.e2, PREC.primary, buf, hgs); } - override void visit(IndexExp e) + void visitIndex(IndexExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); buf.writeByte('['); @@ -2490,19 +2501,19 @@ public: buf.writeByte(']'); } - override void visit(PostExp e) + void visitPost(PostExp e) { expToBuffer(e.e1, precedence[e.op], buf, hgs); buf.writestring(EXPtoString(e.op)); } - override void visit(PreExp e) + void visitPre(PreExp e) { buf.writestring(EXPtoString(e.op)); expToBuffer(e.e1, precedence[e.op], buf, hgs); } - override void visit(RemoveExp e) + void visitRemove(RemoveExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); buf.writestring(".remove("); @@ -2510,7 +2521,7 @@ public: buf.writeByte(')'); } - override void visit(CondExp e) + void visitCond(CondExp e) { expToBuffer(e.econd, PREC.oror, buf, hgs); buf.writestring(" ? "); @@ -2519,15 +2530,90 @@ public: expToBuffer(e.e2, PREC.cond, buf, hgs); } - override void visit(DefaultInitExp e) + void visitDefaultInit(DefaultInitExp e) { buf.writestring(EXPtoString(e.op)); } - override void visit(ClassReferenceExp e) + void visitClassReference(ClassReferenceExp e) { buf.writestring(e.value.toChars()); } + + switch (e.op) + { + default: + if (auto be = e.isBinExp()) + return visitBin(be); + else if (auto ue = e.isUnaExp()) + return visitUna(ue); + else if (auto de = e.isDefaultInitExp()) + return visitDefaultInit(e.isDefaultInitExp()); + return visit(e); + + case EXP.int64: return visitInteger(e.isIntegerExp()); + case EXP.error: return visitError(e.isErrorExp()); + case EXP.void_: return visitVoidInit(e.isVoidInitExp()); + case EXP.float64: return visitReal(e.isRealExp()); + case EXP.complex80: return visitComplex(e.isComplexExp()); + case EXP.identifier: return visitIdentifier(e.isIdentifierExp()); + case EXP.dSymbol: return visitDsymbol(e.isDsymbolExp()); + case EXP.this_: return visitThis(e.isThisExp()); + case EXP.super_: return visitSuper(e.isSuperExp()); + case EXP.null_: return visitNull(e.isNullExp()); + case EXP.string_: return visitString(e.isStringExp()); + case EXP.arrayLiteral: return visitArrayLiteral(e.isArrayLiteralExp()); + case EXP.assocArrayLiteral: return visitAssocArrayLiteral(e.isAssocArrayLiteralExp()); + case EXP.structLiteral: return visitStructLiteral(e.isStructLiteralExp()); + case EXP.compoundLiteral: return visitCompoundLiteral(e.isCompoundLiteralExp()); + case EXP.type: return visitType(e.isTypeExp()); + case EXP.scope_: return visitScope(e.isScopeExp()); + case EXP.template_: return visitTemplate(e.isTemplateExp()); + case EXP.new_: return visitNew(e.isNewExp()); + case EXP.newAnonymousClass: return visitNewAnonClass(e.isNewAnonClassExp()); + case EXP.symbolOffset: return visitSymOff(e.isSymOffExp()); + case EXP.variable: return visitVar(e.isVarExp()); + case EXP.overloadSet: return visitOver(e.isOverExp()); + case EXP.tuple: return visitTuple(e.isTupleExp()); + case EXP.function_: return visitFunc(e.isFuncExp()); + case EXP.declaration: return visitDeclaration(e.isDeclarationExp()); + case EXP.typeid_: return visitTypeid(e.isTypeidExp()); + case EXP.traits: return visitTraits(e.isTraitsExp()); + case EXP.halt: return visitHalt(e.isHaltExp()); + case EXP.is_: return visitIs(e.isExp()); + case EXP.comma: return visitComma(e.isCommaExp()); + case EXP.mixin_: return visitMixin(e.isMixinExp()); + case EXP.import_: return visitImport(e.isImportExp()); + case EXP.assert_: return visitAssert(e.isAssertExp()); + case EXP.throw_: return visitThrow(e.isThrowExp()); + case EXP.dotIdentifier: return visitDotId(e.isDotIdExp()); + case EXP.dotTemplateDeclaration: return visitDotTemplate(e.isDotTemplateExp()); + case EXP.dotVariable: return visitDotVar(e.isDotVarExp()); + case EXP.dotTemplateInstance: return visitDotTemplateInstance(e.isDotTemplateInstanceExp()); + case EXP.delegate_: return visitDelegate(e.isDelegateExp()); + case EXP.dotType: return visitDotType(e.isDotTypeExp()); + case EXP.call: return visitCall(e.isCallExp()); + case EXP.star: return visitPtr(e.isPtrExp()); + case EXP.delete_: return visitDelete(e.isDeleteExp()); + case EXP.cast_: return visitCast(e.isCastExp()); + case EXP.vector: return visitVector(e.isVectorExp()); + case EXP.vectorArray: return visitVectorArray(e.isVectorArrayExp()); + case EXP.slice: return visitSlice(e.isSliceExp()); + case EXP.arrayLength: return visitArrayLength(e.isArrayLengthExp()); + case EXP.interval: return visitInterval(e.isIntervalExp()); + case EXP.delegatePointer: return visitDelegatePtr(e.isDelegatePtrExp()); + case EXP.delegateFunctionPointer: return visitDelegateFuncptr(e.isDelegateFuncptrExp()); + case EXP.array: return visitArray(e.isArrayExp()); + case EXP.dot: return visitDot(e.isDotExp()); + case EXP.index: return visitIndex(e.isIndexExp()); + case EXP.minusMinus: + case EXP.plusPlus: return visitPost(e.isPostExp()); + case EXP.preMinusMinus: + case EXP.prePlusPlus: return visitPre(e.isPreExp()); + case EXP.remove: return visitRemove(e.isRemoveExp()); + case EXP.question: return visitCond(e.isCondExp()); + case EXP.classReference: return visitClassReference(e.isClassReferenceExp()); + } } /** @@ -2547,7 +2633,7 @@ void floatToBuffer(Type type, const real_t value, OutBuffer* buf, const bool all (ie, 8 chars more than mantissa). Plus one for trailing \0. Plus one for rounding. */ const(size_t) BUFFER_LEN = value.sizeof * 3 + 8 + 1 + 1; - char[BUFFER_LEN] buffer; + char[BUFFER_LEN] buffer = void; CTFloat.sprint(buffer.ptr, 'g', value); assert(strlen(buffer.ptr) < BUFFER_LEN); if (allowHex) @@ -2754,7 +2840,7 @@ bool stcToBuffer(OutBuffer* buf, StorageClass stc) } if (stc & STC.returninferred) { - //buf.writestring("return-inferred "); + //buf.writestring((stc & STC.returnScope) ? "return-scope-inferred " : "return-ref-inferred "); stc &= ~(STC.return_ | STC.returninferred); } @@ -2958,8 +3044,7 @@ void functionToBufferWithIdent(TypeFunction tf, OutBuffer* buf, const(char)* ide void toCBuffer(const Expression e, OutBuffer* buf, HdrGenState* hgs) { - scope v = new ExpressionPrettyPrintVisitor(buf, hgs); - (cast() e).accept(v); + expressionPrettyPrint(cast()e, buf, hgs); } /************************************************** @@ -3221,8 +3306,7 @@ private void sizeToBuffer(Expression e, OutBuffer* buf, HdrGenState* hgs) private void expressionToBuffer(Expression e, OutBuffer* buf, HdrGenState* hgs) { - scope v = new ExpressionPrettyPrintVisitor(buf, hgs); - e.accept(v); + expressionPrettyPrint(e, buf, hgs); } /************************************************** diff --git a/gcc/d/dmd/id.d b/gcc/d/dmd/id.d index 2ec75ab2516..11455afe263 100644 --- a/gcc/d/dmd/id.d +++ b/gcc/d/dmd/id.d @@ -491,6 +491,7 @@ immutable Msgtable[] msgtable = { "udaGNUAbiTag", "gnuAbiTag" }, { "udaSelector", "selector" }, { "udaOptional", "optional"}, + { "udaMustUse", "mustuse" }, // C names, for undefined identifier error messages { "NULL" }, diff --git a/gcc/d/dmd/initsem.d b/gcc/d/dmd/initsem.d index 649d88e3d59..c84a9f69ffa 100644 --- a/gcc/d/dmd/initsem.d +++ b/gcc/d/dmd/initsem.d @@ -554,26 +554,39 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ i.exp = e.optimize(WANTvalue); } } + { // Look for the case of statically initializing an array // with a single member. - if (tb.ty == Tsarray && !tb.nextOf().equals(ti.toBasetype().nextOf()) && i.exp.implicitConvTo(tb.nextOf())) + auto tba = tb.isTypeSArray(); + if (tba && !tba.next.equals(ti.toBasetype().nextOf()) && i.exp.implicitConvTo(tba.next)) { /* If the variable is not actually used in compile time, array creation is * redundant. So delay it until invocation of toExpression() or toDt(). */ t = tb.nextOf(); } + + auto tta = t.isTypeSArray(); if (i.exp.implicitConvTo(t)) { i.exp = i.exp.implicitCastTo(sc, t); } + else if (sc.flags & SCOPE.Cfile && i.exp.isStringExp() && + tta && (tta.next.ty == Tint8 || tta.next.ty == Tuns8) && + ti.ty == Tpointer && ti.nextOf().ty == Tchar) + { + /* unsigned char bbb[1] = ""; + * signed char ccc[1] = ""; + */ + i.exp = i.exp.castTo(sc, t); + } else { // Look for mismatch of compile-time known length to emit // better diagnostic message, as same as AssignExp::semantic. - if (tb.ty == Tsarray && i.exp.implicitConvTo(tb.nextOf().arrayOf()) > MATCH.nomatch) + if (tba && i.exp.implicitConvTo(tba.next.arrayOf()) > MATCH.nomatch) { - uinteger_t dim1 = tb.isTypeSArray().dim.toInteger(); + uinteger_t dim1 = tba.dim.toInteger(); uinteger_t dim2 = dim1; if (auto ale = i.exp.isArrayLiteralExp()) { @@ -596,6 +609,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ if (global.endGagging(errors)) currExp.error("cannot implicitly convert expression `%s` of type `%s` to `%s`", currExp.toChars(), et.toChars(), t.toChars()); } + } L1: if (i.exp.op == EXP.error) { @@ -681,7 +695,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ assert(sc); auto tm = vd.type.addMod(ts.mod); auto iz = di.initializer.initializerSemantic(sc, tm, needInterpret); - auto ex = iz.initializerToExpression(); + auto ex = iz.initializerToExpression(null, true); if (ex.op == EXP.error) { errors = true; @@ -1059,7 +1073,7 @@ Initializer inferType(Initializer init, Scope* sc) Initializer visitC(CInitializer i) { - //printf(CInitializer::inferType()\n"); + //printf("CInitializer.inferType()\n"); error(i.loc, "TODO C inferType initializers not supported yet"); return new ErrorInitializer(); } @@ -1086,6 +1100,8 @@ Initializer inferType(Initializer init, Scope* sc) */ extern (C++) Expression initializerToExpression(Initializer init, Type itype = null, const bool isCfile = false) { + //printf("initializerToExpression() isCfile: %d\n", isCfile); + Expression visitVoid(VoidInitializer) { return null; @@ -1183,7 +1199,7 @@ extern (C++) Expression initializerToExpression(Initializer init, Type itype = n assert(j < edim); if (Initializer iz = init.value[i]) { - if (Expression ex = iz.initializerToExpression()) + if (Expression ex = iz.initializerToExpression(null, isCfile)) { (*elements)[j] = ex; ++j; @@ -1271,7 +1287,7 @@ extern (C++) Expression initializerToExpression(Initializer init, Type itype = n Expression visitC(CInitializer i) { - //printf("CInitializer.initializerToExpression()\n"); + //printf("CInitializer.initializerToExpression(null, true)\n"); return null; } @@ -1331,6 +1347,10 @@ private bool hasNonConstPointers(Expression e) } if (auto ae = e.isAddrExp()) { + if (ae.type.nextOf().isImmutable() || ae.type.nextOf().isConst()) + { + return false; + } if (auto se = ae.e1.isStructLiteralExp()) { if (!(se.stageflags & stageSearchPointers)) diff --git a/gcc/d/dmd/json.d b/gcc/d/dmd/json.d index 3183c8dbb79..fc270390fa4 100644 --- a/gcc/d/dmd/json.d +++ b/gcc/d/dmd/json.d @@ -319,10 +319,7 @@ public: // Should not be printed //property(name, "d"); break; - case LINK.system: - // Should not be printed - //property(name, "system"); - break; + case LINK.system: return property(name, "system"); case LINK.c: return property(name, "c"); case LINK.cpp: return property(name, "cpp"); case LINK.windows: return property(name, "windows"); diff --git a/gcc/d/dmd/lexer.d b/gcc/d/dmd/lexer.d index 7cd4bfd5922..5945da32259 100644 --- a/gcc/d/dmd/lexer.d +++ b/gcc/d/dmd/lexer.d @@ -108,8 +108,8 @@ class Lexer size_t endoffset, bool doDocComment, bool commentToken) pure { scanloc = Loc(filename, 1, 1); - //printf("Lexer::Lexer(%p,%d)\n",base,length); - //printf("lexer.filename = %s\n", filename); + // debug printf("Lexer::Lexer(%p)\n", base); + // debug printf("lexer.filename = %s\n", filename); token = Token.init; this.base = base; this.end = base + endoffset; @@ -1236,28 +1236,47 @@ class Lexer { uint v = 0; int n = 0; - while (1) + if (Ccompile && ndigits == 2) { - if (isdigit(cast(char)c)) - c -= '0'; - else if (islower(c)) - c -= 'a' - 10; - else - c -= 'A' - 10; - v = v * 16 + c; - c = *++p; - if (++n == ndigits) - break; - if (!ishex(cast(char)c)) + /* C11 6.4.4.4-7 one to infinity hex digits + */ + do { - .error(loc, "escape hex sequence has %d hex digits instead of %d", n, ndigits); - break; - } + if (isdigit(cast(char)c)) + c -= '0'; + else if (islower(c)) + c -= 'a' - 10; + else + c -= 'A' - 10; + v = v * 16 + c; + c = *++p; + } while (ishex(cast(char)c)); } - if (ndigits != 2 && !utf_isValidDchar(v)) + else { - .error(loc, "invalid UTF character \\U%08x", v); - v = '?'; // recover with valid UTF character + while (1) + { + if (isdigit(cast(char)c)) + c -= '0'; + else if (islower(c)) + c -= 'a' - 10; + else + c -= 'A' - 10; + v = v * 16 + c; + c = *++p; + if (++n == ndigits) + break; + if (!ishex(cast(char)c)) + { + .error(loc, "escape hex sequence has %d hex digits instead of %d", n, ndigits); + break; + } + } + if (ndigits != 2 && !utf_isValidDchar(v)) + { + .error(loc, "invalid UTF character \\U%08x", v); + v = '?'; // recover with valid UTF character + } } c = v; } @@ -2122,7 +2141,7 @@ class Lexer // can't translate invalid octal value, just show a generic message error("octal literals larger than 7 are no longer supported"); else - error("octal literals `0%llo%.*s` are no longer supported, use `std.conv.octal!%llo%.*s` instead", + error("octal literals `0%llo%.*s` are no longer supported, use `std.conv.octal!\"%llo%.*s\"` instead", n, cast(int)(p - psuffix), psuffix, n, cast(int)(p - psuffix), psuffix); } TOK result; @@ -2926,7 +2945,7 @@ class Lexer */ static const(char)* combineComments(const(char)[] c1, const(char)[] c2, bool newParagraph) pure { - //printf("Lexer::combineComments('%s', '%s', '%i')\n", c1, c2, newParagraph); + //debug printf("Lexer::combineComments('%*.s', '%*.s', '%i')\n", cast(int) c1.length, c1.ptr, cast(int) c2.length, c2.ptr, newParagraph); const(int) newParagraphSize = newParagraph ? 1 : 0; // Size of the combining '\n' if (!c1) return c2.ptr; diff --git a/gcc/d/dmd/mtype.d b/gcc/d/dmd/mtype.d index b2c7aa461ca..13df0d71f3f 100644 --- a/gcc/d/dmd/mtype.d +++ b/gcc/d/dmd/mtype.d @@ -4200,27 +4200,28 @@ extern (C++) final class TypeFunction : TypeNext // These flags can be accessed like `bool` properties, // getters and setters are generated for them - private enum FunctionFlag : uint - { - none = 0, - isnothrow = 0x0001, // nothrow - isnogc = 0x0002, // is @nogc - isproperty = 0x0004, // can be called without parentheses - isref = 0x0008, // returns a reference - isreturn = 0x0010, // 'this' is returned by ref - isScopeQual = 0x0020, // 'this' is scope - isreturninferred= 0x0040, // 'this' is return from inference - isscopeinferred = 0x0080, // 'this' is scope from inference - islive = 0x0100, // is @live - incomplete = 0x0200, // return type or default arguments removed - isInOutParam = 0x0400, // inout on the parameters - isInOutQual = 0x0800, // inout on the qualifier - isctor = 0x1000, // the function is a constructor - isreturnscope = 0x2000, // `this` is returned by value - } + private extern (D) static struct BitFields + { + bool isnothrow; /// nothrow + bool isnogc; /// is @nogc + bool isproperty; /// can be called without parentheses + bool isref; /// returns a reference + bool isreturn; /// 'this' is returned by ref + bool isScopeQual; /// 'this' is scope + bool isreturninferred; /// 'this' is return from inference + bool isscopeinferred; /// 'this' is scope from inference + bool islive; /// is @live + bool incomplete; /// return type or default arguments removed + bool isInOutParam; /// inout on the parameters + bool isInOutQual; /// inout on the qualifier + bool isctor; /// the function is a constructor + bool isreturnscope; /// `this` is returned by value + } + + import dmd.common.bitfields : generateBitFields; + mixin(generateBitFields!(BitFields, ushort)); LINK linkage; // calling convention - FunctionFlag funcFlags; TRUST trust; // level of trust PURE purity = PURE.impure; byte inuse; @@ -4359,29 +4360,6 @@ extern (C++) final class TypeFunction : TypeNext return linkage == LINK.d && parameterList.varargs == VarArg.variadic; } - /*************************** - * Examine function signature for parameter p and see if - * the value of p can 'escape' the scope of the function. - * This is useful to minimize the needed annotations for the parameters. - * Params: - * tthis = type of `this` parameter, null if none - * p = parameter to this function - * Returns: - * true if escapes via assignment to global or through a parameter - */ - bool parameterEscapes(Type tthis, Parameter p) - { - /* Scope parameters do not escape. - * Allow 'lazy' to imply 'scope' - - * lazy parameters can be passed along - * as lazy parameters to the next function, but that isn't - * escaping. - */ - if (parameterStorageClass(tthis, p) & (STC.scope_ | STC.lazy_)) - return false; - return true; - } - /************************************ * Take the specified storage class for p, * and use the function signature to infer whether @@ -4409,7 +4387,7 @@ extern (C++) final class TypeFunction : TypeNext /* If haven't inferred the return type yet, can't infer storage classes */ - if (!nextOf()) + if (!nextOf() || !isnothrow()) return stc; purityLevel(); @@ -4460,37 +4438,12 @@ extern (C++) final class TypeFunction : TypeNext } } - /* Inferring STC.return_ here has false positives - * for pure functions, producing spurious error messages - * about escaping references. - * Give up on it for now. - */ - version (none) - { - stc |= STC.scope_; - - Type tret = nextOf().toBasetype(); - if (isref || tret.hasPointers()) - { - /* The result has references, so p could be escaping - * that way. - */ - stc |= STC.return_; - } - } + // Check escaping through return value + Type tret = nextOf().toBasetype(); + if (isref || tret.hasPointers()) + return stc | STC.scope_ | STC.return_ | STC.returnScope; else - { - // Check escaping through return value - Type tret = nextOf().toBasetype(); - if (isref || tret.hasPointers() || !isnothrow()) - { - return stc; - } - - stc |= STC.scope_; - } - - return stc; + return stc | STC.scope_; } override Type addStorageClass(StorageClass stc) @@ -4684,6 +4637,16 @@ extern (C++) final class TypeFunction : TypeNext match = MATCH.convert; // match ... with a "conversion" match level } + // https://issues.dlang.org/show_bug.cgi?id=22997 + if (parameterList.varargs == VarArg.none && nparams > nargs && !parameterList[nargs].defaultArg) + { + OutBuffer buf; + buf.printf("too few arguments, expected `%d`, got `%d`", cast(int)nparams, cast(int)nargs); + if (pMessage) + *pMessage = buf.extractChars(); + goto Nomatch; + } + foreach (u, p; parameterList) { if (u == nargs) @@ -4780,14 +4743,40 @@ extern (C++) final class TypeFunction : TypeNext m = MATCH.exact; else { - m = MATCH.nomatch; 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; - buf.printf("`struct %s` does not define a copy constructor for `%s` to `%s` copies", - argStruct.toChars(), targ.toChars(), tprm.toChars()); + 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.func.setUnsafe()) + s ~= "@safe "; + if (!f.isNogc && sc.func.setGC()) + s ~= "nogc "; + s[$-1] = '\0'; + buf.printf("`%s` copy constructor cannot be called from a `%s` context", f.type.toChars(), s.ptr); + + } + else + { + buf.printf("`struct %s` does not define a copy constructor for `%s` to `%s` copies", + argStruct.toChars(), targ.toChars(), tprm.toChars()); + } + *pMessage = buf.extractChars(); } + m = MATCH.nomatch; goto Nomatch; } } @@ -5086,41 +5075,21 @@ extern (C++) final class TypeFunction : TypeNext return false; } - // Generate getter / setter functions for `FunctionFlag` members so they can be - // treated like regular `bool` fields, instead of requiring bit twiddling to read/write - extern (D) mixin(() { - string result = "extern(C++) pure nothrow @safe @nogc {"; - foreach (string mem; __traits(allMembers, FunctionFlag)) - { - result ~= " - /// set or get if the function has the FunctionFlag attribute of the same name - bool "~mem~"() const { return (funcFlags & FunctionFlag."~mem~") != 0; } - /// ditto - void "~mem~"(bool v) - { - if (v) funcFlags |= FunctionFlag."~mem~"; - else funcFlags &= ~FunctionFlag."~mem~"; - }"; - } - return result ~ "}\n"; - }()); /// Returns: `true` the function is `isInOutQual` or `isInOutParam` ,`false` otherwise. bool iswild() const pure nothrow @safe @nogc { - return (funcFlags & (FunctionFlag.isInOutParam | FunctionFlag.isInOutQual)) != 0; + return isInOutParam || isInOutQual; } /// Returns: whether `this` function type has the same attributes (`@safe`,...) as `other` bool attributesEqual(const scope TypeFunction other) const pure nothrow @safe @nogc { - enum attributes = FunctionFlag.isnothrow - | FunctionFlag.isnogc - | FunctionFlag.islive; - return this.trust == other.trust && this.purity == other.purity && - (this.funcFlags & attributes) == (other.funcFlags & attributes); + this.isnothrow == other.isnothrow && + this.isnogc == other.isnogc && + this.islive == other.islive; } override void accept(Visitor v) diff --git a/gcc/d/dmd/mtype.h b/gcc/d/dmd/mtype.h index 07c574d42b7..6ba47dfa34e 100644 --- a/gcc/d/dmd/mtype.h +++ b/gcc/d/dmd/mtype.h @@ -109,6 +109,7 @@ enum class TY : uint8_t */ enum MODFlags { + MODnone = 0, // default (mutable) MODconst = 1, // type is const MODimmutable = 4, // type is immutable MODshared = 2, // type is shared @@ -595,8 +596,8 @@ public: // .next is the return type ParameterList parameterList; // function parameters + uint16_t bitFields; LINK linkage; // calling convention - unsigned funcFlags; TRUST trust; // level of trust PURE purity; // PURExxxx char inuse; @@ -608,7 +609,6 @@ public: void purityLevel(); bool hasLazyParameters(); bool isDstyleVariadic() const; - bool parameterEscapes(Parameter *p); StorageClass parameterStorageClass(Parameter *p); Type *addStorageClass(StorageClass stc); diff --git a/gcc/d/dmd/mustuse.d b/gcc/d/dmd/mustuse.d new file mode 100644 index 00000000000..4eb422805db --- /dev/null +++ b/gcc/d/dmd/mustuse.d @@ -0,0 +1,244 @@ +/** + * Compile-time checks associated with the @mustuse attribute. + * + * Copyright: Copyright (C) 2022 by The D Language Foundation, All Rights Reserved + * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/mustuse.d, _mustuse.d) + * Documentation: https://dlang.org/phobos/dmd_mustuse.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/mustuse.d + */ + +module dmd.mustuse; + +import dmd.dscope; +import dmd.dsymbol; +import dmd.expression; +import dmd.globals; +import dmd.identifier; + +// Used in isIncrementOrDecrement +private static const StringExp plusPlus, minusMinus; + +// Loc.initial cannot be used in static initializers, so +// these need a static constructor. +static this() +{ + plusPlus = new StringExp(Loc.initial, "++"); + minusMinus = new StringExp(Loc.initial, "--"); +} + +/** + * Check whether discarding an expression would violate the requirements of + * @mustuse. If so, emit an error. + * + * Params: + * e = the expression to check + * sc = scope in which `e` was semantically analyzed + * + * Returns: true on error, false on success. + */ +bool checkMustUse(Expression e, Scope* sc) +{ + import dmd.id : Id; + + assert(e.type); + if (auto sym = e.type.toDsymbol(sc)) + { + auto sd = sym.isStructDeclaration(); + // isStructDeclaration returns non-null for both structs and unions + if (sd && hasMustUseAttribute(sd, sc) && !isAssignment(e) && !isIncrementOrDecrement(e)) + { + e.error("ignored value of `@%s` type `%s`; prepend a `cast(void)` if intentional", + Id.udaMustUse.toChars(), e.type.toPrettyChars(true)); + return true; + } + } + return false; +} + +/** + * Called from a symbol's semantic to check for reserved usage of @mustuse. + * + * If such usage is found, emits an errror. + * + * Params: + * sym = symbol to check + */ +void checkMustUseReserved(Dsymbol sym) +{ + import dmd.attrib : foreachUdaNoSemantic; + import dmd.errors : error; + import dmd.id : Id; + + // Can't use foreachUda (and by extension hasMustUseAttribute) while + // semantic analysis of `sym` is still in progress + foreachUdaNoSemantic(sym, (exp) { + if (isMustUseAttribute(exp)) + { + if (sym.isFuncDeclaration()) + { + error(sym.loc, "`@%s` on functions is reserved for future use", + Id.udaMustUse.toChars()); + sym.errors = true; + } + else if (sym.isClassDeclaration() || sym.isEnumDeclaration()) + { + error(sym.loc, "`@%s` on `%s` types is reserved for future use", + Id.udaMustUse.toChars(), sym.kind()); + sym.errors = true; + } + } + return 0; // continue + }); +} + +/** + * Returns: true if the given expression is an assignment, either simple (a = b) + * or compound (a += b, etc). + */ +private bool isAssignment(Expression e) +{ + if (e.isAssignExp || e.isBinAssignExp) + return true; + if (auto ce = e.isCallExp()) + { + if (auto fd = ce.f) + { + auto id = fd.ident; + if (id && isAssignmentOpId(id)) + return true; + } + } + return false; +} + +/** + * Returns: true if id is the identifier of an assignment operator overload. + */ +private bool isAssignmentOpId(Identifier id) +{ + import dmd.id : Id; + + return id == Id.assign + || id == Id.addass + || id == Id.subass + || id == Id.mulass + || id == Id.divass + || id == Id.modass + || id == Id.andass + || id == Id.orass + || id == Id.xorass + || id == Id.shlass + || id == Id.shrass + || id == Id.ushrass + || id == Id.catass + || id == Id.indexass + || id == Id.slice + || id == Id.sliceass + || id == Id.opOpAssign + || id == Id.opIndexOpAssign + || id == Id.opSliceOpAssign + || id == Id.powass; +} + +/** + * Returns: true if the given expression is an increment (++) or decrement (--). + */ +private bool isIncrementOrDecrement(Expression e) +{ + import dmd.dtemplate : isExpression; + import dmd.globals : Loc; + import dmd.id : Id; + import dmd.tokens : EXP; + + if (e.op == EXP.plusPlus + || e.op == EXP.minusMinus + || e.op == EXP.prePlusPlus + || e.op == EXP.preMinusMinus) + return true; + if (auto call = e.isCallExp()) + { + // Check for overloaded preincrement + // e.g., a.opUnary!"++" + if (auto fd = call.f) + { + if (fd.ident == Id.opUnary && fd.parent) + { + if (auto ti = fd.parent.isTemplateInstance()) + { + auto tiargs = ti.tiargs; + if (tiargs && tiargs.length >= 1) + { + if (auto argExp = (*tiargs)[0].isExpression()) + { + auto op = argExp.isStringExp(); + if (op && (op.compare(plusPlus) == 0 || op.compare(minusMinus) == 0)) + return true; + } + } + } + } + } + } + else if (auto comma = e.isCommaExp()) + { + // Check for overloaded postincrement + // e.g., (auto tmp = a, ++a, tmp) + if (comma.e1) + { + if (auto left = comma.e1.isCommaExp()) + { + if (auto middle = left.e2) + { + if (middle && isIncrementOrDecrement(middle)) + return true; + } + } + } + } + return false; +} + +/** + * Returns: true if the given symbol has the @mustuse attribute. + */ +private bool hasMustUseAttribute(Dsymbol sym, Scope* sc) +{ + import dmd.attrib : foreachUda; + + bool result = false; + + foreachUda(sym, sc, (Expression uda) { + if (isMustUseAttribute(uda)) + { + result = true; + return 1; // break + } + return 0; // continue + }); + + return result; +} + +/** + * Returns: true if the given expression is core.attribute.mustuse. + */ +private bool isMustUseAttribute(Expression e) +{ + import dmd.attrib : isCoreUda; + import dmd.id : Id; + + // Logic based on dmd.objc.Supported.declaredAsOptionalCount + auto typeExp = e.isTypeExp; + if (!typeExp) + return false; + + auto typeEnum = typeExp.type.isTypeEnum(); + if (!typeEnum) + return false; + + if (isCoreUda(typeEnum.sym, Id.udaMustUse)) + return true; + + return false; +} diff --git a/gcc/d/dmd/opover.d b/gcc/d/dmd/opover.d index dbd761fed2f..4f6903cbab7 100644 --- a/gcc/d/dmd/opover.d +++ b/gcc/d/dmd/opover.d @@ -213,7 +213,7 @@ private Expression checkAliasThisForLhs(AggregateDeclaration ad, Scope* sc, BinE */ if (isRecursiveAliasThis(e.att1, e.e1.type)) return null; - //printf("att %s e1 = %s\n", Token::toChars(e.op), e.e1.type.toChars()); + //printf("att %s e1 = %s\n", Token.toChars(e.op), e.e1.type.toChars()); BinExp be = cast(BinExp)e.copy(); // Resolve 'alias this' but in case of assigment don't resolve properties yet // because 'e1 = e2' could mean 'e1(e2)' or 'e1() = e2' @@ -241,7 +241,7 @@ private Expression checkAliasThisForRhs(AggregateDeclaration ad, Scope* sc, BinE */ if (isRecursiveAliasThis(e.att2, e.e2.type)) return null; - //printf("att %s e2 = %s\n", Token::toChars(e.op), e.e2.type.toChars()); + //printf("att %s e2 = %s\n", Token.toChars(e.op), e.e2.type.toChars()); BinExp be = cast(BinExp)e.copy(); be.e2 = resolveAliasThis(sc, e.e2, true); if (!be.e2) diff --git a/gcc/d/dmd/optimize.d b/gcc/d/dmd/optimize.d index 3745a15da42..3cc36b49e01 100644 --- a/gcc/d/dmd/optimize.d +++ b/gcc/d/dmd/optimize.d @@ -271,7 +271,7 @@ package void setLengthVarIfKnown(VarDeclaration lengthVar, Type type) */ Expression Expression_optimize(Expression e, int result, bool keepLvalue) { - //printf("Expression_optimize() %s\n", e.toChars()); + //printf("Expression_optimize() e: %s result: %d keepLvalue %d\n", e.toChars(), result, keepLvalue); Expression ret = e; void error() @@ -426,7 +426,7 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) void visitAddr(AddrExp e) { - //printf("AddrExp::optimize(result = %d) %s\n", result, e.toChars()); + //printf("AddrExp::optimize(result = %d, keepLvalue = %d) %s\n", result, keepLvalue, e.toChars()); /* Rewrite &(a,b) as (a,&b) */ if (auto ce = e.e1.isCommaExp()) @@ -438,7 +438,8 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) } // Keep lvalue-ness if (expOptimize(e.e1, result, true)) - return; + return; // error return + // Convert &*ex to ex if (auto pe = e.e1.isPtrExp()) { @@ -515,6 +516,23 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) } } } + else if (auto ei = e.isIndexExp()) + { + if (auto ve = ei.e1.isVarExp()) + { + if (!ve.var.isReference() && + !ve.var.isImportedSymbol() && + ve.var.isDataseg() && + ve.var.isCsymbol()) + { + if (auto ie = ei.e2.isIntegerExp()) + { + var = ve.var.isVarDeclaration(); + offset += ie.toInteger() * ve.type.toBasetype().nextOf().size(); + } + } + } + } return false; } @@ -538,7 +556,7 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) return; } } - if (auto ae = e.e1.isIndexExp()) + else if (auto ae = e.e1.isIndexExp()) { // Convert &array[n] to &array+n if (ae.e2.isIntegerExp() && ae.e1.isVarExp()) @@ -551,9 +569,10 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) sinteger_t dim = ts.dim.toInteger(); if (index < 0 || index >= dim) { - /* 0 for C static arrays means size is unknown, no need to check + /* 0 for C static arrays means size is unknown, no need to check, + * and address one past the end is OK, too */ - if (!(dim == 0 && ve.var.isCsymbol())) + if (!((dim == 0 || dim == index) && ve.var.isCsymbol())) { e.error("array index %lld is out of bounds `[0..%lld]`", index, dim); return error(); @@ -574,7 +593,7 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) return; } } - // Convert &((a.b)[n]) to (&a.b)+n + // Convert &((a.b)[index]) to (&a.b)+index*elementsize else if (ae.e2.isIntegerExp() && ae.e1.isDotVarExp()) { sinteger_t index = ae.e2.toInteger(); @@ -585,18 +604,28 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) sinteger_t dim = ts.dim.toInteger(); if (index < 0 || index >= dim) { - /* 0 for C static arrays means size is unknown, no need to check + /* 0 for C static arrays means size is unknown, no need to check, + * and address one past the end is OK, too */ - if (!(dim == 0 && ve.var.isCsymbol())) + if (!((dim == 0 || dim == index) && ve.var.isCsymbol())) { e.error("array index %lld is out of bounds `[0..%lld]`", index, dim); return error(); } } + import core.checkedint : mulu; + bool overflow; + const offset = mulu(index, ts.nextOf().size(e.loc), overflow); // index*elementsize + if (overflow) + { + e.error("array offset overflow"); + return error(); + } + auto pe = new AddrExp(e.loc, ve); pe.type = e.type; - ret = new AddExp(e.loc, pe, ae.e2); + ret = new AddExp(e.loc, pe, new IntegerExp(e.loc, offset, Type.tsize_t)); ret.type = e.type; return; } diff --git a/gcc/d/dmd/parse.d b/gcc/d/dmd/parse.d index 7b1b63cba82..4b9c0f2119f 100644 --- a/gcc/d/dmd/parse.d +++ b/gcc/d/dmd/parse.d @@ -987,23 +987,9 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer nextToken(); if (token.value == TOK.assign) { - nextToken(); - if (token.value == TOK.identifier) - s = new AST.DebugSymbol(token.loc, token.ident); - else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal) - s = new AST.DebugSymbol(token.loc, cast(uint)token.unsvalue); - else - { - error("identifier or integer expected, not `%s`", token.toChars()); - s = null; - } - nextToken(); - if (token.value != TOK.semicolon) - error("semicolon expected"); - nextToken(); + s = parseDebugSpecification(); break; } - condition = parseDebugCondition(); goto Lcondition; @@ -1012,20 +998,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer nextToken(); if (token.value == TOK.assign) { - nextToken(); - if (token.value == TOK.identifier) - s = new AST.VersionSymbol(token.loc, token.ident); - else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal) - s = new AST.VersionSymbol(token.loc, cast(uint)token.unsvalue); - else - { - error("identifier or integer expected, not `%s`", token.toChars()); - s = null; - } - nextToken(); - if (token.value != TOK.semicolon) - error("semicolon expected"); - nextToken(); + s = parseVersionSpecification(); break; } condition = parseVersionCondition(); @@ -2197,6 +2170,26 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer return qualified; } + private AST.DebugSymbol parseDebugSpecification() + { + AST.DebugSymbol s; + nextToken(); + if (token.value == TOK.identifier) + s = new AST.DebugSymbol(token.loc, token.ident); + else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal) + s = new AST.DebugSymbol(token.loc, cast(uint)token.unsvalue); + else + { + error("identifier or integer expected, not `%s`", token.toChars()); + s = null; + } + nextToken(); + if (token.value != TOK.semicolon) + error("semicolon expected"); + nextToken(); + return s; + } + /************************************** * Parse a debug conditional */ @@ -2224,6 +2217,29 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } /************************************** + * Parse a version specification + */ + private AST.VersionSymbol parseVersionSpecification() + { + AST.VersionSymbol s; + nextToken(); + if (token.value == TOK.identifier) + s = new AST.VersionSymbol(token.loc, token.ident); + else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal) + s = new AST.VersionSymbol(token.loc, cast(uint)token.unsvalue); + else + { + error("identifier or integer expected, not `%s`", token.toChars()); + s = null; + } + nextToken(); + if (token.value != TOK.semicolon) + error("semicolon expected"); + nextToken(); + return s; + } + + /************************************** * Parse a version conditional */ private AST.Condition parseVersionCondition() @@ -6053,10 +6069,14 @@ LagainStc: nextToken(); if (token.value == TOK.assign) { - error("debug conditions can only be declared at module scope"); - nextToken(); - nextToken(); - goto Lerror; + if (auto ds = parseDebugSpecification()) + { + if (ds.ident) + ds.error("declaration must be at module level"); + else + ds.error("level declaration must be at module level"); + } + break; } cond = parseDebugCondition(); goto Lcondition; @@ -6065,10 +6085,14 @@ LagainStc: nextToken(); if (token.value == TOK.assign) { - error("version conditions can only be declared at module scope"); - nextToken(); - nextToken(); - goto Lerror; + if (auto vs = parseVersionSpecification()) + { + if (vs.ident) + vs.error("declaration must be at module level"); + else + vs.error("level declaration must be at module level"); + } + break; } cond = parseVersionCondition(); goto Lcondition; diff --git a/gcc/d/dmd/root/aav.d b/gcc/d/dmd/root/aav.d index ba5d3312d8a..beceb0f0707 100644 --- a/gcc/d/dmd/root/aav.d +++ b/gcc/d/dmd/root/aav.d @@ -14,6 +14,8 @@ module dmd.root.aav; import core.stdc.string; import dmd.root.rmem; +nothrow: + private size_t hash(size_t a) pure nothrow @nogc @safe { a ^= (a >> 20) ^ (a >> 12); diff --git a/gcc/d/dmd/root/array.d b/gcc/d/dmd/root/array.d index cd2d7096d3d..c2eb3e15c2c 100644 --- a/gcc/d/dmd/root/array.d +++ b/gcc/d/dmd/root/array.d @@ -137,7 +137,7 @@ public: void reserve(size_t nentries) pure nothrow { - //printf("Array::reserve: length = %d, data.length = %d, nentries = %d\n", (int)length, (int)data.length, (int)nentries); + //printf("Array::reserve: length = %d, data.length = %d, nentries = %d\n", cast(int)length, cast(int)data.length, cast(int)nentries); // Cold path void enlarge(size_t nentries) diff --git a/gcc/d/dmd/root/complex.d b/gcc/d/dmd/root/complex.d index d9a396d9677..a7a74381ef9 100644 --- a/gcc/d/dmd/root/complex.d +++ b/gcc/d/dmd/root/complex.d @@ -13,11 +13,15 @@ module dmd.root.complex; import dmd.root.ctfloat; +nothrow: + extern (C++) struct complex_t { real_t re; real_t im; + nothrow: + this() @disable; this(real_t re) diff --git a/gcc/d/dmd/root/file.d b/gcc/d/dmd/root/file.d index a01cfdc2aac..b40413c9ef1 100644 --- a/gcc/d/dmd/root/file.d +++ b/gcc/d/dmd/root/file.d @@ -26,11 +26,15 @@ import dmd.root.string; import dmd.common.file; import dmd.common.string; -/// Owns a (rmem-managed) file buffer. -struct FileBuffer +nothrow: + +/// Owns a (rmem-managed) buffer. +struct Buffer { ubyte[] data; + nothrow: + this(this) @disable; ~this() pure nothrow @@ -54,7 +58,7 @@ struct File static struct ReadResult { bool success; - FileBuffer buffer; + Buffer buffer; /// Transfers ownership of the buffer to the caller. ubyte[] extractSlice() pure nothrow @nogc @safe diff --git a/gcc/d/dmd/root/longdouble.d b/gcc/d/dmd/root/longdouble.d index 27fcf9c849f..d6753aa8490 100644 --- a/gcc/d/dmd/root/longdouble.d +++ b/gcc/d/dmd/root/longdouble.d @@ -29,6 +29,8 @@ nothrow: // Type used by the front-end for compile-time reals struct longdouble { +nothrow: +@nogc: extern (D) this(T)(T r) { this.set(cast(SetType!T)r); diff --git a/gcc/d/dmd/root/optional.d b/gcc/d/dmd/root/optional.d index f2f7389efd2..266846bcb0b 100644 --- a/gcc/d/dmd/root/optional.d +++ b/gcc/d/dmd/root/optional.d @@ -10,6 +10,8 @@ */ module dmd.root.optional; +nothrow: + /// unittest { @@ -45,6 +47,8 @@ extern (C++) struct Optional(T) /// whether `value` is set private bool present; + nothrow: + /// Creates an `Optional` with the given value this(T value) { diff --git a/gcc/d/dmd/root/port.h b/gcc/d/dmd/root/port.h index 069a365addf..66a67605b5c 100644 --- a/gcc/d/dmd/root/port.h +++ b/gcc/d/dmd/root/port.h @@ -13,12 +13,13 @@ // The idea is to minimize #ifdef's in the app code. #include "dsystem.h" +#include "dcompat.h" typedef unsigned char utf8_t; struct Port { - static int memicmp(const char *s1, const char *s2, size_t n); + static int memicmp(const char *s1, const char *s2, d_size_t n); static char *strupr(char *s); static bool isFloat32LiteralOutOfRange(const char *s); @@ -30,5 +31,5 @@ struct Port static unsigned readlongBE(const void *buffer); static unsigned readwordLE(const void *buffer); static unsigned readwordBE(const void *buffer); - static void valcpy(void *dst, uint64_t val, size_t size); + static void valcpy(void *dst, uint64_t val, d_size_t size); }; diff --git a/gcc/d/dmd/semantic2.d b/gcc/d/dmd/semantic2.d index b9029a16ef8..cd65920e38b 100644 --- a/gcc/d/dmd/semantic2.d +++ b/gcc/d/dmd/semantic2.d @@ -478,7 +478,7 @@ private extern(C++) final class Semantic2Visitor : Visitor i.mod.semantic2(null); if (i.mod.needmoduleinfo) { - //printf("module5 %s because of %s\n", sc.module.toChars(), mod.toChars()); + //printf("module5 %s because of %s\n", sc._module.toChars(), mod.toChars()); if (sc) sc._module.needmoduleinfo = 1; } diff --git a/gcc/d/dmd/statement.d b/gcc/d/dmd/statement.d index ae03b8a4c52..71713249009 100644 --- a/gcc/d/dmd/statement.d +++ b/gcc/d/dmd/statement.d @@ -315,6 +315,14 @@ extern (C++) abstract class Statement : ASTNode override void visit(ImportStatement s) { } + + override void visit(CaseStatement s) + { + } + + override void visit(DefaultStatement s) + { + } } scope HasCode hc = new HasCode(); diff --git a/gcc/d/dmd/statementsem.d b/gcc/d/dmd/statementsem.d index c2967d65d5f..2916bbc38dd 100644 --- a/gcc/d/dmd/statementsem.d +++ b/gcc/d/dmd/statementsem.d @@ -51,6 +51,7 @@ import dmd.importc; import dmd.init; import dmd.intrange; import dmd.mtype; +import dmd.mustuse; import dmd.nogc; import dmd.opover; import dmd.parse; @@ -211,7 +212,8 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor if (f.checkForwardRef(s.exp.loc)) s.exp = ErrorExp.get(); } - + if (checkMustUse(s.exp, sc)) + s.exp = ErrorExp.get(); if (!(sc.flags & SCOPE.Cfile) && discardValue(s.exp)) s.exp = ErrorExp.get(); diff --git a/gcc/d/dmd/tokens.d b/gcc/d/dmd/tokens.d index 43feab15f22..03e80245a74 100644 --- a/gcc/d/dmd/tokens.d +++ b/gcc/d/dmd/tokens.d @@ -591,7 +591,7 @@ shared static this() nothrow Identifier.initTable(); foreach (kw; keywords) { - //printf("keyword[%d] = '%s'\n",kw, tochars[kw].ptr); + //printf("keyword[%d] = '%s'\n",kw, Token.tochars[kw].ptr); Identifier.idPool(Token.tochars[kw].ptr, Token.tochars[kw].length, cast(uint)kw); } } diff --git a/gcc/d/dmd/traits.d b/gcc/d/dmd/traits.d index 4b15e8c4739..04e1c47d16e 100644 --- a/gcc/d/dmd/traits.d +++ b/gcc/d/dmd/traits.d @@ -1278,7 +1278,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) { s = s.isImport().mod; } - //printf("getAttributes %s, attrs = %p, scope = %p\n", s.toChars(), s.userAttribDecl, s.scope); + //printf("getAttributes %s, attrs = %p, scope = %p\n", s.toChars(), s.userAttribDecl, s._scope); udad = s.userAttribDecl; } else @@ -1514,7 +1514,9 @@ Expression semanticTraits(TraitsExp e, Scope* sc) TypeFunction tf = toTypeFunction(o, fd); if (tf) - link = tf.linkage; + { + link = fd ? fd.linkage : tf.linkage; + } else { auto s = getDsymbol(o); @@ -1730,69 +1732,33 @@ Expression semanticTraits(TraitsExp e, Scope* sc) bool err = false; auto t = isType(o); - while (t) + auto ex = isExpression(o); + if (t) { - if (auto tm = t.isTypeMixin()) + Dsymbol s; + t.resolve(e.loc, sc2, ex, t, s); + if (t) { - /* The mixin string could be a type or an expression. - * Have to try compiling it to see. - */ - OutBuffer buf; - if (expressionsToString(buf, sc, tm.exps)) - { + t.typeSemantic(e.loc, sc2); + if (t.ty == Terror) err = true; - break; - } - const olderrors = global.errors; - const len = buf.length; - buf.writeByte(0); - const str = buf.extractSlice()[0 .. len]; - scope p = new Parser!ASTCodegen(e.loc, sc._module, str, false); - p.nextToken(); - //printf("p.loc.linnum = %d\n", p.loc.linnum); - - o = p.parseTypeOrAssignExp(TOK.endOfFile); - if (olderrors != global.errors || p.token.value != TOK.endOfFile) - { - err = true; - break; - } - t = o.isType(); } - else - break; + else if (s && s.errors) + err = true; } - - if (!err) + if (ex) { - auto ex = t ? t.typeToExpression() : isExpression(o); - if (!ex && t) - { - Dsymbol s; - t.resolve(e.loc, sc2, ex, t, s); - if (t) - { - t.typeSemantic(e.loc, sc2); - if (t.ty == Terror) - err = true; - } - else if (s && s.errors) - err = true; - } - if (ex) + ex = ex.expressionSemantic(sc2); + ex = resolvePropertiesOnly(sc2, ex); + ex = ex.optimize(WANTvalue); + if (sc2.func && sc2.func.type.ty == Tfunction) { - ex = ex.expressionSemantic(sc2); - ex = resolvePropertiesOnly(sc2, ex); - ex = ex.optimize(WANTvalue); - if (sc2.func && sc2.func.type.ty == Tfunction) - { - const tf = cast(TypeFunction)sc2.func.type; - err |= tf.isnothrow && canThrow(ex, sc2.func, false); - } - ex = checkGC(sc2, ex); - if (ex.op == EXP.error) - err = true; + const tf = cast(TypeFunction)sc2.func.type; + err |= tf.isnothrow && canThrow(ex, sc2.func, false); } + ex = checkGC(sc2, ex); + if (ex.op == EXP.error) + err = true; } // Carefully detach the scope from the parent and throw it away as diff --git a/gcc/d/dmd/transitivevisitor.d b/gcc/d/dmd/transitivevisitor.d index 615c49fd623..0acad6ae14a 100644 --- a/gcc/d/dmd/transitivevisitor.d +++ b/gcc/d/dmd/transitivevisitor.d @@ -17,7 +17,9 @@ import core.stdc.stdio; extern(C++) class ParseTimeTransitiveVisitor(AST) : PermissiveVisitor!AST { alias visit = PermissiveVisitor!AST.visit; - mixin ParseVisitMethods!AST; + + mixin ParseVisitMethods!AST __methods; + alias visit = __methods.visit; } /* This mixin implements the AST traversal logic for parse time AST nodes. The same code diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d index bcdbec5fe2d..f63b17752ed 100644 --- a/gcc/d/dmd/typesem.d +++ b/gcc/d/dmd/typesem.d @@ -1220,6 +1220,9 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) tf.islive = true; tf.linkage = sc.linkage; + if (tf.linkage == LINK.system) + tf.linkage = target.systemLinkage(); + version (none) { /* If the parent is @safe, then this function defaults to safe @@ -1840,7 +1843,7 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) mtype.sym = e.isScopeExp().sds; break; case EXP.tuple: - TupleExp te = e.toTupleExp(); + TupleExp te = e.isTupleExp(); Objects* elems = new Objects(te.exps.dim); foreach (i; 0 .. elems.dim) { @@ -2281,10 +2284,7 @@ RootObject compileTypeMixin(TypeMixin tm, Loc loc, Scope* sc) return null; } - Type t = o.isType(); - Expression e = t ? t.typeToExpression() : o.isExpression(); - - return (!e && t) ? t : e; + return o; } @@ -2306,7 +2306,8 @@ extern (C++) Type merge(Type type) case Tident: case Tinstance: case Tmixin: - return type; + case Ttag: + return type; // don't merge placeholder types case Tsarray: // prevents generating the mangle if the array dim is not yet known @@ -3619,12 +3620,31 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag) e.error("`%s` is not an expression", e.toChars()); return ErrorExp.get(); } - else if (checkUnsafeDotExp(sc, e, ident, flag)) + else if (mt.dim.toUInteger() < 1 && checkUnsafeDotExp(sc, e, ident, flag)) { + // .ptr on static array is @safe unless size is 0 + // https://issues.dlang.org/show_bug.cgi?id=20853 return ErrorExp.get(); } e = e.castTo(sc, e.type.nextOf().pointerTo()); } + else if (ident == Id._tupleof) + { + if (e.isTypeExp()) + { + e.error("`.tupleof` cannot be used on type `%s`", mt.toChars); + return ErrorExp.get(); + } + else + { + const length = cast(size_t)mt.dim.toUInteger(); + auto exps = new Expressions(); + exps.reserve(length); + foreach (i; 0 .. length) + exps.push(new IndexExp(e.loc, e, new IntegerExp(e.loc, i, Type.tsize_t))); + e = new TupleExp(e.loc, exps); + } + } else { e = visitArray(mt); @@ -3885,7 +3905,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag) assert(e.op != EXP.dot); // https://issues.dlang.org/show_bug.cgi?id=14010 - if (ident == Id._mangleof) + if (!(sc.flags & SCOPE.Cfile) && ident == Id._mangleof) { return mt.getProperty(sc, e.loc, ident, flag & 1); } @@ -4637,7 +4657,7 @@ extern (C++) Expression defaultInit(Type mt, const ref Loc loc, const bool isCfi { static if (LOGDEFAULTINIT) { - printf("TypeBasic::defaultInit() '%s'\n", mt.toChars()); + printf("TypeBasic::defaultInit() '%s' isCfile: %d\n", mt.toChars(), isCfile); } dinteger_t value = 0; @@ -4695,7 +4715,7 @@ extern (C++) Expression defaultInit(Type mt, const ref Loc loc, const bool isCfi { static if (LOGDEFAULTINIT) { - printf("TypeSArray::defaultInit() '%s'\n", mt.toChars()); + printf("TypeSArray::defaultInit() '%s' isCfile %d\n", mt.toChars(), isCfile); } if (mt.next.ty == Tvoid) return mt.tuns8.defaultInit(loc, isCfile); diff --git a/gcc/d/dmd/utils.d b/gcc/d/dmd/utils.d index 67e4d86a43e..7f3fb64842d 100644 --- a/gcc/d/dmd/utils.d +++ b/gcc/d/dmd/utils.d @@ -19,6 +19,7 @@ import dmd.root.filename; import dmd.common.outbuffer; import dmd.root.string; +nothrow: /** * Normalize path by turning forward slashes into backslashes @@ -52,13 +53,13 @@ const(char)* toWinPath(const(char)* src) * loc = The line number information from where the call originates * filename = Path to file */ -FileBuffer readFile(Loc loc, const(char)* filename) +Buffer readFile(Loc loc, const(char)* filename) { return readFile(loc, filename.toDString()); } /// Ditto -FileBuffer readFile(Loc loc, const(char)[] filename) +Buffer readFile(Loc loc, const(char)[] filename) { auto result = File.read(filename); if (!result.success) @@ -66,7 +67,7 @@ FileBuffer readFile(Loc loc, const(char)[] filename) error(loc, "error reading file `%.*s`", cast(int)filename.length, filename.ptr); fatal(); } - return FileBuffer(result.extractSlice()); + return Buffer(result.extractSlice()); } @@ -83,7 +84,7 @@ extern (D) void writeFile(Loc loc, const(char)[] filename, const void[] data) ensurePathToNameExists(Loc.initial, filename); if (!File.update(filename, data)) { - error(loc, "Error writing file '%*.s'", cast(int) filename.length, filename.ptr); + error(loc, "Error writing file '%.*s'", cast(int) filename.length, filename.ptr); fatal(); } } diff --git a/gcc/d/expr.cc b/gcc/d/expr.cc index 61a2b50b149..c683d9da333 100644 --- a/gcc/d/expr.cc +++ b/gcc/d/expr.cc @@ -3006,6 +3006,16 @@ public: this->result_ = var; } + /* Build an uninitialized value, generated from void initializers. */ + + void visit (VoidInitExp *e) + { + /* The front-end only generates these for the initializer of globals. + Represent `void' as zeroes, regardless of the type's default value. */ + gcc_assert (this->constp_); + this->result_ = build_zero_cst (build_ctype (e->type)); + } + /* These expressions are mainly just a placeholders in the frontend. We shouldn't see them here. */ diff --git a/gcc/d/lang.opt b/gcc/d/lang.opt index 51e8e5c7e00..b4b81523c14 100644 --- a/gcc/d/lang.opt +++ b/gcc/d/lang.opt @@ -396,6 +396,10 @@ frevert=all D RejectNegative Turn off all revertable D language features. +frevert=dip1000 +D RejectNegative +Revert DIP1000: Scoped pointers. + frevert=dip25 D RejectNegative Revert DIP25: Sealed references. |