diff options
author | Iain Buclaw <ibuclaw@gdcproject.org> | 2024-02-24 10:26:09 +0100 |
---|---|---|
committer | Iain Buclaw <ibuclaw@gdcproject.org> | 2024-02-26 00:06:22 +0100 |
commit | 94687d17ba79cdb9fd01c87b2b40b63512471569 (patch) | |
tree | db4abf352a61c392db2c4c512dc9a8c061541f22 /gcc/d | |
parent | 3f58f96a4e8255e222953f9856bcd6c25f7b33cd (diff) | |
download | gcc-94687d17ba79cdb9fd01c87b2b40b63512471569.zip gcc-94687d17ba79cdb9fd01c87b2b40b63512471569.tar.gz gcc-94687d17ba79cdb9fd01c87b2b40b63512471569.tar.bz2 |
Merge dmd, druntime ceff48bf7d, phobos dcbfbd43a
D front-end changes:
- Import latest fixes from dmd v2.107.1-rc.1.
D runtime changes:
- Import latest fixes from druntime v2.107.1-rc.1.
Phobos changes:
- Import latest fixes from phobos v2.107.1-rc.1.
gcc/d/ChangeLog:
* dmd/MERGE: Merge upstream dmd ceff48bf7d.
libphobos/ChangeLog:
* libdruntime/MERGE: Merge upstream druntime ceff48bf7d.
* libdruntime/Makefile.am (DRUNTIME_DSOURCES_FREEBSD): Add
core/sys/freebsd/net/if_.d.
* libdruntime/Makefile.in: Regenerate.
* src/MERGE: Merge upstream phobos dcbfbd43a.
Diffstat (limited to 'gcc/d')
-rw-r--r-- | gcc/d/dmd/MERGE | 2 | ||||
-rw-r--r-- | gcc/d/dmd/arrayop.d | 2 | ||||
-rw-r--r-- | gcc/d/dmd/ast_node.h | 2 | ||||
-rw-r--r-- | gcc/d/dmd/common/file.d | 89 | ||||
-rw-r--r-- | gcc/d/dmd/common/smallbuffer.d | 30 | ||||
-rw-r--r-- | gcc/d/dmd/cparse.d | 150 | ||||
-rw-r--r-- | gcc/d/dmd/dimport.d | 109 | ||||
-rw-r--r-- | gcc/d/dmd/dmodule.d | 32 | ||||
-rw-r--r-- | gcc/d/dmd/dsymbolsem.d | 97 | ||||
-rw-r--r-- | gcc/d/dmd/expression.d | 4 | ||||
-rw-r--r-- | gcc/d/dmd/expression.h | 2 | ||||
-rw-r--r-- | gcc/d/dmd/expressionsem.d | 97 | ||||
-rw-r--r-- | gcc/d/dmd/func.d | 394 | ||||
-rw-r--r-- | gcc/d/dmd/funcsem.d | 390 | ||||
-rw-r--r-- | gcc/d/dmd/identifier.h | 2 | ||||
-rw-r--r-- | gcc/d/dmd/importc.d | 7 | ||||
-rw-r--r-- | gcc/d/dmd/mtype.d | 1 | ||||
-rw-r--r-- | gcc/d/dmd/parse.d | 48 | ||||
-rw-r--r-- | gcc/d/dmd/root/array.h | 3 | ||||
-rw-r--r-- | gcc/d/dmd/root/bitarray.h | 1 | ||||
-rw-r--r-- | gcc/d/dmd/rootobject.h (renamed from gcc/d/dmd/root/object.h) | 6 | ||||
-rw-r--r-- | gcc/d/dmd/statementsem.d | 2 | ||||
-rw-r--r-- | gcc/d/dmd/staticcond.d | 107 | ||||
-rw-r--r-- | gcc/d/dmd/template.h | 2 |
24 files changed, 822 insertions, 757 deletions
diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE index 021149a..f11c5fb 100644 --- a/gcc/d/dmd/MERGE +++ b/gcc/d/dmd/MERGE @@ -1,4 +1,4 @@ -9471b25db9ed44d71e0e27956430c0c6a09c16db +ceff48bf7db05503117f54fdc0cefcb89b711136 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/arrayop.d b/gcc/d/dmd/arrayop.d index afe6054..af3875e 100644 --- a/gcc/d/dmd/arrayop.d +++ b/gcc/d/dmd/arrayop.d @@ -22,7 +22,7 @@ import dmd.dsymbol; import dmd.errors; import dmd.expression; import dmd.expressionsem; -import dmd.func; +import dmd.funcsem; import dmd.hdrgen; import dmd.id; import dmd.identifier; diff --git a/gcc/d/dmd/ast_node.h b/gcc/d/dmd/ast_node.h index a24218a..db8608e 100644 --- a/gcc/d/dmd/ast_node.h +++ b/gcc/d/dmd/ast_node.h @@ -10,7 +10,7 @@ #pragma once -#include "root/object.h" +#include "rootobject.h" class Visitor; diff --git a/gcc/d/dmd/common/file.d b/gcc/d/dmd/common/file.d index 8a28424..80677f6 100644 --- a/gcc/d/dmd/common/file.d +++ b/gcc/d/dmd/common/file.d @@ -16,24 +16,37 @@ module dmd.common.file; import core.stdc.errno : errno; import core.stdc.stdio : fprintf, remove, rename, stderr; -import core.stdc.stdlib : exit; -import core.stdc.string : strerror, strlen; -import core.sys.windows.winbase; -import core.sys.windows.winnt; -import core.sys.posix.fcntl; -import core.sys.posix.unistd; +import core.stdc.stdlib; +import core.stdc.string : strerror, strlen, memcpy; import dmd.common.smallbuffer; -nothrow: - version (Windows) { + import core.sys.windows.winbase; import core.sys.windows.winnls : CP_ACP; + import core.sys.windows.winnt; + + enum CodePage = CP_ACP; // assume filenames encoded in system default Windows ANSI code page + enum invalidHandle = INVALID_HANDLE_VALUE; +} +else version (Posix) +{ + import core.sys.posix.fcntl; + import core.sys.posix.sys.mman; + import core.sys.posix.sys.stat; + import core.sys.posix.unistd; + import core.sys.posix.utime; - // assume filenames encoded in system default Windows ANSI code page - enum CodePage = CP_ACP; + enum invalidHandle = -1; } +else + static assert(0); + + + + +nothrow: /** Encapsulated management of a memory-mapped file. @@ -48,9 +61,6 @@ struct FileMapping(Datum) static assert(__traits(isPOD, Datum) && Datum.sizeof == 1, "Not tested with other data types yet. Add new types with care."); - version(Posix) enum invalidHandle = -1; - else version(Windows) enum invalidHandle = INVALID_HANDLE_VALUE; - // state { /// Handle of underlying file private auto handle = invalidHandle; @@ -82,9 +92,6 @@ struct FileMapping(Datum) { version (Posix) { - import core.sys.posix.sys.mman; - import core.sys.posix.fcntl : open, O_CREAT, O_RDONLY, O_RDWR, S_IRGRP, S_IROTH, S_IRUSR, S_IWUSR; - handle = open(filename, is(Datum == const) ? O_RDONLY : (O_CREAT | O_RDWR), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); @@ -150,9 +157,6 @@ struct FileMapping(Datum) // Save the name for later. Technically there's no need: on Linux one can use readlink on /proc/self/fd/NNN. // On BSD and OSX one can use fcntl with F_GETPATH. On Windows one can use GetFileInformationByHandleEx. // But just saving the name is simplest, fastest, and most portable... - import core.stdc.string : strlen; - import core.stdc.stdlib : malloc; - import core.stdc.string : memcpy; const totalNameLength = filename.strlen() + 1; auto namex = cast(char*) malloc(totalNameLength); if (!namex) @@ -224,9 +228,6 @@ struct FileMapping(Datum) fakePure({ version (Posix) { - import core.sys.posix.sys.mman : munmap; - import core.sys.posix.unistd : close; - // Cannot call fprintf from inside a destructor, so exiting silently. if (data.ptr && munmap(cast(void*) data.ptr, data.length) != 0) @@ -234,7 +235,7 @@ struct FileMapping(Datum) exit(1); } data = null; - if (handle != invalidHandle && close(handle) != 0) + if (handle != invalidHandle && .close(handle) != 0) { exit(1); } @@ -303,7 +304,6 @@ struct FileMapping(Datum) // In-memory resource freed, now get rid of the underlying temp file. version(Posix) { - import core.sys.posix.unistd : unlink; if (unlink(deleteme) != 0) { fprintf(stderr, "unlink(\"%s\") failed: %s\n", filename, strerror(errno)); @@ -312,7 +312,6 @@ struct FileMapping(Datum) } else version(Windows) { - import core.sys.windows.winbase; if (deleteme[0 .. strlen(deleteme)].extendedPathThen!(p => DeleteFileW(p.ptr)) == 0) { fprintf(stderr, "DeleteFileW error %d\n", GetLastError()); @@ -361,9 +360,6 @@ struct FileMapping(Datum) fakePure({ version(Posix) { - import core.sys.posix.unistd : ftruncate; - import core.sys.posix.sys.mman; - if (data.length) { assert(data.ptr, "Corrupt memory mapping"); @@ -431,7 +427,6 @@ struct FileMapping(Datum) // Fetch the name and then set it to `null` so it doesn't get deallocated auto oldname = name; - import core.stdc.stdlib; scope(exit) free(cast(void*) oldname); name = null; close(); @@ -447,7 +442,6 @@ struct FileMapping(Datum) } else version(Windows) { - import core.sys.windows.winbase; auto r = oldname[0 .. strlen(oldname)].extendedPathThen!( p1 => filename[0 .. strlen(filename)].extendedPathThen!(p2 => MoveFileExW(p1.ptr, p2.ptr, MOVEFILE_REPLACE_EXISTING)) ); @@ -527,8 +521,6 @@ bool touchFile(const char* namez) GetSystemTime(&st); SystemTimeToFileTime(&st, &ft); - import core.stdc.string : strlen; - // get handle to file HANDLE h = namez[0 .. namez.strlen()].extendedPathThen!(p => CreateFile(p.ptr, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, @@ -546,7 +538,6 @@ bool touchFile(const char* namez) } else version (Posix) { - import core.sys.posix.utime; return utime(namez, null) == 0; } else @@ -560,24 +551,28 @@ Params: fd = file handle Returns: file size in bytes, or `ulong.max` on any error. */ version (Posix) -private ulong fileSize(int fd) { - import core.sys.posix.sys.stat; - stat_t buf; - if (fstat(fd, &buf) == 0) - return buf.st_size; - return ulong.max; + private ulong fileSize(int fd) + { + stat_t buf; + if (fstat(fd, &buf) == 0) + return buf.st_size; + return ulong.max; + } } - -/// Ditto -version (Windows) -private ulong fileSize(HANDLE fd) +else version (Windows) { - ulong result; - if (GetFileSizeEx(fd, cast(LARGE_INTEGER*) &result) == 0) - return result; - return ulong.max; + /// Ditto + private ulong fileSize(HANDLE fd) + { + ulong result; + if (GetFileSizeEx(fd, cast(LARGE_INTEGER*) &result) == 0) + return result; + return ulong.max; + } } +else + static assert(0); /** Runs a non-pure function or delegate as pure code. Use with caution. diff --git a/gcc/d/dmd/common/smallbuffer.d b/gcc/d/dmd/common/smallbuffer.d index c6aa7ab..608ecc8 100644 --- a/gcc/d/dmd/common/smallbuffer.d +++ b/gcc/d/dmd/common/smallbuffer.d @@ -107,28 +107,30 @@ unittest } /** -(Windows only) Converts a narrow string to a wide string using `buffer` as strorage. Returns a slice managed by -`buffer` containing the converted string. The terminating zero is not part of the returned slice, -but is guaranteed to follow it. + * (Windows only) Converts a narrow string to a wide string using `buffer` as strorage. + * Params: + * narrow = string to be converted + * buffer = where to place the converted string + * Returns: a slice of `buffer` containing the converted string. A zero follows the slice. */ version(Windows) wchar[] toWStringz(scope const(char)[] narrow, ref SmallBuffer!wchar buffer) nothrow { - import core.sys.windows.winnls : MultiByteToWideChar; - import dmd.common.file : CodePage; - if (narrow is null) return null; - size_t length; - int i; - while (1) + size_t charsToWchars(scope const(char)[] narrow, scope wchar[] buffer) { // https://learn.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-multibytetowidechar - length = MultiByteToWideChar(CodePage, 0, narrow.ptr, cast(int) narrow.length, buffer.ptr, cast(int) buffer.length); - if (length < buffer.length) - break; - buffer.create(length + 1); - assert(++i == 1); // ensure loop should only execute once or twice + import core.sys.windows.winnls : MultiByteToWideChar, CP_ACP; + return MultiByteToWideChar(CP_ACP, 0, narrow.ptr, cast(int) narrow.length, buffer.ptr, cast(int) buffer.length); + } + + size_t length = charsToWchars(narrow, buffer[]); + if (length >= buffer.length) // not enough room in buffer[] + { + buffer.create(length + 1); // extend buffer length + length = charsToWchars(narrow, buffer[]); // try again + assert(length < buffer.length); } buffer[length] = 0; return buffer[0 .. length]; diff --git a/gcc/d/dmd/cparse.d b/gcc/d/dmd/cparse.d index 536a212..e917d2c 100644 --- a/gcc/d/dmd/cparse.d +++ b/gcc/d/dmd/cparse.d @@ -1682,9 +1682,12 @@ final class CParser(AST) : Parser!AST AST.ParameterList parameterList; StorageClass stc = 0; const loc = token.loc; + auto symbolsSave = symbols; + symbols = new AST.Dsymbols(); typedefTab.push(null); auto fbody = cparseStatement(ParseStatementFlags.scope_); typedefTab.pop(); // end of function scope + symbols = symbolsSave; // Rewrite last ExpStatement (if there is one) as a ReturnStatement auto ss = fbody.isScopeStatement(); @@ -1693,8 +1696,11 @@ final class CParser(AST) : Parser!AST if (const len = (*cs.statements).length) { auto s = (*cs.statements)[len - 1]; - if (auto es = s.isExpStatement()) - (*cs.statements)[len - 1] = new AST.ReturnStatement(es.loc, es.exp); + if (s) // error recovery should be with ErrorStatement, not null + { + if (auto es = s.isExpStatement()) + (*cs.statements)[len - 1] = new AST.ReturnStatement(es.loc, es.exp); + } } auto tf = new AST.TypeFunction(parameterList, null, LINK.d, stc); @@ -5520,7 +5526,7 @@ final class CParser(AST) : Parser!AST defines.writeByte('#'); defines.writestring(n.ident.toString()); skipToNextLine(defines); - defines.writeByte('\n'); + defines.writeByte(0); // each #define line is 0 terminated return true; } else if (n.ident == Id.__pragma) @@ -5840,7 +5846,8 @@ final class CParser(AST) : Parser!AST const length = buf.length; buf.writeByte(0); auto slice = buf.peekChars()[0 .. length]; - resetDefineLines(slice); // reset lexer + auto scanlocSave = scanloc; + resetDefineLines(slice); // reset lexer auto save = eSink; auto eLatch = new ErrorSinkLatch(); eSink = eLatch; @@ -5865,12 +5872,14 @@ final class CParser(AST) : Parser!AST (*symbols)[*pd] = s; return; } + assert(symbols, "symbols is null"); defineTab[cast(void*)s.ident] = symbols.length; symbols.push(s); } while (p < endp) { + //printf("|%s|\n", p); if (p[0 .. 7] == "#define") { p += 7; @@ -5884,10 +5893,11 @@ final class CParser(AST) : Parser!AST AST.Type t; + Lswitch: switch (token.value) { - case TOK.endOfLine: // #define identifier - nextDefineLine(); + case TOK.endOfFile: // #define identifier + ++p; continue; case TOK.int32Literal: @@ -5901,7 +5911,7 @@ final class CParser(AST) : Parser!AST Linteger: const intvalue = token.intvalue; nextToken(); - if (token.value == TOK.endOfLine) + if (token.value == TOK.endOfFile) { /* Declare manifest constant: * enum id = intvalue; @@ -5909,7 +5919,7 @@ final class CParser(AST) : Parser!AST AST.Expression e = new AST.IntegerExp(scanloc, intvalue, t); auto v = new AST.VarDeclaration(scanloc, t, id, new AST.ExpInitializer(scanloc, e), STC.manifest); addVar(v); - nextDefineLine(); + ++p; continue; } break; @@ -5924,7 +5934,7 @@ final class CParser(AST) : Parser!AST Lfloat: const floatvalue = token.floatvalue; nextToken(); - if (token.value == TOK.endOfLine) + if (token.value == TOK.endOfFile) { /* Declare manifest constant: * enum id = floatvalue; @@ -5932,7 +5942,7 @@ final class CParser(AST) : Parser!AST AST.Expression e = new AST.RealExp(scanloc, floatvalue, t); auto v = new AST.VarDeclaration(scanloc, t, id, new AST.ExpInitializer(scanloc, e), STC.manifest); addVar(v); - nextDefineLine(); + ++p; continue; } break; @@ -5942,7 +5952,7 @@ final class CParser(AST) : Parser!AST const len = token.len; const postfix = token.postfix; nextToken(); - if (token.value == TOK.endOfLine) + if (token.value == TOK.endOfFile) { /* Declare manifest constant: * enum id = "string"; @@ -5950,19 +5960,20 @@ final class CParser(AST) : Parser!AST AST.Expression e = new AST.StringExp(scanloc, str[0 .. len], len, 1, postfix); auto v = new AST.VarDeclaration(scanloc, null, id, new AST.ExpInitializer(scanloc, e), STC.manifest); addVar(v); - nextDefineLine(); + ++p; continue; } break; case TOK.leftParenthesis: + { /* Look for: * #define ID ( expression ) * and rewrite it to a template function: * auto ID()() { return expression; } */ if (params) - break; // no parameters + goto caseFunctionLike; // version with parameters nextToken(); eLatch.sawErrors = false; auto exp = cparseExpression(); @@ -5971,7 +5982,7 @@ final class CParser(AST) : Parser!AST if (token.value != TOK.rightParenthesis) break; nextToken(); - if (token.value != TOK.endOfLine) + if (token.value != TOK.endOfFile) break; auto ret = new AST.ReturnStatement(exp.loc, exp); auto parameterList = AST.ParameterList(new AST.Parameters(), VarArg.none, 0); @@ -5985,26 +5996,115 @@ final class CParser(AST) : Parser!AST AST.Expression constraint = null; auto tempdecl = new AST.TemplateDeclaration(exp.loc, id, tpl, constraint, decldefs, false); addVar(tempdecl); - nextDefineLine(); + ++p; continue; + } + + caseFunctionLike: + { + /* Parse `( a, b ) expression` + * Create template function: + * auto id(__MP1, __MP2)(__MP1 a, __MP1 b) { return expression; } + */ + //printf("functionlike %s\n", id.toChars()); + + // Capture the parameter list + VarArg varargs = VarArg.none; + auto parameters = new AST.Parameters(); + nextToken(); // skip past `(` + Lwhile: + while (1) + { + if (token.value == TOK.rightParenthesis) + break; + if (token.value == TOK.dotDotDot) + { + static if (0) // variadic macros not supported yet + { + varargs = AST.VarArg.variadic; // C-style variadics + nextToken(); + if (token.value == TOK.rightParenthesis) + break Lwhile; + } + break Lswitch; + } + + if (token.value != TOK.identifier) + break Lswitch; + auto param = new AST.Parameter(token.loc, 0, null, token.ident, null, null); + parameters.push(param); + nextToken(); + if (token.value == TOK.comma) + { + nextToken(); + continue; + } + break; + } + if (token.value != TOK.rightParenthesis) + break; + + //auto pstart = p; + nextToken(); + auto parameterList = AST.ParameterList(parameters, varargs, 0); + /* Create a type for each parameter. Add it to the template parameter list, + * and the parameter list. + */ + auto tpl = new AST.TemplateParameters(); + foreach (param; (*parameters)[]) + { + auto idtype = Identifier.generateId("__MP"); + auto loc = param.loc; + auto tp = new AST.TemplateTypeParameter(loc, idtype, null, null); + tpl.push(tp); + + auto at = new AST.TypeIdentifier(loc, idtype); + param.type = at; + } + + eLatch.sawErrors = false; + auto exp = cparseExpression(); + + //printf("exp: %s tok: %s\n", exp.toChars(), Token.toChars(token.value)); + //printf("parsed: '%.*s'\n", cast(int)(p - pstart), pstart); + assert(symbols); + + if (eLatch.sawErrors) // parsing errors + break; // abandon this #define + + if (token.value != TOK.endOfFile) // did not consume the entire line + break; + + // Generate function + auto ret = new AST.ReturnStatement(exp.loc, exp); + StorageClass stc = STC.auto_; + auto tf = new AST.TypeFunction(parameterList, null, LINK.d, stc); + auto fd = new AST.FuncDeclaration(exp.loc, exp.loc, id, stc, tf, 0); + fd.fbody = ret; + + // Wrap it in an eponymous template + AST.Dsymbols* decldefs = new AST.Dsymbols(); + decldefs.push(fd); + auto tempdecl = new AST.TemplateDeclaration(exp.loc, id, tpl, null, decldefs, false); + addVar(tempdecl); + + ++p; + continue; + } default: break; } } - skipToNextLine(); - } - else - { - scan(&token); - if (token.value != TOK.endOfLine) - { - skipToNextLine(); - } } - nextDefineLine(); + // scan to end of line + while (*p) + ++p; + ++p; // advance to start of next line + scanloc.linnum = scanloc.linnum + 1; } + scanloc = scanlocSave; eSink = save; defines = buf; } diff --git a/gcc/d/dmd/dimport.d b/gcc/d/dmd/dimport.d index b083c03..2efdd31 100644 --- a/gcc/d/dmd/dimport.d +++ b/gcc/d/dmd/dimport.d @@ -12,21 +12,13 @@ module dmd.dimport; import dmd.arraytypes; -import dmd.astenums; -import dmd.declaration; import dmd.dmodule; -import dmd.dscope; import dmd.dsymbol; -import dmd.dsymbolsem; import dmd.errors; -import dmd.expression; -import dmd.globals; import dmd.identifier; import dmd.location; -import dmd.mtype; import dmd.visitor; -import core.stdc.stdio; /*********************************************************** */ extern (C++) final class Import : Dsymbol @@ -76,6 +68,8 @@ extern (C++) final class Import : Dsymbol assert(id); version (none) { + import core.stdc.stdio; + printf("Import::Import("); foreach (id; packages) { @@ -124,105 +118,6 @@ extern (C++) final class Import : Dsymbol } /******************************* - * Load this module. - * Returns: - * true for errors, false for success - */ - extern (D) bool load(Scope* sc) - { - //printf("Import::load('%s') %p\n", toPrettyChars(), this); - // See if existing module - const errors = global.errors; - DsymbolTable dst = Package.resolve(packages, null, &pkg); - version (none) - { - if (pkg && pkg.isModule()) - { - .error(loc, "can only import from a module, not from a member of module `%s`. Did you mean `import %s : %s`?", pkg.toChars(), pkg.toPrettyChars(), id.toChars()); - mod = pkg.isModule(); // Error recovery - treat as import of that module - return true; - } - } - Dsymbol s = dst.lookup(id); - if (s) - { - if (s.isModule()) - mod = cast(Module)s; - else - { - if (s.isAliasDeclaration()) - { - .error(loc, "%s `%s` conflicts with `%s`", s.kind(), s.toPrettyChars(), id.toChars()); - } - else if (Package p = s.isPackage()) - { - if (p.isPkgMod == PKG.unknown) - { - uint preverrors = global.errors; - mod = Module.load(loc, packages, id); - if (!mod) - p.isPkgMod = PKG.package_; - else - { - // mod is a package.d, or a normal module which conflicts with the package name. - if (mod.isPackageFile) - mod.tag = p.tag; // reuse the same package tag - else - { - // show error if Module.load does not - if (preverrors == global.errors) - .error(loc, "%s `%s` from file %s conflicts with %s `%s`", mod.kind(), mod.toPrettyChars(), mod.srcfile.toChars, p.kind(), p.toPrettyChars()); - return true; - } - } - } - else - { - mod = p.isPackageMod(); - } - if (!mod) - { - .error(loc, "can only import from a module, not from package `%s.%s`", p.toPrettyChars(), id.toChars()); - } - } - else if (pkg) - { - .error(loc, "can only import from a module, not from package `%s.%s`", pkg.toPrettyChars(), id.toChars()); - } - else - { - .error(loc, "can only import from a module, not from package `%s`", id.toChars()); - } - } - } - if (!mod) - { - // Load module - mod = Module.load(loc, packages, id); - if (mod) - { - // id may be different from mod.ident, if so then insert alias - dst.insert(id, mod); - } - } - if (mod && !mod.importedFrom) - mod.importedFrom = sc ? sc._module.importedFrom : Module.rootModule; - if (!pkg) - { - if (mod && mod.isPackageFile) - { - // one level depth package.d file (import pkg; ./pkg/package.d) - // it's necessary to use the wrapping Package already created - pkg = mod.pkg; - } - else - pkg = mod; - } - //printf("-Import::load('%s'), pkg = %p\n", toChars(), pkg); - return global.errors != errors; - } - - /******************************* * Mark the imported packages as accessible from the current * scope. This access check is necessary when using FQN b/c * we're using a single global package tree. diff --git a/gcc/d/dmd/dmodule.d b/gcc/d/dmd/dmodule.d index 07d5077..a77e4f3 100644 --- a/gcc/d/dmd/dmodule.d +++ b/gcc/d/dmd/dmodule.d @@ -16,12 +16,14 @@ module dmd.dmodule; import core.stdc.stdio; import core.stdc.stdlib; import core.stdc.string; + import dmd.aggregate; import dmd.arraytypes; import dmd.astcodegen; import dmd.astenums; +import dmd.common.outbuffer; import dmd.compiler; -import dmd.gluelayer; +import dmd.cparse; import dmd.dimport; import dmd.dmacro; import dmd.doc; @@ -35,25 +37,37 @@ import dmd.expressionsem; import dmd.file_manager; import dmd.func; import dmd.globals; +import dmd.gluelayer; import dmd.id; import dmd.identifier; import dmd.location; import dmd.parse; -import dmd.cparse; import dmd.root.array; import dmd.root.file; import dmd.root.filename; -import dmd.common.outbuffer; import dmd.root.port; import dmd.root.rmem; -import dmd.rootobject; import dmd.root.string; +import dmd.rootobject; import dmd.semantic2; import dmd.semantic3; import dmd.target; import dmd.utils; import dmd.visitor; +version (Windows) +{ + import core.sys.windows.winbase : getpid = GetCurrentProcessId; + enum PathSeparator = '\\'; +} +else version (Posix) +{ + import core.sys.posix.unistd : getpid; + enum PathSeparator = '/'; +} +else + static assert(0); + version (IN_GCC) {} else version (IN_LLVM) {} else version = MARS; @@ -141,11 +155,7 @@ private const(char)[] getFilename(Identifier[] packages, Identifier ident) nothr buf.writestring(p); if (modAliases.length) checkModFileAlias(p); - version (Windows) - enum FileSeparator = '\\'; - else - enum FileSeparator = '/'; - buf.writeByte(FileSeparator); + buf.writeByte(PathSeparator); } buf.writestring(filename); if (modAliases.length) @@ -558,10 +568,6 @@ extern (C++) final class Module : Package OutBuffer buf; if (arg == "__stdin.d") { - version (Posix) - import core.sys.posix.unistd : getpid; - else version (Windows) - import core.sys.windows.winbase : getpid = GetCurrentProcessId; buf.printf("__stdin_%d.d", getpid()); arg = buf[]; } diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d index c15d925..bb0a1d6 100644 --- a/gcc/d/dmd/dsymbolsem.d +++ b/gcc/d/dmd/dsymbolsem.d @@ -6916,6 +6916,103 @@ extern(C++) class ImportAllVisitor : Visitor override void visit(StaticForeachDeclaration _) {} } +/******************************* + * Load module. + * Returns: + * true for errors, false for success + */ +extern (D) bool load(Import imp, Scope* sc) +{ + // See if existing module + const errors = global.errors; + DsymbolTable dst = Package.resolve(imp.packages, null, &imp.pkg); + version (none) + { + if (pkg && pkg.isModule()) + { + .error(loc, "can only import from a module, not from a member of module `%s`. Did you mean `import %s : %s`?", pkg.toChars(), pkg.toPrettyChars(), id.toChars()); + mod = pkg.isModule(); // Error recovery - treat as import of that module + return true; + } + } + Dsymbol s = dst.lookup(imp.id); + if (s) + { + if (s.isModule()) + imp.mod = cast(Module)s; + else + { + if (s.isAliasDeclaration()) + { + .error(imp.loc, "%s `%s` conflicts with `%s`", s.kind(), s.toPrettyChars(), imp.id.toChars()); + } + else if (Package p = s.isPackage()) + { + if (p.isPkgMod == PKG.unknown) + { + uint preverrors = global.errors; + imp.mod = Module.load(imp.loc, imp.packages, imp.id); + if (!imp.mod) + p.isPkgMod = PKG.package_; + else + { + // imp.mod is a package.d, or a normal module which conflicts with the package name. + if (imp.mod.isPackageFile) + imp.mod.tag = p.tag; // reuse the same package tag + else + { + // show error if Module.load does not + if (preverrors == global.errors) + .error(imp.loc, "%s `%s` from file %s conflicts with %s `%s`", imp.mod.kind(), imp.mod.toPrettyChars(), imp.mod.srcfile.toChars, p.kind(), p.toPrettyChars()); + return true; + } + } + } + else + { + imp.mod = p.isPackageMod(); + } + if (!imp.mod) + { + .error(imp.loc, "can only import from a module, not from package `%s.%s`", p.toPrettyChars(), imp.id.toChars()); + } + } + else if (imp.pkg) + { + .error(imp.loc, "can only import from a module, not from package `%s.%s`", imp.pkg.toPrettyChars(), imp.id.toChars()); + } + else + { + .error(imp.loc, "can only import from a module, not from package `%s`", imp.id.toChars()); + } + } + } + if (!imp.mod) + { + // Load module + imp.mod = Module.load(imp.loc, imp.packages, imp.id); + if (imp.mod) + { + // imp.id may be different from mod.ident, if so then insert alias + dst.insert(imp.id, imp.mod); + } + } + if (imp.mod && !imp.mod.importedFrom) + imp.mod.importedFrom = sc ? sc._module.importedFrom : Module.rootModule; + if (!imp.pkg) + { + if (imp.mod && imp.mod.isPackageFile) + { + // one level depth package.d file (import pkg; ./pkg/package.d) + // it's necessary to use the wrapping Package already created + imp.pkg = imp.mod.pkg; + } + else + imp.pkg = imp.mod; + } + return global.errors != errors; +} + void setFieldOffset(Dsymbol d, AggregateDeclaration ad, FieldState* fieldState, bool isunion) { scope v = new SetFieldOffsetVisitor(ad, fieldState, isunion); diff --git a/gcc/d/dmd/expression.d b/gcc/d/dmd/expression.d index bc907cf..479ad3a 100644 --- a/gcc/d/dmd/expression.d +++ b/gcc/d/dmd/expression.d @@ -314,6 +314,7 @@ extern (C++) abstract class Expression : ASTNode Type type; // !=null means that semantic() has been run Loc loc; // file location const EXP op; // to minimize use of dynamic_cast + bool parens; // if this is a parenthesized expression extern (D) this(const ref Loc loc, EXP op) scope @safe { @@ -1310,7 +1311,6 @@ extern (C++) final class ComplexExp : Expression extern (C++) class IdentifierExp : Expression { Identifier ident; - bool parens; // if it appears as (identifier) extern (D) this(const ref Loc loc, Identifier ident) scope @safe { @@ -2432,8 +2432,6 @@ extern (C++) final class CompoundLiteralExp : Expression */ extern (C++) final class TypeExp : Expression { - bool parens; // if this is a parenthesized expression - extern (D) this(const ref Loc loc, Type type) @safe { super(loc, EXP.type); diff --git a/gcc/d/dmd/expression.h b/gcc/d/dmd/expression.h index 3bd8ca7..9cd73a9 100644 --- a/gcc/d/dmd/expression.h +++ b/gcc/d/dmd/expression.h @@ -90,6 +90,7 @@ public: Type *type; // !=NULL means that semantic() has been run Loc loc; // file location EXP op; // to minimize use of dynamic_cast + d_bool parens; // if this is a parenthesized expression size_t size() const; static void _init(); @@ -300,7 +301,6 @@ class IdentifierExp : public Expression { public: Identifier *ident; - d_bool parens; static IdentifierExp *create(const Loc &loc, Identifier *ident); bool isLvalue() override final; diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d index cc589b9..b4d5274 100644 --- a/gcc/d/dmd/expressionsem.d +++ b/gcc/d/dmd/expressionsem.d @@ -16628,3 +16628,100 @@ Expression toBoolean(Expression exp, Scope* sc) return e; } } + +/******************************************** + * Semantically analyze and then evaluate a static condition at compile time. + * This is special because short circuit operators &&, || and ?: at the top + * level are not semantically analyzed if the result of the expression is not + * necessary. + * Params: + * sc = instantiating scope + * original = original expression, for error messages + * e = resulting expression + * errors = set to `true` if errors occurred + * negatives = array to store negative clauses + * Returns: + * true if evaluates to true + */ +bool evalStaticCondition(Scope* sc, Expression original, Expression e, out bool errors, Expressions* negatives = null) +{ + if (negatives) + negatives.setDim(0); + + bool impl(Expression e) + { + if (e.isNotExp()) + { + NotExp ne = cast(NotExp)e; + return !impl(ne.e1); + } + + if (e.op == EXP.andAnd || e.op == EXP.orOr) + { + LogicalExp aae = cast(LogicalExp)e; + bool result = impl(aae.e1); + if (errors) + return false; + if (e.op == EXP.andAnd) + { + if (!result) + return false; + } + else + { + if (result) + return true; + } + result = impl(aae.e2); + return !errors && result; + } + + if (e.op == EXP.question) + { + CondExp ce = cast(CondExp)e; + bool result = impl(ce.econd); + if (errors) + return false; + Expression leg = result ? ce.e1 : ce.e2; + result = impl(leg); + return !errors && result; + } + + Expression before = e; + const uint nerrors = global.errors; + + sc = sc.startCTFE(); + sc.flags |= SCOPE.condition; + + e = e.expressionSemantic(sc); + e = resolveProperties(sc, e); + e = e.toBoolean(sc); + + sc = sc.endCTFE(); + e = e.optimize(WANTvalue); + + if (nerrors != global.errors || + e.isErrorExp() || + e.type.toBasetype() == Type.terror) + { + errors = true; + return false; + } + + e = e.ctfeInterpret(); + + const opt = e.toBool(); + if (opt.isEmpty()) + { + if (!e.type.isTypeError()) + error(e.loc, "expression `%s` is not constant", e.toChars()); + errors = true; + return false; + } + + if (negatives && !opt.get()) + negatives.push(before); + return opt.get(); + } + return impl(e); +} diff --git a/gcc/d/dmd/func.d b/gcc/d/dmd/func.d index 4881ad6..d890811 100644 --- a/gcc/d/dmd/func.d +++ b/gcc/d/dmd/func.d @@ -58,7 +58,6 @@ import dmd.semantic3; import dmd.statement_rewrite_walker; import dmd.statement; import dmd.statementsem; -import dmd.templatesem; import dmd.tokens; import dmd.typesem; import dmd.visitor; @@ -2925,395 +2924,6 @@ unittest assert(mismatches.isMutable); } -/// Flag used by $(LREF resolveFuncCall). -enum FuncResolveFlag : ubyte -{ - standard = 0, /// issue error messages, solve the call. - quiet = 1, /// do not issue error message on no match, just return `null`. - overloadOnly = 2, /// only resolve overloads, i.e. do not issue error on ambiguous - /// matches and need explicit this. - ufcs = 4, /// trying to resolve UFCS call -} - -/******************************************* - * Given a symbol that could be either a FuncDeclaration or - * a function template, resolve it to a function symbol. - * Params: - * loc = instantiation location - * sc = instantiation scope - * s = instantiation symbol - * tiargs = initial list of template arguments - * tthis = if !NULL, the `this` argument type - * argumentList = arguments to function - * flags = see $(LREF FuncResolveFlag). - * Returns: - * if match is found, then function symbol, else null - */ -FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, - Objects* tiargs, Type tthis, ArgumentList argumentList, FuncResolveFlag flags) -{ - auto fargs = argumentList.arguments; - if (!s) - return null; // no match - - version (none) - { - printf("resolveFuncCall('%s')\n", s.toChars()); - if (tthis) - printf("\tthis: %s\n", tthis.toChars()); - if (fargs) - { - for (size_t i = 0; i < fargs.length; i++) - { - Expression arg = (*fargs)[i]; - assert(arg.type); - printf("\t%s: %s\n", arg.toChars(), arg.type.toChars()); - } - } - printf("\tfnames: %s\n", fnames ? fnames.toChars() : "null"); - } - - if (tiargs && arrayObjectIsError(*tiargs)) - return null; - if (fargs !is null) - foreach (arg; *fargs) - if (isError(arg)) - return null; - - MatchAccumulator m; - functionResolve(m, s, loc, sc, tiargs, tthis, argumentList); - auto orig_s = s; - - if (m.last > MATCH.nomatch && m.lastf) - { - if (m.count == 1) // exactly one match - { - if (!(flags & FuncResolveFlag.quiet)) - functionSemantic(m.lastf); - return m.lastf; - } - if ((flags & FuncResolveFlag.overloadOnly) && !tthis && m.lastf.needThis()) - { - return m.lastf; - } - } - - /* Failed to find a best match. - * Do nothing or print error. - */ - if (m.last == MATCH.nomatch) - { - // error was caused on matched function, not on the matching itself, - // so return the function to produce a better diagnostic - if (m.count == 1) - return m.lastf; - } - - // We are done at this point, as the rest of this function generate - // a diagnostic on invalid match - if (flags & FuncResolveFlag.quiet) - return null; - - auto fd = s.isFuncDeclaration(); - auto od = s.isOverDeclaration(); - auto td = s.isTemplateDeclaration(); - if (td && td.funcroot) - s = fd = td.funcroot; - - OutBuffer tiargsBuf; - arrayObjectsToBuffer(tiargsBuf, tiargs); - - OutBuffer fargsBuf; - fargsBuf.writeByte('('); - argExpTypesToCBuffer(fargsBuf, fargs); - fargsBuf.writeByte(')'); - if (tthis) - tthis.modToBuffer(fargsBuf); - - // The call is ambiguous - if (m.lastf && m.nextf) - { - TypeFunction tf1 = m.lastf.type.toTypeFunction(); - TypeFunction tf2 = m.nextf.type.toTypeFunction(); - const(char)* lastprms = parametersTypeToChars(tf1.parameterList); - const(char)* nextprms = parametersTypeToChars(tf2.parameterList); - - .error(loc, "`%s.%s` called with argument types `%s` matches both:\n%s: `%s%s%s`\nand:\n%s: `%s%s%s`", - s.parent.toPrettyChars(), s.ident.toChars(), - fargsBuf.peekChars(), - m.lastf.loc.toChars(), m.lastf.toPrettyChars(), lastprms, tf1.modToChars(), - m.nextf.loc.toChars(), m.nextf.toPrettyChars(), nextprms, tf2.modToChars()); - return null; - } - - // no match, generate an error messages - if (flags & FuncResolveFlag.ufcs) - { - auto arg = (*fargs)[0]; - .error(loc, "no property `%s` for `%s` of type `%s`", s.ident.toChars(), arg.toChars(), arg.type.toChars()); - .errorSupplemental(loc, "the following error occured while looking for a UFCS match"); - } - - if (!fd) - { - // all of overloads are templates - if (td) - { - if (!od && !td.overnext) - { - .error(loc, "%s `%s` is not callable using argument types `!(%s)%s`", - td.kind(), td.ident.toChars(), tiargsBuf.peekChars(), fargsBuf.peekChars()); - } - else - { - .error(loc, "none of the overloads of %s `%s.%s` are callable using argument types `!(%s)%s`", - td.kind(), td.parent.toPrettyChars(), td.ident.toChars(), - tiargsBuf.peekChars(), fargsBuf.peekChars()); - } - - - if (!global.gag || global.params.v.showGaggedErrors) - printCandidates(loc, td, sc.isDeprecated()); - return null; - } - /* This case used to happen when several ctors are mixed in an agregate. - A (bad) error message is already generated in overloadApply(). - see https://issues.dlang.org/show_bug.cgi?id=19729 - and https://issues.dlang.org/show_bug.cgi?id=17259 - */ - if (!od) - return null; - } - - if (od) - { - .error(loc, "none of the overloads of `%s` are callable using argument types `!(%s)%s`", - od.ident.toChars(), tiargsBuf.peekChars(), fargsBuf.peekChars()); - return null; - } - - // remove when deprecation period of class allocators and deallocators is over - if (fd.isNewDeclaration() && fd.checkDisabled(loc, sc)) - return null; - - bool hasOverloads = fd.overnext !is null; - auto tf = fd.type.isTypeFunction(); - // if type is an error, the original type should be there for better diagnostics - if (!tf) - tf = fd.originalType.toTypeFunction(); - - // modifier mismatch - if (tthis && (fd.isCtorDeclaration() ? - !MODimplicitConv(tf.mod, tthis.mod) : - !MODimplicitConv(tthis.mod, tf.mod))) - { - OutBuffer thisBuf, funcBuf; - MODMatchToBuffer(&thisBuf, tthis.mod, tf.mod); - auto mismatches = MODMatchToBuffer(&funcBuf, tf.mod, tthis.mod); - if (hasOverloads) - { - OutBuffer buf; - buf.argExpTypesToCBuffer(fargs); - if (fd.isCtorDeclaration()) - .error(loc, "none of the overloads of `%s` can construct a %sobject with argument types `(%s)`", - fd.toChars(), thisBuf.peekChars(), buf.peekChars()); - else - .error(loc, "none of the overloads of `%s` are callable using a %sobject with argument types `(%s)`", - fd.toChars(), thisBuf.peekChars(), buf.peekChars()); - - if (!global.gag || global.params.v.showGaggedErrors) - printCandidates(loc, fd, sc.isDeprecated()); - return null; - } - - bool calledHelper; - void errorHelper(const(char)* failMessage) scope - { - .error(loc, "%s `%s%s%s` is not callable using argument types `%s`", - fd.kind(), fd.toPrettyChars(), parametersTypeToChars(tf.parameterList), - tf.modToChars(), fargsBuf.peekChars()); - errorSupplemental(loc, failMessage); - calledHelper = true; - } - - functionResolve(m, orig_s, loc, sc, tiargs, tthis, argumentList, &errorHelper); - if (calledHelper) - return null; - - if (fd.isCtorDeclaration()) - .error(loc, "%s%s `%s` cannot construct a %sobject", - funcBuf.peekChars(), fd.kind(), fd.toPrettyChars(), thisBuf.peekChars()); - else - .error(loc, "%smethod `%s` is not callable using a %sobject", - funcBuf.peekChars(), fd.toPrettyChars(), thisBuf.peekChars()); - - if (mismatches.isNotShared) - .errorSupplemental(fd.loc, "Consider adding `shared` here"); - else if (mismatches.isMutable) - .errorSupplemental(fd.loc, "Consider adding `const` or `inout` here"); - return null; - } - - //printf("tf = %s, args = %s\n", tf.deco, (*fargs)[0].type.deco); - if (hasOverloads) - { - .error(loc, "none of the overloads of `%s` are callable using argument types `%s`", - fd.toChars(), fargsBuf.peekChars()); - if (!global.gag || global.params.v.showGaggedErrors) - printCandidates(loc, fd, sc.isDeprecated()); - return null; - } - - .error(loc, "%s `%s%s%s` is not callable using argument types `%s`", - fd.kind(), fd.toPrettyChars(), parametersTypeToChars(tf.parameterList), - tf.modToChars(), fargsBuf.peekChars()); - - // re-resolve to check for supplemental message - if (!global.gag || global.params.v.showGaggedErrors) - { - if (tthis) - { - if (auto classType = tthis.isTypeClass()) - { - if (auto baseClass = classType.sym.baseClass) - { - if (auto baseFunction = baseClass.search(baseClass.loc, fd.ident)) - { - MatchAccumulator mErr; - functionResolve(mErr, baseFunction, loc, sc, tiargs, baseClass.type, argumentList); - if (mErr.last > MATCH.nomatch && mErr.lastf) - { - errorSupplemental(loc, "%s `%s` hides base class function `%s`", - fd.kind, fd.toPrettyChars(), mErr.lastf.toPrettyChars()); - errorSupplemental(loc, "add `alias %s = %s` to `%s`'s body to merge the overload sets", - fd.toChars(), mErr.lastf.toPrettyChars(), tthis.toChars()); - return null; - } - } - } - } - } - - void errorHelper2(const(char)* failMessage) scope - { - errorSupplemental(loc, failMessage); - } - - functionResolve(m, orig_s, loc, sc, tiargs, tthis, argumentList, &errorHelper2); - } - return null; -} - -/******************************************* - * Prints template and function overload candidates as supplemental errors. - * Params: - * loc = instantiation location - * declaration = the declaration to print overload candidates for - * showDeprecated = If `false`, `deprecated` function won't be shown - */ -private void printCandidates(Decl)(const ref Loc loc, Decl declaration, bool showDeprecated) -if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration)) -{ - // max num of overloads to print (-v or -verror-supplements overrides this). - const uint DisplayLimit = global.params.v.errorSupplementCount(); - const(char)* constraintsTip; - // determine if the first candidate was printed - int printed; - - bool matchSymbol(Dsymbol s, bool print, bool single_candidate = false) - { - if (auto fd = s.isFuncDeclaration()) - { - // Don't print overloads which have errors. - // Not that if the whole overload set has errors, we'll never reach - // this point so there's no risk of printing no candidate - if (fd.errors || fd.type.ty == Terror) - return false; - // Don't print disabled functions, or `deprecated` outside of deprecated scope - if (fd.storage_class & STC.disable || (fd.isDeprecated() && !showDeprecated)) - return false; - if (!print) - return true; - auto tf = cast(TypeFunction) fd.type; - OutBuffer buf; - buf.writestring(fd.toPrettyChars()); - buf.writestring(parametersTypeToChars(tf.parameterList)); - if (tf.mod) - { - buf.writeByte(' '); - buf.MODtoBuffer(tf.mod); - } - .errorSupplemental(fd.loc, - printed ? " `%s`" : - single_candidate ? "Candidate is: `%s`" : "Candidates are: `%s`", buf.peekChars()); - } - else if (auto td = s.isTemplateDeclaration()) - { - import dmd.staticcond; - - if (!print) - return true; - OutBuffer buf; - HdrGenState hgs; - hgs.skipConstraints = true; - toCharsMaybeConstraints(td, buf, hgs); - const tmsg = buf.peekChars(); - const cmsg = td.getConstraintEvalError(constraintsTip); - - // add blank space if there are multiple candidates - // the length of the blank space is `strlen("Candidates are: ")` - - if (cmsg) - { - .errorSupplemental(td.loc, - printed ? " `%s`\n%s" : - single_candidate ? "Candidate is: `%s`\n%s" : "Candidates are: `%s`\n%s", - tmsg, cmsg); - } - else - { - .errorSupplemental(td.loc, - printed ? " `%s`" : - single_candidate ? "Candidate is: `%s`" : "Candidates are: `%s`", - tmsg); - } - } - return true; - } - // determine if there's > 1 candidate - int count = 0; - overloadApply(declaration, (s) { - if (matchSymbol(s, false)) - count++; - return count > 1; - }); - int skipped = 0; - overloadApply(declaration, (s) { - if (global.params.v.verbose || printed < DisplayLimit) - { - if (matchSymbol(s, true, count == 1)) - printed++; - } - else - { - // Too many overloads to sensibly display. - // Just show count of remaining overloads. - if (matchSymbol(s, false)) - skipped++; - } - return 0; - }); - if (skipped > 0) - .errorSupplemental(loc, "... (%d more, -v to show) ...", skipped); - - // Nothing was displayed, all overloads are either disabled or deprecated - if (!printed) - .errorSupplemental(loc, "All possible candidates are marked as `deprecated` or `@disable`"); - // should be only in verbose mode - if (constraintsTip) - .tip(constraintsTip); -} - /************************************** * Returns an indirect type one step from t. */ @@ -4336,9 +3946,9 @@ bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char) return false; if (sc.func.isSafeBypassingInference()) { - if (!gag) + if (!gag && !sc.isDeprecated()) { - warning(loc, msg, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); + deprecation(loc, msg, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); } } else if (!sc.func.safetyViolation) diff --git a/gcc/d/dmd/funcsem.d b/gcc/d/dmd/funcsem.d index 49da6b2..b8b185c 100644 --- a/gcc/d/dmd/funcsem.d +++ b/gcc/d/dmd/funcsem.d @@ -60,6 +60,7 @@ import dmd.statement_rewrite_walker; import dmd.statement; import dmd.statementsem; import dmd.target; +import dmd.templatesem; import dmd.tokens; import dmd.typesem; import dmd.visitor; @@ -1365,3 +1366,392 @@ BaseClass* overrideInterface(FuncDeclaration fd) } return null; } + +/// Flag used by $(LREF resolveFuncCall). +enum FuncResolveFlag : ubyte +{ + standard = 0, /// issue error messages, solve the call. + quiet = 1, /// do not issue error message on no match, just return `null`. + overloadOnly = 2, /// only resolve overloads, i.e. do not issue error on ambiguous + /// matches and need explicit this. + ufcs = 4, /// trying to resolve UFCS call +} + +/******************************************* + * Given a symbol that could be either a FuncDeclaration or + * a function template, resolve it to a function symbol. + * Params: + * loc = instantiation location + * sc = instantiation scope + * s = instantiation symbol + * tiargs = initial list of template arguments + * tthis = if !NULL, the `this` argument type + * argumentList = arguments to function + * flags = see $(LREF FuncResolveFlag). + * Returns: + * if match is found, then function symbol, else null + */ +FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, + Objects* tiargs, Type tthis, ArgumentList argumentList, FuncResolveFlag flags) +{ + auto fargs = argumentList.arguments; + if (!s) + return null; // no match + + version (none) + { + printf("resolveFuncCall('%s')\n", s.toChars()); + if (tthis) + printf("\tthis: %s\n", tthis.toChars()); + if (fargs) + { + for (size_t i = 0; i < fargs.length; i++) + { + Expression arg = (*fargs)[i]; + assert(arg.type); + printf("\t%s: %s\n", arg.toChars(), arg.type.toChars()); + } + } + printf("\tfnames: %s\n", fnames ? fnames.toChars() : "null"); + } + + if (tiargs && arrayObjectIsError(*tiargs)) + return null; + if (fargs !is null) + foreach (arg; *fargs) + if (isError(arg)) + return null; + + MatchAccumulator m; + functionResolve(m, s, loc, sc, tiargs, tthis, argumentList); + auto orig_s = s; + + if (m.last > MATCH.nomatch && m.lastf) + { + if (m.count == 1) // exactly one match + { + if (!(flags & FuncResolveFlag.quiet)) + functionSemantic(m.lastf); + return m.lastf; + } + if ((flags & FuncResolveFlag.overloadOnly) && !tthis && m.lastf.needThis()) + { + return m.lastf; + } + } + + /* Failed to find a best match. + * Do nothing or print error. + */ + if (m.last == MATCH.nomatch) + { + // error was caused on matched function, not on the matching itself, + // so return the function to produce a better diagnostic + if (m.count == 1) + return m.lastf; + } + + // We are done at this point, as the rest of this function generate + // a diagnostic on invalid match + if (flags & FuncResolveFlag.quiet) + return null; + + auto fd = s.isFuncDeclaration(); + auto od = s.isOverDeclaration(); + auto td = s.isTemplateDeclaration(); + if (td && td.funcroot) + s = fd = td.funcroot; + + OutBuffer tiargsBuf; + arrayObjectsToBuffer(tiargsBuf, tiargs); + + OutBuffer fargsBuf; + fargsBuf.writeByte('('); + argExpTypesToCBuffer(fargsBuf, fargs); + fargsBuf.writeByte(')'); + if (tthis) + tthis.modToBuffer(fargsBuf); + + // The call is ambiguous + if (m.lastf && m.nextf) + { + TypeFunction tf1 = m.lastf.type.toTypeFunction(); + TypeFunction tf2 = m.nextf.type.toTypeFunction(); + const(char)* lastprms = parametersTypeToChars(tf1.parameterList); + const(char)* nextprms = parametersTypeToChars(tf2.parameterList); + + .error(loc, "`%s.%s` called with argument types `%s` matches both:\n%s: `%s%s%s`\nand:\n%s: `%s%s%s`", + s.parent.toPrettyChars(), s.ident.toChars(), + fargsBuf.peekChars(), + m.lastf.loc.toChars(), m.lastf.toPrettyChars(), lastprms, tf1.modToChars(), + m.nextf.loc.toChars(), m.nextf.toPrettyChars(), nextprms, tf2.modToChars()); + return null; + } + + // no match, generate an error messages + if (flags & FuncResolveFlag.ufcs) + { + auto arg = (*fargs)[0]; + .error(loc, "no property `%s` for `%s` of type `%s`", s.ident.toChars(), arg.toChars(), arg.type.toChars()); + .errorSupplemental(loc, "the following error occured while looking for a UFCS match"); + } + + if (!fd) + { + // all of overloads are templates + if (td) + { + if (!od && !td.overnext) + { + .error(loc, "%s `%s` is not callable using argument types `!(%s)%s`", + td.kind(), td.ident.toChars(), tiargsBuf.peekChars(), fargsBuf.peekChars()); + } + else + { + .error(loc, "none of the overloads of %s `%s.%s` are callable using argument types `!(%s)%s`", + td.kind(), td.parent.toPrettyChars(), td.ident.toChars(), + tiargsBuf.peekChars(), fargsBuf.peekChars()); + } + + + if (!global.gag || global.params.v.showGaggedErrors) + printCandidates(loc, td, sc.isDeprecated()); + return null; + } + /* This case used to happen when several ctors are mixed in an agregate. + A (bad) error message is already generated in overloadApply(). + see https://issues.dlang.org/show_bug.cgi?id=19729 + and https://issues.dlang.org/show_bug.cgi?id=17259 + */ + if (!od) + return null; + } + + if (od) + { + .error(loc, "none of the overloads of `%s` are callable using argument types `!(%s)%s`", + od.ident.toChars(), tiargsBuf.peekChars(), fargsBuf.peekChars()); + return null; + } + + // remove when deprecation period of class allocators and deallocators is over + if (fd.isNewDeclaration() && fd.checkDisabled(loc, sc)) + return null; + + bool hasOverloads = fd.overnext !is null; + auto tf = fd.type.isTypeFunction(); + // if type is an error, the original type should be there for better diagnostics + if (!tf) + tf = fd.originalType.toTypeFunction(); + + // modifier mismatch + if (tthis && (fd.isCtorDeclaration() ? + !MODimplicitConv(tf.mod, tthis.mod) : + !MODimplicitConv(tthis.mod, tf.mod))) + { + OutBuffer thisBuf, funcBuf; + MODMatchToBuffer(&thisBuf, tthis.mod, tf.mod); + auto mismatches = MODMatchToBuffer(&funcBuf, tf.mod, tthis.mod); + if (hasOverloads) + { + OutBuffer buf; + buf.argExpTypesToCBuffer(fargs); + if (fd.isCtorDeclaration()) + .error(loc, "none of the overloads of `%s` can construct a %sobject with argument types `(%s)`", + fd.toChars(), thisBuf.peekChars(), buf.peekChars()); + else + .error(loc, "none of the overloads of `%s` are callable using a %sobject with argument types `(%s)`", + fd.toChars(), thisBuf.peekChars(), buf.peekChars()); + + if (!global.gag || global.params.v.showGaggedErrors) + printCandidates(loc, fd, sc.isDeprecated()); + return null; + } + + bool calledHelper; + void errorHelper(const(char)* failMessage) scope + { + .error(loc, "%s `%s%s%s` is not callable using argument types `%s`", + fd.kind(), fd.toPrettyChars(), parametersTypeToChars(tf.parameterList), + tf.modToChars(), fargsBuf.peekChars()); + errorSupplemental(loc, failMessage); + calledHelper = true; + } + + functionResolve(m, orig_s, loc, sc, tiargs, tthis, argumentList, &errorHelper); + if (calledHelper) + return null; + + if (fd.isCtorDeclaration()) + .error(loc, "%s%s `%s` cannot construct a %sobject", + funcBuf.peekChars(), fd.kind(), fd.toPrettyChars(), thisBuf.peekChars()); + else + .error(loc, "%smethod `%s` is not callable using a %sobject", + funcBuf.peekChars(), fd.toPrettyChars(), thisBuf.peekChars()); + + if (mismatches.isNotShared) + .errorSupplemental(fd.loc, "Consider adding `shared` here"); + else if (mismatches.isMutable) + .errorSupplemental(fd.loc, "Consider adding `const` or `inout` here"); + return null; + } + + //printf("tf = %s, args = %s\n", tf.deco, (*fargs)[0].type.deco); + if (hasOverloads) + { + .error(loc, "none of the overloads of `%s` are callable using argument types `%s`", + fd.toChars(), fargsBuf.peekChars()); + if (!global.gag || global.params.v.showGaggedErrors) + printCandidates(loc, fd, sc.isDeprecated()); + return null; + } + + .error(loc, "%s `%s%s%s` is not callable using argument types `%s`", + fd.kind(), fd.toPrettyChars(), parametersTypeToChars(tf.parameterList), + tf.modToChars(), fargsBuf.peekChars()); + + // re-resolve to check for supplemental message + if (!global.gag || global.params.v.showGaggedErrors) + { + if (tthis) + { + if (auto classType = tthis.isTypeClass()) + { + if (auto baseClass = classType.sym.baseClass) + { + if (auto baseFunction = baseClass.search(baseClass.loc, fd.ident)) + { + MatchAccumulator mErr; + functionResolve(mErr, baseFunction, loc, sc, tiargs, baseClass.type, argumentList); + if (mErr.last > MATCH.nomatch && mErr.lastf) + { + errorSupplemental(loc, "%s `%s` hides base class function `%s`", + fd.kind, fd.toPrettyChars(), mErr.lastf.toPrettyChars()); + errorSupplemental(loc, "add `alias %s = %s` to `%s`'s body to merge the overload sets", + fd.toChars(), mErr.lastf.toPrettyChars(), tthis.toChars()); + return null; + } + } + } + } + } + + void errorHelper2(const(char)* failMessage) scope + { + errorSupplemental(loc, failMessage); + } + + functionResolve(m, orig_s, loc, sc, tiargs, tthis, argumentList, &errorHelper2); + } + return null; +} + +/******************************************* + * Prints template and function overload candidates as supplemental errors. + * Params: + * loc = instantiation location + * declaration = the declaration to print overload candidates for + * showDeprecated = If `false`, `deprecated` function won't be shown + */ +private void printCandidates(Decl)(const ref Loc loc, Decl declaration, bool showDeprecated) +if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration)) +{ + // max num of overloads to print (-v or -verror-supplements overrides this). + const uint DisplayLimit = global.params.v.errorSupplementCount(); + const(char)* constraintsTip; + // determine if the first candidate was printed + int printed; + + bool matchSymbol(Dsymbol s, bool print, bool single_candidate = false) + { + if (auto fd = s.isFuncDeclaration()) + { + // Don't print overloads which have errors. + // Not that if the whole overload set has errors, we'll never reach + // this point so there's no risk of printing no candidate + if (fd.errors || fd.type.ty == Terror) + return false; + // Don't print disabled functions, or `deprecated` outside of deprecated scope + if (fd.storage_class & STC.disable || (fd.isDeprecated() && !showDeprecated)) + return false; + if (!print) + return true; + auto tf = cast(TypeFunction) fd.type; + OutBuffer buf; + buf.writestring(fd.toPrettyChars()); + buf.writestring(parametersTypeToChars(tf.parameterList)); + if (tf.mod) + { + buf.writeByte(' '); + buf.MODtoBuffer(tf.mod); + } + .errorSupplemental(fd.loc, + printed ? " `%s`" : + single_candidate ? "Candidate is: `%s`" : "Candidates are: `%s`", buf.peekChars()); + } + else if (auto td = s.isTemplateDeclaration()) + { + import dmd.staticcond; + + if (!print) + return true; + OutBuffer buf; + HdrGenState hgs; + hgs.skipConstraints = true; + toCharsMaybeConstraints(td, buf, hgs); + const tmsg = buf.peekChars(); + const cmsg = td.getConstraintEvalError(constraintsTip); + + // add blank space if there are multiple candidates + // the length of the blank space is `strlen("Candidates are: ")` + + if (cmsg) + { + .errorSupplemental(td.loc, + printed ? " `%s`\n%s" : + single_candidate ? "Candidate is: `%s`\n%s" : "Candidates are: `%s`\n%s", + tmsg, cmsg); + } + else + { + .errorSupplemental(td.loc, + printed ? " `%s`" : + single_candidate ? "Candidate is: `%s`" : "Candidates are: `%s`", + tmsg); + } + } + return true; + } + // determine if there's > 1 candidate + int count = 0; + overloadApply(declaration, (s) { + if (matchSymbol(s, false)) + count++; + return count > 1; + }); + int skipped = 0; + overloadApply(declaration, (s) { + if (global.params.v.verbose || printed < DisplayLimit) + { + if (matchSymbol(s, true, count == 1)) + printed++; + } + else + { + // Too many overloads to sensibly display. + // Just show count of remaining overloads. + if (matchSymbol(s, false)) + skipped++; + } + return 0; + }); + if (skipped > 0) + .errorSupplemental(loc, "... (%d more, -v to show) ...", skipped); + + // Nothing was displayed, all overloads are either disabled or deprecated + if (!printed) + .errorSupplemental(loc, "All possible candidates are marked as `deprecated` or `@disable`"); + // should be only in verbose mode + if (constraintsTip) + .tip(constraintsTip); +} diff --git a/gcc/d/dmd/identifier.h b/gcc/d/dmd/identifier.h index afd3664..4f26801 100644 --- a/gcc/d/dmd/identifier.h +++ b/gcc/d/dmd/identifier.h @@ -11,7 +11,7 @@ #pragma once #include "root/dcompat.h" -#include "root/object.h" +#include "rootobject.h" class Identifier final : public RootObject { diff --git a/gcc/d/dmd/importc.d b/gcc/d/dmd/importc.d index e4d5aa2..ece56c8d 100644 --- a/gcc/d/dmd/importc.d +++ b/gcc/d/dmd/importc.d @@ -243,16 +243,15 @@ Expression castCallAmbiguity(Expression e, Scope* sc) case EXP.call: auto ce = (*pe).isCallExp(); - auto ie = ce.e1.isIdentifierExp(); - if (ie && ie.parens) + if (ce.e1.parens) { - ce.e1 = expressionSemantic(ie, sc); + ce.e1 = expressionSemantic(ce.e1, sc); if (ce.e1.op == EXP.type) { const numArgs = ce.arguments ? ce.arguments.length : 0; if (numArgs >= 1) { - ie.parens = false; + ce.e1.parens = false; Expression arg; foreach (a; (*ce.arguments)[]) { diff --git a/gcc/d/dmd/mtype.d b/gcc/d/dmd/mtype.d index 09ed630..843c402 100644 --- a/gcc/d/dmd/mtype.d +++ b/gcc/d/dmd/mtype.d @@ -30,7 +30,6 @@ import dmd.dtemplate; import dmd.enumsem; import dmd.errors; import dmd.expression; -import dmd.func; import dmd.funcsem; import dmd.globals; import dmd.hdrgen; diff --git a/gcc/d/dmd/parse.d b/gcc/d/dmd/parse.d index 9c446eb..646c4b7 100644 --- a/gcc/d/dmd/parse.d +++ b/gcc/d/dmd/parse.d @@ -7130,7 +7130,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer private void checkParens(TOK value, AST.Expression e) { - if (precedence[e.op] == PREC.rel) + if (precedence[e.op] == PREC.rel && !e.parens) error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`", e.toChars(), Token.toChars(value)); } @@ -8550,6 +8550,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer // ( expression ) nextToken(); e = parseExpression(); + e.parens = true; check(loc, TOK.rightParenthesis); break; } @@ -8874,9 +8875,9 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer nextToken(); return AST.ErrorExp.get(); } - auto te = new AST.TypeExp(loc, t); - te.parens = true; - e = parsePostExp(te); + e = new AST.TypeExp(loc, t); + e.parens = true; + e = parsePostExp(e); } else if (token.value == TOK.leftParenthesis || token.value == TOK.plusPlus || token.value == TOK.minusMinus) @@ -9193,18 +9194,14 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer private AST.Expression parseAndExp() { Loc loc = token.loc; - bool parens = token.value == TOK.leftParenthesis; auto e = parseCmpExp(); while (token.value == TOK.and) { - if (!parens) - checkParens(TOK.and, e); - parens = nextToken() == TOK.leftParenthesis; + checkParens(TOK.and, e); + nextToken(); auto e2 = parseCmpExp(); - if (!parens) - checkParens(TOK.and, e2); + checkParens(TOK.and, e2); e = new AST.AndExp(loc, e, e2); - parens = true; // don't call checkParens() for And loc = token.loc; } return e; @@ -9212,42 +9209,32 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer private AST.Expression parseXorExp() { - Loc loc = token.loc; + const loc = token.loc; - bool parens = token.value == TOK.leftParenthesis; auto e = parseAndExp(); while (token.value == TOK.xor) { - if (!parens) - checkParens(TOK.xor, e); - parens = nextToken() == TOK.leftParenthesis; + checkParens(TOK.xor, e); + nextToken(); auto e2 = parseAndExp(); - if (!parens) - checkParens(TOK.xor, e2); + checkParens(TOK.xor, e2); e = new AST.XorExp(loc, e, e2); - parens = true; - loc = token.loc; } return e; } private AST.Expression parseOrExp() { - Loc loc = token.loc; + const loc = token.loc; - bool parens = token.value == TOK.leftParenthesis; auto e = parseXorExp(); while (token.value == TOK.or) { - if (!parens) - checkParens(TOK.or, e); - parens = nextToken() == TOK.leftParenthesis; + checkParens(TOK.or, e); + nextToken(); auto e2 = parseXorExp(); - if (!parens) - checkParens(TOK.or, e2); + checkParens(TOK.or, e2); e = new AST.OrExp(loc, e, e2); - parens = true; - loc = token.loc; } return e; } @@ -9298,7 +9285,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer AST.Expression parseAssignExp() { - bool parens = token.value == TOK.leftParenthesis; AST.Expression e; e = parseCondExp(); if (e is null) @@ -9307,7 +9293,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer // require parens for e.g. `t ? a = 1 : b = 2` void checkRequiredParens() { - if (e.op == EXP.question && !parens) + if (e.op == EXP.question && !e.parens) eSink.error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`", e.toChars(), Token.toChars(token.value)); } diff --git a/gcc/d/dmd/root/array.h b/gcc/d/dmd/root/array.h index 1033b22..3e28804 100644 --- a/gcc/d/dmd/root/array.h +++ b/gcc/d/dmd/root/array.h @@ -9,7 +9,6 @@ #pragma once #include "dsystem.h" -#include "object.h" #include "rmem.h" template <typename TYPE> @@ -44,7 +43,7 @@ struct Array d_size_t len = 2; for (d_size_t u = 0; u < length; u++) { - buf[u] = ((RootObject *)data.ptr[u])->toChars(); + buf[u] = ((TYPE)data.ptr[u])->toChars(); len += strlen(buf[u]) + 1; } char *str = (char *)mem.xmalloc(len); diff --git a/gcc/d/dmd/root/bitarray.h b/gcc/d/dmd/root/bitarray.h index 2cd7152..2a82703 100644 --- a/gcc/d/dmd/root/bitarray.h +++ b/gcc/d/dmd/root/bitarray.h @@ -9,7 +9,6 @@ #pragma once #include "dsystem.h" -#include "object.h" #include "rmem.h" struct BitArray diff --git a/gcc/d/dmd/root/object.h b/gcc/d/dmd/rootobject.h index f56cb17..718a54f 100644 --- a/gcc/d/dmd/root/object.h +++ b/gcc/d/dmd/rootobject.h @@ -4,13 +4,13 @@ * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. * https://www.boost.org/LICENSE_1_0.txt - * https://github.com/dlang/dmd/blob/master/src/dmd/root/object.h + * https://github.com/dlang/dmd/blob/master/src/dmd/rootobject.h */ #pragma once -#include "dsystem.h" -#include "dcompat.h" +#include "root/dsystem.h" +#include "root/dcompat.h" typedef size_t hash_t; diff --git a/gcc/d/dmd/statementsem.d b/gcc/d/dmd/statementsem.d index 5013c56..1bf36e3 100644 --- a/gcc/d/dmd/statementsem.d +++ b/gcc/d/dmd/statementsem.d @@ -1196,7 +1196,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) } case Taarray: if (fs.op == TOK.foreach_reverse_) - warning(fs.loc, "cannot use `foreach_reverse` with an associative array"); + error(fs.loc, "cannot use `foreach_reverse` with an associative array"); if (checkForArgTypes(fs)) return retError(); diff --git a/gcc/d/dmd/staticcond.d b/gcc/d/dmd/staticcond.d index 72afe02..3ab6885 100644 --- a/gcc/d/dmd/staticcond.d +++ b/gcc/d/dmd/staticcond.d @@ -11,120 +11,13 @@ module dmd.staticcond; -import dmd.arraytypes; -import dmd.dinterpret; -import dmd.dmodule; -import dmd.dscope; -import dmd.dsymbol; -import dmd.errors; import dmd.expression; -import dmd.expressionsem; -import dmd.globals; -import dmd.identifier; -import dmd.mtype; -import dmd.optimize; import dmd.root.array; import dmd.common.outbuffer; import dmd.tokens; -/******************************************** - * Semantically analyze and then evaluate a static condition at compile time. - * This is special because short circuit operators &&, || and ?: at the top - * level are not semantically analyzed if the result of the expression is not - * necessary. - * Params: - * sc = instantiating scope - * original = original expression, for error messages - * e = resulting expression - * errors = set to `true` if errors occurred - * negatives = array to store negative clauses - * Returns: - * true if evaluates to true - */ -bool evalStaticCondition(Scope* sc, Expression original, Expression e, out bool errors, Expressions* negatives = null) -{ - if (negatives) - negatives.setDim(0); - - bool impl(Expression e) - { - if (e.isNotExp()) - { - NotExp ne = cast(NotExp)e; - return !impl(ne.e1); - } - - if (e.op == EXP.andAnd || e.op == EXP.orOr) - { - LogicalExp aae = cast(LogicalExp)e; - bool result = impl(aae.e1); - if (errors) - return false; - if (e.op == EXP.andAnd) - { - if (!result) - return false; - } - else - { - if (result) - return true; - } - result = impl(aae.e2); - return !errors && result; - } - - if (e.op == EXP.question) - { - CondExp ce = cast(CondExp)e; - bool result = impl(ce.econd); - if (errors) - return false; - Expression leg = result ? ce.e1 : ce.e2; - result = impl(leg); - return !errors && result; - } - - Expression before = e; - const uint nerrors = global.errors; - - sc = sc.startCTFE(); - sc.flags |= SCOPE.condition; - - e = e.expressionSemantic(sc); - e = resolveProperties(sc, e); - e = e.toBoolean(sc); - - sc = sc.endCTFE(); - e = e.optimize(WANTvalue); - - if (nerrors != global.errors || - e.isErrorExp() || - e.type.toBasetype() == Type.terror) - { - errors = true; - return false; - } - - e = e.ctfeInterpret(); - - const opt = e.toBool(); - if (opt.isEmpty()) - { - if (!e.type.isTypeError()) - error(e.loc, "expression `%s` is not constant", e.toChars()); - errors = true; - return false; - } - - if (negatives && !opt.get()) - negatives.push(before); - return opt.get(); - } - return impl(e); -} /******************************************** * Format a static condition as a tree-like structure, marking failed and diff --git a/gcc/d/dmd/template.h b/gcc/d/dmd/template.h index 6f12ac3..0f96a1b 100644 --- a/gcc/d/dmd/template.h +++ b/gcc/d/dmd/template.h @@ -36,7 +36,7 @@ public: // kludge for template.isType() DYNCAST dyncast() const override { return DYNCAST_TUPLE; } - const char *toChars() const override { return objects.toChars(); } + const char *toChars() const override; }; struct TemplatePrevious |