From d23c522ee1f173da8e4dc30d66e2e7f3e49b924e Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Fri, 1 Mar 2024 18:18:08 -0800 Subject: [PATCH 001/125] remove unnecessary fatal() in semantic3.d (dlang/dmd!16275) --- dmd/semantic3.d | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dmd/semantic3.d b/dmd/semantic3.d index 882d1a9a355..7277874b9ee 100644 --- a/dmd/semantic3.d +++ b/dmd/semantic3.d @@ -424,9 +424,12 @@ private extern(C++) final class Semantic3Visitor : Visitor .error(funcdecl.loc, "%s `%s` `object.TypeInfo_Tuple` could not be found, but is implicitly used in D-style variadic functions", funcdecl.kind, funcdecl.toPrettyChars); else .error(funcdecl.loc, "%s `%s` `object.TypeInfo` could not be found, but is implicitly used in D-style variadic functions", funcdecl.kind, funcdecl.toPrettyChars); - fatal(); + funcdecl.errors = true; } + } + if (!funcdecl.errors && f.linkage == LINK.d) + { // Declare _arguments[] funcdecl.v_arguments = new VarDeclaration(funcdecl.loc, Type.typeinfotypelist.type, Id._arguments_typeinfo, null); funcdecl.v_arguments.storage_class |= STC.temp | STC.parameter; @@ -442,7 +445,7 @@ private extern(C++) final class Semantic3Visitor : Visitor sc2.insert(_arguments); _arguments.parent = funcdecl; } - if (f.linkage == LINK.d || f.parameterList.length) + if (!funcdecl.errors && (f.linkage == LINK.d || f.parameterList.length)) { // Declare _argptr Type t = target.va_listType(funcdecl.loc, sc); From 2bdf504f47d28463e6b4d2075863341d93b2590b Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Thu, 29 Feb 2024 13:53:57 -0800 Subject: [PATCH 002/125] move CHECKACTION CHECKENABLE to astenums.d --- dmd/astenums.d | 18 ++++++++++++++++++ dmd/globals.d | 18 ------------------ dmd/target.d | 3 ++- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/dmd/astenums.d b/dmd/astenums.d index 4fc15141c39..3a2d65735cd 100644 --- a/dmd/astenums.d +++ b/dmd/astenums.d @@ -441,6 +441,24 @@ enum FileType : ubyte c, /// C source file } +/// In which context checks for assertions, contracts, bounds checks etc. are enabled +enum CHECKENABLE : ubyte +{ + _default, /// initial value + off, /// never do checking + on, /// always do checking + safeonly, /// do checking only in @safe functions +} + +/// What should happend when an assertion fails +enum CHECKACTION : ubyte +{ + D, /// call D assert on failure + C, /// call C assert on failure + halt, /// cause program halt on failure + context, /// call D assert with the error context on failure +} + extern (C++) struct structalign_t { private: diff --git a/dmd/globals.d b/dmd/globals.d index af7b1fa371d..358696fd833 100644 --- a/dmd/globals.d +++ b/dmd/globals.d @@ -40,24 +40,6 @@ enum DiagnosticReporting : ubyte off, /// disable diagnostic } -/// In which context checks for assertions, contracts, bounds checks etc. are enabled -enum CHECKENABLE : ubyte -{ - _default, /// initial value - off, /// never do checking - on, /// always do checking - safeonly, /// do checking only in @safe functions -} - -/// What should happend when an assertion fails -enum CHECKACTION : ubyte -{ - D, /// call D assert on failure - C, /// call C assert on failure - halt, /// cause program halt on failure - context, /// call D assert with the error context on failure -} - /** Each flag represents a field that can be included in the JSON output. diff --git a/dmd/target.d b/dmd/target.d index 389266ff00f..a82123123e3 100644 --- a/dmd/target.d +++ b/dmd/target.d @@ -25,7 +25,8 @@ module dmd.target; -import dmd.globals : Param, CHECKENABLE; +import dmd.astenums : CHECKENABLE; +import dmd.globals : Param; enum CPU : ubyte { From 90a8260053d598f56c4b74ffff2543d440640a86 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sat, 2 Mar 2024 13:11:57 -0800 Subject: [PATCH 003/125] move fatal() calls in cppmangle.d up a level --- dmd/cppmangle.d | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/dmd/cppmangle.d b/dmd/cppmangle.d index 0116aa35c8c..be95a0e2ba0 100644 --- a/dmd/cppmangle.d +++ b/dmd/cppmangle.d @@ -71,6 +71,8 @@ const(char)* toCppMangleItanium(Dsymbol s) OutBuffer buf; scope CppMangleVisitor v = new CppMangleVisitor(&buf, s.loc); v.mangleOf(s); + if (v.errors) + fatal(); return buf.extractChars(); } @@ -82,6 +84,8 @@ const(char)* cppTypeInfoMangleItanium(Dsymbol s) buf.writestring("_ZTI"); // "TI" means typeinfo structure scope CppMangleVisitor v = new CppMangleVisitor(&buf, s.loc); v.cpp_mangle_name(s, false); + if (v.errors) + fatal(); return buf.extractChars(); } @@ -93,6 +97,8 @@ const(char)* cppThunkMangleItanium(FuncDeclaration fd, int offset) buf.printf("_ZThn%u_", offset); // "Th" means thunk, "n%u" is the call offset scope CppMangleVisitor v = new CppMangleVisitor(&buf, fd.loc); v.mangle_function_encoding(fd); + if (v.errors) + fatal(); return buf.extractChars(); } @@ -163,6 +169,7 @@ private final class CppMangleVisitor : Visitor Objects components; /// array of components available for substitution OutBuffer* buf; /// append the mangling to buf[] Loc loc; /// location for use in error messages + bool errors; /// failed to mangle properly /** * Constructor @@ -484,7 +491,8 @@ private final class CppMangleVisitor : Visitor else { .error(ti.loc, "%s `%s` internal compiler error: C++ `%s` template value parameter is not supported", ti.kind, ti.toPrettyChars, tv.valType.toChars()); - fatal(); + errors = true; + return; } } else if (tp.isTemplateAliasParameter()) @@ -519,13 +527,13 @@ private final class CppMangleVisitor : Visitor else { .error(ti.loc, "%s `%s` internal compiler error: C++ `%s` template alias parameter is not supported", ti.kind, ti.toPrettyChars, o.toChars()); - fatal(); + errors = true; } } else if (tp.isTemplateThisParameter()) { .error(ti.loc, "%s `%s` internal compiler error: C++ `%s` template this parameter is not supported", ti.kind, ti.toPrettyChars, o.toChars()); - fatal(); + errors = true; } else { @@ -574,7 +582,8 @@ private final class CppMangleVisitor : Visitor if (t is null) { .error(ti.loc, "%s `%s` internal compiler error: C++ `%s` template value parameter is not supported", ti.kind, ti.toPrettyChars, (*ti.tiargs)[j].toChars()); - fatal(); + errors = true; + return false; } t.accept(this); } @@ -1012,7 +1021,8 @@ private final class CppMangleVisitor : Visitor if (!(d.storage_class & (STC.extern_ | STC.field | STC.gshared))) { .error(d.loc, "%s `%s` internal compiler error: C++ static non-`__gshared` non-`extern` variables not supported", d.kind, d.toPrettyChars); - fatal(); + errors = true; + return; } Dsymbol p = d.toParent(); if (p && !p.isModule()) //for example: char Namespace1::beta[6] should be mangled as "_ZN10Namespace14betaE" @@ -1348,7 +1358,8 @@ private final class CppMangleVisitor : Visitor // Static arrays in D are passed by value; no counterpart in C++ .error(loc, "internal compiler error: unable to pass static array `%s` to extern(C++) function, use pointer instead", t.toChars()); - fatal(); + errors = true; + return; } auto prev = this.context.push({ TypeFunction tf; @@ -1386,7 +1397,7 @@ private final class CppMangleVisitor : Visitor else p = ""; .error(loc, "internal compiler error: %stype `%s` cannot be mapped to C++\n", p, t.toChars()); - fatal(); //Fatal, because this error should be handled in frontend + errors = true; //Fatal, because this error should be handled in frontend } /**************************** From 8ddbc3cc2f1ccaf19faafd22b540a4956f036c18 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sat, 2 Mar 2024 11:27:24 -0800 Subject: [PATCH 004/125] refactor isCppOperator() --- dmd/cppmangle.d | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/dmd/cppmangle.d b/dmd/cppmangle.d index be95a0e2ba0..334088b381c 100644 --- a/dmd/cppmangle.d +++ b/dmd/cppmangle.d @@ -49,19 +49,30 @@ import dmd.typesem; import dmd.visitor; -// helper to check if an identifier is a C++ operator -enum CppOperator { Cast, Assign, Eq, Index, Call, Unary, Binary, OpAssign, Unknown } -package CppOperator isCppOperator(Identifier id) +// C++ operators +enum CppOperator { Unknown, Cast, Assign, Eq, Index, Call, Unary, Binary, OpAssign } + +/************** + * Check if id is a C++ operator + * Params: + * id = identifier to be checked + * Returns: + * CppOperator, or Unknown if not a C++ operator + */ +package CppOperator isCppOperator(const scope Identifier id) { - __gshared const(Identifier)[] operators = null; - if (!operators) - operators = [Id._cast, Id.assign, Id.eq, Id.index, Id.call, Id.opUnary, Id.opBinary, Id.opOpAssign]; - foreach (i, op; operators) - { - if (op == id) - return cast(CppOperator)i; + with (Id) with (CppOperator) + { + return (id == _cast) ? Cast : + (id == assign) ? Assign : + (id == eq) ? Eq : + (id == index) ? Index : + (id == call) ? Call : + (id == opUnary) ? Unary : + (id == opBinary) ? Binary : + (id == opOpAssign) ? OpAssign : + Unknown ; } - return CppOperator.Unknown; } /// From a724fb0903499730ba9cb60841bf19635b6597cf Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sat, 2 Mar 2024 14:11:39 -0800 Subject: [PATCH 005/125] consolidate references to object.d not found --- dmd/arrayop.d | 5 ++- dmd/declaration.d | 49 ++++++++++++++++---------- dmd/dmodule.d | 15 ++------ dmd/dsymbolsem.d | 3 +- dmd/statementsem.d | 4 +-- dmd/typesem.d | 2 +- tests/dmd/fail_compilation/fail17612.d | 6 ++-- tests/dmd/fail_compilation/no_object.d | 3 +- 8 files changed, 46 insertions(+), 41 deletions(-) diff --git a/dmd/arrayop.d b/dmd/arrayop.d index af3875ea6c5..d0ec5ebcdbe 100644 --- a/dmd/arrayop.d +++ b/dmd/arrayop.d @@ -145,7 +145,10 @@ Expression arrayOp(BinExp e, Scope* sc) if (auto te = id.isTemplateExp()) arrayOp = te.td; else - ObjectNotFound(idArrayOp); // fatal error + { + ObjectNotFound(e.loc, idArrayOp); // fatal error + return ErrorExp.get(); + } } auto fd = resolveFuncCall(e.loc, sc, arrayOp, tiargs, null, ArgumentList(args), FuncResolveFlag.standard); diff --git a/dmd/declaration.d b/dmd/declaration.d index 3f9769d7155..384d9118861 100644 --- a/dmd/declaration.d +++ b/dmd/declaration.d @@ -38,6 +38,7 @@ import dmd.location; import dmd.mtype; import dmd.common.outbuffer; import dmd.rootobject; +import dmd.root.filename; import dmd.target; import dmd.tokens; import dmd.typesem; @@ -213,9 +214,21 @@ bool modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1) /****************************************** */ -void ObjectNotFound(Identifier id) +void ObjectNotFound(Loc loc, Identifier id) { - error(Loc.initial, "`%s` not found. object.d may be incorrectly installed or corrupt.", id.toChars()); + error(loc, "`%s` not found. object.d may be incorrectly installed or corrupt.", id.toChars()); + version (IN_LLVM) + { + errorSupplemental(loc, "ldc2 might not be correctly installed."); + errorSupplemental(loc, "Please check your ldc2.conf configuration file."); + errorSupplemental(loc, "Installation instructions can be found at http://wiki.dlang.org/LDC."); + } + else version (MARS) + { + errorSupplemental(loc, "dmd might not be correctly installed. Run 'dmd -man' for installation instructions."); + const dmdConfFile = global.inifilename.length ? FileName.canonicalName(global.inifilename) : "not found"; + errorSupplemental(loc, "config file: %.*s", cast(int)dmdConfFile.length, dmdConfFile.ptr); + } fatal(); } @@ -1832,7 +1845,7 @@ extern (C++) final class TypeInfoStructDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfostruct) { - ObjectNotFound(Id.TypeInfo_Struct); + ObjectNotFound(loc, Id.TypeInfo_Struct); } type = Type.typeinfostruct.type; } @@ -1857,7 +1870,7 @@ extern (C++) final class TypeInfoClassDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfoclass) { - ObjectNotFound(Id.TypeInfo_Class); + ObjectNotFound(loc, Id.TypeInfo_Class); } type = Type.typeinfoclass.type; } @@ -1882,7 +1895,7 @@ extern (C++) final class TypeInfoInterfaceDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfointerface) { - ObjectNotFound(Id.TypeInfo_Interface); + ObjectNotFound(loc, Id.TypeInfo_Interface); } type = Type.typeinfointerface.type; } @@ -1907,7 +1920,7 @@ extern (C++) final class TypeInfoPointerDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfopointer) { - ObjectNotFound(Id.TypeInfo_Pointer); + ObjectNotFound(loc, Id.TypeInfo_Pointer); } type = Type.typeinfopointer.type; } @@ -1932,7 +1945,7 @@ extern (C++) final class TypeInfoArrayDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfoarray) { - ObjectNotFound(Id.TypeInfo_Array); + ObjectNotFound(loc, Id.TypeInfo_Array); } type = Type.typeinfoarray.type; } @@ -1957,7 +1970,7 @@ extern (C++) final class TypeInfoStaticArrayDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfostaticarray) { - ObjectNotFound(Id.TypeInfo_StaticArray); + ObjectNotFound(loc, Id.TypeInfo_StaticArray); } type = Type.typeinfostaticarray.type; } @@ -1982,7 +1995,7 @@ extern (C++) final class TypeInfoAssociativeArrayDeclaration : TypeInfoDeclarati super(tinfo); if (!Type.typeinfoassociativearray) { - ObjectNotFound(Id.TypeInfo_AssociativeArray); + ObjectNotFound(loc, Id.TypeInfo_AssociativeArray); } type = Type.typeinfoassociativearray.type; } @@ -2007,7 +2020,7 @@ extern (C++) final class TypeInfoEnumDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfoenum) { - ObjectNotFound(Id.TypeInfo_Enum); + ObjectNotFound(loc, Id.TypeInfo_Enum); } type = Type.typeinfoenum.type; } @@ -2032,7 +2045,7 @@ extern (C++) final class TypeInfoFunctionDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfofunction) { - ObjectNotFound(Id.TypeInfo_Function); + ObjectNotFound(loc, Id.TypeInfo_Function); } type = Type.typeinfofunction.type; } @@ -2057,7 +2070,7 @@ extern (C++) final class TypeInfoDelegateDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfodelegate) { - ObjectNotFound(Id.TypeInfo_Delegate); + ObjectNotFound(loc, Id.TypeInfo_Delegate); } type = Type.typeinfodelegate.type; } @@ -2082,7 +2095,7 @@ extern (C++) final class TypeInfoTupleDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfotypelist) { - ObjectNotFound(Id.TypeInfo_Tuple); + ObjectNotFound(loc, Id.TypeInfo_Tuple); } type = Type.typeinfotypelist.type; } @@ -2107,7 +2120,7 @@ extern (C++) final class TypeInfoConstDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfoconst) { - ObjectNotFound(Id.TypeInfo_Const); + ObjectNotFound(loc, Id.TypeInfo_Const); } type = Type.typeinfoconst.type; } @@ -2132,7 +2145,7 @@ extern (C++) final class TypeInfoInvariantDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfoinvariant) { - ObjectNotFound(Id.TypeInfo_Invariant); + ObjectNotFound(loc, Id.TypeInfo_Invariant); } type = Type.typeinfoinvariant.type; } @@ -2157,7 +2170,7 @@ extern (C++) final class TypeInfoSharedDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfoshared) { - ObjectNotFound(Id.TypeInfo_Shared); + ObjectNotFound(loc, Id.TypeInfo_Shared); } type = Type.typeinfoshared.type; } @@ -2182,7 +2195,7 @@ extern (C++) final class TypeInfoWildDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfowild) { - ObjectNotFound(Id.TypeInfo_Wild); + ObjectNotFound(loc, Id.TypeInfo_Wild); } type = Type.typeinfowild.type; } @@ -2207,7 +2220,7 @@ extern (C++) final class TypeInfoVectorDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfovector) { - ObjectNotFound(Id.TypeInfo_Vector); + ObjectNotFound(loc, Id.TypeInfo_Vector); } type = Type.typeinfovector.type; } diff --git a/dmd/dmodule.d b/dmd/dmodule.d index 58bf3fd0757..123cfefc4e2 100644 --- a/dmd/dmodule.d +++ b/dmd/dmodule.d @@ -24,6 +24,7 @@ import dmd.astenums; import dmd.common.outbuffer; import dmd.compiler; import dmd.cparse; +import dmd.declaration; import dmd.dimport; import dmd.dmacro; import dmd.doc; @@ -611,19 +612,7 @@ extern (C++) final class Module : Package { if (FileName.equals(srcfile.toString(), "object.d")) { - .error(loc, "cannot find source code for runtime library file 'object.d'"); - version (IN_LLVM) - { - errorSupplemental(loc, "ldc2 might not be correctly installed."); - errorSupplemental(loc, "Please check your ldc2.conf configuration file."); - errorSupplemental(loc, "Installation instructions can be found at http://wiki.dlang.org/LDC."); - } - version (MARS) - { - errorSupplemental(loc, "dmd might not be correctly installed. Run 'dmd -man' for installation instructions."); - const dmdConfFile = global.inifilename.length ? FileName.canonicalName(global.inifilename) : "not found"; - errorSupplemental(loc, "config file: %.*s", cast(int)dmdConfFile.length, dmdConfFile.ptr); - } + ObjectNotFound(loc, ident); } else if (FileName.ext(this.arg) || !loc.isValid()) { diff --git a/dmd/dsymbolsem.d b/dmd/dsymbolsem.d index bb0a1d6e2cc..fd85b253c48 100644 --- a/dmd/dsymbolsem.d +++ b/dmd/dsymbolsem.d @@ -3379,8 +3379,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor { void badObjectDotD() { - .error(cldec.loc, "%s `%s` missing or corrupt object.d", cldec.kind, cldec.toPrettyChars); - fatal(); + ObjectNotFound(cldec.loc, cldec.ident); } if (!cldec.object || cldec.object.errors) diff --git a/dmd/statementsem.d b/dmd/statementsem.d index 1bf36e3082d..084f8d9e6e3 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -3018,8 +3018,8 @@ Statement statementSemanticVisit(Statement s, Scope* sc) */ if (!ClassDeclaration.object) { - error(ss.loc, "missing or corrupt object.d"); - fatal(); + ObjectNotFound(ss.loc, Id.Object); + return setError(); } Type t = ClassDeclaration.object.type; diff --git a/dmd/typesem.d b/dmd/typesem.d index ad87ea0baeb..a825fa27e65 100644 --- a/dmd/typesem.d +++ b/dmd/typesem.d @@ -1686,7 +1686,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) if (!ClassDeclaration.object) { - .error(Loc.initial, "missing or corrupt object.d"); + ObjectNotFound(Loc.initial, cd.ident); return error(); } diff --git a/tests/dmd/fail_compilation/fail17612.d b/tests/dmd/fail_compilation/fail17612.d index d39dd51cb6e..a14e85935a6 100644 --- a/tests/dmd/fail_compilation/fail17612.d +++ b/tests/dmd/fail_compilation/fail17612.d @@ -1,7 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/fail17612.d(14): Error: undefined identifier `string` -fail_compilation/fail17612.d(17): Error: class `object.TypeInfo` missing or corrupt object.d +fail_compilation/fail17612.d(16): Error: undefined identifier `string` +fail_compilation/fail17612.d(19): Error: `TypeInfo` not found. object.d may be incorrectly installed or corrupt. +fail_compilation/fail17612.d(19): dmd might not be correctly installed. Run 'dmd -man' for installation instructions. +fail_compilation/fail17612.d(19): config file: not found --- */ diff --git a/tests/dmd/fail_compilation/no_object.d b/tests/dmd/fail_compilation/no_object.d index 94c41767d3e..99992143dcb 100644 --- a/tests/dmd/fail_compilation/no_object.d +++ b/tests/dmd/fail_compilation/no_object.d @@ -2,10 +2,9 @@ DFLAGS: TEST_OUTPUT: --- -Error: cannot find source code for runtime library file 'object.d' +Error: `object` not found. object.d may be incorrectly installed or corrupt. dmd might not be correctly installed. Run 'dmd -man' for installation instructions. config file: not found -import path[0] = fail_compilation --- */ From 27cfae471ebdf0debb5422e4fc98ee69fada9b95 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sat, 2 Mar 2024 17:18:04 -0800 Subject: [PATCH 006/125] push parseConfFile() fatal() call up the call stack --- dmd/main.d | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/dmd/main.d b/dmd/main.d index 044a216d86c..b25036f6d0a 100644 --- a/dmd/main.d +++ b/dmd/main.d @@ -78,7 +78,9 @@ import dmd.vsoptions; * * Returns: * Return code of the application - */ extern (C) int main(int argc, char** argv) { + */ +extern (C) int main(int argc, char** argv) +{ bool lowmem = false; foreach (i; 1 .. argc) { @@ -102,7 +104,9 @@ import dmd.vsoptions; * * Returns: * Return code of the application - */ extern (C) int _Dmain(char[][]) { + */ +extern (C) int _Dmain(char[][]) +{ // possibly install memory error handler version (DigitalMars) { @@ -670,7 +674,7 @@ private int tryMain(size_t argc, const(char)** argv, ref Param params) * argv = Array of string arguments passed via command line * params = parameters from argv * files = files from argv - * Returns: true on faiure + * Returns: true on failure */ bool parseCommandlineAndConfig(size_t argc, const(char)** argv, ref Param params, ref Strings files) { @@ -729,7 +733,8 @@ bool parseCommandlineAndConfig(size_t argc, const(char)** argv, ref Param params * pick up any DFLAGS settings. */ sections.push("Environment"); - parseConfFile(environment, global.inifilename, inifilepath, inifileBuffer, §ions); + if (parseConfFile(environment, global.inifilename, inifilepath, inifileBuffer, §ions)) + return true; const(char)[] arch = target.isX86_64 ? "64" : "32"; // use default arch = parse_arch_arg(&arguments, arch); @@ -752,7 +757,8 @@ bool parseCommandlineAndConfig(size_t argc, const(char)** argv, ref Param params char[80] envsection = void; snprintf(envsection.ptr, envsection.length, "Environment%.*s", cast(int) arch.length, arch.ptr); sections.push(envsection.ptr); - parseConfFile(environment, global.inifilename, inifilepath, inifileBuffer, §ions); + if (parseConfFile(environment, global.inifilename, inifilepath, inifileBuffer, §ions)) + return true; getenv_setargv(readFromEnv(environment, "DFLAGS"), &arguments); updateRealEnvironment(environment); environment.reset(1); // don't need environment cache any more From 4b92dcbdafc274e559b46f40eb5f8853f35f8063 Mon Sep 17 00:00:00 2001 From: SHOO Date: Sun, 3 Mar 2024 13:35:04 +0900 Subject: [PATCH 007/125] Fix Bugzilla issue 24111 - [ImportC] fatal error C1034: stdio.h: no include path set (dlang/dmd!16248) * Fix Bugzilla issue 24111 - [ImportC] fatal error C1034: stdio.h: no include path set --- tests/dmd/dshell/extra-files/issue24111.c | 1 + tests/dmd/dshell/issue24111.d | 15 +++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 tests/dmd/dshell/extra-files/issue24111.c create mode 100644 tests/dmd/dshell/issue24111.d diff --git a/tests/dmd/dshell/extra-files/issue24111.c b/tests/dmd/dshell/extra-files/issue24111.c new file mode 100644 index 00000000000..53c5fdf1799 --- /dev/null +++ b/tests/dmd/dshell/extra-files/issue24111.c @@ -0,0 +1 @@ +#include diff --git a/tests/dmd/dshell/issue24111.d b/tests/dmd/dshell/issue24111.d new file mode 100644 index 00000000000..175cae47846 --- /dev/null +++ b/tests/dmd/dshell/issue24111.d @@ -0,0 +1,15 @@ +import dshell; + +int main() +{ + version (Windows) + { + auto cmd = "$DMD -m$MODEL -c $EXTRA_FILES" ~ SEP ~ "issue24111.c"; + run(cmd); + + import std.process: environment; + environment.remove("INCLUDE"); + run(cmd); + } + return 0; +} From bf19a740259b60c4c60567bd9e9202af76cf6151 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sat, 2 Mar 2024 19:15:38 -0800 Subject: [PATCH 008/125] lift fatal() out of utils.d --- dmd/main.d | 9 +++++---- dmd/utils.d | 15 +++++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/dmd/main.d b/dmd/main.d index b25036f6d0a..dc5e432a4ed 100644 --- a/dmd/main.d +++ b/dmd/main.d @@ -355,11 +355,12 @@ private int tryMain(size_t argc, const(char)** argv, ref Param params) { foreach (file; ddocfiles) { - auto buffer = readFile(loc, file.toDString()); + Buffer buffer; + if (readFile(loc, file.toDString(), buffer)) + fatal(); // BUG: convert file contents to UTF-8 before use - const data = buffer.data; - //printf("file: '%.*s'\n", cast(int)data.length, data.ptr); - ddocbuf.write(data); + //printf("file: '%.*s'\n", cast(int)buffer.data.length, buffer.data.ptr); + ddocbuf.write(buffer.data); } ddocbufIsRead = true; } diff --git a/dmd/utils.d b/dmd/utils.d index 72d80367bbe..51d38d2ac3f 100644 --- a/dmd/utils.d +++ b/dmd/utils.d @@ -52,16 +52,23 @@ const(char)* toWinPath(const(char)* src) * Params: * loc = The line number information from where the call originates * filename = Path to file + * buf = set to contents of file + * Returns: + * true on failure */ -Buffer readFile(Loc loc, const(char)[] filename) +bool readFile(Loc loc, const(char)[] filename, out Buffer buf) { auto result = File.read(filename); - if (!result.success) + if (result.success) + { + buf.data = result.extractSlice(); + return false; + } + else { error(loc, "error reading file `%.*s`", cast(int)filename.length, filename.ptr); - fatal(); + return true; } - return Buffer(result.extractSlice()); } From 8b9407375948c64896ca0c6ee316b430467d5a6d Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Wed, 31 Jan 2024 19:48:06 +0100 Subject: [PATCH 009/125] Add L postfix for hex string --- dmd/dcast.d | 2 +- dmd/expressionsem.d | 4 ++++ dmd/lexer.d | 7 ++++++- tests/dmd/runnable/literal.d | 11 ++++++++--- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/dmd/dcast.d b/dmd/dcast.d index a49bd575f4b..d2498fe6e2f 100644 --- a/dmd/dcast.d +++ b/dmd/dcast.d @@ -629,7 +629,7 @@ MATCH implicitConvTo(Expression e, Type t) TY tyn = e.type.nextOf().ty; - if (!tyn.isSomeChar) + if (!tyn.isSomeChar && !e.hexString) return visit(e); switch (t.ty) diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index db40ae01dec..bc6f1461eaa 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -4248,6 +4248,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor const data = cast(const ubyte[]) e.peekString(); switch (e.postfix) { + case 'L': + e.sz = 8; + e.type = Type.tuns64.immutableOf().arrayOf(); + break; case 'd': e.sz = 4; e.type = Type.tdstring; diff --git a/dmd/lexer.d b/dmd/lexer.d index 937597cdf89..9c30fa97c81 100644 --- a/dmd/lexer.d +++ b/dmd/lexer.d @@ -1568,8 +1568,13 @@ class Lexer stringbuffer.writeByte(v); } t.setString(stringbuffer); - t.postfix = 'h'; stringPostfix(t); + if (*p == 'L') + { + t.postfix = 'L'; + p++; + } + return TOK.hexadecimalString; default: if (c >= '0' && c <= '9') diff --git a/tests/dmd/runnable/literal.d b/tests/dmd/runnable/literal.d index 27b5543b6fc..0c259c1eb1c 100644 --- a/tests/dmd/runnable/literal.d +++ b/tests/dmd/runnable/literal.d @@ -243,23 +243,28 @@ void test12950() void testHexstring() { - static immutable uint[] x = cast(immutable uint[]) x"FFAADDEE"; + static immutable uint[] x = cast(immutable uint[]) x"FFAADDEE"d; static assert(x[0] == 0xFFAADDEE); assert(x[0] == 0xFFAADDEE); - static immutable ulong[] y = cast(immutable ulong[]) x"1122334455667788AABBCCDDEEFF0099"; + static assert(is(typeof(x""L) == immutable(ulong)[])); + static immutable ulong[] y = x"1122334455667788AABBCCDDEEFF0099"L; static assert(y[0] == 0x1122334455667788); static assert(y[1] == 0xAABBCCDDEEFF0099); assert(y[0] == 0x1122334455667788); assert(y[1] == 0xAABBCCDDEEFF0099); + immutable long[] c = x"1122334455667788AABBCCDDEEFF0099"L; + assert(c[0] == 0x1122334455667788); + assert(c[1] == 0xAABBCCDDEEFF0099); + // Test that mangling of StringExp with size 8 is the same as array literal mangling: void f(immutable ulong[] a)() {} static assert(f!y.mangleof == f!([0x1122334455667788, 0xAABBCCDDEEFF0099]).mangleof); // Test printing StringExp with size 8 enum toStr(immutable ulong[] v) = v.stringof; - static assert(toStr!y == `x"88776655443322119900FFEEDDCCBBAA"`); + static assert(toStr!y == `x"88776655443322119900FFEEDDCCBBAA"L`); // Hex string postfixes // https://issues.dlang.org/show_bug.cgi?id=24363 From b198bb721bf67545bab372256450f4082c55c955 Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Wed, 28 Feb 2024 14:27:13 +0100 Subject: [PATCH 010/125] Make hex strings implicitly convert to integer arrays --- dmd/dcast.d | 29 ++++++++++++++++---------- dmd/expressionsem.d | 11 +++++----- dmd/lexer.d | 6 ------ tests/dmd/fail_compilation/hexstring.d | 3 ++- tests/dmd/runnable/literal.d | 7 +++---- 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/dmd/dcast.d b/dmd/dcast.d index d2498fe6e2f..060b538c844 100644 --- a/dmd/dcast.d +++ b/dmd/dcast.d @@ -703,6 +703,11 @@ MATCH implicitConvTo(Expression e, Type t) return MATCH.nomatch; m = MATCH.constant; } + if (e.hexString && tn.isintegral && (tn.size == e.sz || (!e.committed && (e.len % tn.size) == 0))) + { + m = MATCH.convert; + return m; + } if (!e.committed) { switch (tn.ty) @@ -719,9 +724,6 @@ MATCH implicitConvTo(Expression e, Type t) if (e.postfix != 'd') m = MATCH.convert; return m; - case Tint8: - case Tuns8: - break; case Tenum: if (tn.isTypeEnum().sym.isSpecial()) { @@ -735,14 +737,6 @@ MATCH implicitConvTo(Expression e, Type t) break; } } - if (e.hexString) - { - if (tn.isintegral && tn.size == e.sz) - { - m = MATCH.convert; - return m; - } - } break; default: @@ -1884,6 +1878,19 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) Type tb = t.toBasetype(); Type typeb = e.type.toBasetype(); + if (e.hexString && !e.committed) + { + const szx = cast(ubyte) tb.nextOf().size(); + if (szx != se.sz && (e.len % szx) == 0) + { + import dmd.utils: arrayCastBigEndian; + const data = cast(const ubyte[]) e.peekString(); + se.setData(arrayCastBigEndian(data, szx).ptr, data.length / szx, szx); + se.type = t; + return se; + } + } + //printf("\ttype = %s\n", e.type.toChars()); if (tb.ty == Tdelegate && typeb.ty != Tdelegate) { diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index bc6f1461eaa..c08e2cd7b29 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -4245,22 +4245,22 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (e.hexString) { - const data = cast(const ubyte[]) e.peekString(); + const data = cast(const ubyte[]) e.peekString(); // peek before size is set to something larger than 1 switch (e.postfix) { - case 'L': - e.sz = 8; - e.type = Type.tuns64.immutableOf().arrayOf(); - break; case 'd': + e.committed = true; e.sz = 4; e.type = Type.tdstring; break; case 'w': + e.committed = true; e.sz = 2; e.type = Type.twstring; break; case 'c': + e.committed = true; + goto default; default: e.type = Type.tstring; e.sz = 1; @@ -4271,7 +4271,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor e.type.toChars(), e.sz, cast(int) e.len); e.setData(arrayCastBigEndian(data, e.sz).ptr, e.len / e.sz, e.sz); - e.committed = true; } else switch (e.postfix) { diff --git a/dmd/lexer.d b/dmd/lexer.d index 9c30fa97c81..c9c506e9cf3 100644 --- a/dmd/lexer.d +++ b/dmd/lexer.d @@ -1569,12 +1569,6 @@ class Lexer } t.setString(stringbuffer); stringPostfix(t); - if (*p == 'L') - { - t.postfix = 'L'; - p++; - } - return TOK.hexadecimalString; default: if (c >= '0' && c <= '9') diff --git a/tests/dmd/fail_compilation/hexstring.d b/tests/dmd/fail_compilation/hexstring.d index 95b07e763ff..6245d1e19bc 100644 --- a/tests/dmd/fail_compilation/hexstring.d +++ b/tests/dmd/fail_compilation/hexstring.d @@ -13,10 +13,10 @@ fail_compilation/hexstring.d(39): Error: array cast from `string` to `immutable( fail_compilation/hexstring.d(39): perhaps remove postfix `c` from hex string fail_compilation/hexstring.d(40): Error: hex string with `dstring` type needs to be multiple of 4 bytes, not 5 fail_compilation/hexstring.d(41): Error: cannot implicitly convert expression `x"44332211"d` of type `dstring` to `immutable(float[])` +fail_compilation/hexstring.d(42): Error: cannot implicitly convert expression `x"2211"w` of type `wstring` to `immutable(ubyte[])` fail_compilation/hexstring.d(28): Error: cannot implicitly convert expression `x"123F"` of type `string` to `ubyte[]` --- */ - immutable ubyte[] s0 = x"123F"; static assert(s0[0] == 0x12); static assert(s0[1] == 0x3F); @@ -39,3 +39,4 @@ immutable ushort[] f10 = cast(immutable ushort[]) (x"1122" ~ ""); immutable uint[] f11 = cast(immutable uint[]) x"AABBCCDD"c; immutable uint[] f12 = x"1122334455"d; immutable float[] f13 = x"11223344"d; +immutable ubyte[] f14 = x"1122"w; diff --git a/tests/dmd/runnable/literal.d b/tests/dmd/runnable/literal.d index 0c259c1eb1c..3a5909d028c 100644 --- a/tests/dmd/runnable/literal.d +++ b/tests/dmd/runnable/literal.d @@ -247,14 +247,13 @@ void testHexstring() static assert(x[0] == 0xFFAADDEE); assert(x[0] == 0xFFAADDEE); - static assert(is(typeof(x""L) == immutable(ulong)[])); - static immutable ulong[] y = x"1122334455667788AABBCCDDEEFF0099"L; + static immutable ulong[] y = x"1122334455667788AABBCCDDEEFF0099"; static assert(y[0] == 0x1122334455667788); static assert(y[1] == 0xAABBCCDDEEFF0099); assert(y[0] == 0x1122334455667788); assert(y[1] == 0xAABBCCDDEEFF0099); - immutable long[] c = x"1122334455667788AABBCCDDEEFF0099"L; + immutable long[] c = x"1122334455667788AABBCCDDEEFF0099"; assert(c[0] == 0x1122334455667788); assert(c[1] == 0xAABBCCDDEEFF0099); @@ -264,7 +263,7 @@ void testHexstring() // Test printing StringExp with size 8 enum toStr(immutable ulong[] v) = v.stringof; - static assert(toStr!y == `x"88776655443322119900FFEEDDCCBBAA"L`); + static assert(toStr!y == `x"88776655443322119900FFEEDDCCBBAA"`); // Hex string postfixes // https://issues.dlang.org/show_bug.cgi?id=24363 From 9a7c90b8193bf7f839d74169d5fb9d70d73af926 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 3 Mar 2024 21:30:47 +0100 Subject: [PATCH 011/125] C++ interface: Replace NULL with nullptr As that's built-in, not needing any headers. --- dmd/argtypes.h | 2 +- dmd/cond.h | 4 +- dmd/dsymbol.h | 114 ++++++++++++++++++++++---------------------- dmd/errors.h | 2 +- dmd/expression.h | 2 +- dmd/init.h | 2 +- dmd/mtype.h | 2 +- dmd/root/array.h | 4 +- dmd/root/bitarray.h | 2 +- dmd/root/dcompat.h | 2 +- dmd/root/filename.h | 2 +- dmd/statement.h | 68 +++++++++++++------------- dmd/target.h | 2 +- dmd/tokens.h | 2 +- 14 files changed, 105 insertions(+), 105 deletions(-) diff --git a/dmd/argtypes.h b/dmd/argtypes.h index 2e27a6afe12..8d018ea324b 100644 --- a/dmd/argtypes.h +++ b/dmd/argtypes.h @@ -21,5 +21,5 @@ namespace dmd TypeTuple *toArgTypes_sysv_x64(Type *t); // in argtypes_aarch64.d TypeTuple *toArgTypes_aarch64(Type *t); - bool isHFVA(Type *t, int maxNumElements = 4, Type **rewriteType = NULL); + bool isHFVA(Type *t, int maxNumElements = 4, Type **rewriteType = nullptr); } diff --git a/dmd/cond.h b/dmd/cond.h index fe497c2da7e..fc3ebd4625d 100644 --- a/dmd/cond.h +++ b/dmd/cond.h @@ -39,8 +39,8 @@ class Condition : public ASTNode virtual Condition *syntaxCopy() = 0; virtual int include(Scope *sc) = 0; - virtual DebugCondition *isDebugCondition() { return NULL; } - virtual VersionCondition *isVersionCondition() { return NULL; } + virtual DebugCondition *isDebugCondition() { return nullptr; } + virtual VersionCondition *isVersionCondition() { return nullptr; } void accept(Visitor *v) override { v->visit(this); } }; diff --git a/dmd/dsymbol.h b/dmd/dsymbol.h index f8454354fed..ce29073e2ea 100644 --- a/dmd/dsymbol.h +++ b/dmd/dsymbol.h @@ -222,9 +222,9 @@ class Dsymbol : public ASTNode Dsymbol *toParent2(); Dsymbol *toParentDecl(); Dsymbol *toParentLocal(); - Dsymbol *toParentP(Dsymbol *p1, Dsymbol *p2 = NULL); + Dsymbol *toParentP(Dsymbol *p1, Dsymbol *p2 = nullptr); TemplateInstance *isInstantiated(); - bool followInstantiationContext(Dsymbol *p1, Dsymbol *p2 = NULL); + bool followInstantiationContext(Dsymbol *p1, Dsymbol *p2 = nullptr); TemplateInstance *isSpeculative(); Ungag ungagSpeculative(); @@ -269,61 +269,61 @@ class Dsymbol : public ASTNode bool inNonRoot(); // Eliminate need for dynamic_cast - virtual Package *isPackage() { return NULL; } - virtual Module *isModule() { return NULL; } - virtual EnumMember *isEnumMember() { return NULL; } - virtual TemplateDeclaration *isTemplateDeclaration() { return NULL; } - virtual TemplateInstance *isTemplateInstance() { return NULL; } - virtual TemplateMixin *isTemplateMixin() { return NULL; } - virtual ForwardingAttribDeclaration *isForwardingAttribDeclaration() { return NULL; } - virtual Nspace *isNspace() { return NULL; } - virtual Declaration *isDeclaration() { return NULL; } - virtual StorageClassDeclaration *isStorageClassDeclaration(){ return NULL; } - virtual ExpressionDsymbol *isExpressionDsymbol() { return NULL; } - virtual AliasAssign *isAliasAssign() { return NULL; } - virtual ThisDeclaration *isThisDeclaration() { return NULL; } - virtual BitFieldDeclaration *isBitFieldDeclaration() { return NULL; } - virtual TypeInfoDeclaration *isTypeInfoDeclaration() { return NULL; } - virtual TupleDeclaration *isTupleDeclaration() { return NULL; } - virtual AliasDeclaration *isAliasDeclaration() { return NULL; } - virtual AggregateDeclaration *isAggregateDeclaration() { return NULL; } - virtual FuncDeclaration *isFuncDeclaration() { return NULL; } - virtual FuncAliasDeclaration *isFuncAliasDeclaration() { return NULL; } - virtual OverDeclaration *isOverDeclaration() { return NULL; } - virtual FuncLiteralDeclaration *isFuncLiteralDeclaration() { return NULL; } - virtual CtorDeclaration *isCtorDeclaration() { return NULL; } - virtual PostBlitDeclaration *isPostBlitDeclaration() { return NULL; } - virtual DtorDeclaration *isDtorDeclaration() { return NULL; } - virtual StaticCtorDeclaration *isStaticCtorDeclaration() { return NULL; } - virtual StaticDtorDeclaration *isStaticDtorDeclaration() { return NULL; } - virtual SharedStaticCtorDeclaration *isSharedStaticCtorDeclaration() { return NULL; } - virtual SharedStaticDtorDeclaration *isSharedStaticDtorDeclaration() { return NULL; } - virtual InvariantDeclaration *isInvariantDeclaration() { return NULL; } - virtual UnitTestDeclaration *isUnitTestDeclaration() { return NULL; } - virtual NewDeclaration *isNewDeclaration() { return NULL; } - virtual VarDeclaration *isVarDeclaration() { return NULL; } - virtual VersionSymbol *isVersionSymbol() { return NULL; } - virtual DebugSymbol *isDebugSymbol() { return NULL; } - virtual ClassDeclaration *isClassDeclaration() { return NULL; } - virtual StructDeclaration *isStructDeclaration() { return NULL; } - virtual UnionDeclaration *isUnionDeclaration() { return NULL; } - virtual InterfaceDeclaration *isInterfaceDeclaration() { return NULL; } - virtual ScopeDsymbol *isScopeDsymbol() { return NULL; } - virtual ForwardingScopeDsymbol *isForwardingScopeDsymbol() { return NULL; } - virtual WithScopeSymbol *isWithScopeSymbol() { return NULL; } - virtual ArrayScopeSymbol *isArrayScopeSymbol() { return NULL; } - virtual Import *isImport() { return NULL; } - virtual EnumDeclaration *isEnumDeclaration() { return NULL; } - virtual SymbolDeclaration *isSymbolDeclaration() { return NULL; } - virtual AttribDeclaration *isAttribDeclaration() { return NULL; } - virtual AnonDeclaration *isAnonDeclaration() { return NULL; } - virtual CPPNamespaceDeclaration *isCPPNamespaceDeclaration() { return NULL; } - virtual VisibilityDeclaration *isVisibilityDeclaration() { return NULL; } - virtual OverloadSet *isOverloadSet() { return NULL; } - virtual MixinDeclaration *isMixinDeclaration() { return NULL; } - virtual StaticAssert *isStaticAssert() { return NULL; } - virtual StaticIfDeclaration *isStaticIfDeclaration() { return NULL; } - virtual CAsmDeclaration *isCAsmDeclaration() { return NULL; } + virtual Package *isPackage() { return nullptr; } + virtual Module *isModule() { return nullptr; } + virtual EnumMember *isEnumMember() { return nullptr; } + virtual TemplateDeclaration *isTemplateDeclaration() { return nullptr; } + virtual TemplateInstance *isTemplateInstance() { return nullptr; } + virtual TemplateMixin *isTemplateMixin() { return nullptr; } + virtual ForwardingAttribDeclaration *isForwardingAttribDeclaration() { return nullptr; } + virtual Nspace *isNspace() { return nullptr; } + virtual Declaration *isDeclaration() { return nullptr; } + virtual StorageClassDeclaration *isStorageClassDeclaration(){ return nullptr; } + virtual ExpressionDsymbol *isExpressionDsymbol() { return nullptr; } + virtual AliasAssign *isAliasAssign() { return nullptr; } + virtual ThisDeclaration *isThisDeclaration() { return nullptr; } + virtual BitFieldDeclaration *isBitFieldDeclaration() { return nullptr; } + virtual TypeInfoDeclaration *isTypeInfoDeclaration() { return nullptr; } + virtual TupleDeclaration *isTupleDeclaration() { return nullptr; } + virtual AliasDeclaration *isAliasDeclaration() { return nullptr; } + virtual AggregateDeclaration *isAggregateDeclaration() { return nullptr; } + virtual FuncDeclaration *isFuncDeclaration() { return nullptr; } + virtual FuncAliasDeclaration *isFuncAliasDeclaration() { return nullptr; } + virtual OverDeclaration *isOverDeclaration() { return nullptr; } + virtual FuncLiteralDeclaration *isFuncLiteralDeclaration() { return nullptr; } + virtual CtorDeclaration *isCtorDeclaration() { return nullptr; } + virtual PostBlitDeclaration *isPostBlitDeclaration() { return nullptr; } + virtual DtorDeclaration *isDtorDeclaration() { return nullptr; } + virtual StaticCtorDeclaration *isStaticCtorDeclaration() { return nullptr; } + virtual StaticDtorDeclaration *isStaticDtorDeclaration() { return nullptr; } + virtual SharedStaticCtorDeclaration *isSharedStaticCtorDeclaration() { return nullptr; } + virtual SharedStaticDtorDeclaration *isSharedStaticDtorDeclaration() { return nullptr; } + virtual InvariantDeclaration *isInvariantDeclaration() { return nullptr; } + virtual UnitTestDeclaration *isUnitTestDeclaration() { return nullptr; } + virtual NewDeclaration *isNewDeclaration() { return nullptr; } + virtual VarDeclaration *isVarDeclaration() { return nullptr; } + virtual VersionSymbol *isVersionSymbol() { return nullptr; } + virtual DebugSymbol *isDebugSymbol() { return nullptr; } + virtual ClassDeclaration *isClassDeclaration() { return nullptr; } + virtual StructDeclaration *isStructDeclaration() { return nullptr; } + virtual UnionDeclaration *isUnionDeclaration() { return nullptr; } + virtual InterfaceDeclaration *isInterfaceDeclaration() { return nullptr; } + virtual ScopeDsymbol *isScopeDsymbol() { return nullptr; } + virtual ForwardingScopeDsymbol *isForwardingScopeDsymbol() { return nullptr; } + virtual WithScopeSymbol *isWithScopeSymbol() { return nullptr; } + virtual ArrayScopeSymbol *isArrayScopeSymbol() { return nullptr; } + virtual Import *isImport() { return nullptr; } + virtual EnumDeclaration *isEnumDeclaration() { return nullptr; } + virtual SymbolDeclaration *isSymbolDeclaration() { return nullptr; } + virtual AttribDeclaration *isAttribDeclaration() { return nullptr; } + virtual AnonDeclaration *isAnonDeclaration() { return nullptr; } + virtual CPPNamespaceDeclaration *isCPPNamespaceDeclaration() { return nullptr; } + virtual VisibilityDeclaration *isVisibilityDeclaration() { return nullptr; } + virtual OverloadSet *isOverloadSet() { return nullptr; } + virtual MixinDeclaration *isMixinDeclaration() { return nullptr; } + virtual StaticAssert *isStaticAssert() { return nullptr; } + virtual StaticIfDeclaration *isStaticIfDeclaration() { return nullptr; } + virtual CAsmDeclaration *isCAsmDeclaration() { return nullptr; } void accept(Visitor *v) override { v->visit(this); } }; diff --git a/dmd/errors.h b/dmd/errors.h index a47b5aada56..7a9683b9e67 100644 --- a/dmd/errors.h +++ b/dmd/errors.h @@ -42,7 +42,7 @@ D_ATTRIBUTE_FORMAT(1, 2) void message(const char *format, ...); D_ATTRIBUTE_FORMAT(2, 3) void message(const Loc& loc, const char *format, ...); D_ATTRIBUTE_FORMAT(1, 2) void tip(const char *format, ...); -D_ATTRIBUTE_FORMAT(2, 0) void verrorReport(const Loc& loc, const char *format, va_list ap, ErrorKind kind, const char *p1 = NULL, const char *p2 = NULL); +D_ATTRIBUTE_FORMAT(2, 0) void verrorReport(const Loc& loc, const char *format, va_list ap, ErrorKind kind, const char *p1 = nullptr, const char *p2 = nullptr); D_ATTRIBUTE_FORMAT(2, 0) void verrorReportSupplemental(const Loc& loc, const char* format, va_list ap, ErrorKind kind); #if defined(__GNUC__) || defined(__clang__) diff --git a/dmd/expression.h b/dmd/expression.h index 9cd73a965ba..66b62dea400 100644 --- a/dmd/expression.h +++ b/dmd/expression.h @@ -474,7 +474,7 @@ class StructLiteralExp final : public Expression d_bool isOriginal; // used when moving instances to indicate `this is this.origin` OwnedBy ownedByCtfe; - static StructLiteralExp *create(const Loc &loc, StructDeclaration *sd, void *elements, Type *stype = NULL); + static StructLiteralExp *create(const Loc &loc, StructDeclaration *sd, void *elements, Type *stype = nullptr); bool equals(const RootObject * const o) const override; StructLiteralExp *syntaxCopy() override; diff --git a/dmd/init.h b/dmd/init.h index 2485d78bb59..2ee69f622b3 100644 --- a/dmd/init.h +++ b/dmd/init.h @@ -126,6 +126,6 @@ class CInitializer final : public Initializer namespace dmd { - Expression *initializerToExpression(Initializer *init, Type *t = NULL, const bool isCfile = false); + Expression *initializerToExpression(Initializer *init, Type *t = nullptr, const bool isCfile = false); Initializer *initializerSemantic(Initializer *init, Scope *sc, Type *&tx, NeedInterpret needInterpret); } diff --git a/dmd/mtype.h b/dmd/mtype.h index 2f8bfa68d22..5d724db4f88 100644 --- a/dmd/mtype.h +++ b/dmd/mtype.h @@ -878,7 +878,7 @@ namespace dmd // return the symbol to which type t resolves Dsymbol *toDsymbol(Type *t, Scope *sc); bool equivalent(Type *src, Type *t); - Covariant covariant(Type *, Type *, StorageClass * = NULL, bool = false); + Covariant covariant(Type *, Type *, StorageClass * = nullptr, bool = false); bool isBaseOf(Type *tthis, Type *t, int *poffset); Type *trySemantic(Type *type, const Loc &loc, Scope *sc); Type *pointerTo(Type *type); diff --git a/dmd/root/array.h b/dmd/root/array.h index 3e2880431e9..cb8f8dc7381 100644 --- a/dmd/root/array.h +++ b/dmd/root/array.h @@ -26,7 +26,7 @@ struct Array public: Array() { - data.ptr = NULL; + data.ptr = nullptr; length = 0; data.length = 0; } @@ -86,7 +86,7 @@ struct Array if (nentries <= SMALLARRAYCAP) { data.length = SMALLARRAYCAP; - data.ptr = SMALLARRAYCAP ? &smallarray[0] : NULL; + data.ptr = SMALLARRAYCAP ? &smallarray[0] : nullptr; } else { diff --git a/dmd/root/bitarray.h b/dmd/root/bitarray.h index 2a827033587..0bea2d55a06 100644 --- a/dmd/root/bitarray.h +++ b/dmd/root/bitarray.h @@ -15,7 +15,7 @@ struct BitArray { BitArray() : len(0) - , ptr(NULL) + , ptr(nullptr) {} ~BitArray() diff --git a/dmd/root/dcompat.h b/dmd/root/dcompat.h index db2b2c6abcd..e397fa75c10 100644 --- a/dmd/root/dcompat.h +++ b/dmd/root/dcompat.h @@ -18,7 +18,7 @@ struct DArray size_t length; T *ptr; - DArray() : length(0), ptr(NULL) { } + DArray() : length(0), ptr(nullptr) { } DArray(size_t length_in, T *ptr_in) : length(length_in), ptr(ptr_in) { } diff --git a/dmd/root/filename.h b/dmd/root/filename.h index 0e52b982323..18bf4e54a8f 100644 --- a/dmd/root/filename.h +++ b/dmd/root/filename.h @@ -22,7 +22,7 @@ struct FileName static FileName create(const char *name); static bool equals(const char *name1, const char *name2); static bool absolute(const char *name); - static const char *toAbsolute(const char *name, const char *base = NULL); + static const char *toAbsolute(const char *name, const char *base = nullptr); static const char *ext(const char *); const char *ext(); static const char *removeExt(const char *str); diff --git a/dmd/statement.h b/dmd/statement.h index ea80e510c52..943723824fc 100644 --- a/dmd/statement.h +++ b/dmd/statement.h @@ -119,40 +119,40 @@ class Statement : public ASTNode bool hasCode(); virtual Statement *last(); - virtual ReturnStatement *endsWithReturnStatement() { return NULL; } - - ErrorStatement *isErrorStatement() { return stmt == STMTerror ? (ErrorStatement*)this : NULL; } - ScopeStatement *isScopeStatement() { return stmt == STMTscope ? (ScopeStatement*)this : NULL; } - ExpStatement *isExpStatement() { return stmt == STMTexp ? (ExpStatement*)this : NULL; } - CompoundStatement *isCompoundStatement() { return stmt == STMTcompound ? (CompoundStatement*)this : NULL; } - ReturnStatement *isReturnStatement() { return stmt == STMTreturn ? (ReturnStatement*)this : NULL; } - IfStatement *isIfStatement() { return stmt == STMTif ? (IfStatement*)this : NULL; } - ConditionalStatement *isConditionalStatement() { return stmt == STMTconditional ? (ConditionalStatement*)this : NULL; } - StaticForeachStatement *isStaticForeachStatement() { return stmt == STMTstaticForeach ? (StaticForeachStatement*)this : NULL; } - CaseStatement *isCaseStatement() { return stmt == STMTcase ? (CaseStatement*)this : NULL; } - DefaultStatement *isDefaultStatement() { return stmt == STMTdefault ? (DefaultStatement*)this : NULL; } - LabelStatement *isLabelStatement() { return stmt == STMTlabel ? (LabelStatement*)this : NULL; } - GotoDefaultStatement *isGotoDefaultStatement() { return stmt == STMTgotoDefault ? (GotoDefaultStatement*)this : NULL; } - GotoCaseStatement *isGotoCaseStatement() { return stmt == STMTgotoCase ? (GotoCaseStatement*)this : NULL; } - BreakStatement *isBreakStatement() { return stmt == STMTbreak ? (BreakStatement*)this : NULL; } - DtorExpStatement *isDtorExpStatement() { return stmt == STMTdtorExp ? (DtorExpStatement*)this : NULL; } - MixinStatement *isMixinStatement() { return stmt == STMTmixin ? (MixinStatement*)this : NULL; } - ForwardingStatement *isForwardingStatement() { return stmt == STMTforwarding ? (ForwardingStatement*)this : NULL; } - DoStatement *isDoStatement() { return stmt == STMTdo ? (DoStatement*)this : NULL; } - ForStatement *isForStatement() { return stmt == STMTfor ? (ForStatement*)this : NULL; } - ForeachStatement *isForeachStatement() { return stmt == STMTforeach ? (ForeachStatement*)this : NULL; } - SwitchStatement *isSwitchStatement() { return stmt == STMTswitch ? (SwitchStatement*)this : NULL; } - ContinueStatement *isContinueStatement() { return stmt == STMTcontinue ? (ContinueStatement*)this : NULL; } - WithStatement *isWithStatement() { return stmt == STMTwith ? (WithStatement*)this : NULL; } - TryCatchStatement *isTryCatchStatement() { return stmt == STMTtryCatch ? (TryCatchStatement*)this : NULL; } - ThrowStatement *isThrowStatement() { return stmt == STMTthrow ? (ThrowStatement*)this : NULL; } - DebugStatement *isDebugStatement() { return stmt == STMTdebug ? (DebugStatement*)this : NULL; } - TryFinallyStatement *isTryFinallyStatement() { return stmt == STMTtryFinally ? (TryFinallyStatement*)this : NULL; } - ScopeGuardStatement *isScopeGuardStatement() { return stmt == STMTscopeGuard ? (ScopeGuardStatement*)this : NULL; } - SwitchErrorStatement *isSwitchErrorStatement() { return stmt == STMTswitchError ? (SwitchErrorStatement*)this : NULL; } - UnrolledLoopStatement *isUnrolledLoopStatement() { return stmt == STMTunrolledLoop ? (UnrolledLoopStatement*)this : NULL; } - ForeachRangeStatement *isForeachRangeStatement() { return stmt == STMTforeachRange ? (ForeachRangeStatement*)this : NULL; } - CompoundDeclarationStatement *isCompoundDeclarationStatement() { return stmt == STMTcompoundDeclaration ? (CompoundDeclarationStatement*)this : NULL; } + virtual ReturnStatement *endsWithReturnStatement() { return nullptr; } + + ErrorStatement *isErrorStatement() { return stmt == STMTerror ? (ErrorStatement*)this : nullptr; } + ScopeStatement *isScopeStatement() { return stmt == STMTscope ? (ScopeStatement*)this : nullptr; } + ExpStatement *isExpStatement() { return stmt == STMTexp ? (ExpStatement*)this : nullptr; } + CompoundStatement *isCompoundStatement() { return stmt == STMTcompound ? (CompoundStatement*)this : nullptr; } + ReturnStatement *isReturnStatement() { return stmt == STMTreturn ? (ReturnStatement*)this : nullptr; } + IfStatement *isIfStatement() { return stmt == STMTif ? (IfStatement*)this : nullptr; } + ConditionalStatement *isConditionalStatement() { return stmt == STMTconditional ? (ConditionalStatement*)this : nullptr; } + StaticForeachStatement *isStaticForeachStatement() { return stmt == STMTstaticForeach ? (StaticForeachStatement*)this : nullptr; } + CaseStatement *isCaseStatement() { return stmt == STMTcase ? (CaseStatement*)this : nullptr; } + DefaultStatement *isDefaultStatement() { return stmt == STMTdefault ? (DefaultStatement*)this : nullptr; } + LabelStatement *isLabelStatement() { return stmt == STMTlabel ? (LabelStatement*)this : nullptr; } + GotoDefaultStatement *isGotoDefaultStatement() { return stmt == STMTgotoDefault ? (GotoDefaultStatement*)this : nullptr; } + GotoCaseStatement *isGotoCaseStatement() { return stmt == STMTgotoCase ? (GotoCaseStatement*)this : nullptr; } + BreakStatement *isBreakStatement() { return stmt == STMTbreak ? (BreakStatement*)this : nullptr; } + DtorExpStatement *isDtorExpStatement() { return stmt == STMTdtorExp ? (DtorExpStatement*)this : nullptr; } + MixinStatement *isMixinStatement() { return stmt == STMTmixin ? (MixinStatement*)this : nullptr; } + ForwardingStatement *isForwardingStatement() { return stmt == STMTforwarding ? (ForwardingStatement*)this : nullptr; } + DoStatement *isDoStatement() { return stmt == STMTdo ? (DoStatement*)this : nullptr; } + ForStatement *isForStatement() { return stmt == STMTfor ? (ForStatement*)this : nullptr; } + ForeachStatement *isForeachStatement() { return stmt == STMTforeach ? (ForeachStatement*)this : nullptr; } + SwitchStatement *isSwitchStatement() { return stmt == STMTswitch ? (SwitchStatement*)this : nullptr; } + ContinueStatement *isContinueStatement() { return stmt == STMTcontinue ? (ContinueStatement*)this : nullptr; } + WithStatement *isWithStatement() { return stmt == STMTwith ? (WithStatement*)this : nullptr; } + TryCatchStatement *isTryCatchStatement() { return stmt == STMTtryCatch ? (TryCatchStatement*)this : nullptr; } + ThrowStatement *isThrowStatement() { return stmt == STMTthrow ? (ThrowStatement*)this : nullptr; } + DebugStatement *isDebugStatement() { return stmt == STMTdebug ? (DebugStatement*)this : nullptr; } + TryFinallyStatement *isTryFinallyStatement() { return stmt == STMTtryFinally ? (TryFinallyStatement*)this : nullptr; } + ScopeGuardStatement *isScopeGuardStatement() { return stmt == STMTscopeGuard ? (ScopeGuardStatement*)this : nullptr; } + SwitchErrorStatement *isSwitchErrorStatement() { return stmt == STMTswitchError ? (SwitchErrorStatement*)this : nullptr; } + UnrolledLoopStatement *isUnrolledLoopStatement() { return stmt == STMTunrolledLoop ? (UnrolledLoopStatement*)this : nullptr; } + ForeachRangeStatement *isForeachRangeStatement() { return stmt == STMTforeachRange ? (ForeachRangeStatement*)this : nullptr; } + CompoundDeclarationStatement *isCompoundDeclarationStatement() { return stmt == STMTcompoundDeclaration ? (CompoundDeclarationStatement*)this : nullptr; } void accept(Visitor *v) override { v->visit(this); } }; diff --git a/dmd/target.h b/dmd/target.h index 120950570d4..6237cf14836 100644 --- a/dmd/target.h +++ b/dmd/target.h @@ -198,7 +198,7 @@ struct Target unsigned fieldalign(Type *type); Type *va_listType(const Loc &loc, Scope *sc); // get type of va_list int isVectorTypeSupported(int sz, Type *type); - bool isVectorOpSupported(Type *type, EXP op, Type *t2 = NULL); + bool isVectorOpSupported(Type *type, EXP op, Type *t2 = nullptr); // ABI and backend. LINK systemLinkage(); TypeTuple *toArgTypes(Type *t); diff --git a/dmd/tokens.h b/dmd/tokens.h index ef91001a996..929897a3fa6 100644 --- a/dmd/tokens.h +++ b/dmd/tokens.h @@ -476,7 +476,7 @@ struct Token Identifier *ident; }; - Token() : next(NULL) {} + Token() : next(nullptr) {} const char *toChars() const; static const char *toChars(TOK value); From efa238b7140f65c3f88fc3463b6d9f3e7a386363 Mon Sep 17 00:00:00 2001 From: Bastiaan Veelo Date: Sun, 3 Mar 2024 23:50:03 +0100 Subject: [PATCH 012/125] Move functions from func.d to funcsem.d. (dlang/dmd!16286) These require `import dscope` that we want to remove from func.d in pursuit of https://github.com/orgs/dlang/projects/41. Other functions that still need to be moved include `overloadApply()`, `isUnique()` and `equals()`, the latter needing a visitor akin dlang/dmd!15782. --- dmd/dclass.d | 1 + dmd/declaration.d | 1 + dmd/dmangle.d | 1 + dmd/dstruct.d | 1 + dmd/escape.d | 1 + dmd/frontend.h | 2 - dmd/func.d | 863 ---------------------------------------------- dmd/funcsem.d | 812 +++++++++++++++++++++++++++++++++++++++++++ dmd/opover.d | 1 + dmd/safe.d | 2 +- dmd/semantic2.d | 1 + 11 files changed, 820 insertions(+), 866 deletions(-) diff --git a/dmd/dclass.d b/dmd/dclass.d index 8bac1f4ea26..2199d5d1a77 100644 --- a/dmd/dclass.d +++ b/dmd/dclass.d @@ -26,6 +26,7 @@ import dmd.dsymbol; import dmd.dsymbolsem; import dmd.errors; import dmd.func; +import dmd.funcsem; import dmd.id; import dmd.identifier; import dmd.location; diff --git a/dmd/declaration.d b/dmd/declaration.d index 384d9118861..75e7e911a69 100644 --- a/dmd/declaration.d +++ b/dmd/declaration.d @@ -27,6 +27,7 @@ import dmd.dtemplate; import dmd.errors; import dmd.expression; import dmd.func; +import dmd.funcsem; import dmd.globals; import dmd.gluelayer; import dmd.id; diff --git a/dmd/dmangle.d b/dmd/dmangle.d index 33428ded2d1..3640b67c7a9 100644 --- a/dmd/dmangle.d +++ b/dmd/dmangle.d @@ -147,6 +147,7 @@ import dmd.dtemplate; import dmd.errors; import dmd.expression; import dmd.func; +import dmd.funcsem; import dmd.globals; import dmd.id; import dmd.identifier; diff --git a/dmd/dstruct.d b/dmd/dstruct.d index df4d07a81d9..e4a33dd9815 100644 --- a/dmd/dstruct.d +++ b/dmd/dstruct.d @@ -28,6 +28,7 @@ import dmd.dtemplate; import dmd.errors; import dmd.expression; import dmd.func; +import dmd.funcsem; import dmd.globals; import dmd.id; import dmd.identifier; diff --git a/dmd/escape.d b/dmd/escape.d index 3e17ff4736d..4e1bc59987e 100644 --- a/dmd/escape.d +++ b/dmd/escape.d @@ -25,6 +25,7 @@ import dmd.dsymbol; import dmd.errors; import dmd.expression; import dmd.func; +import dmd.funcsem; import dmd.globals : FeatureState; import dmd.id; import dmd.identifier; diff --git a/dmd/frontend.h b/dmd/frontend.h index 23dbfc72a77..923f47506fe 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -3786,7 +3786,6 @@ class FuncDeclaration : public Declaration bool isCodeseg() const final override; bool isOverloadable() const final override; bool isAbstract() final override; - bool canInferAttributes(Scope* sc); void initInferAttributes(); PURE isPure(); bool isSafe(); @@ -3806,7 +3805,6 @@ class FuncDeclaration : public Declaration bool checkClosure(); bool hasNestedFrameRefs(); static bool needsFensure(FuncDeclaration* fd); - void buildEnsureRequire(); ParameterList getParameterList(); static FuncDeclaration* genCfunc(Array* fparams, Type* treturn, const char* name, StorageClass stc = 0); static FuncDeclaration* genCfunc(Array* fparams, Type* treturn, Identifier* id, StorageClass stc = 0); diff --git a/dmd/func.d b/dmd/func.d index 7003c2b1192..382710d7839 100644 --- a/dmd/func.d +++ b/dmd/func.d @@ -512,149 +512,6 @@ extern (C++) class FuncDeclaration : Declaration return true; } - /******************************************** - * Find function in overload list that exactly matches t. - */ - extern (D) final FuncDeclaration overloadExactMatch(Type t) - { - FuncDeclaration fd; - overloadApply(this, (Dsymbol s) - { - auto f = s.isFuncDeclaration(); - if (!f) - return 0; - if (f.storage_class & STC.disable) - return 0; - if (t.equals(f.type)) - { - fd = f; - return 1; - } - - /* Allow covariant matches, as long as the return type - * is just a const conversion. - * This allows things like pure functions to match with an impure function type. - */ - if (t.ty == Tfunction) - { - auto tf = cast(TypeFunction)f.type; - if (tf.covariant(t) == Covariant.yes && - tf.nextOf().implicitConvTo(t.nextOf()) >= MATCH.constant) - { - fd = f; - return 1; - } - } - return 0; - }); - return fd; - } - - /******************************************** - * Find function in overload list that matches to the 'this' modifier. - * There's four result types. - * - * 1. If the 'tthis' matches only one candidate, it's an "exact match". - * Returns the function and 'hasOverloads' is set to false. - * eg. If 'tthis" is mutable and there's only one mutable method. - * 2. If there's two or more match candidates, but a candidate function will be - * a "better match". - * Returns the better match function but 'hasOverloads' is set to true. - * eg. If 'tthis' is mutable, and there's both mutable and const methods, - * the mutable method will be a better match. - * 3. If there's two or more match candidates, but there's no better match, - * Returns null and 'hasOverloads' is set to true to represent "ambiguous match". - * eg. If 'tthis' is mutable, and there's two or more mutable methods. - * 4. If there's no candidates, it's "no match" and returns null with error report. - * e.g. If 'tthis' is const but there's no const methods. - */ - extern (D) final FuncDeclaration overloadModMatch(const ref Loc loc, Type tthis, ref bool hasOverloads) - { - //printf("FuncDeclaration::overloadModMatch('%s')\n", toChars()); - MatchAccumulator m; - overloadApply(this, (Dsymbol s) - { - auto f = s.isFuncDeclaration(); - if (!f || f == m.lastf) // skip duplicates - return 0; - - auto tf = f.type.toTypeFunction(); - //printf("tf = %s\n", tf.toChars()); - - MATCH match; - if (tthis) // non-static functions are preferred than static ones - { - if (f.needThis()) - match = f.isCtorDeclaration() ? MATCH.exact : MODmethodConv(tthis.mod, tf.mod); - else - match = MATCH.constant; // keep static function in overload candidates - } - else // static functions are preferred than non-static ones - { - if (f.needThis()) - match = MATCH.convert; - else - match = MATCH.exact; - } - if (match == MATCH.nomatch) - return 0; - - if (match > m.last) goto LcurrIsBetter; - if (match < m.last) goto LlastIsBetter; - - // See if one of the matches overrides the other. - if (m.lastf.overrides(f)) goto LlastIsBetter; - if (f.overrides(m.lastf)) goto LcurrIsBetter; - - //printf("\tambiguous\n"); - m.nextf = f; - m.count++; - return 0; - - LlastIsBetter: - //printf("\tlastbetter\n"); - m.count++; // count up - return 0; - - LcurrIsBetter: - //printf("\tisbetter\n"); - if (m.last <= MATCH.convert) - { - // clear last secondary matching - m.nextf = null; - m.count = 0; - } - m.last = match; - m.lastf = f; - m.count++; // count up - return 0; - }); - - if (m.count == 1) // exact match - { - hasOverloads = false; - } - else if (m.count > 1) // better or ambiguous match - { - hasOverloads = true; - } - else // no match - { - hasOverloads = true; - auto tf = this.type.toTypeFunction(); - assert(tthis); - assert(!MODimplicitConv(tthis.mod, tf.mod)); // modifier mismatch - { - OutBuffer thisBuf, funcBuf; - MODMatchToBuffer(&thisBuf, tthis.mod, tf.mod); - MODMatchToBuffer(&funcBuf, tf.mod, tthis.mod); - .error(loc, "%smethod %s is not callable using a %sobject", kind, toPrettyChars, - funcBuf.peekChars(), this.toPrettyChars(), thisBuf.peekChars()); - } - } - return m.lastf; - } - /******************************************** * find function template root in overload list */ @@ -855,43 +712,6 @@ extern (C++) class FuncDeclaration : Declaration return level; } - /*********************************** - * Determine lexical level difference from `this` to nested function `fd`. - * Issue error if `this` cannot call `fd`. - * - * Params: - * loc = location for error messages - * sc = context - * fd = target of call - * decl = The `Declaration` that triggered this check. - * Used to provide a better error message only. - * Returns: - * 0 same level - * >0 decrease nesting by number - * -1 increase nesting by 1 (`fd` is nested within 'this') - * LevelError error - */ - extern (D) final int getLevelAndCheck(const ref Loc loc, Scope* sc, FuncDeclaration fd, - Declaration decl) - { - int level = getLevel(fd, sc.intypeof); - if (level != LevelError) - return level; - - // Don't give error if in template constraint - if (!(sc.flags & SCOPE.constraint)) - { - const(char)* xstatic = isStatic() ? "`static` " : ""; - // better diagnostics for static functions - .error(loc, "%s%s `%s` cannot access %s `%s` in frame of function `%s`", - xstatic, kind(), toPrettyChars(), decl.kind(), decl.toChars(), - fd.toPrettyChars()); - .errorSupplemental(decl.loc, "`%s` declared here", decl.toChars()); - return LevelError; - } - return 1; - } - enum LevelError = -2; override const(char)* toPrettyChars(bool QualifyTypes = false) @@ -990,47 +810,6 @@ extern (C++) class FuncDeclaration : Declaration return false; } - /********************************** - * Decide if attributes for this function can be inferred from examining - * the function body. - * Returns: - * true if can - */ - final bool canInferAttributes(Scope* sc) - { - if (!fbody) - return false; - - if (isVirtualMethod() && - /* - * https://issues.dlang.org/show_bug.cgi?id=21719 - * - * If we have an auto virtual function we can infer - * the attributes. - */ - !(inferRetType && !isCtorDeclaration())) - return false; // since they may be overridden - - if (sc.func && - /********** this is for backwards compatibility for the moment ********/ - (!isMember() || sc.func.isSafeBypassingInference() && !isInstantiated())) - return true; - - if (isFuncLiteralDeclaration() || // externs are not possible with literals - (storage_class & STC.inference) || // do attribute inference - (inferRetType && !isCtorDeclaration())) - return true; - - if (isInstantiated()) - { - auto ti = parent.isTemplateInstance(); - if (ti is null || ti.isTemplateMixin() || ti.tempdecl.ident == ident) - return true; - } - - return false; - } - /***************************************** * Initialize for inferring the attributes of this function. */ @@ -1633,101 +1412,6 @@ extern (C++) class FuncDeclaration : Declaration return result; } - /********************************************* - * In the current function, we are calling 'this' function. - * 1. Check to see if the current function can call 'this' function, issue error if not. - * 2. If the current function is not the parent of 'this' function, then add - * the current function to the list of siblings of 'this' function. - * 3. If the current function is a literal, and it's accessing an uplevel scope, - * then mark it as a delegate. - * Returns true if error occurs. - */ - extern (D) final bool checkNestedReference(Scope* sc, const ref Loc loc) - { - //printf("FuncDeclaration::checkNestedReference() %s\n", toPrettyChars()); - - if (auto fld = this.isFuncLiteralDeclaration()) - { - if (fld.tok == TOK.reserved) - { - fld.tok = TOK.function_; - fld.vthis = null; - } - } - - if (!parent || parent == sc.parent) - return false; - if (ident == Id.require || ident == Id.ensure) - return false; - if (!isThis() && !isNested()) - return false; - - // The current function - FuncDeclaration fdthis = sc.parent.isFuncDeclaration(); - if (!fdthis) - return false; // out of function scope - - Dsymbol p = toParentLocal(); - Dsymbol p2 = toParent2(); - - // Function literals from fdthis to p must be delegates - ensureStaticLinkTo(fdthis, p); - if (p != p2) - ensureStaticLinkTo(fdthis, p2); - - if (isNested()) - { - // The function that this function is in - bool checkEnclosing(FuncDeclaration fdv) - { - if (!fdv) - return false; - if (fdv == fdthis) - return false; - - //printf("this = %s in [%s]\n", this.toChars(), this.loc.toChars()); - //printf("fdv = %s in [%s]\n", fdv .toChars(), fdv .loc.toChars()); - //printf("fdthis = %s in [%s]\n", fdthis.toChars(), fdthis.loc.toChars()); - - // Add this function to the list of those which called us - if (fdthis != this) - { - bool found = false; - for (size_t i = 0; i < siblingCallers.length; ++i) - { - if (siblingCallers[i] == fdthis) - found = true; - } - if (!found) - { - //printf("\tadding sibling %s to %s\n", fdthis.toPrettyChars(), toPrettyChars()); - if (!sc.intypeof && !(sc.flags & SCOPE.compile)) - { - siblingCallers.push(fdthis); - computedEscapingSiblings = false; - } - } - } - - const lv = fdthis.getLevelAndCheck(loc, sc, fdv, this); - if (lv == LevelError) - return true; // error - if (lv == -1) - return false; // downlevel call - if (lv == 0) - return false; // same level call - - return false; // Uplevel call - } - - if (checkEnclosing(p.isFuncDeclaration())) - return true; - if (checkEnclosing(p == p2 ? null : p2.isFuncDeclaration())) - return true; - } - return false; - } - /******************************* * Look at all the variables in this function that are referenced * by nested functions, and determine if a closure needs to be @@ -1922,147 +1606,6 @@ extern (C++) class FuncDeclaration : Declaration return false; } - /**************************************************** - * Check whether result variable can be built. - * Returns: - * `true` if the function has a return type that - * is different from `void`. - */ - extern (D) private bool canBuildResultVar() - { - auto f = cast(TypeFunction)type; - return f && f.nextOf() && f.nextOf().toBasetype().ty != Tvoid; - } - - /**************************************************** - * Merge into this function the 'in' contracts of all it overrides. - * 'in's are OR'd together, i.e. only one of them needs to pass. - */ - extern (D) final Statement mergeFrequire(Statement sf, Expressions* params) - { - /* If a base function and its override both have an IN contract, then - * only one of them needs to succeed. This is done by generating: - * - * void derived.in() { - * try { - * base.in(); - * } - * catch () { - * ... body of derived.in() ... - * } - * } - * - * So if base.in() doesn't throw, derived.in() need not be executed, and the contract is valid. - * If base.in() throws, then derived.in()'s body is executed. - */ - - foreach (fdv; foverrides) - { - /* The semantic pass on the contracts of the overridden functions must - * be completed before code generation occurs. - * https://issues.dlang.org/show_bug.cgi?id=3602 - */ - if (fdv.frequires && fdv.semanticRun != PASS.semantic3done) - { - assert(fdv._scope); - Scope* sc = fdv._scope.push(); - sc.stc &= ~STC.override_; - fdv.semantic3(sc); - sc.pop(); - } - - sf = fdv.mergeFrequire(sf, params); - if (!sf || !fdv.fdrequire) - return null; - //printf("fdv.frequire: %s\n", fdv.frequire.toChars()); - /* Make the call: - * try { __require(params); } - * catch (Throwable) { frequire; } - */ - params = Expression.arraySyntaxCopy(params); - Expression e = new CallExp(loc, new VarExp(loc, fdv.fdrequire, false), params); - Statement s2 = new ExpStatement(loc, e); - - auto c = new Catch(loc, getThrowable(), null, sf); - c.internalCatch = true; - auto catches = new Catches(); - catches.push(c); - sf = new TryCatchStatement(loc, s2, catches); - } - return sf; - } - - /**************************************************** - * Merge into this function the 'in' contracts of all it overrides. - */ - extern (D) final Statement mergeFrequireInclusivePreview(Statement sf, Expressions* params) - { - /* If a base function and its override both have an IN contract, then - * the override in contract must widen the guarantee of the base contract. - * This is checked by generating: - * - * void derived.in() { - * try { - * ... body of derived.in() ... - * } - * catch () { - * // derived in rejected this argument. so parent must also reject it, or we've tightened the contract. - * base.in(); - * assert(false, "Logic error: " ~ thr.msg); - * } - * } - */ - - foreach (fdv; foverrides) - { - /* The semantic pass on the contracts of the overridden functions must - * be completed before code generation occurs. - * https://issues.dlang.org/show_bug.cgi?id=3602 - */ - if (fdv.frequires && fdv.semanticRun != PASS.semantic3done) - { - assert(fdv._scope); - Scope* sc = fdv._scope.push(); - sc.stc &= ~STC.override_; - fdv.semantic3(sc); - sc.pop(); - } - - sf = fdv.mergeFrequireInclusivePreview(sf, params); - if (sf && fdv.fdrequire) - { - const loc = this.fdrequire.loc; - - //printf("fdv.frequire: %s\n", fdv.frequire.toChars()); - /* Make the call: - * try { frequire; } - * catch (Throwable thr) { __require(params); assert(false, "Logic error: " ~ thr.msg); } - */ - Identifier id = Identifier.generateId("thr"); - params = Expression.arraySyntaxCopy(params); - Expression e = new CallExp(loc, new VarExp(loc, fdv.fdrequire, false), params); - Statement s2 = new ExpStatement(loc, e); - // assert(false, ...) - // TODO make this a runtime helper to allow: - // - chaining the original expression - // - nogc concatenation - Expression msg = new StringExp(loc, "Logic error: in-contract was tighter than parent in-contract"); - Statement fail = new ExpStatement(loc, new AssertExp(loc, IntegerExp.literal!0, msg)); - - Statement s3 = new CompoundStatement(loc, s2, fail); - - auto c = new Catch(loc, getThrowable(), id, s3); - c.internalCatch = true; - auto catches = new Catches(); - catches.push(c); - sf = new TryCatchStatement(loc, sf, catches); - } - else - return null; - } - return sf; - } - /**************************************************** * Determine whether an 'out' contract is declared inside * the given function or any of its overrides. @@ -2084,232 +1627,6 @@ extern (C++) class FuncDeclaration : Declaration return false; } - /**************************************************** - * Rewrite contracts as statements. - */ - final void buildEnsureRequire() - { - - if (frequires) - { - /* in { statements1... } - * in { statements2... } - * ... - * becomes: - * in { { statements1... } { statements2... } ... } - */ - assert(frequires.length); - auto loc = (*frequires)[0].loc; - auto s = new Statements; - foreach (r; *frequires) - { - s.push(new ScopeStatement(r.loc, r, r.loc)); - } - frequire = new CompoundStatement(loc, s); - } - - if (fensures) - { - /* out(id1) { statements1... } - * out(id2) { statements2... } - * ... - * becomes: - * out(__result) { { ref id1 = __result; { statements1... } } - * { ref id2 = __result; { statements2... } } ... } - */ - assert(fensures.length); - auto loc = (*fensures)[0].ensure.loc; - auto s = new Statements; - foreach (r; *fensures) - { - if (r.id && canBuildResultVar()) - { - auto rloc = r.ensure.loc; - auto resultId = new IdentifierExp(rloc, Id.result); - auto init = new ExpInitializer(rloc, resultId); - auto stc = STC.ref_ | STC.temp | STC.result; - auto decl = new VarDeclaration(rloc, null, r.id, init, stc); - auto sdecl = new ExpStatement(rloc, decl); - s.push(new ScopeStatement(rloc, new CompoundStatement(rloc, sdecl, r.ensure), rloc)); - } - else - { - s.push(r.ensure); - } - } - fensure = new CompoundStatement(loc, s); - } - - if (!isVirtual()) - return; - - /* Rewrite contracts as nested functions, then call them. Doing it as nested - * functions means that overriding functions can call them. - */ - TypeFunction f = cast(TypeFunction) type; - - /* Make a copy of the parameters and make them all ref */ - static Parameters* toRefCopy(ParameterList parameterList) - { - auto result = new Parameters(); - - foreach (n, p; parameterList) - { - p = p.syntaxCopy(); - if (!p.isLazy()) - p.storageClass = (p.storageClass | STC.ref_) & ~STC.out_; - p.defaultArg = null; // won't be the same with ref - result.push(p); - } - - return result; - } - - if (frequire) - { - /* in { ... } - * becomes: - * void __require(ref params) { ... } - * __require(params); - */ - Loc loc = frequire.loc; - fdrequireParams = new Expressions(); - if (parameters) - { - foreach (vd; *parameters) - fdrequireParams.push(new VarExp(loc, vd)); - } - auto fo = cast(TypeFunction)(originalType ? originalType : f); - auto fparams = toRefCopy(fo.parameterList); - auto tf = new TypeFunction(ParameterList(fparams), Type.tvoid, LINK.d); - tf.isnothrow = f.isnothrow; - tf.isnogc = f.isnogc; - tf.purity = f.purity; - tf.trust = f.trust; - auto fd = new FuncDeclaration(loc, loc, Id.require, STC.undefined_, tf); - fd.fbody = frequire; - Statement s1 = new ExpStatement(loc, fd); - Expression e = new CallExp(loc, new VarExp(loc, fd, false), fdrequireParams); - Statement s2 = new ExpStatement(loc, e); - frequire = new CompoundStatement(loc, s1, s2); - fdrequire = fd; - } - - /* We need to set fdensureParams here and not in the block below to - * have the parameters available when calling a base class ensure(), - * even if this function doesn't have an out contract. - */ - fdensureParams = new Expressions(); - if (canBuildResultVar()) - fdensureParams.push(new IdentifierExp(loc, Id.result)); - if (parameters) - { - foreach (vd; *parameters) - fdensureParams.push(new VarExp(loc, vd)); - } - - if (fensure) - { - /* out (result) { ... } - * becomes: - * void __ensure(ref tret result, ref params) { ... } - * __ensure(result, params); - */ - Loc loc = fensure.loc; - auto fparams = new Parameters(); - if (canBuildResultVar()) - { - Parameter p = new Parameter(loc, STC.ref_ | STC.const_, f.nextOf(), Id.result, null, null); - fparams.push(p); - } - auto fo = cast(TypeFunction)(originalType ? originalType : f); - fparams.pushSlice((*toRefCopy(fo.parameterList))[]); - auto tf = new TypeFunction(ParameterList(fparams), Type.tvoid, LINK.d); - tf.isnothrow = f.isnothrow; - tf.isnogc = f.isnogc; - tf.purity = f.purity; - tf.trust = f.trust; - auto fd = new FuncDeclaration(loc, loc, Id.ensure, STC.undefined_, tf); - fd.fbody = fensure; - Statement s1 = new ExpStatement(loc, fd); - Expression e = new CallExp(loc, new VarExp(loc, fd, false), fdensureParams); - Statement s2 = new ExpStatement(loc, e); - fensure = new CompoundStatement(loc, s1, s2); - fdensure = fd; - } - } - - /**************************************************** - * Merge into this function the 'out' contracts of all it overrides. - * 'out's are AND'd together, i.e. all of them need to pass. - */ - extern (D) final Statement mergeFensure(Statement sf, Identifier oid, Expressions* params) - { - /* Same comments as for mergeFrequire(), except that we take care - * of generating a consistent reference to the 'result' local by - * explicitly passing 'result' to the nested function as a reference - * argument. - * This won't work for the 'this' parameter as it would require changing - * the semantic code for the nested function so that it looks on the parameter - * list for the 'this' pointer, something that would need an unknown amount - * of tweaking of various parts of the compiler that I'd rather leave alone. - */ - foreach (fdv; foverrides) - { - /* The semantic pass on the contracts of the overridden functions must - * be completed before code generation occurs. - * https://issues.dlang.org/show_bug.cgi?id=3602 and - * https://issues.dlang.org/show_bug.cgi?id=5230 - */ - if (needsFensure(fdv) && fdv.semanticRun != PASS.semantic3done) - { - assert(fdv._scope); - Scope* sc = fdv._scope.push(); - sc.stc &= ~STC.override_; - fdv.semantic3(sc); - sc.pop(); - } - - sf = fdv.mergeFensure(sf, oid, params); - if (fdv.fdensure) - { - //printf("fdv.fensure: %s\n", fdv.fensure.toChars()); - // Make the call: __ensure(result, params) - params = Expression.arraySyntaxCopy(params); - if (canBuildResultVar()) - { - Type t1 = fdv.type.nextOf().toBasetype(); - Type t2 = this.type.nextOf().toBasetype(); - if (t1.isBaseOf(t2, null)) - { - /* Making temporary reference variable is necessary - * in covariant return. - * https://issues.dlang.org/show_bug.cgi?id=5204 - * https://issues.dlang.org/show_bug.cgi?id=10479 - */ - Expression* eresult = &(*params)[0]; - auto ei = new ExpInitializer(Loc.initial, *eresult); - auto v = new VarDeclaration(Loc.initial, t1, Identifier.generateId("__covres"), ei); - v.storage_class |= STC.temp; - auto de = new DeclarationExp(Loc.initial, v); - auto ve = new VarExp(Loc.initial, v); - *eresult = new CommaExp(Loc.initial, de, ve); - } - } - Expression e = new CallExp(loc, new VarExp(loc, fdv.fdensure, false), params); - Statement s2 = new ExpStatement(loc, e); - - if (sf) - { - sf = new CompoundStatement(sf.loc, s2, sf); - } - else - sf = s2; - } - } - return sf; - } - /********************************************* * Returns: the function's parameter list, and whether * it is variadic or not. @@ -3073,56 +2390,6 @@ extern (C++) final class FuncLiteralDeclaration : FuncDeclaration return false; } - /******************************* - * Modify all expression type of return statements to tret. - * - * On function literals, return type may be modified based on the context type - * after its semantic3 is done, in FuncExp::implicitCastTo. - * - * A function() dg = (){ return new B(); } // OK if is(B : A) == true - * - * If B to A conversion is convariant that requires offseet adjusting, - * all return statements should be adjusted to return expressions typed A. - */ - extern (D) void modifyReturns(Scope* sc, Type tret) - { - import dmd.statement_rewrite_walker; - - extern (C++) final class RetWalker : StatementRewriteWalker - { - alias visit = typeof(super).visit; - public: - Scope* sc; - Type tret; - FuncLiteralDeclaration fld; - - override void visit(ReturnStatement s) - { - Expression exp = s.exp; - if (exp && !exp.type.equals(tret)) - s.exp = exp.implicitCastTo(sc, tret); - } - } - - if (semanticRun < PASS.semantic3done) - return; - - if (fes) - return; - - scope RetWalker w = new RetWalker(); - w.sc = sc; - w.tret = tret; - w.fld = this; - fbody.accept(w); - - // Also update the inferred function type to match the new return type. - // This is required so the code generator does not try to cast the - // modified returns back to the original type. - if (inferRetType && type.nextOf() != tret) - type.toTypeFunction().next = tret; - } - override inout(FuncLiteralDeclaration) isFuncLiteralDeclaration() inout { return this; @@ -3654,136 +2921,6 @@ extern (C++) final class NewDeclaration : FuncDeclaration } } -/************************************** - * When a traits(compiles) is used on a function literal call - * we need to take into account if the body of the function - * violates any attributes, however, we must not affect the - * attribute inference on the outer function. The attributes - * of the function literal still need to be inferred, therefore - * we need a way to check for the scope that the traits compiles - * introduces. - * - * Params: - * sc = scope to be checked for - * - * Returns: `true` if the provided scope is the root - * of the traits compiles list of scopes. - */ -bool isRootTraitsCompilesScope(Scope* sc) -{ - return (sc.flags & SCOPE.compile) && !(sc.func.flags & SCOPE.compile); -} - -/************************************** - * A statement / expression in this scope is not `@safe`, - * so mark the enclosing function as `@system` - * - * Params: - * sc = scope that the unsafe statement / expression is in - * gag = surpress error message (used in escape.d) - * loc = location of error - * fmt = printf-style format string - * arg0 = (optional) argument for first %s format specifier - * arg1 = (optional) argument for second %s format specifier - * arg2 = (optional) argument for third %s format specifier - * Returns: whether there's a safe error - */ -bool setUnsafe(Scope* sc, - bool gag = false, Loc loc = Loc.init, const(char)* fmt = null, - RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) -{ - if (sc.intypeof) - return false; // typeof(cast(int*)0) is safe - - if (sc.flags & SCOPE.debug_) // debug {} scopes are permissive - return false; - - if (!sc.func) - { - if (sc.varDecl) - { - if (sc.varDecl.storage_class & STC.safe) - { - .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); - return true; - } - else if (!(sc.varDecl.storage_class & STC.trusted)) - { - sc.varDecl.storage_class |= STC.system; - sc.varDecl.systemInferred = true; - } - } - return false; - } - - - if (isRootTraitsCompilesScope(sc)) // __traits(compiles, x) - { - if (sc.func.isSafeBypassingInference()) - { - // Message wil be gagged, but still call error() to update global.errors and for - // -verrors=spec - .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); - return true; - } - return false; - } - - return sc.func.setUnsafe(gag, loc, fmt, arg0, arg1, arg2); -} - -/*************************************** - * Like `setUnsafe`, but for safety errors still behind preview switches - * - * Given a `FeatureState fs`, for example dip1000 / dip25 / systemVariables, - * the behavior changes based on the setting: - * - * - In case of `-revert=fs`, it does nothing. - * - In case of `-preview=fs`, it's the same as `setUnsafe` - * - By default, print a deprecation in `@safe` functions, or store an attribute violation in inferred functions. - * - * Params: - * sc = used to find affected function/variable, and for checking whether we are in a deprecated / speculative scope - * fs = feature state from the preview flag - * gag = surpress error message - * loc = location of error - * msg = printf-style format string - * arg0 = (optional) argument for first %s format specifier - * arg1 = (optional) argument for second %s format specifier - * arg2 = (optional) argument for third %s format specifier - * Returns: whether an actual safe error (not deprecation) occured - */ -bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char)* msg, - RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) -{ - //printf("setUnsafePreview() fs:%d %s\n", fs, msg); - with (FeatureState) final switch (fs) - { - case disabled: - return false; - - case enabled: - return sc.setUnsafe(gag, loc, msg, arg0, arg1, arg2); - - case default_: - if (!sc.func) - return false; - if (sc.func.isSafeBypassingInference()) - { - if (!gag && !sc.isDeprecated()) - { - deprecation(loc, msg, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); - } - } - else if (!sc.func.safetyViolation) - { - import dmd.func : AttributeViolation; - sc.func.safetyViolation = new AttributeViolation(loc, msg, arg0, arg1, arg2); - } - return false; - } -} - /// Stores a reason why a function failed to infer a function attribute like `@safe` or `pure` /// /// Has two modes: diff --git a/dmd/funcsem.d b/dmd/funcsem.d index 2cadc401911..e058deb0f94 100644 --- a/dmd/funcsem.d +++ b/dmd/funcsem.d @@ -1891,6 +1891,309 @@ Expression addInvariant(AggregateDeclaration ad, VarDeclaration vthis) return e; } +/******************************************** + * Find function in overload list that exactly matches t. + */ +FuncDeclaration overloadExactMatch(FuncDeclaration thisfd, Type t) +{ + FuncDeclaration fd; + overloadApply(thisfd, (Dsymbol s) + { + auto f = s.isFuncDeclaration(); + if (!f) + return 0; + if (f.storage_class & STC.disable) + return 0; + if (t.equals(f.type)) + { + fd = f; + return 1; + } + /* Allow covariant matches, as long as the return type + * is just a const conversion. + * This allows things like pure functions to match with an impure function type. + */ + if (t.ty == Tfunction) + { + auto tf = cast(TypeFunction)f.type; + if (tf.covariant(t) == Covariant.yes && + tf.nextOf().implicitConvTo(t.nextOf()) >= MATCH.constant) + { + fd = f; + return 1; + } + } + return 0; + }); + return fd; +} + +/******************************************** + * Find function in overload list that matches to the 'this' modifier. + * There's four result types. + * + * 1. If the 'tthis' matches only one candidate, it's an "exact match". + * Returns the function and 'hasOverloads' is set to false. + * eg. If 'tthis" is mutable and there's only one mutable method. + * 2. If there's two or more match candidates, but a candidate function will be + * a "better match". + * Returns the better match function but 'hasOverloads' is set to true. + * eg. If 'tthis' is mutable, and there's both mutable and const methods, + * the mutable method will be a better match. + * 3. If there's two or more match candidates, but there's no better match, + * Returns null and 'hasOverloads' is set to true to represent "ambiguous match". + * eg. If 'tthis' is mutable, and there's two or more mutable methods. + * 4. If there's no candidates, it's "no match" and returns null with error report. + * e.g. If 'tthis' is const but there's no const methods. + */ +FuncDeclaration overloadModMatch(FuncDeclaration thisfd, const ref Loc loc, Type tthis, ref bool hasOverloads) +{ + //printf("FuncDeclaration::overloadModMatch('%s')\n", toChars()); + MatchAccumulator m; + overloadApply(thisfd, (Dsymbol s) + { + auto f = s.isFuncDeclaration(); + if (!f || f == m.lastf) // skip duplicates + return 0; + auto tf = f.type.toTypeFunction(); + //printf("tf = %s\n", tf.toChars()); + MATCH match; + if (tthis) // non-static functions are preferred than static ones + { + if (f.needThis()) + match = f.isCtorDeclaration() ? MATCH.exact : MODmethodConv(tthis.mod, tf.mod); + else + match = MATCH.constant; // keep static function in overload candidates + } + else // static functions are preferred than non-static ones + { + if (f.needThis()) + match = MATCH.convert; + else + match = MATCH.exact; + } + if (match == MATCH.nomatch) + return 0; + if (match > m.last) goto LcurrIsBetter; + if (match < m.last) goto LlastIsBetter; + // See if one of the matches overrides the other. + if (m.lastf.overrides(f)) goto LlastIsBetter; + if (f.overrides(m.lastf)) goto LcurrIsBetter; + //printf("\tambiguous\n"); + m.nextf = f; + m.count++; + return 0; + LlastIsBetter: + //printf("\tlastbetter\n"); + m.count++; // count up + return 0; + LcurrIsBetter: + //printf("\tisbetter\n"); + if (m.last <= MATCH.convert) + { + // clear last secondary matching + m.nextf = null; + m.count = 0; + } + m.last = match; + m.lastf = f; + m.count++; // count up + return 0; + }); + if (m.count == 1) // exact match + { + hasOverloads = false; + } + else if (m.count > 1) // better or ambiguous match + { + hasOverloads = true; + } + else // no match + { + hasOverloads = true; + auto tf = thisfd.type.toTypeFunction(); + assert(tthis); + assert(!MODimplicitConv(tthis.mod, tf.mod)); // modifier mismatch + { + OutBuffer thisBuf, funcBuf; + MODMatchToBuffer(&thisBuf, tthis.mod, tf.mod); + MODMatchToBuffer(&funcBuf, tf.mod, tthis.mod); + .error(loc, "%smethod %s is not callable using a %sobject", thisfd.kind, thisfd.toPrettyChars, + funcBuf.peekChars(), thisfd.toPrettyChars(), thisBuf.peekChars()); + } + } + return m.lastf; +} + +/*********************************** + * Determine lexical level difference from `fd` to nested function `target`. + * Issue error if `fd` cannot call `target`. + * + * Params: + * fd = function + * loc = location for error messages + * sc = context + * target = target of call + * decl = The `Declaration` that triggered this check. + * Used to provide a better error message only. + * Returns: + * 0 same level + * >0 decrease nesting by number + * -1 increase nesting by 1 (`target` is nested within 'fd') + * LevelError error + */ +int getLevelAndCheck(FuncDeclaration fd, const ref Loc loc, Scope* sc, FuncDeclaration target, + Declaration decl) +{ + int level = fd.getLevel(target, sc.intypeof); + if (level != fd.LevelError) + return level; + // Don't give error if in template constraint + if (!(sc.flags & SCOPE.constraint)) + { + const(char)* xstatic = fd.isStatic() ? "`static` " : ""; + // better diagnostics for static functions + .error(loc, "%s%s `%s` cannot access %s `%s` in frame of function `%s`", + xstatic, fd.kind(), fd.toPrettyChars(), decl.kind(), decl.toChars(), + target.toPrettyChars()); + .errorSupplemental(decl.loc, "`%s` declared here", decl.toChars()); + return fd.LevelError; + } + return 1; +} + +/********************************** + * Decide if attributes for this function can be inferred from examining + * the function body. + * Returns: + * true if can + */ +bool canInferAttributes(FuncDeclaration fd, Scope* sc) +{ + if (!fd.fbody) + return false; + if (fd.isVirtualMethod() && + /* + * https://issues.dlang.org/show_bug.cgi?id=21719 + * + * If we have an auto virtual function we can infer + * the attributes. + */ + !(fd.inferRetType && !fd.isCtorDeclaration())) + return false; // since they may be overridden + if (sc.func && + /********** this is for backwards compatibility for the moment ********/ + (!fd.isMember() || sc.func.isSafeBypassingInference() && !fd.isInstantiated())) + return true; + if (fd.isFuncLiteralDeclaration() || // externs are not possible with literals + (fd.storage_class & STC.inference) || // do attribute inference + (fd.inferRetType && !fd.isCtorDeclaration())) + return true; + if (fd.isInstantiated()) + { + auto ti = fd.parent.isTemplateInstance(); + if (ti is null || ti.isTemplateMixin() || ti.tempdecl.ident == fd.ident) + return true; + } + return false; +} + +/********************************************* + * In the current function, we are calling 'this' function. + * 1. Check to see if the current function can call 'this' function, issue error if not. + * 2. If the current function is not the parent of 'this' function, then add + * the current function to the list of siblings of 'this' function. + * 3. If the current function is a literal, and it's accessing an uplevel scope, + * then mark it as a delegate. + * Returns true if error occurs. + */ +bool checkNestedReference(FuncDeclaration fd, Scope* sc, const ref Loc loc) +{ + //printf("FuncDeclaration::checkNestedReference() %s\n", toPrettyChars()); + if (auto fld = fd.isFuncLiteralDeclaration()) + { + if (fld.tok == TOK.reserved) + { + fld.tok = TOK.function_; + fld.vthis = null; + } + } + if (!fd.parent || fd.parent == sc.parent) + return false; + if (fd.ident == Id.require || fd.ident == Id.ensure) + return false; + if (!fd.isThis() && !fd.isNested()) + return false; + // The current function + FuncDeclaration fdthis = sc.parent.isFuncDeclaration(); + if (!fdthis) + return false; // out of function scope + Dsymbol p = fd.toParentLocal(); + Dsymbol p2 = fd.toParent2(); + // Function literals from fdthis to p must be delegates + ensureStaticLinkTo(fdthis, p); + if (p != p2) + ensureStaticLinkTo(fdthis, p2); + if (fd.isNested()) + { + // The function that this function is in + bool checkEnclosing(FuncDeclaration fdv) + { + if (!fdv) + return false; + if (fdv == fdthis) + return false; + //printf("this = %s in [%s]\n", this.toChars(), this.loc.toChars()); + //printf("fdv = %s in [%s]\n", fdv .toChars(), fdv .loc.toChars()); + //printf("fdthis = %s in [%s]\n", fdthis.toChars(), fdthis.loc.toChars()); + // Add this function to the list of those which called us + if (fdthis != fd) + { + bool found = false; + for (size_t i = 0; i < fd.siblingCallers.length; ++i) + { + if (fd.siblingCallers[i] == fdthis) + found = true; + } + if (!found) + { + //printf("\tadding sibling %s to %s\n", fdthis.toPrettyChars(), toPrettyChars()); + if (!sc.intypeof && !(sc.flags & SCOPE.compile)) + { + fd.siblingCallers.push(fdthis); + fd.computedEscapingSiblings = false; + } + } + } + const lv = fdthis.getLevelAndCheck(loc, sc, fdv, fd); + if (lv == fd.LevelError) + return true; // error + if (lv == -1) + return false; // downlevel call + if (lv == 0) + return false; // same level call + return false; // Uplevel call + } + if (checkEnclosing(p.isFuncDeclaration())) + return true; + if (checkEnclosing(p == p2 ? null : p2.isFuncDeclaration())) + return true; + } + return false; +} + +/**************************************************** + * Check whether result variable can be built. + * Returns: + * `true` if the function has a return type that + * is different from `void`. + */ +private bool canBuildResultVar(FuncDeclaration fd) +{ + auto f = cast(TypeFunction)fd.type; + return f && f.nextOf() && f.nextOf().toBasetype().ty != Tvoid; +} + /**************************************************** * Declare result variable lazily. */ @@ -1923,3 +2226,512 @@ void buildResultVar(FuncDeclaration fd, Scope* sc, Type tret) assert(fd.vresult.parent == fd); } } + +/**************************************************** + * Merge into this function the 'in' contracts of all it overrides. + * 'in's are OR'd together, i.e. only one of them needs to pass. + */ +Statement mergeFrequire(FuncDeclaration fd, Statement sf, Expressions* params) +{ + /* If a base function and its override both have an IN contract, then + * only one of them needs to succeed. This is done by generating: + * + * void derived.in() { + * try { + * base.in(); + * } + * catch () { + * ... body of derived.in() ... + * } + * } + * + * So if base.in() doesn't throw, derived.in() need not be executed, and the contract is valid. + * If base.in() throws, then derived.in()'s body is executed. + */ + foreach (fdv; fd.foverrides) + { + /* The semantic pass on the contracts of the overridden functions must + * be completed before code generation occurs. + * https://issues.dlang.org/show_bug.cgi?id=3602 + */ + if (fdv.frequires && fdv.semanticRun != PASS.semantic3done) + { + assert(fdv._scope); + Scope* sc = fdv._scope.push(); + sc.stc &= ~STC.override_; + fdv.semantic3(sc); + sc.pop(); + } + sf = fdv.mergeFrequire(sf, params); + if (!sf || !fdv.fdrequire) + return null; + //printf("fdv.frequire: %s\n", fdv.frequire.toChars()); + /* Make the call: + * try { __require(params); } + * catch (Throwable) { frequire; } + */ + params = Expression.arraySyntaxCopy(params); + Expression e = new CallExp(fd.loc, new VarExp(fd.loc, fdv.fdrequire, false), params); + Statement s2 = new ExpStatement(fd.loc, e); + auto c = new Catch(fd.loc, getThrowable(), null, sf); + c.internalCatch = true; + auto catches = new Catches(); + catches.push(c); + sf = new TryCatchStatement(fd.loc, s2, catches); + } + return sf; +} + +/**************************************************** + * Merge into this function the 'in' contracts of all it overrides. + */ +Statement mergeFrequireInclusivePreview(FuncDeclaration fd, Statement sf, Expressions* params) +{ + /* If a base function and its override both have an IN contract, then + * the override in contract must widen the guarantee of the base contract. + * This is checked by generating: + * + * void derived.in() { + * try { + * ... body of derived.in() ... + * } + * catch () { + * // derived in rejected this argument. so parent must also reject it, or we've tightened the contract. + * base.in(); + * assert(false, "Logic error: " ~ thr.msg); + * } + * } + */ + foreach (fdv; fd.foverrides) + { + /* The semantic pass on the contracts of the overridden functions must + * be completed before code generation occurs. + * https://issues.dlang.org/show_bug.cgi?id=3602 + */ + if (fdv.frequires && fdv.semanticRun != PASS.semantic3done) + { + assert(fdv._scope); + Scope* sc = fdv._scope.push(); + sc.stc &= ~STC.override_; + fdv.semantic3(sc); + sc.pop(); + } + sf = fdv.mergeFrequireInclusivePreview(sf, params); + if (sf && fdv.fdrequire) + { + const loc = fd.fdrequire.loc; + //printf("fdv.frequire: %s\n", fdv.frequire.toChars()); + /* Make the call: + * try { frequire; } + * catch (Throwable thr) { __require(params); assert(false, "Logic error: " ~ thr.msg); } + */ + Identifier id = Identifier.generateId("thr"); + params = Expression.arraySyntaxCopy(params); + Expression e = new CallExp(loc, new VarExp(loc, fdv.fdrequire, false), params); + Statement s2 = new ExpStatement(loc, e); + // assert(false, ...) + // TODO make this a runtime helper to allow: + // - chaining the original expression + // - nogc concatenation + Expression msg = new StringExp(loc, "Logic error: in-contract was tighter than parent in-contract"); + Statement fail = new ExpStatement(loc, new AssertExp(loc, IntegerExp.literal!0, msg)); + Statement s3 = new CompoundStatement(loc, s2, fail); + auto c = new Catch(loc, getThrowable(), id, s3); + c.internalCatch = true; + auto catches = new Catches(); + catches.push(c); + sf = new TryCatchStatement(loc, sf, catches); + } + else + return null; + } + return sf; +} + +/**************************************************** + * Rewrite contracts as statements. + */ +void buildEnsureRequire(FuncDeclaration thisfd) +{ + if (thisfd.frequires) + { + /* in { statements1... } + * in { statements2... } + * ... + * becomes: + * in { { statements1... } { statements2... } ... } + */ + assert(thisfd.frequires.length); + auto loc = (*thisfd.frequires)[0].loc; + auto s = new Statements; + foreach (r; *thisfd.frequires) + { + s.push(new ScopeStatement(r.loc, r, r.loc)); + } + thisfd.frequire = new CompoundStatement(loc, s); + } + if (thisfd.fensures) + { + /* out(id1) { statements1... } + * out(id2) { statements2... } + * ... + * becomes: + * out(__result) { { ref id1 = __result; { statements1... } } + * { ref id2 = __result; { statements2... } } ... } + */ + assert(thisfd.fensures.length); + auto loc = (*thisfd.fensures)[0].ensure.loc; + auto s = new Statements; + foreach (r; *thisfd.fensures) + { + if (r.id && thisfd.canBuildResultVar()) + { + auto rloc = r.ensure.loc; + auto resultId = new IdentifierExp(rloc, Id.result); + auto init = new ExpInitializer(rloc, resultId); + auto stc = STC.ref_ | STC.temp | STC.result; + auto decl = new VarDeclaration(rloc, null, r.id, init, stc); + auto sdecl = new ExpStatement(rloc, decl); + s.push(new ScopeStatement(rloc, new CompoundStatement(rloc, sdecl, r.ensure), rloc)); + } + else + { + s.push(r.ensure); + } + } + thisfd.fensure = new CompoundStatement(loc, s); + } + if (!thisfd.isVirtual()) + return; + /* Rewrite contracts as nested functions, then call them. Doing it as nested + * functions means that overriding functions can call them. + */ + TypeFunction f = cast(TypeFunction) thisfd.type; + /* Make a copy of the parameters and make them all ref */ + static Parameters* toRefCopy(ParameterList parameterList) + { + auto result = new Parameters(); + foreach (n, p; parameterList) + { + p = p.syntaxCopy(); + if (!p.isLazy()) + p.storageClass = (p.storageClass | STC.ref_) & ~STC.out_; + p.defaultArg = null; // won't be the same with ref + result.push(p); + } + return result; + } + if (thisfd.frequire) + { + /* in { ... } + * becomes: + * void __require(ref params) { ... } + * __require(params); + */ + Loc loc = thisfd.frequire.loc; + thisfd.fdrequireParams = new Expressions(); + if (thisfd.parameters) + { + foreach (vd; *thisfd.parameters) + thisfd.fdrequireParams.push(new VarExp(loc, vd)); + } + auto fo = cast(TypeFunction)(thisfd.originalType ? thisfd.originalType : f); + auto fparams = toRefCopy(fo.parameterList); + auto tf = new TypeFunction(ParameterList(fparams), Type.tvoid, LINK.d); + tf.isnothrow = f.isnothrow; + tf.isnogc = f.isnogc; + tf.purity = f.purity; + tf.trust = f.trust; + auto fd = new FuncDeclaration(loc, loc, Id.require, STC.undefined_, tf); + fd.fbody = thisfd.frequire; + Statement s1 = new ExpStatement(loc, fd); + Expression e = new CallExp(loc, new VarExp(loc, fd, false), thisfd.fdrequireParams); + Statement s2 = new ExpStatement(loc, e); + thisfd.frequire = new CompoundStatement(loc, s1, s2); + thisfd.fdrequire = fd; + } + /* We need to set fdensureParams here and not in the block below to + * have the parameters available when calling a base class ensure(), + * even if this function doesn't have an out contract. + */ + thisfd.fdensureParams = new Expressions(); + if (thisfd.canBuildResultVar()) + thisfd.fdensureParams.push(new IdentifierExp(thisfd.loc, Id.result)); + if (thisfd.parameters) + { + foreach (vd; *thisfd.parameters) + thisfd.fdensureParams.push(new VarExp(thisfd.loc, vd)); + } + if (thisfd.fensure) + { + /* out (result) { ... } + * becomes: + * void __ensure(ref tret result, ref params) { ... } + * __ensure(result, params); + */ + Loc loc = thisfd.fensure.loc; + auto fparams = new Parameters(); + if (thisfd.canBuildResultVar()) + { + Parameter p = new Parameter(loc, STC.ref_ | STC.const_, f.nextOf(), Id.result, null, null); + fparams.push(p); + } + auto fo = cast(TypeFunction)(thisfd.originalType ? thisfd.originalType : f); + fparams.pushSlice((*toRefCopy(fo.parameterList))[]); + auto tf = new TypeFunction(ParameterList(fparams), Type.tvoid, LINK.d); + tf.isnothrow = f.isnothrow; + tf.isnogc = f.isnogc; + tf.purity = f.purity; + tf.trust = f.trust; + auto fd = new FuncDeclaration(loc, loc, Id.ensure, STC.undefined_, tf); + fd.fbody = thisfd.fensure; + Statement s1 = new ExpStatement(loc, fd); + Expression e = new CallExp(loc, new VarExp(loc, fd, false), thisfd.fdensureParams); + Statement s2 = new ExpStatement(loc, e); + thisfd.fensure = new CompoundStatement(loc, s1, s2); + thisfd.fdensure = fd; + } +} + +/**************************************************** + * Merge into this function the 'out' contracts of all it overrides. + * 'out's are AND'd together, i.e. all of them need to pass. + */ +Statement mergeFensure(FuncDeclaration fd, Statement sf, Identifier oid, Expressions* params) +{ + /* Same comments as for mergeFrequire(), except that we take care + * of generating a consistent reference to the 'result' local by + * explicitly passing 'result' to the nested function as a reference + * argument. + * This won't work for the 'this' parameter as it would require changing + * the semantic code for the nested function so that it looks on the parameter + * list for the 'this' pointer, something that would need an unknown amount + * of tweaking of various parts of the compiler that I'd rather leave alone. + */ + foreach (fdv; fd.foverrides) + { + /* The semantic pass on the contracts of the overridden functions must + * be completed before code generation occurs. + * https://issues.dlang.org/show_bug.cgi?id=3602 and + * https://issues.dlang.org/show_bug.cgi?id=5230 + */ + if (fd.needsFensure(fdv) && fdv.semanticRun != PASS.semantic3done) + { + assert(fdv._scope); + Scope* sc = fdv._scope.push(); + sc.stc &= ~STC.override_; + fdv.semantic3(sc); + sc.pop(); + } + sf = fdv.mergeFensure(sf, oid, params); + if (fdv.fdensure) + { + //printf("fdv.fensure: %s\n", fdv.fensure.toChars()); + // Make the call: __ensure(result, params) + params = Expression.arraySyntaxCopy(params); + if (fd.canBuildResultVar()) + { + Type t1 = fdv.type.nextOf().toBasetype(); + Type t2 = fd.type.nextOf().toBasetype(); + if (t1.isBaseOf(t2, null)) + { + /* Making temporary reference variable is necessary + * in covariant return. + * https://issues.dlang.org/show_bug.cgi?id=5204 + * https://issues.dlang.org/show_bug.cgi?id=10479 + */ + Expression* eresult = &(*params)[0]; + auto ei = new ExpInitializer(Loc.initial, *eresult); + auto v = new VarDeclaration(Loc.initial, t1, Identifier.generateId("__covres"), ei); + v.storage_class |= STC.temp; + auto de = new DeclarationExp(Loc.initial, v); + auto ve = new VarExp(Loc.initial, v); + *eresult = new CommaExp(Loc.initial, de, ve); + } + } + Expression e = new CallExp(fd.loc, new VarExp(fd.loc, fdv.fdensure, false), params); + Statement s2 = new ExpStatement(fd.loc, e); + if (sf) + { + sf = new CompoundStatement(sf.loc, s2, sf); + } + else + sf = s2; + } + } + return sf; +} + +/******************************* + * Modify all expression type of return statements to tret. + * + * On function literals, return type may be modified based on the context type + * after its semantic3 is done, in FuncExp::implicitCastTo. + * + * A function() dg = (){ return new B(); } // OK if is(B : A) == true + * + * If B to A conversion is convariant that requires offseet adjusting, + * all return statements should be adjusted to return expressions typed A. + */ +void modifyReturns(FuncLiteralDeclaration fld, Scope* sc, Type tret) +{ + import dmd.statement_rewrite_walker; + extern (C++) final class RetWalker : StatementRewriteWalker + { + alias visit = typeof(super).visit; + public: + Scope* sc; + Type tret; + FuncLiteralDeclaration fld; + override void visit(ReturnStatement s) + { + Expression exp = s.exp; + if (exp && !exp.type.equals(tret)) + s.exp = exp.implicitCastTo(sc, tret); + } + } + if (fld.semanticRun < PASS.semantic3done) + return; + if (fld.fes) + return; + scope RetWalker w = new RetWalker(); + w.sc = sc; + w.tret = tret; + w.fld = fld; + fld.fbody.accept(w); + // Also update the inferred function type to match the new return type. + // This is required so the code generator does not try to cast the + // modified returns back to the original type. + if (fld.inferRetType && fld.type.nextOf() != tret) + fld.type.toTypeFunction().next = tret; +} + +/************************************** + * When a traits(compiles) is used on a function literal call + * we need to take into account if the body of the function + * violates any attributes, however, we must not affect the + * attribute inference on the outer function. The attributes + * of the function literal still need to be inferred, therefore + * we need a way to check for the scope that the traits compiles + * introduces. + * + * Params: + * sc = scope to be checked for + * + * Returns: `true` if the provided scope is the root + * of the traits compiles list of scopes. + */ +bool isRootTraitsCompilesScope(Scope* sc) +{ + return (sc.flags & SCOPE.compile) && !(sc.func.flags & SCOPE.compile); +} + +/************************************** + * A statement / expression in this scope is not `@safe`, + * so mark the enclosing function as `@system` + * + * Params: + * sc = scope that the unsafe statement / expression is in + * gag = surpress error message (used in escape.d) + * loc = location of error + * fmt = printf-style format string + * arg0 = (optional) argument for first %s format specifier + * arg1 = (optional) argument for second %s format specifier + * arg2 = (optional) argument for third %s format specifier + * Returns: whether there's a safe error + */ +bool setUnsafe(Scope* sc, + bool gag = false, Loc loc = Loc.init, const(char)* fmt = null, + RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) +{ + if (sc.intypeof) + return false; // typeof(cast(int*)0) is safe + + if (sc.flags & SCOPE.debug_) // debug {} scopes are permissive + return false; + + if (!sc.func) + { + if (sc.varDecl) + { + if (sc.varDecl.storage_class & STC.safe) + { + .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); + return true; + } + else if (!(sc.varDecl.storage_class & STC.trusted)) + { + sc.varDecl.storage_class |= STC.system; + sc.varDecl.systemInferred = true; + } + } + return false; + } + + + if (isRootTraitsCompilesScope(sc)) // __traits(compiles, x) + { + if (sc.func.isSafeBypassingInference()) + { + // Message wil be gagged, but still call error() to update global.errors and for + // -verrors=spec + .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); + return true; + } + return false; + } + + return sc.func.setUnsafe(gag, loc, fmt, arg0, arg1, arg2); +} + +/*************************************** + * Like `setUnsafe`, but for safety errors still behind preview switches + * + * Given a `FeatureState fs`, for example dip1000 / dip25 / systemVariables, + * the behavior changes based on the setting: + * + * - In case of `-revert=fs`, it does nothing. + * - In case of `-preview=fs`, it's the same as `setUnsafe` + * - By default, print a deprecation in `@safe` functions, or store an attribute violation in inferred functions. + * + * Params: + * sc = used to find affected function/variable, and for checking whether we are in a deprecated / speculative scope + * fs = feature state from the preview flag + * gag = surpress error message + * loc = location of error + * msg = printf-style format string + * arg0 = (optional) argument for first %s format specifier + * arg1 = (optional) argument for second %s format specifier + * arg2 = (optional) argument for third %s format specifier + * Returns: whether an actual safe error (not deprecation) occured + */ +bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char)* msg, + RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) +{ + //printf("setUnsafePreview() fs:%d %s\n", fs, msg); + with (FeatureState) final switch (fs) + { + case disabled: + return false; + + case enabled: + return sc.setUnsafe(gag, loc, msg, arg0, arg1, arg2); + + case default_: + if (!sc.func) + return false; + if (sc.func.isSafeBypassingInference()) + { + if (!gag && !sc.isDeprecated()) + { + deprecation(loc, msg, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); + } + } + else if (!sc.func.safetyViolation) + { + import dmd.func : AttributeViolation; + sc.func.safetyViolation = new AttributeViolation(loc, msg, arg0, arg1, arg2); + } + return false; + } +} diff --git a/dmd/opover.d b/dmd/opover.d index 70eeaff7f47..0d32d7d9713 100644 --- a/dmd/opover.d +++ b/dmd/opover.d @@ -29,6 +29,7 @@ import dmd.errors; import dmd.expression; import dmd.expressionsem; import dmd.func; +import dmd.funcsem; import dmd.globals; import dmd.hdrgen; import dmd.id; diff --git a/dmd/safe.d b/dmd/safe.d index 1e5fb47a3dc..741f979184e 100644 --- a/dmd/safe.d +++ b/dmd/safe.d @@ -27,7 +27,7 @@ import dmd.mtype; import dmd.target; import dmd.tokens; import dmd.typesem : hasPointers, arrayOf; -import dmd.func : setUnsafe, setUnsafePreview; +import dmd.funcsem : setUnsafe, setUnsafePreview; /************************************************************* * Check for unsafe access in @safe code: diff --git a/dmd/semantic2.d b/dmd/semantic2.d index f5ce0c0ada6..4bb39028f04 100644 --- a/dmd/semantic2.d +++ b/dmd/semantic2.d @@ -40,6 +40,7 @@ import dmd.escape; import dmd.expression; import dmd.expressionsem; import dmd.func; +import dmd.funcsem; import dmd.globals; import dmd.id; import dmd.identifier; From 6dcca7ecae9f14de2bff312f7e7da03fd66f4c0f Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 3 Mar 2024 14:31:02 +0100 Subject: [PATCH 013/125] GHA Windows: Make sure building compiler/druntime/Phobos does NOT need an MSVC env anymore After dlang/dmd!16248. --- runtime/druntime/Makefile | 4 ---- 1 file changed, 4 deletions(-) diff --git a/runtime/druntime/Makefile b/runtime/druntime/Makefile index febb25a9f62..5aac2b73ef8 100644 --- a/runtime/druntime/Makefile +++ b/runtime/druntime/Makefile @@ -374,10 +374,6 @@ $(abspath ../generated/$(OS)/$(BUILD)/$(MODEL)/dmd$(DOTEXE)): ../generated/$(OS) ################### C/ASM Targets ############################ -# Although dmd is compiling the .c files, the preprocessor used on Windows is cl.exe. -# Until https://issues.dlang.org/show_bug.cgi?id=24111 is fixed, the INCLUDE -# enviroment variable needs to be set with the path to the VC system include files. - OBJS:=$(ROOT)/errno_c$(DOTOBJ) ifeq (32omf,$(MODEL)) # minit.asm is only used for -m32omf on Windows; there's a pre-built minit.obj From 333f7e266065c5e3be97aef5b3a1e2ec695ebeac Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sun, 3 Mar 2024 16:47:36 -0800 Subject: [PATCH 014/125] revamp File.read() --- dmd/file_manager.d | 11 +++--- dmd/main.d | 14 ++++---- dmd/root/file.d | 86 +++++++++++++++++++--------------------------- dmd/utils.d | 13 +++---- dmd/vsoptions.d | 30 ++++++++-------- 5 files changed, 68 insertions(+), 86 deletions(-) diff --git a/dmd/file_manager.d b/dmd/file_manager.d index c696a5c240f..407b1b2ce47 100644 --- a/dmd/file_manager.d +++ b/dmd/file_manager.d @@ -11,6 +11,7 @@ module dmd.file_manager; import core.stdc.stdio; +import dmd.common.outbuffer; import dmd.root.stringtable : StringTable; import dmd.root.file : File, Buffer; import dmd.root.filename : FileName, isDirSeparator; @@ -273,11 +274,13 @@ nothrow: if (FileName.exists(name) != 1) // if not an ordinary file return null; - auto readResult = File.read(name); - if (!readResult.success) - return null; + OutBuffer buf; + if (File.read(name, buf)) + return null; // failed + buf.write32(0); // terminating dchar 0 - const ubyte[] fb = readResult.extractSlice(); + const length = buf.length; + const ubyte[] fb = cast(ubyte[])(buf.extractSlice()[0 .. length - 4]); if (files.insert(name, fb) is null) assert(0, "Insert after lookup failure should never return `null`"); diff --git a/dmd/main.d b/dmd/main.d index dc5e432a4ed..c9f804b946f 100644 --- a/dmd/main.d +++ b/dmd/main.d @@ -355,12 +355,10 @@ private int tryMain(size_t argc, const(char)** argv, ref Param params) { foreach (file; ddocfiles) { - Buffer buffer; - if (readFile(loc, file.toDString(), buffer)) + if (readFile(loc, file.toDString(), ddocbuf)) fatal(); // BUG: convert file contents to UTF-8 before use //printf("file: '%.*s'\n", cast(int)buffer.data.length, buffer.data.ptr); - ddocbuf.write(buffer.data); } ddocbufIsRead = true; } @@ -722,8 +720,10 @@ bool parseCommandlineAndConfig(size_t argc, const(char)** argv, ref Param params global.inifilename = findConfFile(params.argv0, iniName); } // Read the configuration file - const iniReadResult = File.read(global.inifilename); - const inifileBuffer = iniReadResult.buffer.data; + OutBuffer inifileBuffer; + File.read(global.inifilename, inifileBuffer); + inifileBuffer.writeByte(0); // ensure sentinel + /* Need path of configuration file, for use in expanding @P macro */ const(char)[] inifilepath = FileName.path(global.inifilename); @@ -734,7 +734,7 @@ bool parseCommandlineAndConfig(size_t argc, const(char)** argv, ref Param params * pick up any DFLAGS settings. */ sections.push("Environment"); - if (parseConfFile(environment, global.inifilename, inifilepath, inifileBuffer, §ions)) + if (parseConfFile(environment, global.inifilename, inifilepath, cast(ubyte[])inifileBuffer[], §ions)) return true; const(char)[] arch = target.isX86_64 ? "64" : "32"; // use default @@ -758,7 +758,7 @@ bool parseCommandlineAndConfig(size_t argc, const(char)** argv, ref Param params char[80] envsection = void; snprintf(envsection.ptr, envsection.length, "Environment%.*s", cast(int) arch.length, arch.ptr); sections.push(envsection.ptr); - if (parseConfFile(environment, global.inifilename, inifilepath, inifileBuffer, §ions)) + if (parseConfFile(environment, global.inifilename, inifilepath, cast(ubyte[])inifileBuffer[], §ions)) return true; getenv_setargv(readFromEnv(environment, "DFLAGS"), &arguments); updateRealEnvironment(environment); diff --git a/dmd/root/file.d b/dmd/root/file.d index a4362e17178..034dba61b13 100644 --- a/dmd/root/file.d +++ b/dmd/root/file.d @@ -19,11 +19,13 @@ import core.sys.posix.fcntl; import core.sys.posix.unistd; import core.sys.windows.winbase; import core.sys.windows.winnt; + import dmd.root.filename; import dmd.root.rmem; import dmd.root.string; import dmd.common.file; +import dmd.common.outbuffer; import dmd.common.smallbuffer; nothrow: @@ -76,62 +78,52 @@ struct File } nothrow: - /// Read the full content of a file. - static ReadResult read(const(char)[] name) + /** Read the full content of a file, and append it to `buffer` + * Params: + * name = name of file + * buffer = file contents appended to it + * Returns: + * false = success, true = failed + */ + static bool read(const char[] name, ref OutBuffer buffer) { - ReadResult result; + enum Success = false; + enum Failure = true; version (Posix) { - size_t size; - stat_t buf; - ssize_t numread; //printf("File::read('%s')\n",name); int fd = name.toCStringThen!(slice => open(slice.ptr, O_RDONLY)); if (fd == -1) { //perror("\topen error"); - return result; + return Failure; } //printf("\tfile opened\n"); - if (fstat(fd, &buf)) + stat_t statbuf; + if (fstat(fd, &statbuf)) { //perror("\tfstat error"); close(fd); - return result; + return Failure; } - size = cast(size_t)buf.st_size; - ubyte* buffer = cast(ubyte*)mem.xmalloc_noscan(size + 4); - numread = .read(fd, buffer, size); + size_t size = cast(size_t)statbuf.st_size; + auto buf = buffer.allocate(size); + ssize_t numread = .read(fd, buf.ptr, size); if (numread != size) { //perror("\tread error"); - goto err2; + close(fd); + return Failure; } if (close(fd) == -1) { //perror("\tclose error"); - goto err; + return Failure; } - // Always store a wchar ^Z past end of buffer so scanner has a - // sentinel, although ^Z got obselete, so fill with two 0s and add - // two more so lexer doesn't read pass the buffer. - buffer[size .. size + 4] = 0; - - result.success = true; - result.buffer.data = buffer[0 .. size]; - return result; - err2: - close(fd); - err: - mem.xfree(buffer); - return result; } else version (Windows) { - DWORD size; - DWORD numread; - // work around Windows file path length limitation // (see documentation for extendedPathThen). HANDLE h = name.extendedPathThen! @@ -143,32 +135,24 @@ nothrow: FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, null)); if (h == INVALID_HANDLE_VALUE) - return result; - size = GetFileSize(h, null); - ubyte* buffer = cast(ubyte*)mem.xmalloc_noscan(size + 4); - if (ReadFile(h, buffer, size, &numread, null) != TRUE) - goto err2; - if (numread != size) - goto err2; + return Failure; + DWORD size = GetFileSize(h, null); + auto buf = buffer.allocate(size); + DWORD numread; + if (ReadFile(h, buf.ptr, size, &numread, null) != TRUE || + numread != size) + { + CloseHandle(h); + return Failure; + } if (!CloseHandle(h)) - goto err; - // Always store a wchar ^Z past end of buffer so scanner has a - // sentinel, although ^Z got obselete, so fill with two 0s and add - // two more so lexer doesn't read pass the buffer. - buffer[size .. size + 4] = 0; - result.success = true; - result.buffer.data = buffer[0 .. size]; - return result; - err2: - CloseHandle(h); - err: - mem.xfree(buffer); - return result; + return Failure; } else { - assert(0); + static assert(0); } + return Success; } /// Write a file, returning `true` on success. diff --git a/dmd/utils.d b/dmd/utils.d index 51d38d2ac3f..9228ba69b1c 100644 --- a/dmd/utils.d +++ b/dmd/utils.d @@ -52,23 +52,18 @@ const(char)* toWinPath(const(char)* src) * Params: * loc = The line number information from where the call originates * filename = Path to file - * buf = set to contents of file + * buf = append contents of file to * Returns: * true on failure */ -bool readFile(Loc loc, const(char)[] filename, out Buffer buf) +bool readFile(Loc loc, const(char)[] filename, ref OutBuffer buf) { - auto result = File.read(filename); - if (result.success) - { - buf.data = result.extractSlice(); - return false; - } - else + if (File.read(filename, buf)) { error(loc, "error reading file `%.*s`", cast(int)filename.length, filename.ptr); return true; } + return false; } diff --git a/dmd/vsoptions.d b/dmd/vsoptions.d index fc05c35138b..8ed775aff76 100644 --- a/dmd/vsoptions.d +++ b/dmd/vsoptions.d @@ -343,21 +343,21 @@ private: if (FileName.exists(defverFile)) { // VS 2017 - auto readResult = File.read(defverFile.toDString); // adds sentinel 0 at end of file - if (readResult.success) - { - auto ver = cast(char*)readResult.buffer.data.ptr; - // trim version number - while (*ver && isspace(*ver)) - ver++; - auto p = ver; - while (*p == '.' || (*p >= '0' && *p <= '9')) - p++; - *p = 0; - - if (ver && *ver) - VCToolsInstallDir = FileName.buildPath(VCInstallDir.toDString, r"Tools\MSVC", ver.toDString).ptr; - } + OutBuffer buf; + if (File.read(defverFile.toDString, buf)) // read file into buf + return; // failed to read the file + + auto ver = cast(char*)buf.extractSlice(true).ptr; + // trim version number + while (*ver && isspace(*ver)) + ver++; + auto p = ver; + while (*p == '.' || (*p >= '0' && *p <= '9')) + p++; + *p = 0; + + if (ver && *ver) + VCToolsInstallDir = FileName.buildPath(VCInstallDir.toDString, r"Tools\MSVC", ver.toDString).ptr; } } } From e47526a9821d7a06dfd67911e29035dd14387cb5 Mon Sep 17 00:00:00 2001 From: RazvanN7 Date: Tue, 5 Mar 2024 12:24:54 +0200 Subject: [PATCH 015/125] Make retrieve_scopes example compile --- .../dub_package/{retrieveScope.d => retrieve_scope.d} | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) rename tests/dmd/dub_package/{retrieveScope.d => retrieve_scope.d} (94%) diff --git a/tests/dmd/dub_package/retrieveScope.d b/tests/dmd/dub_package/retrieve_scope.d similarity index 94% rename from tests/dmd/dub_package/retrieveScope.d rename to tests/dmd/dub_package/retrieve_scope.d index bfd6d5f4614..05dd643e06b 100755 --- a/tests/dmd/dub_package/retrieveScope.d +++ b/tests/dmd/dub_package/retrieve_scope.d @@ -62,11 +62,11 @@ private struct CallbackHelper { int main() { auto dmdParentDir = dirName(dirName(dirName(dirName(__FILE_FULL_PATH__)))); - global.path = new Strings(); - global.path.push((dmdParentDir ~ "/phobos").ptr); - global.path.push((dmdParentDir ~ "/dmd/druntime/import").ptr); + global.path = Strings(); + global.path.push((dirName(dmdParentDir) ~ "/phobos" ~ '\0').ptr); + global.path.push((dmdParentDir ~ "/druntime/import" ~ '\0').ptr); - /* comment for error output in parsing & semantic */ + // comment for error output in parsing & semantic diagnosticHandler = (const ref Loc location, Color headerColor, const(char)* header, @@ -77,7 +77,7 @@ int main() global.gag = 1; initDMD(diagnosticHandler); - Module m = parseModule(__FILE_FULL_PATH__ ~ "/testfiles/correct.d").module_; + Module m = parseModule("testfiles/correct.d").module_; m.importedFrom = m; // m.isRoot() == true CallbackHelper.cursorLoc = Loc(to!string(m.srcfile).ptr, 22, 10); From 9ff06af04f0be16549525f8c1615eb4ea0d5d7c9 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Wed, 6 Mar 2024 02:41:27 -0800 Subject: [PATCH 016/125] another correction for caller destroying args (dlang/dmd!16167) --- dmd/inline.d | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/dmd/inline.d b/dmd/inline.d index d24866c3f01..a95ded38f2b 100644 --- a/dmd/inline.d +++ b/dmd/inline.d @@ -1814,16 +1814,21 @@ private bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool stat * 2. don't inline when the return value has a destructor, as it doesn't * get handled properly */ - if (tf.next && tf.next.ty != Tvoid && - (!(fd.hasReturnExp & 1) || - statementsToo && hasDtor(tf.next)) && - !hdrscan) + if (auto tfnext = tf.next) { - static if (CANINLINE_LOG) + /* for the isTypeSArray() case see https://github.com/dlang/dmd/pull/16145#issuecomment-1932776873 + */ + if (tfnext.ty != Tvoid && + (!(fd.hasReturnExp & 1) || + hasDtor(tfnext) && (statementsToo || tfnext.isTypeSArray())) && + !hdrscan) { - printf("\t3: no %s\n", fd.toChars()); + static if (CANINLINE_LOG) + { + printf("\t3: no %s\n", fd.toChars()); + } + goto Lno; } - goto Lno; } /* https://issues.dlang.org/show_bug.cgi?id=14560 From e3a1091ab46362fbdd7d1d3255fdd896aa4bb06d Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Wed, 6 Mar 2024 22:09:04 -0800 Subject: [PATCH 017/125] use OutBuffer in readFromStdin() --- dmd/file_manager.d | 53 +++++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/dmd/file_manager.d b/dmd/file_manager.d index 407b1b2ce47..23bfe3c539b 100644 --- a/dmd/file_manager.d +++ b/dmd/file_manager.d @@ -16,6 +16,7 @@ import dmd.root.stringtable : StringTable; import dmd.root.file : File, Buffer; import dmd.root.filename : FileName, isDirSeparator; import dmd.root.string : toDString; +import dmd.errors; import dmd.globals; import dmd.identifier; import dmd.location; @@ -262,21 +263,21 @@ nothrow: if (auto val = files.lookup(name)) // if `name` is cached return val.value; // return its contents + OutBuffer buf; if (name == "__stdin.d") // special name for reading from stdin { - const ubyte[] buffer = readFromStdin().extractSlice(); - if (this.files.insert(name, buffer) is null) - // this.files already contains the name - assert(0, "stdin: Insert after lookup failure should never return `null`"); - return buffer; + if (readFromStdin(buf)) + fatal(); } + else + { + if (FileName.exists(name) != 1) // if not an ordinary file + return null; - if (FileName.exists(name) != 1) // if not an ordinary file - return null; + if (File.read(name, buf)) + return null; // failed + } - OutBuffer buf; - if (File.read(name, buf)) - return null; // failed buf.write32(0); // terminating dchar 0 const length = buf.length; @@ -401,44 +402,34 @@ nothrow: } } -private Buffer readFromStdin() nothrow +private bool readFromStdin(ref OutBuffer sink) nothrow { import core.stdc.stdio; import dmd.errors; - import dmd.root.rmem; - enum bufIncrement = 128 * 1024; - size_t pos = 0; - size_t sz = bufIncrement; + enum BufIncrement = 128 * 1024; - ubyte* buffer = null; - for (;;) + for (size_t j; 1; ++j) { - buffer = cast(ubyte*)mem.xrealloc(buffer, sz + 4); // +2 for sentinel and +2 for lexer + char[] buffer = sink.allocate(BufIncrement); // Fill up buffer + size_t filled = 0; do { - assert(sz > pos); - size_t rlen = fread(buffer + pos, 1, sz - pos, stdin); - pos += rlen; + filled += fread(buffer.ptr + filled, 1, buffer.length - filled, stdin); if (ferror(stdin)) { import core.stdc.errno; error(Loc.initial, "cannot read from stdin, errno = %d", errno); - fatal(); + return true; } - if (feof(stdin)) + if (feof(stdin)) // successful completion { - // We're done - assert(pos < sz + 2); - buffer[pos .. pos + 4] = '\0'; - return Buffer(buffer[0 .. pos]); + sink.setsize(j * BufIncrement + filled); + return false; } - } while (pos < sz); - - // Buffer full, expand - sz += bufIncrement; + } while (filled < BufIncrement); } assert(0); From 75f9829be970d959e351f3fe5a67fb9877a6d416 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sat, 9 Mar 2024 18:22:02 -0800 Subject: [PATCH 018/125] push memory allocation up out of FileName.splitPath() (dlang/dmd!16297) --- dmd/frontend.h | 2 +- dmd/main.d | 8 +++----- dmd/root/filename.d | 6 ++---- dmd/root/filename.h | 2 +- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/dmd/frontend.h b/dmd/frontend.h index 923f47506fe..4cbb5452035 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -766,7 +766,7 @@ struct FileName final const char* name() const; static const char* path(const char* str); static const char* combine(const char* path, const char* name); - static Array* splitPath(const char* path); + static void appendSplitPath(const char* path, Array& array); static const char* defaultExt(const char* name, const char* ext); static const char* forceExt(const char* name, const char* ext); static bool equalsExt(const char* name, const char* ext); diff --git a/dmd/main.d b/dmd/main.d index c9f804b946f..a97a60bae0d 100644 --- a/dmd/main.d +++ b/dmd/main.d @@ -318,14 +318,12 @@ private int tryMain(size_t argc, const(char)** argv, ref Param params) static void buildPath(ref Strings imppath, ref Strings result) { + Strings array; foreach (const path; imppath) { - Strings* a = FileName.splitPath(path); - if (a) - { - result.append(a); - } + FileName.appendSplitPath(path, array); } + result.append(&array); } if (params.mixinOut.doOutput) diff --git a/dmd/root/filename.d b/dmd/root/filename.d index 41c2050057d..5cf9a213ee6 100644 --- a/dmd/root/filename.d +++ b/dmd/root/filename.d @@ -452,17 +452,15 @@ nothrow: assert(buildPath("a/", "bb", "ccc") == "a/bb/ccc"); } - // Split a path into an Array of paths - extern (C++) static Strings* splitPath(const(char)* path) + // Split a path and append the results to `array` + extern (C++) static void appendSplitPath(const(char)* path, ref Strings array) { - auto array = new Strings(); int sink(const(char)* p) nothrow { array.push(p); return 0; } splitPath(&sink, path); - return array; } /**** diff --git a/dmd/root/filename.h b/dmd/root/filename.h index 18bf4e54a8f..4e5a5a88371 100644 --- a/dmd/root/filename.h +++ b/dmd/root/filename.h @@ -31,7 +31,7 @@ struct FileName static const char *path(const char *); static const char *combine(const char *path, const char *name); - static Strings *splitPath(const char *path); + static void appendSplitPath(const char *path, Strings& array); static const char *defaultExt(const char *name, const char *ext); static const char *forceExt(const char *name, const char *ext); static bool equalsExt(const char *name, const char *ext); From 0fd68aa3021030976d88fadfb1cd53334f80deab Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sat, 9 Mar 2024 23:49:58 -0800 Subject: [PATCH 019/125] use slices to eliminate call to removeExt() --- dmd/identifier.d | 6 +++--- dmd/mars.d | 28 ++++++++++++---------------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/dmd/identifier.d b/dmd/identifier.d index 8ace310937d..8b40e9d6bbc 100644 --- a/dmd/identifier.d +++ b/dmd/identifier.d @@ -269,12 +269,12 @@ nothrow: /******************************************** * Create an identifier in the string table. */ - static Identifier idPool(const(char)* s, uint len) + static Identifier idPool(scope const(char)* s, uint len) { return idPool(s[0 .. len]); } - extern (D) static Identifier idPool(const(char)[] s, bool isAnonymous = false) + extern (D) static Identifier idPool(scope const(char)[] s, bool isAnonymous = false) { auto sv = stringtable.update(s); auto id = sv.value; @@ -292,7 +292,7 @@ nothrow: * s = string for keyword * value = TOK.xxxx for the keyword */ - extern (D) static void idPool(const(char)[] s, TOK value) + extern (D) static void idPool(scope const(char)[] s, TOK value) { auto sv = stringtable.insert(s, null); assert(sv); diff --git a/dmd/mars.d b/dmd/mars.d index 22e07cf383d..23f595636cd 100644 --- a/dmd/mars.d +++ b/dmd/mars.d @@ -1711,13 +1711,11 @@ Returns: private Module createModule(const(char)* file, ref Strings libmodules, const ref Target target) { - const(char)[] name; version (Windows) { file = toWinPath(file); } - const(char)[] p = file.toDString(); - p = FileName.name(p); // strip path + const(char)[] p = FileName.name(file.toDString()); // strip path const(char)[] ext = FileName.ext(p); if (!ext) { @@ -1795,25 +1793,23 @@ Module createModule(const(char)* file, ref Strings libmodules, const ref Target FileName.equals(ext, c_ext ) || FileName.equals(ext, i_ext )) { - name = FileName.removeExt(p); + // strip off .ext + const(char)[] name = p[0 .. p.length - ext.length - 1]; // -1 for the . if (!name.length || name == ".." || name == ".") { error(Loc.initial, "invalid file name '%s'", file); fatal(); } - } - else - { - error(Loc.initial, "unrecognized file extension %.*s", cast(int)ext.length, ext.ptr); - fatal(); - } - - /* At this point, name is the D source file name stripped of - * its path and extension. - */ - auto id = Identifier.idPool(name); + /* name is the D source file name stripped of + * its path and extension. + */ + auto id = Identifier.idPool(name); - return new Module(file.toDString, id, global.params.ddoc.doOutput, global.params.dihdr.doOutput); + return new Module(file.toDString, id, global.params.ddoc.doOutput, global.params.dihdr.doOutput); + } + error(Loc.initial, "unrecognized file extension %.*s", cast(int)ext.length, ext.ptr); + fatal(); + assert(0); } /** From ce4b592b600792361db005124c3a8a09f734a1ab Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Sat, 10 Feb 2024 21:13:13 -0700 Subject: [PATCH 020/125] replace usages of md5 with blake3 --- dmd/common/blake3.d | 346 ++++++++++++++++++++++++++++++++++++++++++++ dmd/common/md5.d | 287 ------------------------------------ 2 files changed, 346 insertions(+), 287 deletions(-) create mode 100644 dmd/common/blake3.d delete mode 100644 dmd/common/md5.d diff --git a/dmd/common/blake3.d b/dmd/common/blake3.d new file mode 100644 index 00000000000..52696997961 --- /dev/null +++ b/dmd/common/blake3.d @@ -0,0 +1,346 @@ +module dmd.common.blake3; +// based on https://github.com/oconnor663/blake3_reference_impl_c/blob/main/reference_impl.c + +nothrow: + +/** + * Implementation of Blake 3 hash function with streaming disabled + * meaning we hash the whole buffer at once. + * Input is split into 1KB Chunks which could be hashed independently. + * That said, in the compiler I expect almost all inputs will be 1 chunk. + * + * Chunks get split into 64B Blocks which get hashed and then mixed together + * + * Params: + * data = byte array to hash + * Returns: Blake 3 hash of data + **/ +public ubyte[32] blake3(const ubyte[] data) +{ + + + ChunkState state; + CVStack cvStack; + + size_t cursor = 0; + + //greater because if it's == we still need to finalize the last + //chunk + while(data.length - cursor > ChunkLength){ + + const ubyte[] chunk = data[cursor .. cursor + ChunkLength]; + updateChunkStateFull(state, chunk); + + //the chainingValue is now used to build up the merkle tree with other chunks + addChunkToTree(cvStack, state.chainingValue, state.chunkCounter); + + //reset chunk, leaving chunkCounter as is + state.chainingValue = IV; + state.block[] = 0; + state.blocksCompressed = 0; + + cursor += ChunkLength; + } + + //now handle the final chunk which might not be full + + //handle all but last block + while(data.length - cursor > BlockLength){ + uint[16] blockWords = bytesToWords(data[cursor .. cursor + BlockLength]); + with(state) + { + //can't be end since we handle the last block separately below + uint flag = blocksCompressed == 0 ? ChunkStartFlag : 0; + uint[16] compressed = compress(chainingValue, blockWords, 64, //full block + chunkCounter, flag); + chainingValue = compressed[0 .. 8]; + blocksCompressed++; + } + cursor += BlockLength; + } + + //handle last block, which could be the first block too + uint flag = ChunkEndFlag | (state.blocksCompressed == 0 ? ChunkStartFlag : 0); + + //cast is safe bc this must be <= BlockLength + const remainingBytes = cast(uint)(data.length - cursor); + ubyte[BlockLength] lastBlock = 0; + lastBlock[0 .. remainingBytes] = data[cursor .. $]; + + uint[16] lastBlockWords = bytesToWords(lastBlock); + + uint[16] compressed = compress(state.chainingValue, lastBlockWords, remainingBytes, + state.chunkCounter, + flag | (state.chunkCounter == 0 ? RootFlag : 0)); + + + + //merge all the remaining parent chunks in the tree + uint[8] cv = compressed[0 .. 8]; + while (!cvStack.empty) + { + const leftSib = cvStack.pop(); + uint[16] blockWords; + blockWords[0 .. 8] = leftSib[]; + blockWords[8 .. $] = cv[]; + cv[] = compress(IV, blockWords, 64, 0, + ParentFlag | (cvStack.empty ? RootFlag : 0))[0 .. 8]; + } + + //finally finalize the root chunk + //convert words to bytes, little endian + ubyte[32] ret; + foreach (i, word; cv) + { + ret[i*4] = word & 0xFF; + ret[i*4 + 1] = (word >> 8) & 0xFF; + ret[i*4 + 2] = (word >> 16) & 0xFF; + ret[i*4 + 3] = (word >> 24) & 0xFF; + } + return ret; +} + +private: + +const static uint[8] IV = [0x6a09e667 ,0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19]; + +const static uint[16] permutation = [2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8]; + +enum BlockLength = 64; +enum ChunkLength = 1024; +enum ChunkStartFlag = 1; +enum ChunkEndFlag = 2; +enum ParentFlag = 4; +enum RootFlag = 8; +enum HashLength = 32; + + +struct ChunkState +{ + //initialized for a non-keyed hash + //todo reorder for alignment/caching? + uint[8] chainingValue = IV; + ulong chunkCounter = 0; + ubyte[32] block = 0; //up to 32 bytes + ubyte blocksCompressed = 0; + +} + +uint rotateRight(uint x, uint n) +{ + return (x >> n ) | ( x << (32 - n)); +} + + +//round function +//a,b,c,d are elements of the state, m1, m2 are 2 words of the message +void g(ref uint a, ref uint b, ref uint c, ref uint d, uint m1, uint m2) +{ + a = a + b + m1; + d = rotateRight(d ^ a, 16); + c = c + d; + b = rotateRight(b ^ c, 12); + a = a + b + m2; + d = rotateRight(d ^ a, 8); + c = c + d; + b = rotateRight(b ^ c, 7); +} + +void roundFunction(ref uint[16] state, const ref uint[16] message) +{ + //columns of 4x4 state matrix + g(state[0], state[4], state[8], state[12], message[0], message[1]); + g(state[1], state[5], state[9], state[13], message[2], message[3]); + g(state[2], state[6], state[10], state[14], message[4], message[5]); + g(state[3], state[7], state[11], state[15], message[6], message[7]); + + //diagonals + g(state[0], state[5], state[10], state[15], message[8], message[9]); + g(state[1], state[6], state[11], state[12], message[10], message[11]); + g(state[2], state[7], state[8], state[13], message[12], message[13]); + g(state[3], state[4], state[9], state[14], message[14], message[15]); +} + +void permute(ref uint[16] block){ + uint[16] permuted; + foreach (i; 0 .. 16) + { + permuted[i] = block[permutation[i]]; + } + block = permuted; +} + + +//Note, for our implementation, I think only the first 8 words are ever used +uint[16] compress(const ref uint[8] chainingValue, const ref uint[16] blockWords, + uint blockLength, //in case the block isn't full + ulong chunkCounter, uint flags) +{ + + + uint[16] state; + state[0 .. 8] = chainingValue[]; + state[8 .. 12] = IV[0 .. 4]; + state[12] = cast(uint)chunkCounter; + state[13] = cast(uint)(chunkCounter >> 32); + state[14] = blockLength; + state[15] = flags; + + uint[16] block = blockWords; + foreach (i; 0..6) + { + roundFunction(state, block); + permute(block); + } + roundFunction(state, block); //skip permuation the last time + + foreach (i; 0 .. 8) + { + state[i] ^= state[i + 8]; + state[i + 8] ^= chainingValue[i]; + } + + return state; +} + + +//if block isn't full, only the first blockLength/4 words +//will be filled in +uint[16] bytesToWords(const ubyte[] block){ + uint[16] ret = 0; + foreach(i; 0 .. (block.length/4)) + { + ret[i] = block[4*i]; + ret[i] |= (cast(uint)block[4*i + 1]) << 8; + ret[i] |= (cast(uint)block[4*i + 2]) << 16; + ret[i] |= (cast(uint)block[4*i + 3]) << 24; + } + return ret; +} + + +//full sized chunks, so no need to check for partial blocks, etc +void updateChunkStateFull(ref ChunkState chunkState, const ubyte[] chunk) +{ + for (size_t cursor = 0; cursor < ChunkLength; cursor += BlockLength) + { + uint[16] blockWords = bytesToWords(chunk[cursor .. cursor + BlockLength]); + + with(chunkState) + { + //first block gets ChunkStart, last gets ChunkEnd + uint flag = blocksCompressed == 0 ? ChunkStartFlag : + (blocksCompressed == (ChunkLength/BlockLength -1) ? ChunkEndFlag : 0); + + uint[16] compressed = compress(chainingValue, blockWords, + 64, //full blocks + chunkCounter, + flag); //start flag + + //use the first 8 bytes of this + chainingValue = compressed[0..8]; + blocksCompressed++; + + } + } + chunkState.chunkCounter++; + //Need to handle one more block if this one is partial +} + + +struct CVStack +{ + uint[8][54] data; //enough space for a really deep tree + uint size = 0; + + nothrow: + + bool empty() const { return size == 0; } + + void push(uint[8] cv) + { + data[size] = cv; + size++; + } + + uint[8] pop() + { + return data[--size]; + } +} + + + +void addChunkToTree(ref CVStack cvStack, uint[8] cv, ulong totalChunks){ + //if the total number of chunks ends in 0 bits, then this completed + //a subtree, so we'll add as many parent nodes as we can up the tree + + //this method won't be called on the final chunk, so we never set the + //ROOT flag + while ( (totalChunks & 1) == 0) + { + const top = cvStack.pop(); + uint[16] blockWords; + blockWords[0 .. 8] = top[]; + blockWords[8 .. $] = cv[]; + cv = compress(IV, blockWords, 64, 0, ParentFlag)[0 .. 8]; + totalChunks >>= 1; + } + cvStack.push(cv); +} + + + +unittest { + //test vectors from the spec. Run it on inputs of + //[0, 1, 2, ... 249, 250, 0, 1, 2, ...] of various lengths + + //available here: + //https://github.com/oconnor663/blake3_reference_impl_c/blob/main/test_vectors.json + + ubyte[N] testVector(size_t N)() + { + ubyte[N] ret; + foreach (i, ref x; ret) + { + x = i % 251; + } + return ret; + } + + const oneByte = testVector!1; + //todo use hex literals once DMD bootstrap version gets big enough + assert(blake3(oneByte) == [0x2d,0x3a,0xde,0xdf,0xf1,0x1b,0x61,0xf1, + 0x4c,0x88,0x6e,0x35,0xaf,0xa0,0x36,0x73, + 0x6d,0xcd,0x87,0xa7,0x4d,0x27,0xb5,0xc1, + 0x51,0x02,0x25,0xd0,0xf5,0x92,0xe2,0x13]); + + const twoBlockInput = testVector!65; + assert(blake3(twoBlockInput) == [0xde,0x1e,0x5f,0xa0,0xbe,0x70,0xdf,0x6d, + 0x2b,0xe8,0xff,0xfd,0x0e,0x99,0xce,0xaa, + 0x8e,0xb6,0xe8,0xc9,0x3a,0x63,0xf2,0xd8, + 0xd1,0xc3,0x0e,0xcb,0x6b,0x26,0x3d,0xee]); + + + const barelyOneChunk = testVector!1024; + assert(blake3(barelyOneChunk) == [0x42,0x21,0x47,0x39,0xf0,0x95,0xa4,0x06, + 0xf3,0xfc,0x83,0xde,0xb8,0x89,0x74,0x4a, + 0xc0,0x0d,0xf8,0x31,0xc1,0x0d,0xaa,0x55, + 0x18,0x9b,0x5d,0x12,0x1c,0x85,0x5a,0xf7]); + + + + + const barelyTwoChunks = testVector!1025; + assert(blake3(barelyTwoChunks) == [0xd0,0x02,0x78,0xae,0x47,0xeb,0x27,0xb3, + 0x4f,0xae,0xcf,0x67,0xb4,0xfe,0x26,0x3f, + 0x82,0xd5,0x41,0x29,0x16,0xc1,0xff,0xd9, + 0x7c,0x8c,0xb7,0xfb,0x81,0x4b,0x84,0x44]); + + const bigInput = testVector!31_744; + assert(blake3(bigInput) == [0x62,0xb6,0x96,0x0e,0x1a,0x44,0xbc,0xc1, + 0xeb,0x1a,0x61,0x1a,0x8d,0x62,0x35,0xb6, + 0xb4,0xb7,0x8f,0x32,0xe7,0xab,0xc4,0xfb, + 0x4c,0x6c,0xdc,0xce,0x94,0x89,0x5c,0x47]); +} diff --git a/dmd/common/md5.d b/dmd/common/md5.d deleted file mode 100644 index 42bc43adb65..00000000000 --- a/dmd/common/md5.d +++ /dev/null @@ -1,287 +0,0 @@ -/* - ********************************************************************** - ** md5.c ** - ** RSA Data Security, Inc. MD5 Message Digest Algorithm ** - ** Created: 2/17/90 RLR ** - ** Revised: 1/91 SRD,AJ,BSK,JT Reference C Version ** - ********************************************************************** - */ - -module dmd.common.md5; - -/* - ********************************************************************** - ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** - ** ** - ** License to copy and use this software is granted provided that ** - ** it is identified as the "RSA Data Security, Inc. MD5 Message ** - ** Digest Algorithm" in all material mentioning or referencing this ** - ** software or this function. ** - ** ** - ** License is also granted to make and use derivative works ** - ** provided that such works are identified as "derived from the RSA ** - ** Data Security, Inc. MD5 Message Digest Algorithm" in all ** - ** material mentioning or referencing the derived work. ** - ** ** - ** RSA Data Security, Inc. makes no representations concerning ** - ** either the merchantability of this software or the suitability ** - ** of this software for any particular purpose. It is provided "as ** - ** is" without express or implied warranty of any kind. ** - ** ** - ** These notices must be retained in any copies of any part of this ** - ** documentation and/or software. ** - ********************************************************************** - */ - -/* -- include the following line if the md5.h header file is separate -- */ - -nothrow: -@nogc: -@safe: -private: - -/* typedef a 32 bit type */ -alias UINT4 = uint; - -/* Data structure for MD5 (Message Digest) computation */ -public struct MD5_CTX { - UINT4[2] i; /* number of _bits_ handled mod 2^64 */ - UINT4[4] buf; /* scratch buffer */ - ubyte[64] in_; /* input buffer */ - ubyte[16] digest; /* actual digest after MD5Final call */ -} - - -__gshared ubyte[64] PADDING = [ - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -]; - -/* F, G and H are basic MD5 functions: selection, majority, parity */ -UINT4 F(UINT4 x, UINT4 y, UINT4 z) { return (x & y) | (~x & z); } -UINT4 G(UINT4 x, UINT4 y, UINT4 z) { return (x & z) | (y & ~z); } -UINT4 H(UINT4 x, UINT4 y, UINT4 z) { return x ^ y ^ z; } -UINT4 I(UINT4 x, UINT4 y, UINT4 z) { return y ^ (x | ~z); } - -/* ROTATE_LEFT rotates x left n bits */ -UINT4 ROTATE_LEFT(UINT4 x, UINT4 n) { return (x << n) | (x >> (32-n)); } - -/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ -/* Rotation is separate from addition to prevent recomputation */ -void FF(ref UINT4 a, UINT4 b, UINT4 c, UINT4 d, UINT4 x, UINT4 s, UINT4 ac) - {a += F (b, c, d) + x + cast(UINT4)ac; - a = ROTATE_LEFT (a, s); - a += b; - } -void GG(ref UINT4 a, UINT4 b, UINT4 c, UINT4 d, UINT4 x, UINT4 s, UINT4 ac) - {a += G (b, c, d) + x + cast(UINT4)ac; - a = ROTATE_LEFT (a, s); - a += b; - } -void HH(ref UINT4 a, UINT4 b, UINT4 c, UINT4 d, UINT4 x, UINT4 s, UINT4 ac) - {a += H (b, c, d) + x + cast(UINT4)ac; - a = ROTATE_LEFT (a, s); - a += b; - } -void II(ref UINT4 a, UINT4 b, UINT4 c, UINT4 d, UINT4 x, UINT4 s, UINT4 ac) - {a += I (b, c, d) + x + cast(UINT4)ac; - a = ROTATE_LEFT (a, s); - a += b; - } - -public void MD5Init (MD5_CTX *mdContext) -{ - mdContext.i[0] = mdContext.i[1] = cast(UINT4)0; - - /* Load magic initialization constants. - */ - mdContext.buf[0] = cast(UINT4)0x67452301; - mdContext.buf[1] = cast(UINT4)0xefcdab89; - mdContext.buf[2] = cast(UINT4)0x98badcfe; - mdContext.buf[3] = cast(UINT4)0x10325476; -} - -@trusted -public void MD5Update (MD5_CTX *mdContext, ubyte *inBuf, uint inLen) -{ - UINT4[16] in_; - int mdi; - uint i, ii; - - /* compute number of bytes mod 64 */ - mdi = cast(int)((mdContext.i[0] >> 3) & 0x3F); - - /* update number of bits */ - if ((mdContext.i[0] + (cast(UINT4)inLen << 3)) < mdContext.i[0]) - mdContext.i[1]++; - mdContext.i[0] += (cast(UINT4)inLen << 3); - mdContext.i[1] += (cast(UINT4)inLen >> 29); - - while (inLen--) { - /* add new character to buffer, increment mdi */ - mdContext.in_[mdi++] = *inBuf++; - - /* transform if necessary */ - if (mdi == 0x40) { - for (i = 0, ii = 0; i < 16; i++, ii += 4) - in_[i] = ((cast(UINT4)mdContext.in_[ii+3]) << 24) | - ((cast(UINT4)mdContext.in_[ii+2]) << 16) | - ((cast(UINT4)mdContext.in_[ii+1]) << 8) | - (cast(UINT4)mdContext.in_[ii]); - Transform (&mdContext.buf[0], &in_[0]); - mdi = 0; - } - } -} - -@trusted -public void MD5Final (MD5_CTX *mdContext) -{ - UINT4[16] in_; - int mdi; - uint i, ii; - uint padLen; - - /* save number of bits */ - in_[14] = mdContext.i[0]; - in_[15] = mdContext.i[1]; - - /* compute number of bytes mod 64 */ - mdi = cast(int)((mdContext.i[0] >> 3) & 0x3F); - - /* pad out to 56 mod 64 */ - padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); - MD5Update (mdContext, &PADDING[0], padLen); - - /* append length in bits and transform */ - for (i = 0, ii = 0; i < 14; i++, ii += 4) - in_[i] = ((cast(UINT4)mdContext.in_[ii+3]) << 24) | - ((cast(UINT4)mdContext.in_[ii+2]) << 16) | - ((cast(UINT4)mdContext.in_[ii+1]) << 8) | - (cast(UINT4)mdContext.in_[ii]); - Transform (&mdContext.buf[0], &in_[0]); - - /* store buffer in digest */ - for (i = 0, ii = 0; i < 4; i++, ii += 4) { - mdContext.digest[ii] = cast(ubyte)(mdContext.buf[i] & 0xFF); - mdContext.digest[ii+1] = - cast(ubyte)((mdContext.buf[i] >> 8) & 0xFF); - mdContext.digest[ii+2] = - cast(ubyte)((mdContext.buf[i] >> 16) & 0xFF); - mdContext.digest[ii+3] = - cast(ubyte)((mdContext.buf[i] >> 24) & 0xFF); - } -} - -/* Basic MD5 step. Transform buf based on in. - */ -@trusted -void Transform (UINT4 *buf, UINT4 *in_) -{ - UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; - - /* Round 1 */ - enum S11 = 7; - enum S12 = 12; - enum S13 = 17; - enum S14 = 22; - FF ( a, b, c, d, in_[ 0], S11, 3614090360U); /* 1 */ - FF ( d, a, b, c, in_[ 1], S12, 3905402710U); /* 2 */ - FF ( c, d, a, b, in_[ 2], S13, 606105819U); /* 3 */ - FF ( b, c, d, a, in_[ 3], S14, 3250441966U); /* 4 */ - FF ( a, b, c, d, in_[ 4], S11, 4118548399U); /* 5 */ - FF ( d, a, b, c, in_[ 5], S12, 1200080426U); /* 6 */ - FF ( c, d, a, b, in_[ 6], S13, 2821735955U); /* 7 */ - FF ( b, c, d, a, in_[ 7], S14, 4249261313U); /* 8 */ - FF ( a, b, c, d, in_[ 8], S11, 1770035416U); /* 9 */ - FF ( d, a, b, c, in_[ 9], S12, 2336552879U); /* 10 */ - FF ( c, d, a, b, in_[10], S13, 4294925233U); /* 11 */ - FF ( b, c, d, a, in_[11], S14, 2304563134U); /* 12 */ - FF ( a, b, c, d, in_[12], S11, 1804603682U); /* 13 */ - FF ( d, a, b, c, in_[13], S12, 4254626195U); /* 14 */ - FF ( c, d, a, b, in_[14], S13, 2792965006U); /* 15 */ - FF ( b, c, d, a, in_[15], S14, 1236535329U); /* 16 */ - - /* Round 2 */ - enum S21 = 5; - enum S22 = 9; - enum S23 = 14; - enum S24 = 20; - GG ( a, b, c, d, in_[ 1], S21, 4129170786U); /* 17 */ - GG ( d, a, b, c, in_[ 6], S22, 3225465664U); /* 18 */ - GG ( c, d, a, b, in_[11], S23, 643717713U); /* 19 */ - GG ( b, c, d, a, in_[ 0], S24, 3921069994U); /* 20 */ - GG ( a, b, c, d, in_[ 5], S21, 3593408605U); /* 21 */ - GG ( d, a, b, c, in_[10], S22, 38016083U); /* 22 */ - GG ( c, d, a, b, in_[15], S23, 3634488961U); /* 23 */ - GG ( b, c, d, a, in_[ 4], S24, 3889429448U); /* 24 */ - GG ( a, b, c, d, in_[ 9], S21, 568446438U); /* 25 */ - GG ( d, a, b, c, in_[14], S22, 3275163606U); /* 26 */ - GG ( c, d, a, b, in_[ 3], S23, 4107603335U); /* 27 */ - GG ( b, c, d, a, in_[ 8], S24, 1163531501U); /* 28 */ - GG ( a, b, c, d, in_[13], S21, 2850285829U); /* 29 */ - GG ( d, a, b, c, in_[ 2], S22, 4243563512U); /* 30 */ - GG ( c, d, a, b, in_[ 7], S23, 1735328473U); /* 31 */ - GG ( b, c, d, a, in_[12], S24, 2368359562U); /* 32 */ - - /* Round 3 */ - enum S31 = 4; - enum S32 = 11; - enum S33 = 16; - enum S34 = 23; - HH ( a, b, c, d, in_[ 5], S31, 4294588738U); /* 33 */ - HH ( d, a, b, c, in_[ 8], S32, 2272392833U); /* 34 */ - HH ( c, d, a, b, in_[11], S33, 1839030562U); /* 35 */ - HH ( b, c, d, a, in_[14], S34, 4259657740U); /* 36 */ - HH ( a, b, c, d, in_[ 1], S31, 2763975236U); /* 37 */ - HH ( d, a, b, c, in_[ 4], S32, 1272893353U); /* 38 */ - HH ( c, d, a, b, in_[ 7], S33, 4139469664U); /* 39 */ - HH ( b, c, d, a, in_[10], S34, 3200236656U); /* 40 */ - HH ( a, b, c, d, in_[13], S31, 681279174U); /* 41 */ - HH ( d, a, b, c, in_[ 0], S32, 3936430074U); /* 42 */ - HH ( c, d, a, b, in_[ 3], S33, 3572445317U); /* 43 */ - HH ( b, c, d, a, in_[ 6], S34, 76029189U); /* 44 */ - HH ( a, b, c, d, in_[ 9], S31, 3654602809U); /* 45 */ - HH ( d, a, b, c, in_[12], S32, 3873151461U); /* 46 */ - HH ( c, d, a, b, in_[15], S33, 530742520U); /* 47 */ - HH ( b, c, d, a, in_[ 2], S34, 3299628645U); /* 48 */ - - /* Round 4 */ - enum S41 = 6; - enum S42 = 10; - enum S43 = 15; - enum S44 = 21; - II ( a, b, c, d, in_[ 0], S41, 4096336452U); /* 49 */ - II ( d, a, b, c, in_[ 7], S42, 1126891415U); /* 50 */ - II ( c, d, a, b, in_[14], S43, 2878612391U); /* 51 */ - II ( b, c, d, a, in_[ 5], S44, 4237533241U); /* 52 */ - II ( a, b, c, d, in_[12], S41, 1700485571U); /* 53 */ - II ( d, a, b, c, in_[ 3], S42, 2399980690U); /* 54 */ - II ( c, d, a, b, in_[10], S43, 4293915773U); /* 55 */ - II ( b, c, d, a, in_[ 1], S44, 2240044497U); /* 56 */ - II ( a, b, c, d, in_[ 8], S41, 1873313359U); /* 57 */ - II ( d, a, b, c, in_[15], S42, 4264355552U); /* 58 */ - II ( c, d, a, b, in_[ 6], S43, 2734768916U); /* 59 */ - II ( b, c, d, a, in_[13], S44, 1309151649U); /* 60 */ - II ( a, b, c, d, in_[ 4], S41, 4149444226U); /* 61 */ - II ( d, a, b, c, in_[11], S42, 3174756917U); /* 62 */ - II ( c, d, a, b, in_[ 2], S43, 718787259U); /* 63 */ - II ( b, c, d, a, in_[ 9], S44, 3951481745U); /* 64 */ - - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; -} - -/* - ********************************************************************** - ** End of md5.c ** - ******************************* (cut) ******************************** - */ From 44c1ee69a1e190905898b1488b62693ffd620020 Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Mon, 12 Feb 2024 09:31:54 -0700 Subject: [PATCH 021/125] fix style issues --- dmd/common/blake3.d | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/dmd/common/blake3.d b/dmd/common/blake3.d index 52696997961..2d0a3079453 100644 --- a/dmd/common/blake3.d +++ b/dmd/common/blake3.d @@ -17,16 +17,14 @@ nothrow: **/ public ubyte[32] blake3(const ubyte[] data) { - - ChunkState state; CVStack cvStack; - size_t cursor = 0; //greater because if it's == we still need to finalize the last //chunk - while(data.length - cursor > ChunkLength){ + while (data.length - cursor > ChunkLength) + { const ubyte[] chunk = data[cursor .. cursor + ChunkLength]; updateChunkStateFull(state, chunk); @@ -45,7 +43,8 @@ public ubyte[32] blake3(const ubyte[] data) //now handle the final chunk which might not be full //handle all but last block - while(data.length - cursor > BlockLength){ + while (data.length - cursor > BlockLength) + { uint[16] blockWords = bytesToWords(data[cursor .. cursor + BlockLength]); with(state) { From 6329309d4247db77b935e410764870c1d8c9a8b8 Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Tue, 20 Feb 2024 10:20:09 -0700 Subject: [PATCH 022/125] dkorpel comments --- dmd/common/blake3.d | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/dmd/common/blake3.d b/dmd/common/blake3.d index 2d0a3079453..6dd56a0c89d 100644 --- a/dmd/common/blake3.d +++ b/dmd/common/blake3.d @@ -1,6 +1,7 @@ module dmd.common.blake3; // based on https://github.com/oconnor663/blake3_reference_impl_c/blob/main/reference_impl.c +@safe: nothrow: /** @@ -161,7 +162,8 @@ void roundFunction(ref uint[16] state, const ref uint[16] message) g(state[3], state[4], state[9], state[14], message[14], message[15]); } -void permute(ref uint[16] block){ +void permute(ref uint[16] block) +{ uint[16] permuted; foreach (i; 0 .. 16) { @@ -206,7 +208,8 @@ uint[16] compress(const ref uint[8] chainingValue, const ref uint[16] blockWords //if block isn't full, only the first blockLength/4 words //will be filled in -uint[16] bytesToWords(const ubyte[] block){ +uint[16] bytesToWords(const ubyte[] block) +{ uint[16] ret = 0; foreach(i; 0 .. (block.length/4)) { @@ -254,6 +257,7 @@ struct CVStack uint size = 0; nothrow: + @safe: bool empty() const { return size == 0; } @@ -271,7 +275,8 @@ struct CVStack -void addChunkToTree(ref CVStack cvStack, uint[8] cv, ulong totalChunks){ +void addChunkToTree(ref CVStack cvStack, uint[8] cv, ulong totalChunks) +{ //if the total number of chunks ends in 0 bits, then this completed //a subtree, so we'll add as many parent nodes as we can up the tree From c5f5adf6ae693e0c6ed133e09a5e4573be4341ec Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Fri, 1 Mar 2024 14:03:56 -0700 Subject: [PATCH 023/125] add nogc --- dmd/common/blake3.d | 51 ++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/dmd/common/blake3.d b/dmd/common/blake3.d index 6dd56a0c89d..555bb86188c 100644 --- a/dmd/common/blake3.d +++ b/dmd/common/blake3.d @@ -3,6 +3,7 @@ module dmd.common.blake3; @safe: nothrow: +@nogc: /** * Implementation of Blake 3 hash function with streaming disabled @@ -258,6 +259,7 @@ struct CVStack nothrow: @safe: + @nogc: bool empty() const { return size == 0; } @@ -315,36 +317,37 @@ unittest { const oneByte = testVector!1; //todo use hex literals once DMD bootstrap version gets big enough - assert(blake3(oneByte) == [0x2d,0x3a,0xde,0xdf,0xf1,0x1b,0x61,0xf1, - 0x4c,0x88,0x6e,0x35,0xaf,0xa0,0x36,0x73, - 0x6d,0xcd,0x87,0xa7,0x4d,0x27,0xb5,0xc1, - 0x51,0x02,0x25,0xd0,0xf5,0x92,0xe2,0x13]); - + static const expectedOneByte = [0x2d,0x3a,0xde,0xdf,0xf1,0x1b,0x61,0xf1, + 0x4c,0x88,0x6e,0x35,0xaf,0xa0,0x36,0x73, + 0x6d,0xcd,0x87,0xa7,0x4d,0x27,0xb5,0xc1, + 0x51,0x02,0x25,0xd0,0xf5,0x92,0xe2,0x13]; + assert(blake3(oneByte) == expectedOneByte); + + static const expectedTwoBlocks = [0xde,0x1e,0x5f,0xa0,0xbe,0x70,0xdf,0x6d, + 0x2b,0xe8,0xff,0xfd,0x0e,0x99,0xce,0xaa, + 0x8e,0xb6,0xe8,0xc9,0x3a,0x63,0xf2,0xd8, + 0xd1,0xc3,0x0e,0xcb,0x6b,0x26,0x3d,0xee]; const twoBlockInput = testVector!65; - assert(blake3(twoBlockInput) == [0xde,0x1e,0x5f,0xa0,0xbe,0x70,0xdf,0x6d, - 0x2b,0xe8,0xff,0xfd,0x0e,0x99,0xce,0xaa, - 0x8e,0xb6,0xe8,0xc9,0x3a,0x63,0xf2,0xd8, - 0xd1,0xc3,0x0e,0xcb,0x6b,0x26,0x3d,0xee]); - + assert(blake3(twoBlockInput) == expectedTwoBlocks); + static const expectedOneChunk = [0x42,0x21,0x47,0x39,0xf0,0x95,0xa4,0x06, + 0xf3,0xfc,0x83,0xde,0xb8,0x89,0x74,0x4a, + 0xc0,0x0d,0xf8,0x31,0xc1,0x0d,0xaa,0x55, + 0x18,0x9b,0x5d,0x12,0x1c,0x85,0x5a,0xf7]; const barelyOneChunk = testVector!1024; - assert(blake3(barelyOneChunk) == [0x42,0x21,0x47,0x39,0xf0,0x95,0xa4,0x06, - 0xf3,0xfc,0x83,0xde,0xb8,0x89,0x74,0x4a, - 0xc0,0x0d,0xf8,0x31,0xc1,0x0d,0xaa,0x55, - 0x18,0x9b,0x5d,0x12,0x1c,0x85,0x5a,0xf7]); - - - + assert(blake3(barelyOneChunk) == expectedOneChunk); + static const expectedTwoChunks = [0xd0,0x02,0x78,0xae,0x47,0xeb,0x27,0xb3, + 0x4f,0xae,0xcf,0x67,0xb4,0xfe,0x26,0x3f, + 0x82,0xd5,0x41,0x29,0x16,0xc1,0xff,0xd9, + 0x7c,0x8c,0xb7,0xfb,0x81,0x4b,0x84,0x44]; const barelyTwoChunks = testVector!1025; - assert(blake3(barelyTwoChunks) == [0xd0,0x02,0x78,0xae,0x47,0xeb,0x27,0xb3, - 0x4f,0xae,0xcf,0x67,0xb4,0xfe,0x26,0x3f, - 0x82,0xd5,0x41,0x29,0x16,0xc1,0xff,0xd9, - 0x7c,0x8c,0xb7,0xfb,0x81,0x4b,0x84,0x44]); + assert(blake3(barelyTwoChunks) == expectedTwoChunks); - const bigInput = testVector!31_744; - assert(blake3(bigInput) == [0x62,0xb6,0x96,0x0e,0x1a,0x44,0xbc,0xc1, + static const expectedBig = [0x62,0xb6,0x96,0x0e,0x1a,0x44,0xbc,0xc1, 0xeb,0x1a,0x61,0x1a,0x8d,0x62,0x35,0xb6, 0xb4,0xb7,0x8f,0x32,0xe7,0xab,0xc4,0xfb, - 0x4c,0x6c,0xdc,0xce,0x94,0x89,0x5c,0x47]); + 0x4c,0x6c,0xdc,0xce,0x94,0x89,0x5c,0x47]; + const bigInput = testVector!31_744; + assert(blake3(bigInput) == expectedBig); } From 56c1e0f4e0269f47acac78f3d506140e76ecc18f Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Tue, 12 Mar 2024 01:11:07 -0700 Subject: [PATCH 024/125] regrange - simplify with foreach (dlang/dmd!16303) --- dmd/common/blake3.d | 1 + 1 file changed, 1 insertion(+) diff --git a/dmd/common/blake3.d b/dmd/common/blake3.d index 555bb86188c..10b7c45c05b 100644 --- a/dmd/common/blake3.d +++ b/dmd/common/blake3.d @@ -17,6 +17,7 @@ nothrow: * data = byte array to hash * Returns: Blake 3 hash of data **/ +@trusted public ubyte[32] blake3(const ubyte[] data) { ChunkState state; From c15fbf41aec7f0e3f1e3d4604cfd7aae8b9a1075 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Tue, 12 Mar 2024 01:14:07 -0700 Subject: [PATCH 025/125] add sansExt() (dlang/dmd!16300) --- dmd/file_manager.d | 5 +---- dmd/root/filename.d | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/dmd/file_manager.d b/dmd/file_manager.d index 23bfe3c539b..946cc131519 100644 --- a/dmd/file_manager.d +++ b/dmd/file_manager.d @@ -192,10 +192,7 @@ nothrow: } FileName.free(n.ptr); - const b = FileName.removeExt(filename); - n = FileName.combine(p, b); - FileName.free(b.ptr); - + n = FileName.combine(p, FileName.sansExt(filename)); scope(exit) FileName.free(n.ptr); // also cache this if we are looking for package.d[i] diff --git a/dmd/root/filename.d b/dmd/root/filename.d index 5cf9a213ee6..d2987b52b2b 100644 --- a/dmd/root/filename.d +++ b/dmd/root/filename.d @@ -269,6 +269,20 @@ nothrow: assert(removeExt("/foo/bar/frontend.di"[]) == "/foo/bar/frontend"); } + /******************************** + * Slice of file name without extension. + * Params: + * filename = file name + * Returns: + * the slice + */ + extern (D) static const(char)[] sansExt(const char[] filename) + { + auto e = ext(filename); + size_t length = e.length; + return filename[0 .. filename.length - (length ? length + 1 : 0)]; // +1 for . + } + /******************************** * Return filename name excluding path (read-only). */ From 1f2581f6d45ac0a001c02c45a196ecea2e809902 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Tue, 12 Mar 2024 17:03:53 -0700 Subject: [PATCH 026/125] add toCString() (dlang/dmd!16305) --- dmd/root/string.d | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/dmd/root/string.d b/dmd/root/string.d index e82b0d2fe6d..59ec1aa6909 100644 --- a/dmd/root/string.d +++ b/dmd/root/string.d @@ -10,6 +10,9 @@ */ module dmd.root.string; +import core.stdc.string; +import dmd.root.rmem; + /// Slices a `\0`-terminated C-string, excluding the terminator inout(char)[] toDString (inout(char)* s) pure nothrow @nogc { @@ -87,6 +90,23 @@ unittest assert(null.toCStringThen!((v) => v == "\0")); } +/********************************************* + * Convert a D string to a C string by allocating memory, + * copying it, and adding a terminating 0. + * Params: + * s = string to copy + * Result: + * 0-terminated copy of s + */ +char[] toCString(scope const(char)[] s) nothrow +{ + const length = s.length; + char* p = cast(char*)mem.xmalloc_noscan(length + 1); + memcpy(p, s.ptr, length); + p[length] = 0; + return p[0 .. length]; +} + /** * Strips one leading line terminator of the given string. * From accd7b166931a29c8f14caa06f2d9c8bd6d899be Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Tue, 12 Mar 2024 19:02:36 -0600 Subject: [PATCH 027/125] make blake3 input scope --- dmd/common/blake3.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dmd/common/blake3.d b/dmd/common/blake3.d index 10b7c45c05b..ba83f21b71e 100644 --- a/dmd/common/blake3.d +++ b/dmd/common/blake3.d @@ -17,8 +17,8 @@ nothrow: * data = byte array to hash * Returns: Blake 3 hash of data **/ -@trusted -public ubyte[32] blake3(const ubyte[] data) + +public ubyte[32] blake3(scope const ubyte[] data) { ChunkState state; CVStack cvStack; From 0e59b4db2ab9cb82329b6c3cdb42f9d7fef115e9 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Tue, 12 Mar 2024 21:34:41 -0700 Subject: [PATCH 028/125] make more use of sansExt() (dlang/dmd!16304) --- dmd/dmodule.d | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/dmd/dmodule.d b/dmd/dmodule.d index 123cfefc4e2..4f84eec7c0d 100644 --- a/dmd/dmodule.d +++ b/dmd/dmodule.d @@ -455,7 +455,7 @@ extern (C++) final class Module : Package FileName.exists(filename) == 1) { FileName.free(srcfilename.ptr); - srcfilename = FileName.removeExt(filename); // just does a mem.strdup(filename) + srcfilename = FileName.sansExt(filename); } else if (!FileName.equalsExt(srcfilename, mars_ext) && !FileName.equalsExt(srcfilename, hdr_ext) && @@ -610,7 +610,8 @@ extern (C++) final class Module : Package */ private void onFileReadError(const ref Loc loc) { - if (FileName.equals(srcfile.toString(), "object.d")) + const name = srcfile.toString(); + if (FileName.equals(name, "object.d")) { ObjectNotFound(loc, ident); } @@ -619,20 +620,20 @@ extern (C++) final class Module : Package // Modules whose original argument name has an extension, or do not // have a valid location come from the command-line. // Error that their file cannot be found and return early. - .error(loc, "cannot find input file `%s`", srcfile.toChars()); + .error(loc, "cannot find input file `%.*s`", cast(int)name.length, name.ptr); } else { // if module is not named 'package' but we're trying to read 'package.d', we're looking for a package module bool isPackageMod = (strcmp(toChars(), "package") != 0) && isPackageFileName(srcfile); if (isPackageMod) - .error(loc, "importing package '%s' requires a 'package.d' file which cannot be found in '%s'", toChars(), srcfile.toChars()); + .error(loc, "importing package '%s' requires a 'package.d' file which cannot be found in '%.*s'", toChars(), cast(int)name.length, name.ptr); else { .error(loc, "unable to read module `%s`", toChars()); - const pkgfile = FileName.combine(FileName.removeExt(srcfile.toString()), package_d); - .errorSupplemental(loc, "Expected '%s' or '%s' in one of the following import paths:", - srcfile.toChars(), pkgfile.ptr); + const pkgfile = FileName.combine(FileName.sansExt(name), package_d); + .errorSupplemental(loc, "Expected '%.*s' or '%.*s' in one of the following import paths:", + cast(int)name.length, name.ptr, cast(int)pkgfile.length, pkgfile.ptr); } } if (!global.gag) @@ -646,7 +647,7 @@ extern (C++) final class Module : Package } else { - fprintf(stderr, "Specify path to file '%s' with -I switch\n", srcfile.toChars()); + fprintf(stderr, "Specify path to file '%.*s' with -I switch\n", cast(int)name.length, name.ptr); } removeHdrFilesAndFail(global.params, Module.amodules); From 63eba454fe55e1d90ad819dfc80cb6e053a69cc3 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Wed, 13 Mar 2024 01:57:46 -0700 Subject: [PATCH 029/125] turn tryagain into while loop (dlang/dmd!16307) --- dmd/common/blake3.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dmd/common/blake3.d b/dmd/common/blake3.d index ba83f21b71e..797a58963fb 100644 --- a/dmd/common/blake3.d +++ b/dmd/common/blake3.d @@ -17,7 +17,7 @@ nothrow: * data = byte array to hash * Returns: Blake 3 hash of data **/ - +@trusted public ubyte[32] blake3(scope const ubyte[] data) { ChunkState state; From fedddc34516acb0e070ad828e43e7846799f70bd Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Wed, 13 Mar 2024 12:18:46 +0100 Subject: [PATCH 030/125] Fix dip1000 errors in blake3.d --- dmd/common/blake3.d | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dmd/common/blake3.d b/dmd/common/blake3.d index 797a58963fb..c040483a648 100644 --- a/dmd/common/blake3.d +++ b/dmd/common/blake3.d @@ -17,7 +17,6 @@ nothrow: * data = byte array to hash * Returns: Blake 3 hash of data **/ -@trusted public ubyte[32] blake3(scope const ubyte[] data) { ChunkState state; @@ -210,7 +209,7 @@ uint[16] compress(const ref uint[8] chainingValue, const ref uint[16] blockWords //if block isn't full, only the first blockLength/4 words //will be filled in -uint[16] bytesToWords(const ubyte[] block) +uint[16] bytesToWords(scope const ubyte[] block) { uint[16] ret = 0; foreach(i; 0 .. (block.length/4)) @@ -225,7 +224,7 @@ uint[16] bytesToWords(const ubyte[] block) //full sized chunks, so no need to check for partial blocks, etc -void updateChunkStateFull(ref ChunkState chunkState, const ubyte[] chunk) +void updateChunkStateFull(ref ChunkState chunkState, scope const ubyte[] chunk) { for (size_t cursor = 0; cursor < ChunkLength; cursor += BlockLength) { From cbbd86a89c565186ff50cf922d8860a238ea2ca1 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Thu, 14 Mar 2024 08:47:42 -0700 Subject: [PATCH 031/125] refactor implicitConvToWithoutAliasThis() (dlang/dmd!16309) --- dmd/dcast.d | 2 +- dmd/mtype.d | 101 +++++++++++++++++++++++----------------------------- 2 files changed, 45 insertions(+), 58 deletions(-) diff --git a/dmd/dcast.d b/dmd/dcast.d index 060b538c844..a78405fcc1c 100644 --- a/dmd/dcast.d +++ b/dmd/dcast.d @@ -69,7 +69,7 @@ Expression implicitCastTo(Expression e, Scope* sc, Type t) { Expression visit(Expression e) { - // printf("Expression.implicitCastTo(%s of type %s) => %s\n", e.toChars(), e.type.toChars(), t.toChars()); + //printf("Expression.implicitCastTo(%s of type %s) => %s\n", e.toChars(), e.type.toChars(), t.toChars()); if (const match = (sc && sc.flags & SCOPE.Cfile) ? e.cimplicitConvTo(t) : e.implicitConvTo(t)) { // no need for an extra cast when matching is exact diff --git a/dmd/mtype.d b/dmd/mtype.d index 2c9e058bf32..7c22fa759b1 100644 --- a/dmd/mtype.d +++ b/dmd/mtype.d @@ -3985,84 +3985,71 @@ extern (C++) final class TypeStruct : Type extern (D) MATCH implicitConvToWithoutAliasThis(Type to) { - MATCH m; + //printf("TypeStruct::implicitConvToWithoutAliasThis(%s => %s)\n", toChars(), to.toChars()); + + auto tos = to.isTypeStruct(); + if (!(tos && sym == tos.sym)) + return MATCH.nomatch; + + if (mod == to.mod) + return MATCH.exact; + + if (MODimplicitConv(mod, to.mod)) + return MATCH.constant; - if (ty == to.ty && sym == (cast(TypeStruct)to).sym) + /* Check all the fields. If they can all be converted, + * allow the conversion. + */ + MATCH m = MATCH.constant; + uint offset = ~0; // must never match a field offset + foreach (v; sym.fields[]) { - m = MATCH.exact; // exact match - if (mod != to.mod) - { - m = MATCH.constant; - if (MODimplicitConv(mod, to.mod)) - { - } - else - { - /* Check all the fields. If they can all be converted, - * allow the conversion. - */ - uint offset = ~0; // dead-store to prevent spurious warning - for (size_t i = 0; i < sym.fields.length; i++) - { - VarDeclaration v = sym.fields[i]; - if (i == 0) - { - } - else if (v.offset == offset) - { - if (m > MATCH.nomatch) - continue; - } - else - { - if (m == MATCH.nomatch) - return m; - } - - // 'from' type - Type tvf = v.type.addMod(mod); - - // 'to' type - Type tv = v.type.addMod(to.mod); - - // field match - MATCH mf = tvf.implicitConvTo(tv); - //printf("\t%s => %s, match = %d\n", v.type.toChars(), tv.toChars(), mf); - - if (mf == MATCH.nomatch) - return mf; - if (mf < m) // if field match is worse - m = mf; - offset = v.offset; - } - } - } + /* Why are we only looking at the first member of a union? + * The check should check for overlap of v with the previous field, + * not just starting at the same point + */ + if (v.offset == offset) // v is at same offset as previous field + continue; // ignore + + Type tvf = v.type.addMod(mod); // from type + Type tvt = v.type.addMod(to.mod); // to type + + // field match + MATCH mf = tvf.implicitConvTo(tvt); + //printf("\t%s => %s, match = %d\n", v.type.toChars(), tvt.toChars(), mf); + + if (mf == MATCH.nomatch) + return MATCH.nomatch; + if (mf < m) // if field match is worse + m = mf; + offset = v.offset; } return m; } extern (D) MATCH implicitConvToThroughAliasThis(Type to) { - MATCH m; - if (!(ty == to.ty && sym == (cast(TypeStruct)to).sym) && sym.aliasthis && !(att & AliasThisRec.tracing)) + auto tos = to.isTypeStruct(); + if (!(tos && sym == tos.sym) && + sym.aliasthis && + !(att & AliasThisRec.tracing)) { if (auto ato = aliasthisOf(this)) { att = cast(AliasThisRec)(att | AliasThisRec.tracing); - m = ato.implicitConvTo(to); + MATCH m = ato.implicitConvTo(to); att = cast(AliasThisRec)(att & ~AliasThisRec.tracing); + return m; } - else - m = MATCH.nomatch; // no match } - return m; + return MATCH.nomatch; } override MATCH implicitConvTo(Type to) { //printf("TypeStruct::implicitConvTo(%s => %s)\n", toChars(), to.toChars()); MATCH m = implicitConvToWithoutAliasThis(to); - return m ? m : implicitConvToThroughAliasThis(to); + return m == MATCH.nomatch ? implicitConvToThroughAliasThis(to) : m; } override MATCH constConv(Type to) From b46aae53917b4f1c44a0490316a8c1fdaa87c56b Mon Sep 17 00:00:00 2001 From: "Richard (Rikki) Andrew Cattermole" Date: Tue, 19 Mar 2024 07:19:16 +1300 Subject: [PATCH 032/125] Implement UAX31 character ranges (dlang/dmd!15307) --- dmd/cli.d | 20 + dmd/common/charactertables.d | 267 ++ dmd/common/charactertables.h | 20 + dmd/common/identifiertables.d | 4241 +++++++++++++++++++++++ dmd/dmangle.d | 4 +- dmd/doc.d | 50 +- dmd/frontend.h | 79 +- dmd/globals.d | 13 + dmd/globals.h | 17 + dmd/identifier.d | 69 +- dmd/lexer.d | 340 +- dmd/main.d | 48 + dmd/mars.d | 52 + dmd/pragmasem.d | 4 +- dmd/root/utf.d | 275 -- tests/dmd/compilable/ident_UAX31.c | 9 + tests/dmd/compilable/ident_UAX31.d | 5 + tests/dmd/compilable/ident_all.c | 11 + tests/dmd/compilable/ident_all.d | 10 + tests/dmd/compilable/ident_c11.c | 9 + tests/dmd/compilable/ident_c11.d | 5 + tests/dmd/compilable/ident_c99.c | 6 + tests/dmd/compilable/ident_c99.d | 5 + tests/dmd/fail_compilation/lexer23465.d | 2 +- 24 files changed, 5105 insertions(+), 456 deletions(-) create mode 100644 dmd/common/charactertables.d create mode 100644 dmd/common/charactertables.h create mode 100644 dmd/common/identifiertables.d create mode 100644 tests/dmd/compilable/ident_UAX31.c create mode 100644 tests/dmd/compilable/ident_UAX31.d create mode 100644 tests/dmd/compilable/ident_all.c create mode 100644 tests/dmd/compilable/ident_all.d create mode 100644 tests/dmd/compilable/ident_c11.c create mode 100644 tests/dmd/compilable/ident_c11.d create mode 100644 tests/dmd/compilable/ident_c99.c create mode 100644 tests/dmd/compilable/ident_c99.d diff --git a/dmd/cli.d b/dmd/cli.d index 9c94557afe0..24cf50729b2 100644 --- a/dmd/cli.d +++ b/dmd/cli.d @@ -466,6 +466,26 @@ dmd -cov -unittest myprog.d $(P Note that multiple `-i=...` options are allowed, each one adds a pattern.)}" ), + Option("identifiers=", + "Specify the non-ASCII tables for D identifiers", + `Set the identifier table to use for the non-ASCII values. + $(UL + $(LI $(I UAX31): UAX31) + $(LI $(I c99): C99) + $(LI $(I c11): C11) + $(LI $(I all): All, the least restrictive set, which comes all others (default)) + )` + ), + Option("identifiers-importc=
", + "Specify the non-ASCII tables for ImportC identifiers", + `Set the identifier table to use for the non-ASCII values. + $(UL + $(LI $(I UAX31): UAX31) + $(LI $(I c99): C99) + $(LI $(I c11): C11 (default)) + $(LI $(I all): All, the least restrictive set, which comes all others) + )` + ), Option("ignore", "deprecated flag, unsupported pragmas are always ignored now" ), diff --git a/dmd/common/charactertables.d b/dmd/common/charactertables.d new file mode 100644 index 00000000000..e0fb7de93d2 --- /dev/null +++ b/dmd/common/charactertables.d @@ -0,0 +1,267 @@ +/** + * Character tables related to identifiers. + * + * Supports UAX31, C99, C11 and least restrictive (All). + * + * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Authors: $(LINK2 https://cattermole.co.nz, Richard (Rikki) Andrew Cattermole) + * 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/charactertables.d, common/charactertables.d) + * Documentation: https://dlang.org/phobos/dmd_common_charactertables.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/common/charactertables.d + */ +module dmd.common.charactertables; + +@safe nothrow @nogc pure: + +extern(C++): + +/// +enum IdentifierTable { + UAX31, /// + C99, /// + C11, /// + LR, /// Least Restrictive aka All +} + +/// +struct IdentifierCharLookup +{ + @safe nothrow @nogc pure: + + /// + extern(C++) bool function(dchar) isStart; + /// + extern(C++) bool function(dchar) isContinue; + + /// Lookup the table given the table name + static IdentifierCharLookup forTable(IdentifierTable table) + { + import dmd.common.identifiertables; + + // Awful solution to require these lambdas. + // However without them the extern(C++) ABI issues crop up for isInRange, + // and then it can't access the tables. + final switch(table) { + case IdentifierTable.UAX31: + return IdentifierCharLookup( + (c) => isInRange!UAX31_Start(c), + (c) => isInRange!UAX31_Continue(c)); + case IdentifierTable.C99: + return IdentifierCharLookup( + (c) => isInRange!FixedTable_C99_Start(c), + (c) => isInRange!FixedTable_C99_Continue(c)); + case IdentifierTable.C11: + return IdentifierCharLookup( + (c) => isInRange!FixedTable_C11_Start(c), + (c) => isInRange!FixedTable_C11_Continue(c)); + case IdentifierTable.LR: + return IdentifierCharLookup( + (c) => isInRange!LeastRestrictive_Start(c), + (c) => isInRange!LeastRestrictive_Continue(c)); + } + } +} + +/** +Convenience function for use in places where we just don't care, +what the identifier ranges are, or if it is start/continue. + +Returns: is character a member of least restrictive of all. +*/ +bool isAnyIdentifierCharacter(dchar c) +{ + import dmd.common.identifiertables; + return isInRange!LeastRestrictive_OfAll(c); +} + +/// +unittest +{ + assert(isAnyContinue('ğ')); +} + +/** +Convenience function for use in places where we just don't care, +what the identifier ranges are. + +Returns: is character a member of restrictive Start +*/ +bool isAnyStart(dchar c) +{ + import dmd.common.identifiertables; + return isInRange!LeastRestrictive_Start(c); +} + +/// +unittest +{ + assert(isAnyStart('ğ')); +} + +/** +Convenience function for use in places where we just don't care, +what the identifier ranges are. + +Returns: is character a member of least restrictive Continue +*/ +bool isAnyContinue(dchar c) +{ + import dmd.common.identifiertables; + return isInRange!LeastRestrictive_Continue(c); +} + +/// +unittest +{ + assert(isAnyContinue('ğ')); +} + +/// UTF line separator +enum LS = 0x2028; +/// UTF paragraph separator +enum PS = 0x2029; + +private +{ + enum CMoctal = 0x1; + enum CMhex = 0x2; + enum CMidchar = 0x4; + enum CMzerosecond = 0x8; + enum CMdigitsecond = 0x10; + enum CMsinglechar = 0x20; +} + +/// +bool isoctal(const char c) +{ + return (cmtable[c] & CMoctal) != 0; +} + +/// +bool ishex(const char c) +{ + return (cmtable[c] & CMhex) != 0; +} + +/// +bool isidchar(const char c) +{ + return (cmtable[c] & CMidchar) != 0; +} + +/// +bool isZeroSecond(const char c) +{ + return (cmtable[c] & CMzerosecond) != 0; +} + +/// +bool isDigitSecond(const char c) +{ + return (cmtable[c] & CMdigitsecond) != 0; +} + +/// +bool issinglechar(const char c) +{ + return (cmtable[c] & CMsinglechar) != 0; +} + +/// +bool c_isxdigit(const int c) +{ + return (( c >= '0' && c <= '9') || + ( c >= 'a' && c <= 'f') || + ( c >= 'A' && c <= 'F')); +} + +/// +bool c_isalnum(const int c) +{ + return (( c >= '0' && c <= '9') || + ( c >= 'a' && c <= 'z') || + ( c >= 'A' && c <= 'Z')); +} + +extern(D) private: + +// originally from dmd.root.utf +bool isInRange(alias Ranges)(dchar c) +{ + size_t high = Ranges.length - 1; + // Shortcut search if c is out of range + size_t low = (c < Ranges[0][0] || Ranges[high][1] < c) ? high + 1 : 0; + // Binary search + while (low <= high) + { + const size_t mid = low + ((high - low) >> 1); + if (c < Ranges[mid][0]) + high = mid - 1; + else if (Ranges[mid][1] < c) + low = mid + 1; + else + { + assert(Ranges[mid][0] <= c && c <= Ranges[mid][1]); + return true; + } + } + return false; +} + +/******************************************** + * Do our own char maps + */ +// originally from dmd.lexer (was private) +static immutable cmtable = () +{ + ubyte[256] table; + foreach (const c; 0 .. table.length) + { + if ('0' <= c && c <= '7') + table[c] |= CMoctal; + if (c_isxdigit(c)) + table[c] |= CMhex; + if (c_isalnum(c) || c == '_') + table[c] |= CMidchar; + + switch (c) + { + case 'x': case 'X': + case 'b': case 'B': + table[c] |= CMzerosecond; + break; + + case '0': .. case '9': + case 'e': case 'E': + case 'f': case 'F': + case 'l': case 'L': + case 'p': case 'P': + case 'u': case 'U': + case 'i': + case '.': + case '_': + table[c] |= CMzerosecond | CMdigitsecond; + break; + + default: + break; + } + + switch (c) + { + case '\\': + case '\n': + case '\r': + case 0: + case 0x1A: + case '\'': + break; + default: + if (!(c & 0x80)) + table[c] |= CMsinglechar; + break; + } + } + return table; +}(); diff --git a/dmd/common/charactertables.h b/dmd/common/charactertables.h new file mode 100644 index 00000000000..4afd9891bb4 --- /dev/null +++ b/dmd/common/charactertables.h @@ -0,0 +1,20 @@ +/** + * Character tables related to identifiers. + * + * Supports UAX31, C99, C11 and least restrictive (All). + * + * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Authors: $(LINK2 https://cattermole.co.nz, Richard (Rikki) Andrew Cattermole) + * 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/charactertables.d, common/charactertables.d) + */ + +#pragma once + +struct IdentifierCharLookup final +{ + bool(*isStart)(char32_t); + bool(*isContinue)(char32_t); + + // constructor not provided here. +}; diff --git a/dmd/common/identifiertables.d b/dmd/common/identifiertables.d new file mode 100644 index 00000000000..b87e9fac094 --- /dev/null +++ b/dmd/common/identifiertables.d @@ -0,0 +1,4241 @@ +// Generated by compiler/tools/unicode_tables.d DO NOT MODIFY!!! +module dmd.common.identifiertables; + +/** +UAX31 profile Start +Entries: 136892 +*/ +static immutable dchar[2][] UAX31_Start = [ + [0xAA, 0xAA], + [0xB5, 0xB5], + [0xBA, 0xBA], + [0xC0, 0xD6], + [0xD8, 0xF6], + [0xF8, 0x2C1], + [0x2C6, 0x2D1], + [0x2E0, 0x2E4], + [0x2EC, 0x2EC], + [0x2EE, 0x2EE], + [0x370, 0x374], + [0x376, 0x377], + [0x37B, 0x37D], + [0x37F, 0x37F], + [0x386, 0x386], + [0x388, 0x38A], + [0x38C, 0x38C], + [0x38E, 0x3A1], + [0x3A3, 0x3F5], + [0x3F7, 0x481], + [0x48A, 0x52F], + [0x531, 0x556], + [0x559, 0x559], + [0x560, 0x588], + [0x5D0, 0x5EA], + [0x5EF, 0x5F2], + [0x620, 0x64A], + [0x66E, 0x66F], + [0x671, 0x6D3], + [0x6D5, 0x6D5], + [0x6E5, 0x6E6], + [0x6EE, 0x6EF], + [0x6FA, 0x6FC], + [0x6FF, 0x6FF], + [0x710, 0x710], + [0x712, 0x72F], + [0x74D, 0x7A5], + [0x7B1, 0x7B1], + [0x7CA, 0x7EA], + [0x7F4, 0x7F5], + [0x7FA, 0x7FA], + [0x800, 0x815], + [0x81A, 0x81A], + [0x824, 0x824], + [0x828, 0x828], + [0x840, 0x858], + [0x860, 0x86A], + [0x870, 0x887], + [0x889, 0x88E], + [0x8A0, 0x8C9], + [0x904, 0x939], + [0x93D, 0x93D], + [0x950, 0x950], + [0x958, 0x961], + [0x971, 0x980], + [0x985, 0x98C], + [0x98F, 0x990], + [0x993, 0x9A8], + [0x9AA, 0x9B0], + [0x9B2, 0x9B2], + [0x9B6, 0x9B9], + [0x9BD, 0x9BD], + [0x9CE, 0x9CE], + [0x9DC, 0x9DD], + [0x9DF, 0x9E1], + [0x9F0, 0x9F1], + [0x9FC, 0x9FC], + [0xA05, 0xA0A], + [0xA0F, 0xA10], + [0xA13, 0xA28], + [0xA2A, 0xA30], + [0xA32, 0xA33], + [0xA35, 0xA36], + [0xA38, 0xA39], + [0xA59, 0xA5C], + [0xA5E, 0xA5E], + [0xA72, 0xA74], + [0xA85, 0xA8D], + [0xA8F, 0xA91], + [0xA93, 0xAA8], + [0xAAA, 0xAB0], + [0xAB2, 0xAB3], + [0xAB5, 0xAB9], + [0xABD, 0xABD], + [0xAD0, 0xAD0], + [0xAE0, 0xAE1], + [0xAF9, 0xAF9], + [0xB05, 0xB0C], + [0xB0F, 0xB10], + [0xB13, 0xB28], + [0xB2A, 0xB30], + [0xB32, 0xB33], + [0xB35, 0xB39], + [0xB3D, 0xB3D], + [0xB5C, 0xB5D], + [0xB5F, 0xB61], + [0xB71, 0xB71], + [0xB83, 0xB83], + [0xB85, 0xB8A], + [0xB8E, 0xB90], + [0xB92, 0xB95], + [0xB99, 0xB9A], + [0xB9C, 0xB9C], + [0xB9E, 0xB9F], + [0xBA3, 0xBA4], + [0xBA8, 0xBAA], + [0xBAE, 0xBB9], + [0xBD0, 0xBD0], + [0xC05, 0xC0C], + [0xC0E, 0xC10], + [0xC12, 0xC28], + [0xC2A, 0xC39], + [0xC3D, 0xC3D], + [0xC58, 0xC5A], + [0xC5D, 0xC5D], + [0xC60, 0xC61], + [0xC80, 0xC80], + [0xC85, 0xC8C], + [0xC8E, 0xC90], + [0xC92, 0xCA8], + [0xCAA, 0xCB3], + [0xCB5, 0xCB9], + [0xCBD, 0xCBD], + [0xCDD, 0xCDE], + [0xCE0, 0xCE1], + [0xCF1, 0xCF2], + [0xD04, 0xD0C], + [0xD0E, 0xD10], + [0xD12, 0xD3A], + [0xD3D, 0xD3D], + [0xD4E, 0xD4E], + [0xD54, 0xD56], + [0xD5F, 0xD61], + [0xD7A, 0xD7F], + [0xD85, 0xD96], + [0xD9A, 0xDB1], + [0xDB3, 0xDBB], + [0xDBD, 0xDBD], + [0xDC0, 0xDC6], + [0xE01, 0xE30], + [0xE32, 0xE32], + [0xE40, 0xE46], + [0xE81, 0xE82], + [0xE84, 0xE84], + [0xE86, 0xE8A], + [0xE8C, 0xEA3], + [0xEA5, 0xEA5], + [0xEA7, 0xEB0], + [0xEB2, 0xEB2], + [0xEBD, 0xEBD], + [0xEC0, 0xEC4], + [0xEC6, 0xEC6], + [0xEDC, 0xEDF], + [0xF00, 0xF00], + [0xF40, 0xF47], + [0xF49, 0xF6C], + [0xF88, 0xF8C], + [0x1000, 0x102A], + [0x103F, 0x103F], + [0x1050, 0x1055], + [0x105A, 0x105D], + [0x1061, 0x1061], + [0x1065, 0x1066], + [0x106E, 0x1070], + [0x1075, 0x1081], + [0x108E, 0x108E], + [0x10A0, 0x10C5], + [0x10C7, 0x10C7], + [0x10CD, 0x10CD], + [0x10D0, 0x10FA], + [0x10FC, 0x1248], + [0x124A, 0x124D], + [0x1250, 0x1256], + [0x1258, 0x1258], + [0x125A, 0x125D], + [0x1260, 0x1288], + [0x128A, 0x128D], + [0x1290, 0x12B0], + [0x12B2, 0x12B5], + [0x12B8, 0x12BE], + [0x12C0, 0x12C0], + [0x12C2, 0x12C5], + [0x12C8, 0x12D6], + [0x12D8, 0x1310], + [0x1312, 0x1315], + [0x1318, 0x135A], + [0x1380, 0x138F], + [0x13A0, 0x13F5], + [0x13F8, 0x13FD], + [0x1401, 0x166C], + [0x166F, 0x167F], + [0x1681, 0x169A], + [0x16A0, 0x16EA], + [0x16EE, 0x16F8], + [0x1700, 0x1711], + [0x171F, 0x1731], + [0x1740, 0x1751], + [0x1760, 0x176C], + [0x176E, 0x1770], + [0x1780, 0x17B3], + [0x17D7, 0x17D7], + [0x17DC, 0x17DC], + [0x1820, 0x1878], + [0x1880, 0x18A8], + [0x18AA, 0x18AA], + [0x18B0, 0x18F5], + [0x1900, 0x191E], + [0x1950, 0x196D], + [0x1970, 0x1974], + [0x1980, 0x19AB], + [0x19B0, 0x19C9], + [0x1A00, 0x1A16], + [0x1A20, 0x1A54], + [0x1AA7, 0x1AA7], + [0x1B05, 0x1B33], + [0x1B45, 0x1B4C], + [0x1B83, 0x1BA0], + [0x1BAE, 0x1BAF], + [0x1BBA, 0x1BE5], + [0x1C00, 0x1C23], + [0x1C4D, 0x1C4F], + [0x1C5A, 0x1C7D], + [0x1C80, 0x1C88], + [0x1C90, 0x1CBA], + [0x1CBD, 0x1CBF], + [0x1CE9, 0x1CEC], + [0x1CEE, 0x1CF3], + [0x1CF5, 0x1CF6], + [0x1CFA, 0x1CFA], + [0x1D00, 0x1DBF], + [0x1E00, 0x1F15], + [0x1F18, 0x1F1D], + [0x1F20, 0x1F45], + [0x1F48, 0x1F4D], + [0x1F50, 0x1F57], + [0x1F59, 0x1F59], + [0x1F5B, 0x1F5B], + [0x1F5D, 0x1F5D], + [0x1F5F, 0x1F7D], + [0x1F80, 0x1FB4], + [0x1FB6, 0x1FBC], + [0x1FBE, 0x1FBE], + [0x1FC2, 0x1FC4], + [0x1FC6, 0x1FCC], + [0x1FD0, 0x1FD3], + [0x1FD6, 0x1FDB], + [0x1FE0, 0x1FEC], + [0x1FF2, 0x1FF4], + [0x1FF6, 0x1FFC], + [0x2071, 0x2071], + [0x207F, 0x207F], + [0x2090, 0x209C], + [0x2102, 0x2102], + [0x2107, 0x2107], + [0x210A, 0x2113], + [0x2115, 0x2115], + [0x2118, 0x211D], + [0x2124, 0x2124], + [0x2126, 0x2126], + [0x2128, 0x2128], + [0x212A, 0x2139], + [0x213C, 0x213F], + [0x2145, 0x2149], + [0x214E, 0x214E], + [0x2160, 0x2188], + [0x2C00, 0x2CE4], + [0x2CEB, 0x2CEE], + [0x2CF2, 0x2CF3], + [0x2D00, 0x2D25], + [0x2D27, 0x2D27], + [0x2D2D, 0x2D2D], + [0x2D30, 0x2D67], + [0x2D6F, 0x2D6F], + [0x2D80, 0x2D96], + [0x2DA0, 0x2DA6], + [0x2DA8, 0x2DAE], + [0x2DB0, 0x2DB6], + [0x2DB8, 0x2DBE], + [0x2DC0, 0x2DC6], + [0x2DC8, 0x2DCE], + [0x2DD0, 0x2DD6], + [0x2DD8, 0x2DDE], + [0x3005, 0x3007], + [0x3021, 0x3029], + [0x3031, 0x3035], + [0x3038, 0x303C], + [0x3041, 0x3096], + [0x309D, 0x309F], + [0x30A1, 0x30FA], + [0x30FC, 0x30FF], + [0x3105, 0x312F], + [0x3131, 0x318E], + [0x31A0, 0x31BF], + [0x31F0, 0x31FF], + [0x3400, 0x4DBF], + [0x4E00, 0xA48C], + [0xA4D0, 0xA4FD], + [0xA500, 0xA60C], + [0xA610, 0xA61F], + [0xA62A, 0xA62B], + [0xA640, 0xA66E], + [0xA67F, 0xA69D], + [0xA6A0, 0xA6EF], + [0xA717, 0xA71F], + [0xA722, 0xA788], + [0xA78B, 0xA7CA], + [0xA7D0, 0xA7D1], + [0xA7D3, 0xA7D3], + [0xA7D5, 0xA7D9], + [0xA7F2, 0xA801], + [0xA803, 0xA805], + [0xA807, 0xA80A], + [0xA80C, 0xA822], + [0xA840, 0xA873], + [0xA882, 0xA8B3], + [0xA8F2, 0xA8F7], + [0xA8FB, 0xA8FB], + [0xA8FD, 0xA8FE], + [0xA90A, 0xA925], + [0xA930, 0xA946], + [0xA960, 0xA97C], + [0xA984, 0xA9B2], + [0xA9CF, 0xA9CF], + [0xA9E0, 0xA9E4], + [0xA9E6, 0xA9EF], + [0xA9FA, 0xA9FE], + [0xAA00, 0xAA28], + [0xAA40, 0xAA42], + [0xAA44, 0xAA4B], + [0xAA60, 0xAA76], + [0xAA7A, 0xAA7A], + [0xAA7E, 0xAAAF], + [0xAAB1, 0xAAB1], + [0xAAB5, 0xAAB6], + [0xAAB9, 0xAABD], + [0xAAC0, 0xAAC0], + [0xAAC2, 0xAAC2], + [0xAADB, 0xAADD], + [0xAAE0, 0xAAEA], + [0xAAF2, 0xAAF4], + [0xAB01, 0xAB06], + [0xAB09, 0xAB0E], + [0xAB11, 0xAB16], + [0xAB20, 0xAB26], + [0xAB28, 0xAB2E], + [0xAB30, 0xAB5A], + [0xAB5C, 0xAB69], + [0xAB70, 0xABE2], + [0xAC00, 0xD7A3], + [0xD7B0, 0xD7C6], + [0xD7CB, 0xD7FB], + [0xF900, 0xFA6D], + [0xFA70, 0xFAD9], + [0xFB00, 0xFB06], + [0xFB13, 0xFB17], + [0xFB1D, 0xFB1D], + [0xFB1F, 0xFB28], + [0xFB2A, 0xFB36], + [0xFB38, 0xFB3C], + [0xFB3E, 0xFB3E], + [0xFB40, 0xFB41], + [0xFB43, 0xFB44], + [0xFB46, 0xFBB1], + [0xFBD3, 0xFC5D], + [0xFC64, 0xFD3D], + [0xFD50, 0xFD8F], + [0xFD92, 0xFDC7], + [0xFDF0, 0xFDF9], + [0xFE71, 0xFE71], + [0xFE73, 0xFE73], + [0xFE77, 0xFE77], + [0xFE79, 0xFE79], + [0xFE7B, 0xFE7B], + [0xFE7D, 0xFE7D], + [0xFE7F, 0xFEFC], + [0xFF21, 0xFF3A], + [0xFF41, 0xFF5A], + [0xFF66, 0xFF9D], + [0xFFA0, 0xFFBE], + [0xFFC2, 0xFFC7], + [0xFFCA, 0xFFCF], + [0xFFD2, 0xFFD7], + [0xFFDA, 0xFFDC], + [0x10000, 0x1000B], + [0x1000D, 0x10026], + [0x10028, 0x1003A], + [0x1003C, 0x1003D], + [0x1003F, 0x1004D], + [0x10050, 0x1005D], + [0x10080, 0x100FA], + [0x10140, 0x10174], + [0x10280, 0x1029C], + [0x102A0, 0x102D0], + [0x10300, 0x1031F], + [0x1032D, 0x1034A], + [0x10350, 0x10375], + [0x10380, 0x1039D], + [0x103A0, 0x103C3], + [0x103C8, 0x103CF], + [0x103D1, 0x103D5], + [0x10400, 0x1049D], + [0x104B0, 0x104D3], + [0x104D8, 0x104FB], + [0x10500, 0x10527], + [0x10530, 0x10563], + [0x10570, 0x1057A], + [0x1057C, 0x1058A], + [0x1058C, 0x10592], + [0x10594, 0x10595], + [0x10597, 0x105A1], + [0x105A3, 0x105B1], + [0x105B3, 0x105B9], + [0x105BB, 0x105BC], + [0x10600, 0x10736], + [0x10740, 0x10755], + [0x10760, 0x10767], + [0x10780, 0x10785], + [0x10787, 0x107B0], + [0x107B2, 0x107BA], + [0x10800, 0x10805], + [0x10808, 0x10808], + [0x1080A, 0x10835], + [0x10837, 0x10838], + [0x1083C, 0x1083C], + [0x1083F, 0x10855], + [0x10860, 0x10876], + [0x10880, 0x1089E], + [0x108E0, 0x108F2], + [0x108F4, 0x108F5], + [0x10900, 0x10915], + [0x10920, 0x10939], + [0x10980, 0x109B7], + [0x109BE, 0x109BF], + [0x10A00, 0x10A00], + [0x10A10, 0x10A13], + [0x10A15, 0x10A17], + [0x10A19, 0x10A35], + [0x10A60, 0x10A7C], + [0x10A80, 0x10A9C], + [0x10AC0, 0x10AC7], + [0x10AC9, 0x10AE4], + [0x10B00, 0x10B35], + [0x10B40, 0x10B55], + [0x10B60, 0x10B72], + [0x10B80, 0x10B91], + [0x10C00, 0x10C48], + [0x10C80, 0x10CB2], + [0x10CC0, 0x10CF2], + [0x10D00, 0x10D23], + [0x10E80, 0x10EA9], + [0x10EB0, 0x10EB1], + [0x10F00, 0x10F1C], + [0x10F27, 0x10F27], + [0x10F30, 0x10F45], + [0x10F70, 0x10F81], + [0x10FB0, 0x10FC4], + [0x10FE0, 0x10FF6], + [0x11003, 0x11037], + [0x11071, 0x11072], + [0x11075, 0x11075], + [0x11083, 0x110AF], + [0x110D0, 0x110E8], + [0x11103, 0x11126], + [0x11144, 0x11144], + [0x11147, 0x11147], + [0x11150, 0x11172], + [0x11176, 0x11176], + [0x11183, 0x111B2], + [0x111C1, 0x111C4], + [0x111DA, 0x111DA], + [0x111DC, 0x111DC], + [0x11200, 0x11211], + [0x11213, 0x1122B], + [0x1123F, 0x11240], + [0x11280, 0x11286], + [0x11288, 0x11288], + [0x1128A, 0x1128D], + [0x1128F, 0x1129D], + [0x1129F, 0x112A8], + [0x112B0, 0x112DE], + [0x11305, 0x1130C], + [0x1130F, 0x11310], + [0x11313, 0x11328], + [0x1132A, 0x11330], + [0x11332, 0x11333], + [0x11335, 0x11339], + [0x1133D, 0x1133D], + [0x11350, 0x11350], + [0x1135D, 0x11361], + [0x11400, 0x11434], + [0x11447, 0x1144A], + [0x1145F, 0x11461], + [0x11480, 0x114AF], + [0x114C4, 0x114C5], + [0x114C7, 0x114C7], + [0x11580, 0x115AE], + [0x115D8, 0x115DB], + [0x11600, 0x1162F], + [0x11644, 0x11644], + [0x11680, 0x116AA], + [0x116B8, 0x116B8], + [0x11700, 0x1171A], + [0x11740, 0x11746], + [0x11800, 0x1182B], + [0x118A0, 0x118DF], + [0x118FF, 0x11906], + [0x11909, 0x11909], + [0x1190C, 0x11913], + [0x11915, 0x11916], + [0x11918, 0x1192F], + [0x1193F, 0x1193F], + [0x11941, 0x11941], + [0x119A0, 0x119A7], + [0x119AA, 0x119D0], + [0x119E1, 0x119E1], + [0x119E3, 0x119E3], + [0x11A00, 0x11A00], + [0x11A0B, 0x11A32], + [0x11A3A, 0x11A3A], + [0x11A50, 0x11A50], + [0x11A5C, 0x11A89], + [0x11A9D, 0x11A9D], + [0x11AB0, 0x11AF8], + [0x11C00, 0x11C08], + [0x11C0A, 0x11C2E], + [0x11C40, 0x11C40], + [0x11C72, 0x11C8F], + [0x11D00, 0x11D06], + [0x11D08, 0x11D09], + [0x11D0B, 0x11D30], + [0x11D46, 0x11D46], + [0x11D60, 0x11D65], + [0x11D67, 0x11D68], + [0x11D6A, 0x11D89], + [0x11D98, 0x11D98], + [0x11EE0, 0x11EF2], + [0x11F02, 0x11F02], + [0x11F04, 0x11F10], + [0x11F12, 0x11F33], + [0x11FB0, 0x11FB0], + [0x12000, 0x12399], + [0x12400, 0x1246E], + [0x12480, 0x12543], + [0x12F90, 0x12FF0], + [0x13000, 0x1342F], + [0x13441, 0x13446], + [0x14400, 0x14646], + [0x16800, 0x16A38], + [0x16A40, 0x16A5E], + [0x16A70, 0x16ABE], + [0x16AD0, 0x16AED], + [0x16B00, 0x16B2F], + [0x16B40, 0x16B43], + [0x16B63, 0x16B77], + [0x16B7D, 0x16B8F], + [0x16E40, 0x16E7F], + [0x16F00, 0x16F4A], + [0x16F50, 0x16F50], + [0x16F93, 0x16F9F], + [0x16FE0, 0x16FE1], + [0x16FE3, 0x16FE3], + [0x17000, 0x187F7], + [0x18800, 0x18CD5], + [0x18D00, 0x18D08], + [0x1AFF0, 0x1AFF3], + [0x1AFF5, 0x1AFFB], + [0x1AFFD, 0x1AFFE], + [0x1B000, 0x1B122], + [0x1B132, 0x1B132], + [0x1B150, 0x1B152], + [0x1B155, 0x1B155], + [0x1B164, 0x1B167], + [0x1B170, 0x1B2FB], + [0x1BC00, 0x1BC6A], + [0x1BC70, 0x1BC7C], + [0x1BC80, 0x1BC88], + [0x1BC90, 0x1BC99], + [0x1D400, 0x1D454], + [0x1D456, 0x1D49C], + [0x1D49E, 0x1D49F], + [0x1D4A2, 0x1D4A2], + [0x1D4A5, 0x1D4A6], + [0x1D4A9, 0x1D4AC], + [0x1D4AE, 0x1D4B9], + [0x1D4BB, 0x1D4BB], + [0x1D4BD, 0x1D4C3], + [0x1D4C5, 0x1D505], + [0x1D507, 0x1D50A], + [0x1D50D, 0x1D514], + [0x1D516, 0x1D51C], + [0x1D51E, 0x1D539], + [0x1D53B, 0x1D53E], + [0x1D540, 0x1D544], + [0x1D546, 0x1D546], + [0x1D54A, 0x1D550], + [0x1D552, 0x1D6A5], + [0x1D6A8, 0x1D6C0], + [0x1D6C2, 0x1D6DA], + [0x1D6DC, 0x1D6FA], + [0x1D6FC, 0x1D714], + [0x1D716, 0x1D734], + [0x1D736, 0x1D74E], + [0x1D750, 0x1D76E], + [0x1D770, 0x1D788], + [0x1D78A, 0x1D7A8], + [0x1D7AA, 0x1D7C2], + [0x1D7C4, 0x1D7CB], + [0x1DF00, 0x1DF1E], + [0x1DF25, 0x1DF2A], + [0x1E030, 0x1E06D], + [0x1E100, 0x1E12C], + [0x1E137, 0x1E13D], + [0x1E14E, 0x1E14E], + [0x1E290, 0x1E2AD], + [0x1E2C0, 0x1E2EB], + [0x1E4D0, 0x1E4EB], + [0x1E7E0, 0x1E7E6], + [0x1E7E8, 0x1E7EB], + [0x1E7ED, 0x1E7EE], + [0x1E7F0, 0x1E7FE], + [0x1E800, 0x1E8C4], + [0x1E900, 0x1E943], + [0x1E94B, 0x1E94B], + [0x1EE00, 0x1EE03], + [0x1EE05, 0x1EE1F], + [0x1EE21, 0x1EE22], + [0x1EE24, 0x1EE24], + [0x1EE27, 0x1EE27], + [0x1EE29, 0x1EE32], + [0x1EE34, 0x1EE37], + [0x1EE39, 0x1EE39], + [0x1EE3B, 0x1EE3B], + [0x1EE42, 0x1EE42], + [0x1EE47, 0x1EE47], + [0x1EE49, 0x1EE49], + [0x1EE4B, 0x1EE4B], + [0x1EE4D, 0x1EE4F], + [0x1EE51, 0x1EE52], + [0x1EE54, 0x1EE54], + [0x1EE57, 0x1EE57], + [0x1EE59, 0x1EE59], + [0x1EE5B, 0x1EE5B], + [0x1EE5D, 0x1EE5D], + [0x1EE5F, 0x1EE5F], + [0x1EE61, 0x1EE62], + [0x1EE64, 0x1EE64], + [0x1EE67, 0x1EE6A], + [0x1EE6C, 0x1EE72], + [0x1EE74, 0x1EE77], + [0x1EE79, 0x1EE7C], + [0x1EE7E, 0x1EE7E], + [0x1EE80, 0x1EE89], + [0x1EE8B, 0x1EE9B], + [0x1EEA1, 0x1EEA3], + [0x1EEA5, 0x1EEA9], + [0x1EEAB, 0x1EEBB], + [0x20000, 0x2A6DF], + [0x2A700, 0x2B739], + [0x2B740, 0x2B81D], + [0x2B820, 0x2CEA1], + [0x2CEB0, 0x2EBE0], + [0x2EBF0, 0x2EE5D], + [0x2F800, 0x2FA1D], + [0x30000, 0x3134A], + [0x31350, 0x323AF], +]; + +/** +UAX31 profile Continue +Entries: 140026 +*/ +static immutable dchar[2][] UAX31_Continue = [ + [0xAA, 0xAA], + [0xB5, 0xB5], + [0xB7, 0xB7], + [0xBA, 0xBA], + [0xC0, 0xD6], + [0xD8, 0xF6], + [0xF8, 0x2C1], + [0x2C6, 0x2D1], + [0x2E0, 0x2E4], + [0x2EC, 0x2EC], + [0x2EE, 0x2EE], + [0x300, 0x374], + [0x376, 0x377], + [0x37B, 0x37D], + [0x37F, 0x37F], + [0x386, 0x38A], + [0x38C, 0x38C], + [0x38E, 0x3A1], + [0x3A3, 0x3F5], + [0x3F7, 0x481], + [0x483, 0x487], + [0x48A, 0x52F], + [0x531, 0x556], + [0x559, 0x559], + [0x560, 0x588], + [0x591, 0x5BD], + [0x5BF, 0x5BF], + [0x5C1, 0x5C2], + [0x5C4, 0x5C5], + [0x5C7, 0x5C7], + [0x5D0, 0x5EA], + [0x5EF, 0x5F2], + [0x610, 0x61A], + [0x620, 0x669], + [0x66E, 0x6D3], + [0x6D5, 0x6DC], + [0x6DF, 0x6E8], + [0x6EA, 0x6FC], + [0x6FF, 0x6FF], + [0x710, 0x74A], + [0x74D, 0x7B1], + [0x7C0, 0x7F5], + [0x7FA, 0x7FA], + [0x7FD, 0x7FD], + [0x800, 0x82D], + [0x840, 0x85B], + [0x860, 0x86A], + [0x870, 0x887], + [0x889, 0x88E], + [0x898, 0x8E1], + [0x8E3, 0x963], + [0x966, 0x96F], + [0x971, 0x983], + [0x985, 0x98C], + [0x98F, 0x990], + [0x993, 0x9A8], + [0x9AA, 0x9B0], + [0x9B2, 0x9B2], + [0x9B6, 0x9B9], + [0x9BC, 0x9C4], + [0x9C7, 0x9C8], + [0x9CB, 0x9CE], + [0x9D7, 0x9D7], + [0x9DC, 0x9DD], + [0x9DF, 0x9E3], + [0x9E6, 0x9F1], + [0x9FC, 0x9FC], + [0x9FE, 0x9FE], + [0xA01, 0xA03], + [0xA05, 0xA0A], + [0xA0F, 0xA10], + [0xA13, 0xA28], + [0xA2A, 0xA30], + [0xA32, 0xA33], + [0xA35, 0xA36], + [0xA38, 0xA39], + [0xA3C, 0xA3C], + [0xA3E, 0xA42], + [0xA47, 0xA48], + [0xA4B, 0xA4D], + [0xA51, 0xA51], + [0xA59, 0xA5C], + [0xA5E, 0xA5E], + [0xA66, 0xA75], + [0xA81, 0xA83], + [0xA85, 0xA8D], + [0xA8F, 0xA91], + [0xA93, 0xAA8], + [0xAAA, 0xAB0], + [0xAB2, 0xAB3], + [0xAB5, 0xAB9], + [0xABC, 0xAC5], + [0xAC7, 0xAC9], + [0xACB, 0xACD], + [0xAD0, 0xAD0], + [0xAE0, 0xAE3], + [0xAE6, 0xAEF], + [0xAF9, 0xAFF], + [0xB01, 0xB03], + [0xB05, 0xB0C], + [0xB0F, 0xB10], + [0xB13, 0xB28], + [0xB2A, 0xB30], + [0xB32, 0xB33], + [0xB35, 0xB39], + [0xB3C, 0xB44], + [0xB47, 0xB48], + [0xB4B, 0xB4D], + [0xB55, 0xB57], + [0xB5C, 0xB5D], + [0xB5F, 0xB63], + [0xB66, 0xB6F], + [0xB71, 0xB71], + [0xB82, 0xB83], + [0xB85, 0xB8A], + [0xB8E, 0xB90], + [0xB92, 0xB95], + [0xB99, 0xB9A], + [0xB9C, 0xB9C], + [0xB9E, 0xB9F], + [0xBA3, 0xBA4], + [0xBA8, 0xBAA], + [0xBAE, 0xBB9], + [0xBBE, 0xBC2], + [0xBC6, 0xBC8], + [0xBCA, 0xBCD], + [0xBD0, 0xBD0], + [0xBD7, 0xBD7], + [0xBE6, 0xBEF], + [0xC00, 0xC0C], + [0xC0E, 0xC10], + [0xC12, 0xC28], + [0xC2A, 0xC39], + [0xC3C, 0xC44], + [0xC46, 0xC48], + [0xC4A, 0xC4D], + [0xC55, 0xC56], + [0xC58, 0xC5A], + [0xC5D, 0xC5D], + [0xC60, 0xC63], + [0xC66, 0xC6F], + [0xC80, 0xC83], + [0xC85, 0xC8C], + [0xC8E, 0xC90], + [0xC92, 0xCA8], + [0xCAA, 0xCB3], + [0xCB5, 0xCB9], + [0xCBC, 0xCC4], + [0xCC6, 0xCC8], + [0xCCA, 0xCCD], + [0xCD5, 0xCD6], + [0xCDD, 0xCDE], + [0xCE0, 0xCE3], + [0xCE6, 0xCEF], + [0xCF1, 0xCF3], + [0xD00, 0xD0C], + [0xD0E, 0xD10], + [0xD12, 0xD44], + [0xD46, 0xD48], + [0xD4A, 0xD4E], + [0xD54, 0xD57], + [0xD5F, 0xD63], + [0xD66, 0xD6F], + [0xD7A, 0xD7F], + [0xD81, 0xD83], + [0xD85, 0xD96], + [0xD9A, 0xDB1], + [0xDB3, 0xDBB], + [0xDBD, 0xDBD], + [0xDC0, 0xDC6], + [0xDCA, 0xDCA], + [0xDCF, 0xDD4], + [0xDD6, 0xDD6], + [0xDD8, 0xDDF], + [0xDE6, 0xDEF], + [0xDF2, 0xDF3], + [0xE01, 0xE3A], + [0xE40, 0xE4E], + [0xE50, 0xE59], + [0xE81, 0xE82], + [0xE84, 0xE84], + [0xE86, 0xE8A], + [0xE8C, 0xEA3], + [0xEA5, 0xEA5], + [0xEA7, 0xEBD], + [0xEC0, 0xEC4], + [0xEC6, 0xEC6], + [0xEC8, 0xECE], + [0xED0, 0xED9], + [0xEDC, 0xEDF], + [0xF00, 0xF00], + [0xF18, 0xF19], + [0xF20, 0xF29], + [0xF35, 0xF35], + [0xF37, 0xF37], + [0xF39, 0xF39], + [0xF3E, 0xF47], + [0xF49, 0xF6C], + [0xF71, 0xF84], + [0xF86, 0xF97], + [0xF99, 0xFBC], + [0xFC6, 0xFC6], + [0x1000, 0x1049], + [0x1050, 0x109D], + [0x10A0, 0x10C5], + [0x10C7, 0x10C7], + [0x10CD, 0x10CD], + [0x10D0, 0x10FA], + [0x10FC, 0x1248], + [0x124A, 0x124D], + [0x1250, 0x1256], + [0x1258, 0x1258], + [0x125A, 0x125D], + [0x1260, 0x1288], + [0x128A, 0x128D], + [0x1290, 0x12B0], + [0x12B2, 0x12B5], + [0x12B8, 0x12BE], + [0x12C0, 0x12C0], + [0x12C2, 0x12C5], + [0x12C8, 0x12D6], + [0x12D8, 0x1310], + [0x1312, 0x1315], + [0x1318, 0x135A], + [0x135D, 0x135F], + [0x1369, 0x1371], + [0x1380, 0x138F], + [0x13A0, 0x13F5], + [0x13F8, 0x13FD], + [0x1401, 0x166C], + [0x166F, 0x167F], + [0x1681, 0x169A], + [0x16A0, 0x16EA], + [0x16EE, 0x16F8], + [0x1700, 0x1715], + [0x171F, 0x1734], + [0x1740, 0x1753], + [0x1760, 0x176C], + [0x176E, 0x1770], + [0x1772, 0x1773], + [0x1780, 0x17D3], + [0x17D7, 0x17D7], + [0x17DC, 0x17DD], + [0x17E0, 0x17E9], + [0x180B, 0x180D], + [0x180F, 0x1819], + [0x1820, 0x1878], + [0x1880, 0x18AA], + [0x18B0, 0x18F5], + [0x1900, 0x191E], + [0x1920, 0x192B], + [0x1930, 0x193B], + [0x1946, 0x196D], + [0x1970, 0x1974], + [0x1980, 0x19AB], + [0x19B0, 0x19C9], + [0x19D0, 0x19DA], + [0x1A00, 0x1A1B], + [0x1A20, 0x1A5E], + [0x1A60, 0x1A7C], + [0x1A7F, 0x1A89], + [0x1A90, 0x1A99], + [0x1AA7, 0x1AA7], + [0x1AB0, 0x1ABD], + [0x1ABF, 0x1ACE], + [0x1B00, 0x1B4C], + [0x1B50, 0x1B59], + [0x1B6B, 0x1B73], + [0x1B80, 0x1BF3], + [0x1C00, 0x1C37], + [0x1C40, 0x1C49], + [0x1C4D, 0x1C7D], + [0x1C80, 0x1C88], + [0x1C90, 0x1CBA], + [0x1CBD, 0x1CBF], + [0x1CD0, 0x1CD2], + [0x1CD4, 0x1CFA], + [0x1D00, 0x1F15], + [0x1F18, 0x1F1D], + [0x1F20, 0x1F45], + [0x1F48, 0x1F4D], + [0x1F50, 0x1F57], + [0x1F59, 0x1F59], + [0x1F5B, 0x1F5B], + [0x1F5D, 0x1F5D], + [0x1F5F, 0x1F7D], + [0x1F80, 0x1FB4], + [0x1FB6, 0x1FBC], + [0x1FBE, 0x1FBE], + [0x1FC2, 0x1FC4], + [0x1FC6, 0x1FCC], + [0x1FD0, 0x1FD3], + [0x1FD6, 0x1FDB], + [0x1FE0, 0x1FEC], + [0x1FF2, 0x1FF4], + [0x1FF6, 0x1FFC], + [0x200C, 0x200D], + [0x203F, 0x2040], + [0x2054, 0x2054], + [0x2071, 0x2071], + [0x207F, 0x207F], + [0x2090, 0x209C], + [0x20D0, 0x20DC], + [0x20E1, 0x20E1], + [0x20E5, 0x20F0], + [0x2102, 0x2102], + [0x2107, 0x2107], + [0x210A, 0x2113], + [0x2115, 0x2115], + [0x2118, 0x211D], + [0x2124, 0x2124], + [0x2126, 0x2126], + [0x2128, 0x2128], + [0x212A, 0x2139], + [0x213C, 0x213F], + [0x2145, 0x2149], + [0x214E, 0x214E], + [0x2160, 0x2188], + [0x2C00, 0x2CE4], + [0x2CEB, 0x2CF3], + [0x2D00, 0x2D25], + [0x2D27, 0x2D27], + [0x2D2D, 0x2D2D], + [0x2D30, 0x2D67], + [0x2D6F, 0x2D6F], + [0x2D7F, 0x2D96], + [0x2DA0, 0x2DA6], + [0x2DA8, 0x2DAE], + [0x2DB0, 0x2DB6], + [0x2DB8, 0x2DBE], + [0x2DC0, 0x2DC6], + [0x2DC8, 0x2DCE], + [0x2DD0, 0x2DD6], + [0x2DD8, 0x2DDE], + [0x2DE0, 0x2DFF], + [0x3005, 0x3007], + [0x3021, 0x302F], + [0x3031, 0x3035], + [0x3038, 0x303C], + [0x3041, 0x3096], + [0x3099, 0x309A], + [0x309D, 0x309F], + [0x30A1, 0x30FF], + [0x3105, 0x312F], + [0x3131, 0x318E], + [0x31A0, 0x31BF], + [0x31F0, 0x31FF], + [0x3400, 0x4DBF], + [0x4E00, 0xA48C], + [0xA4D0, 0xA4FD], + [0xA500, 0xA60C], + [0xA610, 0xA62B], + [0xA640, 0xA66F], + [0xA674, 0xA67D], + [0xA67F, 0xA6F1], + [0xA717, 0xA71F], + [0xA722, 0xA788], + [0xA78B, 0xA7CA], + [0xA7D0, 0xA7D1], + [0xA7D3, 0xA7D3], + [0xA7D5, 0xA7D9], + [0xA7F2, 0xA827], + [0xA82C, 0xA82C], + [0xA840, 0xA873], + [0xA880, 0xA8C5], + [0xA8D0, 0xA8D9], + [0xA8E0, 0xA8F7], + [0xA8FB, 0xA8FB], + [0xA8FD, 0xA92D], + [0xA930, 0xA953], + [0xA960, 0xA97C], + [0xA980, 0xA9C0], + [0xA9CF, 0xA9D9], + [0xA9E0, 0xA9FE], + [0xAA00, 0xAA36], + [0xAA40, 0xAA4D], + [0xAA50, 0xAA59], + [0xAA60, 0xAA76], + [0xAA7A, 0xAAC2], + [0xAADB, 0xAADD], + [0xAAE0, 0xAAEF], + [0xAAF2, 0xAAF6], + [0xAB01, 0xAB06], + [0xAB09, 0xAB0E], + [0xAB11, 0xAB16], + [0xAB20, 0xAB26], + [0xAB28, 0xAB2E], + [0xAB30, 0xAB5A], + [0xAB5C, 0xAB69], + [0xAB70, 0xABEA], + [0xABEC, 0xABED], + [0xABF0, 0xABF9], + [0xAC00, 0xD7A3], + [0xD7B0, 0xD7C6], + [0xD7CB, 0xD7FB], + [0xF900, 0xFA6D], + [0xFA70, 0xFAD9], + [0xFB00, 0xFB06], + [0xFB13, 0xFB17], + [0xFB1D, 0xFB28], + [0xFB2A, 0xFB36], + [0xFB38, 0xFB3C], + [0xFB3E, 0xFB3E], + [0xFB40, 0xFB41], + [0xFB43, 0xFB44], + [0xFB46, 0xFBB1], + [0xFBD3, 0xFC5D], + [0xFC64, 0xFD3D], + [0xFD50, 0xFD8F], + [0xFD92, 0xFDC7], + [0xFDF0, 0xFDF9], + [0xFE00, 0xFE0F], + [0xFE20, 0xFE2F], + [0xFE33, 0xFE34], + [0xFE4D, 0xFE4F], + [0xFE71, 0xFE71], + [0xFE73, 0xFE73], + [0xFE77, 0xFE77], + [0xFE79, 0xFE79], + [0xFE7B, 0xFE7B], + [0xFE7D, 0xFE7D], + [0xFE7F, 0xFEFC], + [0xFF10, 0xFF19], + [0xFF21, 0xFF3A], + [0xFF3F, 0xFF3F], + [0xFF41, 0xFF5A], + [0xFF65, 0xFFBE], + [0xFFC2, 0xFFC7], + [0xFFCA, 0xFFCF], + [0xFFD2, 0xFFD7], + [0xFFDA, 0xFFDC], + [0x10000, 0x1000B], + [0x1000D, 0x10026], + [0x10028, 0x1003A], + [0x1003C, 0x1003D], + [0x1003F, 0x1004D], + [0x10050, 0x1005D], + [0x10080, 0x100FA], + [0x10140, 0x10174], + [0x101FD, 0x101FD], + [0x10280, 0x1029C], + [0x102A0, 0x102D0], + [0x102E0, 0x102E0], + [0x10300, 0x1031F], + [0x1032D, 0x1034A], + [0x10350, 0x1037A], + [0x10380, 0x1039D], + [0x103A0, 0x103C3], + [0x103C8, 0x103CF], + [0x103D1, 0x103D5], + [0x10400, 0x1049D], + [0x104A0, 0x104A9], + [0x104B0, 0x104D3], + [0x104D8, 0x104FB], + [0x10500, 0x10527], + [0x10530, 0x10563], + [0x10570, 0x1057A], + [0x1057C, 0x1058A], + [0x1058C, 0x10592], + [0x10594, 0x10595], + [0x10597, 0x105A1], + [0x105A3, 0x105B1], + [0x105B3, 0x105B9], + [0x105BB, 0x105BC], + [0x10600, 0x10736], + [0x10740, 0x10755], + [0x10760, 0x10767], + [0x10780, 0x10785], + [0x10787, 0x107B0], + [0x107B2, 0x107BA], + [0x10800, 0x10805], + [0x10808, 0x10808], + [0x1080A, 0x10835], + [0x10837, 0x10838], + [0x1083C, 0x1083C], + [0x1083F, 0x10855], + [0x10860, 0x10876], + [0x10880, 0x1089E], + [0x108E0, 0x108F2], + [0x108F4, 0x108F5], + [0x10900, 0x10915], + [0x10920, 0x10939], + [0x10980, 0x109B7], + [0x109BE, 0x109BF], + [0x10A00, 0x10A03], + [0x10A05, 0x10A06], + [0x10A0C, 0x10A13], + [0x10A15, 0x10A17], + [0x10A19, 0x10A35], + [0x10A38, 0x10A3A], + [0x10A3F, 0x10A3F], + [0x10A60, 0x10A7C], + [0x10A80, 0x10A9C], + [0x10AC0, 0x10AC7], + [0x10AC9, 0x10AE6], + [0x10B00, 0x10B35], + [0x10B40, 0x10B55], + [0x10B60, 0x10B72], + [0x10B80, 0x10B91], + [0x10C00, 0x10C48], + [0x10C80, 0x10CB2], + [0x10CC0, 0x10CF2], + [0x10D00, 0x10D27], + [0x10D30, 0x10D39], + [0x10E80, 0x10EA9], + [0x10EAB, 0x10EAC], + [0x10EB0, 0x10EB1], + [0x10EFD, 0x10F1C], + [0x10F27, 0x10F27], + [0x10F30, 0x10F50], + [0x10F70, 0x10F85], + [0x10FB0, 0x10FC4], + [0x10FE0, 0x10FF6], + [0x11000, 0x11046], + [0x11066, 0x11075], + [0x1107F, 0x110BA], + [0x110C2, 0x110C2], + [0x110D0, 0x110E8], + [0x110F0, 0x110F9], + [0x11100, 0x11134], + [0x11136, 0x1113F], + [0x11144, 0x11147], + [0x11150, 0x11173], + [0x11176, 0x11176], + [0x11180, 0x111C4], + [0x111C9, 0x111CC], + [0x111CE, 0x111DA], + [0x111DC, 0x111DC], + [0x11200, 0x11211], + [0x11213, 0x11237], + [0x1123E, 0x11241], + [0x11280, 0x11286], + [0x11288, 0x11288], + [0x1128A, 0x1128D], + [0x1128F, 0x1129D], + [0x1129F, 0x112A8], + [0x112B0, 0x112EA], + [0x112F0, 0x112F9], + [0x11300, 0x11303], + [0x11305, 0x1130C], + [0x1130F, 0x11310], + [0x11313, 0x11328], + [0x1132A, 0x11330], + [0x11332, 0x11333], + [0x11335, 0x11339], + [0x1133B, 0x11344], + [0x11347, 0x11348], + [0x1134B, 0x1134D], + [0x11350, 0x11350], + [0x11357, 0x11357], + [0x1135D, 0x11363], + [0x11366, 0x1136C], + [0x11370, 0x11374], + [0x11400, 0x1144A], + [0x11450, 0x11459], + [0x1145E, 0x11461], + [0x11480, 0x114C5], + [0x114C7, 0x114C7], + [0x114D0, 0x114D9], + [0x11580, 0x115B5], + [0x115B8, 0x115C0], + [0x115D8, 0x115DD], + [0x11600, 0x11640], + [0x11644, 0x11644], + [0x11650, 0x11659], + [0x11680, 0x116B8], + [0x116C0, 0x116C9], + [0x11700, 0x1171A], + [0x1171D, 0x1172B], + [0x11730, 0x11739], + [0x11740, 0x11746], + [0x11800, 0x1183A], + [0x118A0, 0x118E9], + [0x118FF, 0x11906], + [0x11909, 0x11909], + [0x1190C, 0x11913], + [0x11915, 0x11916], + [0x11918, 0x11935], + [0x11937, 0x11938], + [0x1193B, 0x11943], + [0x11950, 0x11959], + [0x119A0, 0x119A7], + [0x119AA, 0x119D7], + [0x119DA, 0x119E1], + [0x119E3, 0x119E4], + [0x11A00, 0x11A3E], + [0x11A47, 0x11A47], + [0x11A50, 0x11A99], + [0x11A9D, 0x11A9D], + [0x11AB0, 0x11AF8], + [0x11C00, 0x11C08], + [0x11C0A, 0x11C36], + [0x11C38, 0x11C40], + [0x11C50, 0x11C59], + [0x11C72, 0x11C8F], + [0x11C92, 0x11CA7], + [0x11CA9, 0x11CB6], + [0x11D00, 0x11D06], + [0x11D08, 0x11D09], + [0x11D0B, 0x11D36], + [0x11D3A, 0x11D3A], + [0x11D3C, 0x11D3D], + [0x11D3F, 0x11D47], + [0x11D50, 0x11D59], + [0x11D60, 0x11D65], + [0x11D67, 0x11D68], + [0x11D6A, 0x11D8E], + [0x11D90, 0x11D91], + [0x11D93, 0x11D98], + [0x11DA0, 0x11DA9], + [0x11EE0, 0x11EF6], + [0x11F00, 0x11F10], + [0x11F12, 0x11F3A], + [0x11F3E, 0x11F42], + [0x11F50, 0x11F59], + [0x11FB0, 0x11FB0], + [0x12000, 0x12399], + [0x12400, 0x1246E], + [0x12480, 0x12543], + [0x12F90, 0x12FF0], + [0x13000, 0x1342F], + [0x13440, 0x13455], + [0x14400, 0x14646], + [0x16800, 0x16A38], + [0x16A40, 0x16A5E], + [0x16A60, 0x16A69], + [0x16A70, 0x16ABE], + [0x16AC0, 0x16AC9], + [0x16AD0, 0x16AED], + [0x16AF0, 0x16AF4], + [0x16B00, 0x16B36], + [0x16B40, 0x16B43], + [0x16B50, 0x16B59], + [0x16B63, 0x16B77], + [0x16B7D, 0x16B8F], + [0x16E40, 0x16E7F], + [0x16F00, 0x16F4A], + [0x16F4F, 0x16F87], + [0x16F8F, 0x16F9F], + [0x16FE0, 0x16FE1], + [0x16FE3, 0x16FE4], + [0x16FF0, 0x16FF1], + [0x17000, 0x187F7], + [0x18800, 0x18CD5], + [0x18D00, 0x18D08], + [0x1AFF0, 0x1AFF3], + [0x1AFF5, 0x1AFFB], + [0x1AFFD, 0x1AFFE], + [0x1B000, 0x1B122], + [0x1B132, 0x1B132], + [0x1B150, 0x1B152], + [0x1B155, 0x1B155], + [0x1B164, 0x1B167], + [0x1B170, 0x1B2FB], + [0x1BC00, 0x1BC6A], + [0x1BC70, 0x1BC7C], + [0x1BC80, 0x1BC88], + [0x1BC90, 0x1BC99], + [0x1BC9D, 0x1BC9E], + [0x1CF00, 0x1CF2D], + [0x1CF30, 0x1CF46], + [0x1D165, 0x1D169], + [0x1D16D, 0x1D172], + [0x1D17B, 0x1D182], + [0x1D185, 0x1D18B], + [0x1D1AA, 0x1D1AD], + [0x1D242, 0x1D244], + [0x1D400, 0x1D454], + [0x1D456, 0x1D49C], + [0x1D49E, 0x1D49F], + [0x1D4A2, 0x1D4A2], + [0x1D4A5, 0x1D4A6], + [0x1D4A9, 0x1D4AC], + [0x1D4AE, 0x1D4B9], + [0x1D4BB, 0x1D4BB], + [0x1D4BD, 0x1D4C3], + [0x1D4C5, 0x1D505], + [0x1D507, 0x1D50A], + [0x1D50D, 0x1D514], + [0x1D516, 0x1D51C], + [0x1D51E, 0x1D539], + [0x1D53B, 0x1D53E], + [0x1D540, 0x1D544], + [0x1D546, 0x1D546], + [0x1D54A, 0x1D550], + [0x1D552, 0x1D6A5], + [0x1D6A8, 0x1D6C0], + [0x1D6C2, 0x1D6DA], + [0x1D6DC, 0x1D6FA], + [0x1D6FC, 0x1D714], + [0x1D716, 0x1D734], + [0x1D736, 0x1D74E], + [0x1D750, 0x1D76E], + [0x1D770, 0x1D788], + [0x1D78A, 0x1D7A8], + [0x1D7AA, 0x1D7C2], + [0x1D7C4, 0x1D7CB], + [0x1D7CE, 0x1D7FF], + [0x1DA00, 0x1DA36], + [0x1DA3B, 0x1DA6C], + [0x1DA75, 0x1DA75], + [0x1DA84, 0x1DA84], + [0x1DA9B, 0x1DA9F], + [0x1DAA1, 0x1DAAF], + [0x1DF00, 0x1DF1E], + [0x1DF25, 0x1DF2A], + [0x1E000, 0x1E006], + [0x1E008, 0x1E018], + [0x1E01B, 0x1E021], + [0x1E023, 0x1E024], + [0x1E026, 0x1E02A], + [0x1E030, 0x1E06D], + [0x1E08F, 0x1E08F], + [0x1E100, 0x1E12C], + [0x1E130, 0x1E13D], + [0x1E140, 0x1E149], + [0x1E14E, 0x1E14E], + [0x1E290, 0x1E2AE], + [0x1E2C0, 0x1E2F9], + [0x1E4D0, 0x1E4F9], + [0x1E7E0, 0x1E7E6], + [0x1E7E8, 0x1E7EB], + [0x1E7ED, 0x1E7EE], + [0x1E7F0, 0x1E7FE], + [0x1E800, 0x1E8C4], + [0x1E8D0, 0x1E8D6], + [0x1E900, 0x1E94B], + [0x1E950, 0x1E959], + [0x1EE00, 0x1EE03], + [0x1EE05, 0x1EE1F], + [0x1EE21, 0x1EE22], + [0x1EE24, 0x1EE24], + [0x1EE27, 0x1EE27], + [0x1EE29, 0x1EE32], + [0x1EE34, 0x1EE37], + [0x1EE39, 0x1EE39], + [0x1EE3B, 0x1EE3B], + [0x1EE42, 0x1EE42], + [0x1EE47, 0x1EE47], + [0x1EE49, 0x1EE49], + [0x1EE4B, 0x1EE4B], + [0x1EE4D, 0x1EE4F], + [0x1EE51, 0x1EE52], + [0x1EE54, 0x1EE54], + [0x1EE57, 0x1EE57], + [0x1EE59, 0x1EE59], + [0x1EE5B, 0x1EE5B], + [0x1EE5D, 0x1EE5D], + [0x1EE5F, 0x1EE5F], + [0x1EE61, 0x1EE62], + [0x1EE64, 0x1EE64], + [0x1EE67, 0x1EE6A], + [0x1EE6C, 0x1EE72], + [0x1EE74, 0x1EE77], + [0x1EE79, 0x1EE7C], + [0x1EE7E, 0x1EE7E], + [0x1EE80, 0x1EE89], + [0x1EE8B, 0x1EE9B], + [0x1EEA1, 0x1EEA3], + [0x1EEA5, 0x1EEA9], + [0x1EEAB, 0x1EEBB], + [0x1FBF0, 0x1FBF9], + [0x20000, 0x2A6DF], + [0x2A700, 0x2B739], + [0x2B740, 0x2B81D], + [0x2B820, 0x2CEA1], + [0x2CEB0, 0x2EBE0], + [0x2EBF0, 0x2EE5D], + [0x2F800, 0x2FA1D], + [0x30000, 0x3134A], + [0x31350, 0x323AF], + [0xE0100, 0xE01EF], +]; + +/** +C99 Start +Entries: 34958 +*/ +alias FixedTable_C99_Start = FixedTable_C99_Continue; + +/** +C99 Continue +Entries: 34958 +*/ +static immutable dchar[2][] FixedTable_C99_Continue = [ + [0xAA, 0xAA], + [0xB5, 0xB5], + [0xB7, 0xB7], + [0xBA, 0xBA], + [0xC0, 0xD6], + [0xD8, 0xF6], + [0xF8, 0x1F5], + [0x1FA, 0x217], + [0x250, 0x2A8], + [0x2B0, 0x2B8], + [0x2BB, 0x2BB], + [0x2BD, 0x2C1], + [0x2D0, 0x2D1], + [0x2E0, 0x2E4], + [0x37A, 0x37A], + [0x386, 0x386], + [0x388, 0x38A], + [0x38C, 0x38C], + [0x38E, 0x3A1], + [0x3A3, 0x3CE], + [0x3D0, 0x3D6], + [0x3DA, 0x3DA], + [0x3DC, 0x3DC], + [0x3DE, 0x3DE], + [0x3E0, 0x3E0], + [0x3E2, 0x3F3], + [0x401, 0x40C], + [0x40E, 0x44F], + [0x451, 0x45C], + [0x45E, 0x481], + [0x490, 0x4C4], + [0x4C7, 0x4C8], + [0x4CB, 0x4CC], + [0x4D0, 0x4EB], + [0x4EE, 0x4F5], + [0x4F8, 0x4F9], + [0x531, 0x556], + [0x559, 0x559], + [0x561, 0x587], + [0x5B0, 0x5B9], + [0x5BB, 0x5BD], + [0x5BF, 0x5BF], + [0x5C1, 0x5C2], + [0x5D0, 0x5EA], + [0x5F0, 0x5F2], + [0x621, 0x63A], + [0x640, 0x652], + [0x660, 0x669], + [0x670, 0x6B7], + [0x6BA, 0x6BE], + [0x6C0, 0x6CE], + [0x6D0, 0x6DC], + [0x6E5, 0x6E8], + [0x6EA, 0x6ED], + [0x6F0, 0x6F9], + [0x901, 0x903], + [0x905, 0x939], + [0x93D, 0x94D], + [0x950, 0x952], + [0x958, 0x963], + [0x966, 0x96F], + [0x981, 0x983], + [0x985, 0x98C], + [0x98F, 0x990], + [0x993, 0x9A8], + [0x9AA, 0x9B0], + [0x9B2, 0x9B2], + [0x9B6, 0x9B9], + [0x9BE, 0x9C4], + [0x9C7, 0x9C8], + [0x9CB, 0x9CD], + [0x9DC, 0x9DD], + [0x9DF, 0x9E3], + [0x9E6, 0x9F1], + [0xA02, 0xA02], + [0xA05, 0xA0A], + [0xA0F, 0xA10], + [0xA13, 0xA28], + [0xA2A, 0xA30], + [0xA32, 0xA33], + [0xA35, 0xA36], + [0xA38, 0xA39], + [0xA3E, 0xA42], + [0xA47, 0xA48], + [0xA4B, 0xA4D], + [0xA59, 0xA5C], + [0xA5E, 0xA5E], + [0xA66, 0xA6F], + [0xA74, 0xA74], + [0xA81, 0xA83], + [0xA85, 0xA8B], + [0xA8D, 0xA8D], + [0xA8F, 0xA91], + [0xA93, 0xAA8], + [0xAAA, 0xAB0], + [0xAB2, 0xAB3], + [0xAB5, 0xAB9], + [0xABD, 0xAC5], + [0xAC7, 0xAC9], + [0xACB, 0xACD], + [0xAD0, 0xAD0], + [0xAE0, 0xAE0], + [0xAE6, 0xAEF], + [0xB01, 0xB03], + [0xB05, 0xB0C], + [0xB0F, 0xB10], + [0xB13, 0xB28], + [0xB2A, 0xB30], + [0xB32, 0xB33], + [0xB36, 0xB39], + [0xB3D, 0xB43], + [0xB47, 0xB48], + [0xB4B, 0xB4D], + [0xB5C, 0xB5D], + [0xB5F, 0xB61], + [0xB66, 0xB6F], + [0xB82, 0xB83], + [0xB85, 0xB8A], + [0xB8E, 0xB90], + [0xB92, 0xB95], + [0xB99, 0xB9A], + [0xB9C, 0xB9C], + [0xB9E, 0xB9F], + [0xBA3, 0xBA4], + [0xBA8, 0xBAA], + [0xBAE, 0xBB5], + [0xBB7, 0xBB9], + [0xBBE, 0xBC2], + [0xBC6, 0xBC8], + [0xBCA, 0xBCD], + [0xBE7, 0xBEF], + [0xC01, 0xC03], + [0xC05, 0xC0C], + [0xC0E, 0xC10], + [0xC12, 0xC28], + [0xC2A, 0xC33], + [0xC35, 0xC39], + [0xC3E, 0xC44], + [0xC46, 0xC48], + [0xC4A, 0xC4D], + [0xC60, 0xC61], + [0xC66, 0xC6F], + [0xC82, 0xC83], + [0xC85, 0xC8C], + [0xC8E, 0xC90], + [0xC92, 0xCA8], + [0xCAA, 0xCB3], + [0xCB5, 0xCB9], + [0xCBE, 0xCC4], + [0xCC6, 0xCC8], + [0xCCA, 0xCCD], + [0xCDE, 0xCDE], + [0xCE0, 0xCE1], + [0xCE6, 0xCEF], + [0xD02, 0xD03], + [0xD05, 0xD0C], + [0xD0E, 0xD10], + [0xD12, 0xD28], + [0xD2A, 0xD39], + [0xD3E, 0xD43], + [0xD46, 0xD48], + [0xD4A, 0xD4D], + [0xD60, 0xD61], + [0xD66, 0xD6F], + [0xE01, 0xE3A], + [0xE40, 0xE5B], + [0xE81, 0xE82], + [0xE84, 0xE84], + [0xE87, 0xE88], + [0xE8A, 0xE8A], + [0xE8D, 0xE8D], + [0xE94, 0xE97], + [0xE99, 0xE9F], + [0xEA1, 0xEA3], + [0xEA5, 0xEA5], + [0xEA7, 0xEA7], + [0xEAA, 0xEAB], + [0xEAD, 0xEAE], + [0xEB0, 0xEB9], + [0xEBB, 0xEBD], + [0xEC0, 0xEC4], + [0xEC6, 0xEC6], + [0xEC8, 0xECD], + [0xED0, 0xED9], + [0xEDC, 0xEDD], + [0xF00, 0xF00], + [0xF18, 0xF19], + [0xF20, 0xF33], + [0xF35, 0xF35], + [0xF37, 0xF37], + [0xF39, 0xF39], + [0xF3E, 0xF47], + [0xF49, 0xF69], + [0xF71, 0xF84], + [0xF86, 0xF8B], + [0xF90, 0xF95], + [0xF97, 0xF97], + [0xF99, 0xFAD], + [0xFB1, 0xFB7], + [0xFB9, 0xFB9], + [0x10A0, 0x10C5], + [0x10D0, 0x10F6], + [0x1E00, 0x1E9B], + [0x1EA0, 0x1EF9], + [0x1F00, 0x1F15], + [0x1F18, 0x1F1D], + [0x1F20, 0x1F45], + [0x1F48, 0x1F4D], + [0x1F50, 0x1F57], + [0x1F59, 0x1F59], + [0x1F5B, 0x1F5B], + [0x1F5D, 0x1F5D], + [0x1F5F, 0x1F7D], + [0x1F80, 0x1FB4], + [0x1FB6, 0x1FBC], + [0x1FBE, 0x1FBE], + [0x1FC2, 0x1FC4], + [0x1FC6, 0x1FCC], + [0x1FD0, 0x1FD3], + [0x1FD6, 0x1FDB], + [0x1FE0, 0x1FEC], + [0x1FF2, 0x1FF4], + [0x1FF6, 0x1FFC], + [0x203F, 0x2040], + [0x207F, 0x207F], + [0x2102, 0x2102], + [0x2107, 0x2107], + [0x210A, 0x2113], + [0x2115, 0x2115], + [0x2118, 0x211D], + [0x2124, 0x2124], + [0x2126, 0x2126], + [0x2128, 0x2128], + [0x212A, 0x2131], + [0x2133, 0x2138], + [0x2160, 0x2182], + [0x3005, 0x3007], + [0x3021, 0x3029], + [0x3041, 0x3093], + [0x309B, 0x309C], + [0x30A1, 0x30F6], + [0x30FB, 0x30FC], + [0x3105, 0x312C], + [0x4E00, 0x9FA5], + [0xAC00, 0xD7A3], +]; + +/** +C11 Start +Entries: 971620 +*/ +alias FixedTable_C11_Start = FixedTable_C11_Continue; + +/** +C11 Continue +Entries: 971620 +*/ +static immutable dchar[2][] FixedTable_C11_Continue = [ + [0xA8, 0xA8], + [0xAA, 0xAA], + [0xAD, 0xAD], + [0xAF, 0xAF], + [0xB2, 0xB5], + [0xB7, 0xBA], + [0xBC, 0xBE], + [0xC0, 0xD6], + [0xD8, 0xF6], + [0xF8, 0xFF], + [0x100, 0x167F], + [0x1681, 0x180D], + [0x180F, 0x1FFF], + [0x200B, 0x200D], + [0x202A, 0x202E], + [0x203F, 0x2040], + [0x2054, 0x2054], + [0x2060, 0x206F], + [0x2070, 0x218F], + [0x2460, 0x24FF], + [0x2776, 0x2793], + [0x2C00, 0x2DFF], + [0x2E80, 0x2FFF], + [0x3004, 0x3007], + [0x3021, 0x302F], + [0x3031, 0x303F], + [0x3040, 0xD7FF], + [0xF900, 0xFD3D], + [0xFD40, 0xFDCF], + [0xFDF0, 0xFE44], + [0xFE47, 0xFFFD], + [0x10000, 0x1FFFD], + [0x20000, 0x2FFFD], + [0x30000, 0x3FFFD], + [0x40000, 0x4FFFD], + [0x50000, 0x5FFFD], + [0x60000, 0x6FFFD], + [0x70000, 0x7FFFD], + [0x80000, 0x8FFFD], + [0x90000, 0x9FFFD], + [0xA0000, 0xAFFFD], + [0xB0000, 0xBFFFD], + [0xC0000, 0xCFFFD], + [0xD0000, 0xDFFFD], + [0xE0000, 0xEFFFD], +]; + +/** +Least restrictive with both Start and Continue +Entries: 860486 +*/ +static immutable dchar[2][] LeastRestrictive_OfAll = [ + [0xA8, 0xA8], + [0xAA, 0xAA], + [0xAD, 0xAD], + [0xAF, 0xAF], + [0xB2, 0xB5], + [0xB7, 0xBA], + [0xBC, 0xBE], + [0xC0, 0xD6], + [0xD8, 0xF6], + [0xF8, 0x217], + [0x250, 0x2A8], + [0x2B0, 0x2B8], + [0x2BB, 0x2BB], + [0x2BD, 0x2C1], + [0x2C6, 0x2D1], + [0x2E0, 0x2E4], + [0x2EC, 0x2EC], + [0x2EE, 0x2EE], + [0x300, 0x374], + [0x376, 0x377], + [0x37A, 0x37D], + [0x37F, 0x37F], + [0x386, 0x386], + [0x388, 0x38A], + [0x38C, 0x38C], + [0x38E, 0x3A1], + [0x3A3, 0x3D6], + [0x3DA, 0x3DA], + [0x3DC, 0x3DC], + [0x3DE, 0x3DE], + [0x3E0, 0x3E0], + [0x3E2, 0x3F3], + [0x3F7, 0x40C], + [0x40E, 0x44F], + [0x451, 0x45C], + [0x45E, 0x481], + [0x483, 0x487], + [0x48A, 0x4C4], + [0x4C7, 0x4C8], + [0x4CB, 0x4CC], + [0x4D0, 0x4EB], + [0x4EE, 0x4F5], + [0x4F8, 0x4F9], + [0x531, 0x556], + [0x559, 0x559], + [0x560, 0x587], + [0x591, 0x5B9], + [0x5BB, 0x5BD], + [0x5BF, 0x5BF], + [0x5C1, 0x5C2], + [0x5C4, 0x5C5], + [0x5C7, 0x5C7], + [0x5D0, 0x5EA], + [0x5EF, 0x5F2], + [0x610, 0x61A], + [0x620, 0x63A], + [0x640, 0x652], + [0x660, 0x669], + [0x66E, 0x6BE], + [0x6C0, 0x6CE], + [0x6D0, 0x6D5], + [0x6DF, 0x6E8], + [0x6EA, 0x6F9], + [0x6FF, 0x6FF], + [0x710, 0x710], + [0x712, 0x72F], + [0x74D, 0x7A5], + [0x7B1, 0x7B1], + [0x7C0, 0x7EA], + [0x7F4, 0x7F5], + [0x7FA, 0x7FA], + [0x7FD, 0x7FD], + [0x800, 0x815], + [0x81A, 0x81A], + [0x824, 0x824], + [0x828, 0x828], + [0x840, 0x858], + [0x860, 0x86A], + [0x870, 0x887], + [0x889, 0x88E], + [0x898, 0x8C9], + [0x8E3, 0x939], + [0x93D, 0x94D], + [0x950, 0x952], + [0x958, 0x963], + [0x966, 0x96F], + [0x971, 0x983], + [0x985, 0x98C], + [0x98F, 0x990], + [0x993, 0x9A8], + [0x9AA, 0x9B0], + [0x9B2, 0x9B2], + [0x9B6, 0x9B9], + [0x9BC, 0x9C4], + [0x9C7, 0x9C8], + [0x9CB, 0x9CE], + [0x9D7, 0x9D7], + [0x9DC, 0x9DD], + [0x9DF, 0x9E3], + [0x9E6, 0x9F1], + [0x9FC, 0x9FC], + [0x9FE, 0x9FE], + [0xA01, 0xA02], + [0xA05, 0xA0A], + [0xA0F, 0xA10], + [0xA13, 0xA28], + [0xA2A, 0xA30], + [0xA32, 0xA33], + [0xA35, 0xA36], + [0xA38, 0xA39], + [0xA3C, 0xA3C], + [0xA3E, 0xA42], + [0xA47, 0xA48], + [0xA4B, 0xA4D], + [0xA51, 0xA51], + [0xA59, 0xA5C], + [0xA5E, 0xA5E], + [0xA66, 0xA6F], + [0xA72, 0xA74], + [0xA81, 0xA83], + [0xA85, 0xA8B], + [0xA8D, 0xA8D], + [0xA8F, 0xA91], + [0xA93, 0xAA8], + [0xAAA, 0xAB0], + [0xAB2, 0xAB3], + [0xAB5, 0xAB9], + [0xABC, 0xABD], + [0xAC7, 0xAC9], + [0xACB, 0xACD], + [0xAD0, 0xAD0], + [0xAE0, 0xAE0], + [0xAE6, 0xAEF], + [0xAF9, 0xAFF], + [0xB01, 0xB03], + [0xB05, 0xB0C], + [0xB0F, 0xB10], + [0xB13, 0xB28], + [0xB2A, 0xB30], + [0xB32, 0xB33], + [0xB35, 0xB39], + [0xB3C, 0xB43], + [0xB47, 0xB48], + [0xB4B, 0xB4D], + [0xB55, 0xB57], + [0xB5C, 0xB5D], + [0xB5F, 0xB61], + [0xB66, 0xB6F], + [0xB71, 0xB71], + [0xB82, 0xB83], + [0xB85, 0xB8A], + [0xB8E, 0xB90], + [0xB92, 0xB95], + [0xB99, 0xB9A], + [0xB9C, 0xB9C], + [0xB9E, 0xB9F], + [0xBA3, 0xBA4], + [0xBA8, 0xBAA], + [0xBAE, 0xBB5], + [0xBB7, 0xBB9], + [0xBBE, 0xBC2], + [0xBC6, 0xBC8], + [0xBCA, 0xBCD], + [0xBD0, 0xBD0], + [0xBD7, 0xBD7], + [0xBE6, 0xBEF], + [0xC00, 0xC03], + [0xC05, 0xC0C], + [0xC0E, 0xC10], + [0xC12, 0xC28], + [0xC2A, 0xC33], + [0xC35, 0xC39], + [0xC3C, 0xC44], + [0xC46, 0xC48], + [0xC4A, 0xC4D], + [0xC55, 0xC56], + [0xC58, 0xC5A], + [0xC5D, 0xC5D], + [0xC60, 0xC61], + [0xC66, 0xC6F], + [0xC80, 0xC83], + [0xC85, 0xC8C], + [0xC8E, 0xC90], + [0xC92, 0xCA8], + [0xCAA, 0xCB3], + [0xCB5, 0xCB9], + [0xCBC, 0xCC4], + [0xCC6, 0xCC8], + [0xCCA, 0xCCD], + [0xCD5, 0xCD6], + [0xCDD, 0xCDE], + [0xCE0, 0xCE1], + [0xCE6, 0xCEF], + [0xCF1, 0xCF2], + [0xD00, 0xD0C], + [0xD0E, 0xD10], + [0xD12, 0xD39], + [0xD3D, 0xD43], + [0xD46, 0xD48], + [0xD4A, 0xD4E], + [0xD54, 0xD57], + [0xD5F, 0xD61], + [0xD66, 0xD6F], + [0xD7A, 0xD7F], + [0xD81, 0xD83], + [0xD85, 0xD96], + [0xD9A, 0xDB1], + [0xDB3, 0xDBB], + [0xDBD, 0xDBD], + [0xDC0, 0xDC6], + [0xDCA, 0xDCA], + [0xDCF, 0xDD4], + [0xDD6, 0xDD6], + [0xDD8, 0xDDF], + [0xDE6, 0xDEF], + [0xDF2, 0xDF3], + [0xE01, 0xE32], + [0xE40, 0xE4E], + [0xE50, 0xE59], + [0xE81, 0xE82], + [0xE84, 0xE84], + [0xE86, 0xE88], + [0xE8A, 0xE8A], + [0xE8C, 0xE8D], + [0xE94, 0xE97], + [0xE99, 0xE9F], + [0xEA1, 0xEA3], + [0xEA5, 0xEA5], + [0xEA7, 0xEAB], + [0xEAD, 0xEAE], + [0xEB0, 0xEB9], + [0xEBB, 0xEBD], + [0xEC0, 0xEC4], + [0xEC6, 0xEC6], + [0xEC8, 0xECE], + [0xED0, 0xED9], + [0xEDC, 0xEDF], + [0xF00, 0xF00], + [0xF18, 0xF19], + [0xF20, 0xF29], + [0xF35, 0xF35], + [0xF37, 0xF37], + [0xF39, 0xF39], + [0xF3E, 0xF47], + [0xF49, 0xF6C], + [0xF71, 0xF84], + [0xF86, 0xF95], + [0xF97, 0xF97], + [0xF99, 0xFB7], + [0xFB9, 0xFB9], + [0xFC6, 0xFC6], + [0x1000, 0x103F], + [0x1050, 0x105D], + [0x1061, 0x1061], + [0x1065, 0x1066], + [0x106E, 0x1070], + [0x1075, 0x1081], + [0x108E, 0x108E], + [0x10A0, 0x10C5], + [0x10C7, 0x10C7], + [0x10CD, 0x10CD], + [0x10D0, 0x10FA], + [0x10FC, 0x1248], + [0x124A, 0x124D], + [0x1250, 0x1256], + [0x1258, 0x1258], + [0x125A, 0x125D], + [0x1260, 0x1288], + [0x128A, 0x128D], + [0x1290, 0x12B0], + [0x12B2, 0x12B5], + [0x12B8, 0x12BE], + [0x12C0, 0x12C0], + [0x12C2, 0x12C5], + [0x12C8, 0x12D6], + [0x12D8, 0x1310], + [0x1312, 0x1315], + [0x1318, 0x135A], + [0x135D, 0x135F], + [0x1369, 0x1371], + [0x1380, 0x138F], + [0x13A0, 0x13F5], + [0x13F8, 0x13FD], + [0x1401, 0x166C], + [0x166F, 0x167F], + [0x1681, 0x169A], + [0x16A0, 0x16EA], + [0x16EE, 0x16F8], + [0x1700, 0x1711], + [0x171F, 0x1731], + [0x1740, 0x1751], + [0x1760, 0x176C], + [0x176E, 0x1770], + [0x1772, 0x1773], + [0x1780, 0x17B3], + [0x17D7, 0x17D7], + [0x17DC, 0x17DC], + [0x17E0, 0x17E9], + [0x180B, 0x180D], + [0x180F, 0x1878], + [0x1880, 0x18A8], + [0x18AA, 0x18AA], + [0x18B0, 0x18F5], + [0x1900, 0x191E], + [0x1920, 0x192B], + [0x1930, 0x193B], + [0x1946, 0x196D], + [0x1970, 0x1974], + [0x1980, 0x19AB], + [0x19B0, 0x19C9], + [0x19D0, 0x19DA], + [0x1A00, 0x1A16], + [0x1A20, 0x1A54], + [0x1A60, 0x1A7C], + [0x1A7F, 0x1A89], + [0x1A90, 0x1A99], + [0x1AA7, 0x1AA7], + [0x1AB0, 0x1ABD], + [0x1ABF, 0x1ACE], + [0x1B00, 0x1B33], + [0x1B45, 0x1B4C], + [0x1B50, 0x1B59], + [0x1B6B, 0x1B73], + [0x1B80, 0x1BA0], + [0x1BAE, 0x1BAF], + [0x1BBA, 0x1BE5], + [0x1C00, 0x1C37], + [0x1C40, 0x1C49], + [0x1C4D, 0x1C7D], + [0x1C80, 0x1C88], + [0x1C90, 0x1CBA], + [0x1CBD, 0x1CBF], + [0x1CD0, 0x1CD2], + [0x1CD4, 0x1CEC], + [0x1CEE, 0x1CF3], + [0x1CF5, 0x1CF6], + [0x1CFA, 0x1CFA], + [0x1D00, 0x1EF9], + [0x1F00, 0x1F15], + [0x1F18, 0x1F1D], + [0x1F20, 0x1F45], + [0x1F48, 0x1F4D], + [0x1F50, 0x1F57], + [0x1F59, 0x1F59], + [0x1F5B, 0x1F5B], + [0x1F5D, 0x1F5D], + [0x1F5F, 0x1F7D], + [0x1F80, 0x1FB4], + [0x1FB6, 0x1FBC], + [0x1FBE, 0x1FBE], + [0x1FC2, 0x1FC4], + [0x1FC6, 0x1FCC], + [0x1FD0, 0x1FD3], + [0x1FD6, 0x1FDB], + [0x1FE0, 0x1FEC], + [0x1FF2, 0x1FF4], + [0x1FF6, 0x1FFC], + [0x200B, 0x200D], + [0x202A, 0x202E], + [0x203F, 0x2040], + [0x2054, 0x2054], + [0x2060, 0x2071], + [0x207F, 0x207F], + [0x2090, 0x209C], + [0x20D0, 0x20DC], + [0x20E1, 0x20E1], + [0x20E5, 0x20F0], + [0x2102, 0x2102], + [0x2107, 0x2107], + [0x210A, 0x2113], + [0x2115, 0x2115], + [0x2118, 0x211D], + [0x2124, 0x2124], + [0x2126, 0x2126], + [0x2128, 0x2128], + [0x212A, 0x2138], + [0x213C, 0x213F], + [0x2145, 0x2149], + [0x214E, 0x214E], + [0x2160, 0x2188], + [0x2460, 0x24FF], + [0x2776, 0x2793], + [0x2C00, 0x2CE4], + [0x2CEB, 0x2CF3], + [0x2D00, 0x2D25], + [0x2D27, 0x2D27], + [0x2D2D, 0x2D2D], + [0x2D30, 0x2D67], + [0x2D6F, 0x2D6F], + [0x2D7F, 0x2D96], + [0x2DA0, 0x2DA6], + [0x2DA8, 0x2DAE], + [0x2DB0, 0x2DB6], + [0x2DB8, 0x2DBE], + [0x2DC0, 0x2DC6], + [0x2DC8, 0x2DCE], + [0x2DD0, 0x2DD6], + [0x2DD8, 0x2DDE], + [0x2DE0, 0x2DFF], + [0x2E80, 0x2FFF], + [0x3004, 0x3007], + [0x3021, 0x302F], + [0x3031, 0x303C], + [0x3041, 0x3096], + [0x3099, 0x309F], + [0x30A1, 0x30FC], + [0x3105, 0x312F], + [0x3131, 0x318E], + [0x31A0, 0x31BF], + [0x31F0, 0x31FF], + [0x3400, 0x4DBF], + [0x4E00, 0xA48C], + [0xA4D0, 0xA4FD], + [0xA500, 0xA60C], + [0xA610, 0xA61F], + [0xA62A, 0xA62B], + [0xA640, 0xA66F], + [0xA674, 0xA67D], + [0xA67F, 0xA6EF], + [0xA717, 0xA71F], + [0xA722, 0xA788], + [0xA78B, 0xA7CA], + [0xA7D0, 0xA7D1], + [0xA7D3, 0xA7D3], + [0xA7D5, 0xA7D9], + [0xA7F2, 0xA805], + [0xA807, 0xA80A], + [0xA80C, 0xA822], + [0xA82C, 0xA82C], + [0xA840, 0xA873], + [0xA880, 0xA8B3], + [0xA8D0, 0xA8D9], + [0xA8E0, 0xA8F7], + [0xA8FB, 0xA8FB], + [0xA8FD, 0xA8FE], + [0xA90A, 0xA925], + [0xA930, 0xA946], + [0xA960, 0xA97C], + [0xA980, 0xA9B2], + [0xA9CF, 0xA9CF], + [0xA9E0, 0xA9E4], + [0xA9E6, 0xA9EF], + [0xA9FA, 0xA9FE], + [0xAA00, 0xAA28], + [0xAA40, 0xAA42], + [0xAA44, 0xAA4B], + [0xAA50, 0xAA59], + [0xAA60, 0xAA76], + [0xAA7A, 0xAA7A], + [0xAA7E, 0xAAAF], + [0xAAB1, 0xAAB1], + [0xAAB5, 0xAAB6], + [0xAAB9, 0xAABD], + [0xAAC0, 0xAAC0], + [0xAAC2, 0xAAC2], + [0xAADB, 0xAADD], + [0xAAE0, 0xAAEA], + [0xAAF2, 0xAAF4], + [0xAB01, 0xAB06], + [0xAB09, 0xAB0E], + [0xAB11, 0xAB16], + [0xAB20, 0xAB26], + [0xAB28, 0xAB2E], + [0xAB30, 0xAB5A], + [0xAB5C, 0xAB69], + [0xAB70, 0xABE2], + [0xABEC, 0xABED], + [0xABF0, 0xABF9], + [0xAC00, 0xD7A3], + [0xD7B0, 0xD7C6], + [0xD7CB, 0xD7FB], + [0xF900, 0xFA6D], + [0xFA70, 0xFAD9], + [0xFB00, 0xFB06], + [0xFB13, 0xFB17], + [0xFB1D, 0xFB1D], + [0xFB1F, 0xFB28], + [0xFB2A, 0xFB36], + [0xFB38, 0xFB3C], + [0xFB3E, 0xFB3E], + [0xFB40, 0xFB41], + [0xFB43, 0xFB44], + [0xFB46, 0xFBB1], + [0xFBD3, 0xFC5D], + [0xFC64, 0xFD3D], + [0xFD40, 0xFD8F], + [0xFD92, 0xFDC7], + [0xFDF0, 0xFDF9], + [0xFE00, 0xFE0F], + [0xFE20, 0xFE2F], + [0xFE33, 0xFE34], + [0xFE47, 0xFE71], + [0xFE73, 0xFE73], + [0xFE77, 0xFE77], + [0xFE79, 0xFE79], + [0xFE7B, 0xFE7B], + [0xFE7D, 0xFE7D], + [0xFE7F, 0xFEFC], + [0xFF10, 0xFF19], + [0xFF21, 0xFF3A], + [0xFF3F, 0xFF3F], + [0xFF41, 0xFF5A], + [0xFF65, 0xFF9D], + [0xFFA0, 0xFFBE], + [0xFFC2, 0xFFC7], + [0xFFCA, 0xFFCF], + [0xFFD2, 0xFFD7], + [0xFFDA, 0xFFDC], + [0x10000, 0x1000B], + [0x1000D, 0x10026], + [0x10028, 0x1003A], + [0x1003C, 0x1003D], + [0x1003F, 0x1004D], + [0x10050, 0x1005D], + [0x10080, 0x100FA], + [0x10140, 0x10174], + [0x101FD, 0x101FD], + [0x10280, 0x1029C], + [0x102A0, 0x102D0], + [0x102E0, 0x102E0], + [0x10300, 0x1031F], + [0x1032D, 0x1034A], + [0x10350, 0x10375], + [0x10380, 0x1039D], + [0x103A0, 0x103C3], + [0x103C8, 0x103CF], + [0x103D1, 0x103D5], + [0x10400, 0x1049D], + [0x104A0, 0x104A9], + [0x104B0, 0x104D3], + [0x104D8, 0x104FB], + [0x10500, 0x10527], + [0x10530, 0x10563], + [0x10570, 0x1057A], + [0x1057C, 0x1058A], + [0x1058C, 0x10592], + [0x10594, 0x10595], + [0x10597, 0x105A1], + [0x105A3, 0x105B1], + [0x105B3, 0x105B9], + [0x105BB, 0x105BC], + [0x10600, 0x10736], + [0x10740, 0x10755], + [0x10760, 0x10767], + [0x10780, 0x10785], + [0x10787, 0x107B0], + [0x107B2, 0x107BA], + [0x10800, 0x10805], + [0x10808, 0x10808], + [0x1080A, 0x10835], + [0x10837, 0x10838], + [0x1083C, 0x1083C], + [0x1083F, 0x10855], + [0x10860, 0x10876], + [0x10880, 0x1089E], + [0x108E0, 0x108F2], + [0x108F4, 0x108F5], + [0x10900, 0x10915], + [0x10920, 0x10939], + [0x10980, 0x109B7], + [0x109BE, 0x109BF], + [0x10A00, 0x10A03], + [0x10A05, 0x10A06], + [0x10A0C, 0x10A13], + [0x10A15, 0x10A17], + [0x10A19, 0x10A35], + [0x10A38, 0x10A3A], + [0x10A3F, 0x10A3F], + [0x10A60, 0x10A7C], + [0x10A80, 0x10A9C], + [0x10AC0, 0x10AC7], + [0x10AC9, 0x10AE6], + [0x10B00, 0x10B35], + [0x10B40, 0x10B55], + [0x10B60, 0x10B72], + [0x10B80, 0x10B91], + [0x10C00, 0x10C48], + [0x10C80, 0x10CB2], + [0x10CC0, 0x10CF2], + [0x10D00, 0x10D23], + [0x10D30, 0x10D39], + [0x10E80, 0x10EA9], + [0x10EAB, 0x10EAC], + [0x10EB0, 0x10EB1], + [0x10EFD, 0x10F1C], + [0x10F27, 0x10F27], + [0x10F30, 0x10F45], + [0x10F70, 0x10F81], + [0x10FB0, 0x10FC4], + [0x10FE0, 0x10FF6], + [0x11000, 0x11037], + [0x11066, 0x11072], + [0x11075, 0x11075], + [0x1107F, 0x110AF], + [0x110C2, 0x110C2], + [0x110D0, 0x110E8], + [0x110F0, 0x110F9], + [0x11100, 0x11126], + [0x11136, 0x1113F], + [0x11144, 0x11147], + [0x11150, 0x11173], + [0x11176, 0x11176], + [0x11180, 0x111B2], + [0x111C1, 0x111C4], + [0x111C9, 0x111CC], + [0x111CE, 0x111DA], + [0x111DC, 0x111DC], + [0x11200, 0x11211], + [0x11213, 0x11237], + [0x1123E, 0x11240], + [0x11280, 0x11286], + [0x11288, 0x11288], + [0x1128A, 0x1128D], + [0x1128F, 0x1129D], + [0x1129F, 0x112A8], + [0x112B0, 0x112EA], + [0x112F0, 0x112F9], + [0x11300, 0x11303], + [0x11305, 0x1130C], + [0x1130F, 0x11310], + [0x11313, 0x11328], + [0x1132A, 0x11330], + [0x11332, 0x11333], + [0x11335, 0x11339], + [0x1133B, 0x1133D], + [0x11347, 0x11348], + [0x1134B, 0x1134D], + [0x11350, 0x11350], + [0x11357, 0x11357], + [0x1135D, 0x11363], + [0x11366, 0x1136C], + [0x11370, 0x11374], + [0x11400, 0x1144A], + [0x11450, 0x11459], + [0x1145E, 0x11461], + [0x11480, 0x114C5], + [0x114C7, 0x114C7], + [0x114D0, 0x114D9], + [0x11580, 0x115B5], + [0x115B8, 0x115C0], + [0x115D8, 0x115DD], + [0x11600, 0x11640], + [0x11644, 0x11644], + [0x11650, 0x11659], + [0x11680, 0x116B8], + [0x116C0, 0x116C9], + [0x11700, 0x1171A], + [0x1171D, 0x1172B], + [0x11730, 0x11739], + [0x11740, 0x11746], + [0x11800, 0x1183A], + [0x118A0, 0x118E9], + [0x118FF, 0x11906], + [0x11909, 0x11909], + [0x1190C, 0x11913], + [0x11915, 0x11916], + [0x11918, 0x11935], + [0x11937, 0x11938], + [0x1193B, 0x1193F], + [0x11941, 0x11941], + [0x11950, 0x11959], + [0x119A0, 0x119A7], + [0x119AA, 0x119D7], + [0x119DA, 0x119E1], + [0x119E3, 0x119E3], + [0x11A00, 0x11A32], + [0x11A3A, 0x11A3A], + [0x11A47, 0x11A47], + [0x11A50, 0x11A89], + [0x11A9D, 0x11A9D], + [0x11AB0, 0x11AF8], + [0x11C00, 0x11C08], + [0x11C0A, 0x11C36], + [0x11C38, 0x11C40], + [0x11C50, 0x11C59], + [0x11C72, 0x11C8F], + [0x11C92, 0x11CA7], + [0x11CA9, 0x11CB6], + [0x11D00, 0x11D06], + [0x11D08, 0x11D09], + [0x11D0B, 0x11D36], + [0x11D3A, 0x11D3A], + [0x11D3C, 0x11D3D], + [0x11D3F, 0x11D46], + [0x11D50, 0x11D59], + [0x11D60, 0x11D65], + [0x11D67, 0x11D68], + [0x11D6A, 0x11D89], + [0x11D90, 0x11D91], + [0x11D93, 0x11D98], + [0x11DA0, 0x11DA9], + [0x11EE0, 0x11EF2], + [0x11F00, 0x11F02], + [0x11F04, 0x11F10], + [0x11F12, 0x11F33], + [0x11F3E, 0x11F42], + [0x11F50, 0x11F59], + [0x11FB0, 0x11FB0], + [0x12000, 0x12399], + [0x12400, 0x1246E], + [0x12480, 0x12543], + [0x12F90, 0x12FF0], + [0x13000, 0x1342F], + [0x13440, 0x13446], + [0x14400, 0x14646], + [0x16800, 0x16A38], + [0x16A40, 0x16A5E], + [0x16A60, 0x16A69], + [0x16A70, 0x16ABE], + [0x16AC0, 0x16AC9], + [0x16AD0, 0x16AED], + [0x16AF0, 0x16AF4], + [0x16B00, 0x16B2F], + [0x16B40, 0x16B43], + [0x16B50, 0x16B59], + [0x16B63, 0x16B77], + [0x16B7D, 0x16B8F], + [0x16E40, 0x16E7F], + [0x16F00, 0x16F4A], + [0x16F4F, 0x16F50], + [0x16F8F, 0x16F9F], + [0x16FE0, 0x16FE1], + [0x16FE3, 0x16FE3], + [0x16FF0, 0x16FF1], + [0x17000, 0x187F7], + [0x18800, 0x18CD5], + [0x18D00, 0x18D08], + [0x1AFF0, 0x1AFF3], + [0x1AFF5, 0x1AFFB], + [0x1AFFD, 0x1AFFE], + [0x1B000, 0x1B122], + [0x1B132, 0x1B132], + [0x1B150, 0x1B152], + [0x1B155, 0x1B155], + [0x1B164, 0x1B167], + [0x1B170, 0x1B2FB], + [0x1BC00, 0x1BC6A], + [0x1BC70, 0x1BC7C], + [0x1BC80, 0x1BC88], + [0x1BC90, 0x1BC99], + [0x1BC9D, 0x1BC9E], + [0x1CF00, 0x1CF2D], + [0x1CF30, 0x1CF46], + [0x1D165, 0x1D169], + [0x1D16D, 0x1D172], + [0x1D17B, 0x1D182], + [0x1D185, 0x1D18B], + [0x1D1AA, 0x1D1AD], + [0x1D242, 0x1D244], + [0x1D400, 0x1D454], + [0x1D456, 0x1D49C], + [0x1D49E, 0x1D49F], + [0x1D4A2, 0x1D4A2], + [0x1D4A5, 0x1D4A6], + [0x1D4A9, 0x1D4AC], + [0x1D4AE, 0x1D4B9], + [0x1D4BB, 0x1D4BB], + [0x1D4BD, 0x1D4C3], + [0x1D4C5, 0x1D505], + [0x1D507, 0x1D50A], + [0x1D50D, 0x1D514], + [0x1D516, 0x1D51C], + [0x1D51E, 0x1D539], + [0x1D53B, 0x1D53E], + [0x1D540, 0x1D544], + [0x1D546, 0x1D546], + [0x1D54A, 0x1D550], + [0x1D552, 0x1D6A5], + [0x1D6A8, 0x1D6C0], + [0x1D6C2, 0x1D6DA], + [0x1D6DC, 0x1D6FA], + [0x1D6FC, 0x1D714], + [0x1D716, 0x1D734], + [0x1D736, 0x1D74E], + [0x1D750, 0x1D76E], + [0x1D770, 0x1D788], + [0x1D78A, 0x1D7A8], + [0x1D7AA, 0x1D7C2], + [0x1D7C4, 0x1D7CB], + [0x1D7CE, 0x1D7FF], + [0x1DA00, 0x1DA36], + [0x1DA3B, 0x1DA6C], + [0x1DA75, 0x1DA75], + [0x1DA84, 0x1DA84], + [0x1DA9B, 0x1DA9F], + [0x1DAA1, 0x1DAAF], + [0x1DF00, 0x1DF1E], + [0x1DF25, 0x1DF2A], + [0x1E000, 0x1E006], + [0x1E008, 0x1E018], + [0x1E01B, 0x1E021], + [0x1E023, 0x1E024], + [0x1E026, 0x1E02A], + [0x1E030, 0x1E06D], + [0x1E08F, 0x1E08F], + [0x1E100, 0x1E12C], + [0x1E130, 0x1E13D], + [0x1E140, 0x1E149], + [0x1E14E, 0x1E14E], + [0x1E290, 0x1E2AE], + [0x1E2C0, 0x1E2F9], + [0x1E4D0, 0x1E4F9], + [0x1E7E0, 0x1E7E6], + [0x1E7E8, 0x1E7EB], + [0x1E7ED, 0x1E7EE], + [0x1E7F0, 0x1E7FE], + [0x1E800, 0x1E8C4], + [0x1E8D0, 0x1E8D6], + [0x1E900, 0x1E94B], + [0x1E950, 0x1E959], + [0x1EE00, 0x1EE03], + [0x1EE05, 0x1EE1F], + [0x1EE21, 0x1EE22], + [0x1EE24, 0x1EE24], + [0x1EE27, 0x1EE27], + [0x1EE29, 0x1EE32], + [0x1EE34, 0x1EE37], + [0x1EE39, 0x1EE39], + [0x1EE3B, 0x1EE3B], + [0x1EE42, 0x1EE42], + [0x1EE47, 0x1EE47], + [0x1EE49, 0x1EE49], + [0x1EE4B, 0x1EE4B], + [0x1EE4D, 0x1EE4F], + [0x1EE51, 0x1EE52], + [0x1EE54, 0x1EE54], + [0x1EE57, 0x1EE57], + [0x1EE59, 0x1EE59], + [0x1EE5B, 0x1EE5B], + [0x1EE5D, 0x1EE5D], + [0x1EE5F, 0x1EE5F], + [0x1EE61, 0x1EE62], + [0x1EE64, 0x1EE64], + [0x1EE67, 0x1EE6A], + [0x1EE6C, 0x1EE72], + [0x1EE74, 0x1EE77], + [0x1EE79, 0x1EE7C], + [0x1EE7E, 0x1EE7E], + [0x1EE80, 0x1EE89], + [0x1EE8B, 0x1EE9B], + [0x1EEA1, 0x1EEA3], + [0x1EEA5, 0x1EEA9], + [0x1EEAB, 0x1EEBB], + [0x1FBF0, 0x1FBF9], + [0x20000, 0x2B739], + [0x2B740, 0x2B81D], + [0x2B820, 0x2CEA1], + [0x2CEB0, 0x2EBE0], + [0x2EBF0, 0x2EE5D], + [0x2F800, 0x2FA1D], + [0x30000, 0x323AF], + [0x40000, 0x4FFFD], + [0x50000, 0x5FFFD], + [0x60000, 0x6FFFD], + [0x70000, 0x7FFFD], + [0x80000, 0x8FFFD], + [0x90000, 0x9FFFD], + [0xA0000, 0xAFFFD], + [0xB0000, 0xBFFFD], + [0xC0000, 0xCFFFD], + [0xD0000, 0xDFFFD], + [0xE0000, 0xEFFFD], +]; + +/** +Least restrictive Start +Entries: 858717 +*/ +static immutable dchar[2][] LeastRestrictive_Start = [ + [0xA8, 0xA8], + [0xAA, 0xAA], + [0xAD, 0xAD], + [0xAF, 0xAF], + [0xB2, 0xB5], + [0xB7, 0xBA], + [0xBC, 0xBE], + [0xC0, 0xD6], + [0xD8, 0xF6], + [0xF8, 0x217], + [0x250, 0x2A8], + [0x2B0, 0x2B8], + [0x2BB, 0x2BB], + [0x2BD, 0x2C1], + [0x2C6, 0x2D1], + [0x2E0, 0x2E4], + [0x2EC, 0x2EC], + [0x2EE, 0x2EE], + [0x370, 0x374], + [0x376, 0x377], + [0x37A, 0x37D], + [0x37F, 0x37F], + [0x386, 0x386], + [0x388, 0x38A], + [0x38C, 0x38C], + [0x38E, 0x3A1], + [0x3A3, 0x3CE], + [0x3D0, 0x3D6], + [0x3DA, 0x3DA], + [0x3DC, 0x3DC], + [0x3DE, 0x3DE], + [0x3E0, 0x3E0], + [0x3E2, 0x3F3], + [0x3F7, 0x40C], + [0x40E, 0x44F], + [0x451, 0x45C], + [0x45E, 0x481], + [0x48A, 0x4C4], + [0x4C7, 0x4C8], + [0x4CB, 0x4CC], + [0x4D0, 0x4EB], + [0x4EE, 0x4F5], + [0x4F8, 0x4F9], + [0x531, 0x556], + [0x559, 0x559], + [0x560, 0x587], + [0x5B0, 0x5B9], + [0x5BB, 0x5BD], + [0x5BF, 0x5BF], + [0x5C1, 0x5C2], + [0x5D0, 0x5EA], + [0x5EF, 0x5F2], + [0x620, 0x63A], + [0x640, 0x652], + [0x660, 0x669], + [0x66E, 0x6BE], + [0x6C0, 0x6CE], + [0x6D0, 0x6D5], + [0x6E5, 0x6E8], + [0x6EA, 0x6FC], + [0x6FF, 0x6FF], + [0x710, 0x710], + [0x712, 0x72F], + [0x74D, 0x7A5], + [0x7B1, 0x7B1], + [0x7CA, 0x7EA], + [0x7F4, 0x7F5], + [0x7FA, 0x7FA], + [0x800, 0x815], + [0x81A, 0x81A], + [0x824, 0x824], + [0x828, 0x828], + [0x840, 0x858], + [0x860, 0x86A], + [0x870, 0x887], + [0x889, 0x88E], + [0x8A0, 0x8C9], + [0x901, 0x939], + [0x93D, 0x94D], + [0x950, 0x952], + [0x958, 0x963], + [0x966, 0x96F], + [0x971, 0x983], + [0x985, 0x98C], + [0x98F, 0x990], + [0x993, 0x9A8], + [0x9AA, 0x9B0], + [0x9B2, 0x9B2], + [0x9B6, 0x9B9], + [0x9BD, 0x9C4], + [0x9C7, 0x9C8], + [0x9CB, 0x9CE], + [0x9DC, 0x9DD], + [0x9DF, 0x9E3], + [0x9E6, 0x9F1], + [0x9FC, 0x9FC], + [0xA02, 0xA02], + [0xA05, 0xA0A], + [0xA0F, 0xA10], + [0xA13, 0xA28], + [0xA2A, 0xA30], + [0xA32, 0xA33], + [0xA35, 0xA36], + [0xA38, 0xA39], + [0xA3E, 0xA42], + [0xA47, 0xA48], + [0xA4B, 0xA4D], + [0xA59, 0xA5C], + [0xA5E, 0xA5E], + [0xA66, 0xA6F], + [0xA72, 0xA74], + [0xA81, 0xA83], + [0xA85, 0xA8B], + [0xA8D, 0xA8D], + [0xA8F, 0xA91], + [0xA93, 0xAA8], + [0xAAA, 0xAB0], + [0xAB2, 0xAB3], + [0xAB5, 0xAB9], + [0xABD, 0xABD], + [0xAC7, 0xAC9], + [0xACB, 0xACD], + [0xAD0, 0xAD0], + [0xAE0, 0xAE0], + [0xAE6, 0xAEF], + [0xAF9, 0xAF9], + [0xB01, 0xB03], + [0xB05, 0xB0C], + [0xB0F, 0xB10], + [0xB13, 0xB28], + [0xB2A, 0xB30], + [0xB32, 0xB33], + [0xB35, 0xB39], + [0xB3D, 0xB43], + [0xB47, 0xB48], + [0xB4B, 0xB4D], + [0xB5C, 0xB5D], + [0xB5F, 0xB61], + [0xB66, 0xB6F], + [0xB71, 0xB71], + [0xB82, 0xB83], + [0xB85, 0xB8A], + [0xB8E, 0xB90], + [0xB92, 0xB95], + [0xB99, 0xB9A], + [0xB9C, 0xB9C], + [0xB9E, 0xB9F], + [0xBA3, 0xBA4], + [0xBA8, 0xBAA], + [0xBAE, 0xBB5], + [0xBB7, 0xBB9], + [0xBBE, 0xBC2], + [0xBC6, 0xBC8], + [0xBCA, 0xBCD], + [0xBD0, 0xBD0], + [0xBE7, 0xBEF], + [0xC01, 0xC03], + [0xC05, 0xC0C], + [0xC0E, 0xC10], + [0xC12, 0xC28], + [0xC2A, 0xC33], + [0xC35, 0xC39], + [0xC3D, 0xC44], + [0xC46, 0xC48], + [0xC4A, 0xC4D], + [0xC58, 0xC5A], + [0xC5D, 0xC5D], + [0xC60, 0xC61], + [0xC66, 0xC6F], + [0xC80, 0xC80], + [0xC82, 0xC83], + [0xC85, 0xC8C], + [0xC8E, 0xC90], + [0xC92, 0xCA8], + [0xCAA, 0xCB3], + [0xCB5, 0xCB9], + [0xCBD, 0xCC4], + [0xCC6, 0xCC8], + [0xCCA, 0xCCD], + [0xCDD, 0xCDE], + [0xCE0, 0xCE1], + [0xCE6, 0xCEF], + [0xCF1, 0xCF2], + [0xD02, 0xD0C], + [0xD0E, 0xD10], + [0xD12, 0xD28], + [0xD2A, 0xD39], + [0xD3D, 0xD43], + [0xD46, 0xD48], + [0xD4A, 0xD4E], + [0xD54, 0xD56], + [0xD5F, 0xD61], + [0xD66, 0xD6F], + [0xD7A, 0xD7F], + [0xD85, 0xD96], + [0xD9A, 0xDB1], + [0xDB3, 0xDBB], + [0xDBD, 0xDBD], + [0xDC0, 0xDC6], + [0xE01, 0xE30], + [0xE32, 0xE32], + [0xE40, 0xE5B], + [0xE81, 0xE82], + [0xE84, 0xE84], + [0xE86, 0xE88], + [0xE8A, 0xE8A], + [0xE8C, 0xE8D], + [0xE94, 0xE97], + [0xE99, 0xE9F], + [0xEA1, 0xEA3], + [0xEA5, 0xEA5], + [0xEA7, 0xEAB], + [0xEAD, 0xEAE], + [0xEB0, 0xEB2], + [0xEBB, 0xEBD], + [0xEC0, 0xEC4], + [0xEC6, 0xEC6], + [0xEC8, 0xECD], + [0xED0, 0xED9], + [0xEDC, 0xEDD], + [0xF00, 0xF00], + [0xF18, 0xF19], + [0xF20, 0xF33], + [0xF35, 0xF35], + [0xF37, 0xF37], + [0xF39, 0xF39], + [0xF3E, 0xF47], + [0xF49, 0xF69], + [0xF71, 0xF84], + [0xF86, 0xF8C], + [0xF90, 0xF95], + [0xF97, 0xF97], + [0xF99, 0xFAD], + [0xFB1, 0xFB7], + [0xFB9, 0xFB9], + [0x1000, 0x102A], + [0x103F, 0x103F], + [0x1050, 0x1055], + [0x105A, 0x105D], + [0x1061, 0x1061], + [0x1065, 0x1066], + [0x106E, 0x1070], + [0x1075, 0x1081], + [0x108E, 0x108E], + [0x10A0, 0x10C5], + [0x10C7, 0x10C7], + [0x10CD, 0x10CD], + [0x10D0, 0x10FA], + [0x10FC, 0x1248], + [0x124A, 0x124D], + [0x1250, 0x1256], + [0x1258, 0x1258], + [0x125A, 0x125D], + [0x1260, 0x1288], + [0x128A, 0x128D], + [0x1290, 0x12B0], + [0x12B2, 0x12B5], + [0x12B8, 0x12BE], + [0x12C0, 0x12C0], + [0x12C2, 0x12C5], + [0x12C8, 0x12D6], + [0x12D8, 0x1310], + [0x1312, 0x1315], + [0x1318, 0x135A], + [0x1380, 0x138F], + [0x13A0, 0x13F5], + [0x13F8, 0x13FD], + [0x1401, 0x166C], + [0x166F, 0x167F], + [0x1681, 0x169A], + [0x16A0, 0x16EA], + [0x16EE, 0x16F8], + [0x1700, 0x1711], + [0x171F, 0x1731], + [0x1740, 0x1751], + [0x1760, 0x176C], + [0x176E, 0x1770], + [0x1780, 0x17B3], + [0x17D7, 0x17D7], + [0x17DC, 0x17DC], + [0x180F, 0x1878], + [0x1880, 0x18A8], + [0x18AA, 0x18AA], + [0x18B0, 0x18F5], + [0x1900, 0x191E], + [0x1950, 0x196D], + [0x1970, 0x1974], + [0x1980, 0x19AB], + [0x19B0, 0x19C9], + [0x1A00, 0x1A16], + [0x1A20, 0x1A54], + [0x1AA7, 0x1AA7], + [0x1B05, 0x1B33], + [0x1B45, 0x1B4C], + [0x1B83, 0x1BA0], + [0x1BAE, 0x1BAF], + [0x1BBA, 0x1BE5], + [0x1C00, 0x1C23], + [0x1C4D, 0x1C4F], + [0x1C5A, 0x1C7D], + [0x1C80, 0x1C88], + [0x1C90, 0x1CBA], + [0x1CBD, 0x1CBF], + [0x1CE9, 0x1CEC], + [0x1CEE, 0x1CF3], + [0x1CF5, 0x1CF6], + [0x1CFA, 0x1CFA], + [0x1D00, 0x1DBF], + [0x1E00, 0x1EF9], + [0x1F00, 0x1F15], + [0x1F18, 0x1F1D], + [0x1F20, 0x1F45], + [0x1F48, 0x1F4D], + [0x1F50, 0x1F57], + [0x1F59, 0x1F59], + [0x1F5B, 0x1F5B], + [0x1F5D, 0x1F5D], + [0x1F5F, 0x1F7D], + [0x1F80, 0x1FB4], + [0x1FB6, 0x1FBC], + [0x1FBE, 0x1FBE], + [0x1FC2, 0x1FC4], + [0x1FC6, 0x1FCC], + [0x1FD0, 0x1FD3], + [0x1FD6, 0x1FDB], + [0x1FE0, 0x1FEC], + [0x1FF2, 0x1FF4], + [0x1FF6, 0x1FFC], + [0x200B, 0x200D], + [0x202A, 0x202E], + [0x203F, 0x2040], + [0x2054, 0x2054], + [0x2060, 0x2071], + [0x207F, 0x207F], + [0x2090, 0x209C], + [0x2102, 0x2102], + [0x2107, 0x2107], + [0x210A, 0x2113], + [0x2115, 0x2115], + [0x2118, 0x211D], + [0x2124, 0x2124], + [0x2126, 0x2126], + [0x2128, 0x2128], + [0x212A, 0x2138], + [0x213C, 0x213F], + [0x2145, 0x2149], + [0x214E, 0x214E], + [0x2160, 0x2188], + [0x2460, 0x24FF], + [0x2776, 0x2793], + [0x2C00, 0x2CE4], + [0x2CEB, 0x2CEE], + [0x2CF2, 0x2CF3], + [0x2D00, 0x2D25], + [0x2D27, 0x2D27], + [0x2D2D, 0x2D2D], + [0x2D30, 0x2D67], + [0x2D6F, 0x2D6F], + [0x2D80, 0x2D96], + [0x2DA0, 0x2DA6], + [0x2DA8, 0x2DAE], + [0x2DB0, 0x2DB6], + [0x2DB8, 0x2DBE], + [0x2DC0, 0x2DC6], + [0x2DC8, 0x2DCE], + [0x2DD0, 0x2DD6], + [0x2DD8, 0x2DDE], + [0x2E80, 0x2FFF], + [0x3004, 0x3007], + [0x3021, 0x3029], + [0x3031, 0x3035], + [0x3038, 0x303C], + [0x3041, 0x3096], + [0x309B, 0x309F], + [0x30A1, 0x30FF], + [0x3105, 0x312F], + [0x3131, 0x318E], + [0x31A0, 0x31BF], + [0x31F0, 0x31FF], + [0x3400, 0x4DBF], + [0x4E00, 0xA48C], + [0xA4D0, 0xA4FD], + [0xA500, 0xA60C], + [0xA610, 0xA61F], + [0xA62A, 0xA62B], + [0xA640, 0xA66E], + [0xA67F, 0xA69D], + [0xA6A0, 0xA6EF], + [0xA717, 0xA71F], + [0xA722, 0xA788], + [0xA78B, 0xA7CA], + [0xA7D0, 0xA7D1], + [0xA7D3, 0xA7D3], + [0xA7D5, 0xA7D9], + [0xA7F2, 0xA801], + [0xA803, 0xA805], + [0xA807, 0xA80A], + [0xA80C, 0xA822], + [0xA840, 0xA873], + [0xA882, 0xA8B3], + [0xA8F2, 0xA8F7], + [0xA8FB, 0xA8FB], + [0xA8FD, 0xA8FE], + [0xA90A, 0xA925], + [0xA930, 0xA946], + [0xA960, 0xA97C], + [0xA984, 0xA9B2], + [0xA9CF, 0xA9CF], + [0xA9E0, 0xA9E4], + [0xA9E6, 0xA9EF], + [0xA9FA, 0xA9FE], + [0xAA00, 0xAA28], + [0xAA40, 0xAA42], + [0xAA44, 0xAA4B], + [0xAA60, 0xAA76], + [0xAA7A, 0xAA7A], + [0xAA7E, 0xAAAF], + [0xAAB1, 0xAAB1], + [0xAAB5, 0xAAB6], + [0xAAB9, 0xAABD], + [0xAAC0, 0xAAC0], + [0xAAC2, 0xAAC2], + [0xAADB, 0xAADD], + [0xAAE0, 0xAAEA], + [0xAAF2, 0xAAF4], + [0xAB01, 0xAB06], + [0xAB09, 0xAB0E], + [0xAB11, 0xAB16], + [0xAB20, 0xAB26], + [0xAB28, 0xAB2E], + [0xAB30, 0xAB5A], + [0xAB5C, 0xAB69], + [0xAB70, 0xABE2], + [0xAC00, 0xD7A3], + [0xD7B0, 0xD7C6], + [0xD7CB, 0xD7FB], + [0xF900, 0xFA6D], + [0xFA70, 0xFAD9], + [0xFB00, 0xFB06], + [0xFB13, 0xFB17], + [0xFB1D, 0xFB1D], + [0xFB1F, 0xFB28], + [0xFB2A, 0xFB36], + [0xFB38, 0xFB3C], + [0xFB3E, 0xFB3E], + [0xFB40, 0xFB41], + [0xFB43, 0xFB44], + [0xFB46, 0xFBB1], + [0xFBD3, 0xFC5D], + [0xFC64, 0xFD3D], + [0xFD40, 0xFD8F], + [0xFD92, 0xFDC7], + [0xFDF0, 0xFDF9], + [0xFE47, 0xFE71], + [0xFE73, 0xFE73], + [0xFE77, 0xFE77], + [0xFE79, 0xFE79], + [0xFE7B, 0xFE7B], + [0xFE7D, 0xFE7D], + [0xFE7F, 0xFEFC], + [0xFF21, 0xFF3A], + [0xFF41, 0xFF5A], + [0xFF66, 0xFF9D], + [0xFFA0, 0xFFBE], + [0xFFC2, 0xFFC7], + [0xFFCA, 0xFFCF], + [0xFFD2, 0xFFD7], + [0xFFDA, 0xFFDC], + [0x10000, 0x1000B], + [0x1000D, 0x10026], + [0x10028, 0x1003A], + [0x1003C, 0x1003D], + [0x1003F, 0x1004D], + [0x10050, 0x1005D], + [0x10080, 0x100FA], + [0x10140, 0x10174], + [0x10280, 0x1029C], + [0x102A0, 0x102D0], + [0x10300, 0x1031F], + [0x1032D, 0x1034A], + [0x10350, 0x10375], + [0x10380, 0x1039D], + [0x103A0, 0x103C3], + [0x103C8, 0x103CF], + [0x103D1, 0x103D5], + [0x10400, 0x1049D], + [0x104B0, 0x104D3], + [0x104D8, 0x104FB], + [0x10500, 0x10527], + [0x10530, 0x10563], + [0x10570, 0x1057A], + [0x1057C, 0x1058A], + [0x1058C, 0x10592], + [0x10594, 0x10595], + [0x10597, 0x105A1], + [0x105A3, 0x105B1], + [0x105B3, 0x105B9], + [0x105BB, 0x105BC], + [0x10600, 0x10736], + [0x10740, 0x10755], + [0x10760, 0x10767], + [0x10780, 0x10785], + [0x10787, 0x107B0], + [0x107B2, 0x107BA], + [0x10800, 0x10805], + [0x10808, 0x10808], + [0x1080A, 0x10835], + [0x10837, 0x10838], + [0x1083C, 0x1083C], + [0x1083F, 0x10855], + [0x10860, 0x10876], + [0x10880, 0x1089E], + [0x108E0, 0x108F2], + [0x108F4, 0x108F5], + [0x10900, 0x10915], + [0x10920, 0x10939], + [0x10980, 0x109B7], + [0x109BE, 0x109BF], + [0x10A00, 0x10A00], + [0x10A10, 0x10A13], + [0x10A15, 0x10A17], + [0x10A19, 0x10A35], + [0x10A60, 0x10A7C], + [0x10A80, 0x10A9C], + [0x10AC0, 0x10AC7], + [0x10AC9, 0x10AE4], + [0x10B00, 0x10B35], + [0x10B40, 0x10B55], + [0x10B60, 0x10B72], + [0x10B80, 0x10B91], + [0x10C00, 0x10C48], + [0x10C80, 0x10CB2], + [0x10CC0, 0x10CF2], + [0x10D00, 0x10D23], + [0x10E80, 0x10EA9], + [0x10EB0, 0x10EB1], + [0x10F00, 0x10F1C], + [0x10F27, 0x10F27], + [0x10F30, 0x10F45], + [0x10F70, 0x10F81], + [0x10FB0, 0x10FC4], + [0x10FE0, 0x10FF6], + [0x11003, 0x11037], + [0x11071, 0x11072], + [0x11075, 0x11075], + [0x11083, 0x110AF], + [0x110D0, 0x110E8], + [0x11103, 0x11126], + [0x11144, 0x11144], + [0x11147, 0x11147], + [0x11150, 0x11172], + [0x11176, 0x11176], + [0x11183, 0x111B2], + [0x111C1, 0x111C4], + [0x111DA, 0x111DA], + [0x111DC, 0x111DC], + [0x11200, 0x11211], + [0x11213, 0x1122B], + [0x1123F, 0x11240], + [0x11280, 0x11286], + [0x11288, 0x11288], + [0x1128A, 0x1128D], + [0x1128F, 0x1129D], + [0x1129F, 0x112A8], + [0x112B0, 0x112DE], + [0x11305, 0x1130C], + [0x1130F, 0x11310], + [0x11313, 0x11328], + [0x1132A, 0x11330], + [0x11332, 0x11333], + [0x11335, 0x11339], + [0x1133D, 0x1133D], + [0x11350, 0x11350], + [0x1135D, 0x11361], + [0x11400, 0x11434], + [0x11447, 0x1144A], + [0x1145F, 0x11461], + [0x11480, 0x114AF], + [0x114C4, 0x114C5], + [0x114C7, 0x114C7], + [0x11580, 0x115AE], + [0x115D8, 0x115DB], + [0x11600, 0x1162F], + [0x11644, 0x11644], + [0x11680, 0x116AA], + [0x116B8, 0x116B8], + [0x11700, 0x1171A], + [0x11740, 0x11746], + [0x11800, 0x1182B], + [0x118A0, 0x118DF], + [0x118FF, 0x11906], + [0x11909, 0x11909], + [0x1190C, 0x11913], + [0x11915, 0x11916], + [0x11918, 0x1192F], + [0x1193F, 0x1193F], + [0x11941, 0x11941], + [0x119A0, 0x119A7], + [0x119AA, 0x119D0], + [0x119E1, 0x119E1], + [0x119E3, 0x119E3], + [0x11A00, 0x11A00], + [0x11A0B, 0x11A32], + [0x11A3A, 0x11A3A], + [0x11A50, 0x11A50], + [0x11A5C, 0x11A89], + [0x11A9D, 0x11A9D], + [0x11AB0, 0x11AF8], + [0x11C00, 0x11C08], + [0x11C0A, 0x11C2E], + [0x11C40, 0x11C40], + [0x11C72, 0x11C8F], + [0x11D00, 0x11D06], + [0x11D08, 0x11D09], + [0x11D0B, 0x11D30], + [0x11D46, 0x11D46], + [0x11D60, 0x11D65], + [0x11D67, 0x11D68], + [0x11D6A, 0x11D89], + [0x11D98, 0x11D98], + [0x11EE0, 0x11EF2], + [0x11F02, 0x11F02], + [0x11F04, 0x11F10], + [0x11F12, 0x11F33], + [0x11FB0, 0x11FB0], + [0x12000, 0x12399], + [0x12400, 0x1246E], + [0x12480, 0x12543], + [0x12F90, 0x12FF0], + [0x13000, 0x1342F], + [0x13441, 0x13446], + [0x14400, 0x14646], + [0x16800, 0x16A38], + [0x16A40, 0x16A5E], + [0x16A70, 0x16ABE], + [0x16AD0, 0x16AED], + [0x16B00, 0x16B2F], + [0x16B40, 0x16B43], + [0x16B63, 0x16B77], + [0x16B7D, 0x16B8F], + [0x16E40, 0x16E7F], + [0x16F00, 0x16F4A], + [0x16F50, 0x16F50], + [0x16F93, 0x16F9F], + [0x16FE0, 0x16FE1], + [0x16FE3, 0x16FE3], + [0x17000, 0x187F7], + [0x18800, 0x18CD5], + [0x18D00, 0x18D08], + [0x1AFF0, 0x1AFF3], + [0x1AFF5, 0x1AFFB], + [0x1AFFD, 0x1AFFE], + [0x1B000, 0x1B122], + [0x1B132, 0x1B132], + [0x1B150, 0x1B152], + [0x1B155, 0x1B155], + [0x1B164, 0x1B167], + [0x1B170, 0x1B2FB], + [0x1BC00, 0x1BC6A], + [0x1BC70, 0x1BC7C], + [0x1BC80, 0x1BC88], + [0x1BC90, 0x1BC99], + [0x1D400, 0x1D454], + [0x1D456, 0x1D49C], + [0x1D49E, 0x1D49F], + [0x1D4A2, 0x1D4A2], + [0x1D4A5, 0x1D4A6], + [0x1D4A9, 0x1D4AC], + [0x1D4AE, 0x1D4B9], + [0x1D4BB, 0x1D4BB], + [0x1D4BD, 0x1D4C3], + [0x1D4C5, 0x1D505], + [0x1D507, 0x1D50A], + [0x1D50D, 0x1D514], + [0x1D516, 0x1D51C], + [0x1D51E, 0x1D539], + [0x1D53B, 0x1D53E], + [0x1D540, 0x1D544], + [0x1D546, 0x1D546], + [0x1D54A, 0x1D550], + [0x1D552, 0x1D6A5], + [0x1D6A8, 0x1D6C0], + [0x1D6C2, 0x1D6DA], + [0x1D6DC, 0x1D6FA], + [0x1D6FC, 0x1D714], + [0x1D716, 0x1D734], + [0x1D736, 0x1D74E], + [0x1D750, 0x1D76E], + [0x1D770, 0x1D788], + [0x1D78A, 0x1D7A8], + [0x1D7AA, 0x1D7C2], + [0x1D7C4, 0x1D7CB], + [0x1DF00, 0x1DF1E], + [0x1DF25, 0x1DF2A], + [0x1E030, 0x1E06D], + [0x1E100, 0x1E12C], + [0x1E137, 0x1E13D], + [0x1E14E, 0x1E14E], + [0x1E290, 0x1E2AD], + [0x1E2C0, 0x1E2EB], + [0x1E4D0, 0x1E4EB], + [0x1E7E0, 0x1E7E6], + [0x1E7E8, 0x1E7EB], + [0x1E7ED, 0x1E7EE], + [0x1E7F0, 0x1E7FE], + [0x1E800, 0x1E8C4], + [0x1E900, 0x1E943], + [0x1E94B, 0x1E94B], + [0x1EE00, 0x1EE03], + [0x1EE05, 0x1EE1F], + [0x1EE21, 0x1EE22], + [0x1EE24, 0x1EE24], + [0x1EE27, 0x1EE27], + [0x1EE29, 0x1EE32], + [0x1EE34, 0x1EE37], + [0x1EE39, 0x1EE39], + [0x1EE3B, 0x1EE3B], + [0x1EE42, 0x1EE42], + [0x1EE47, 0x1EE47], + [0x1EE49, 0x1EE49], + [0x1EE4B, 0x1EE4B], + [0x1EE4D, 0x1EE4F], + [0x1EE51, 0x1EE52], + [0x1EE54, 0x1EE54], + [0x1EE57, 0x1EE57], + [0x1EE59, 0x1EE59], + [0x1EE5B, 0x1EE5B], + [0x1EE5D, 0x1EE5D], + [0x1EE5F, 0x1EE5F], + [0x1EE61, 0x1EE62], + [0x1EE64, 0x1EE64], + [0x1EE67, 0x1EE6A], + [0x1EE6C, 0x1EE72], + [0x1EE74, 0x1EE77], + [0x1EE79, 0x1EE7C], + [0x1EE7E, 0x1EE7E], + [0x1EE80, 0x1EE89], + [0x1EE8B, 0x1EE9B], + [0x1EEA1, 0x1EEA3], + [0x1EEA5, 0x1EEA9], + [0x1EEAB, 0x1EEBB], + [0x20000, 0x2B739], + [0x2B740, 0x2B81D], + [0x2B820, 0x2CEA1], + [0x2CEB0, 0x2EBE0], + [0x2EBF0, 0x2EE5D], + [0x2F800, 0x2FA1D], + [0x30000, 0x323AF], + [0x40000, 0x4FFFD], + [0x50000, 0x5FFFD], + [0x60000, 0x6FFFD], + [0x70000, 0x7FFFD], + [0x80000, 0x8FFFD], + [0x90000, 0x9FFFD], + [0xA0000, 0xAFFFD], + [0xB0000, 0xBFFFD], + [0xC0000, 0xCFFFD], + [0xD0000, 0xDFFFD], + [0xE0000, 0xEFFFD], +]; + +/** +Least restrictive Continue +Entries: 796056 +*/ +static immutable dchar[2][] LeastRestrictive_Continue = [ + [0xA8, 0xA8], + [0xAA, 0xAA], + [0xAD, 0xAD], + [0xAF, 0xAF], + [0xB2, 0xB5], + [0xB7, 0xBA], + [0xBC, 0xBE], + [0xC0, 0xD6], + [0xD8, 0xF6], + [0xF8, 0x217], + [0x250, 0x2A8], + [0x2B0, 0x2B8], + [0x2BB, 0x2BB], + [0x2BD, 0x2C1], + [0x2C6, 0x2D1], + [0x2E0, 0x2E4], + [0x2EC, 0x2EC], + [0x2EE, 0x2EE], + [0x300, 0x374], + [0x376, 0x377], + [0x37A, 0x37D], + [0x37F, 0x37F], + [0x386, 0x386], + [0x388, 0x38A], + [0x38C, 0x38C], + [0x38E, 0x3A1], + [0x3A3, 0x3D6], + [0x3DA, 0x3DA], + [0x3DC, 0x3DC], + [0x3DE, 0x3DE], + [0x3E0, 0x3E0], + [0x3E2, 0x3F3], + [0x3F7, 0x40C], + [0x40E, 0x44F], + [0x451, 0x45C], + [0x45E, 0x481], + [0x483, 0x487], + [0x48A, 0x4C4], + [0x4C7, 0x4C8], + [0x4CB, 0x4CC], + [0x4D0, 0x4EB], + [0x4EE, 0x4F5], + [0x4F8, 0x4F9], + [0x531, 0x556], + [0x559, 0x559], + [0x560, 0x587], + [0x591, 0x5B9], + [0x5BB, 0x5BD], + [0x5BF, 0x5BF], + [0x5C1, 0x5C2], + [0x5C4, 0x5C5], + [0x5C7, 0x5C7], + [0x5D0, 0x5EA], + [0x5EF, 0x5F2], + [0x610, 0x61A], + [0x620, 0x63A], + [0x640, 0x652], + [0x660, 0x669], + [0x66E, 0x6B7], + [0x6BA, 0x6BE], + [0x6C0, 0x6CE], + [0x6D0, 0x6DC], + [0x6DF, 0x6E8], + [0x6EA, 0x6ED], + [0x6F0, 0x6F9], + [0x6FF, 0x6FF], + [0x710, 0x74A], + [0x74D, 0x7B1], + [0x7C0, 0x7F5], + [0x7FA, 0x7FA], + [0x7FD, 0x7FD], + [0x800, 0x82D], + [0x840, 0x85B], + [0x860, 0x86A], + [0x870, 0x887], + [0x889, 0x88E], + [0x898, 0x8E1], + [0x8E3, 0x903], + [0x905, 0x939], + [0x93D, 0x94D], + [0x950, 0x952], + [0x958, 0x963], + [0x966, 0x96F], + [0x971, 0x983], + [0x985, 0x98C], + [0x98F, 0x990], + [0x993, 0x9A8], + [0x9AA, 0x9B0], + [0x9B2, 0x9B2], + [0x9B6, 0x9B9], + [0x9BC, 0x9C4], + [0x9C7, 0x9C8], + [0x9CB, 0x9CD], + [0x9D7, 0x9D7], + [0x9DC, 0x9DD], + [0x9DF, 0x9E3], + [0x9E6, 0x9F1], + [0x9FC, 0x9FC], + [0x9FE, 0x9FE], + [0xA01, 0xA02], + [0xA05, 0xA0A], + [0xA0F, 0xA10], + [0xA13, 0xA28], + [0xA2A, 0xA30], + [0xA32, 0xA33], + [0xA35, 0xA36], + [0xA38, 0xA39], + [0xA3C, 0xA3C], + [0xA3E, 0xA42], + [0xA47, 0xA48], + [0xA4B, 0xA4D], + [0xA51, 0xA51], + [0xA59, 0xA5C], + [0xA5E, 0xA5E], + [0xA66, 0xA6F], + [0xA74, 0xA74], + [0xA81, 0xA83], + [0xA85, 0xA8B], + [0xA8D, 0xA8D], + [0xA8F, 0xA91], + [0xA93, 0xAA8], + [0xAAA, 0xAB0], + [0xAB2, 0xAB3], + [0xAB5, 0xAB9], + [0xABC, 0xAC5], + [0xAC7, 0xAC9], + [0xACB, 0xACD], + [0xAD0, 0xAD0], + [0xAE0, 0xAE0], + [0xAE6, 0xAEF], + [0xAF9, 0xAFF], + [0xB01, 0xB03], + [0xB05, 0xB0C], + [0xB0F, 0xB10], + [0xB13, 0xB28], + [0xB2A, 0xB30], + [0xB32, 0xB33], + [0xB35, 0xB39], + [0xB3C, 0xB43], + [0xB47, 0xB48], + [0xB4B, 0xB4D], + [0xB55, 0xB57], + [0xB5C, 0xB5D], + [0xB5F, 0xB61], + [0xB66, 0xB6F], + [0xB71, 0xB71], + [0xB82, 0xB83], + [0xB85, 0xB8A], + [0xB8E, 0xB90], + [0xB92, 0xB95], + [0xB99, 0xB9A], + [0xB9C, 0xB9C], + [0xB9E, 0xB9F], + [0xBA3, 0xBA4], + [0xBA8, 0xBAA], + [0xBAE, 0xBB5], + [0xBB7, 0xBB9], + [0xBBE, 0xBC2], + [0xBC6, 0xBC8], + [0xBCA, 0xBCD], + [0xBD0, 0xBD0], + [0xBD7, 0xBD7], + [0xBE6, 0xBEF], + [0xC00, 0xC03], + [0xC05, 0xC0C], + [0xC0E, 0xC10], + [0xC12, 0xC28], + [0xC2A, 0xC33], + [0xC35, 0xC39], + [0xC3C, 0xC44], + [0xC46, 0xC48], + [0xC4A, 0xC4D], + [0xC55, 0xC56], + [0xC58, 0xC5A], + [0xC5D, 0xC5D], + [0xC60, 0xC61], + [0xC66, 0xC6F], + [0xC80, 0xC83], + [0xC85, 0xC8C], + [0xC8E, 0xC90], + [0xC92, 0xCA8], + [0xCAA, 0xCB3], + [0xCB5, 0xCB9], + [0xCBC, 0xCC4], + [0xCC6, 0xCC8], + [0xCCA, 0xCCD], + [0xCD5, 0xCD6], + [0xCDD, 0xCDE], + [0xCE0, 0xCE1], + [0xCE6, 0xCEF], + [0xCF1, 0xCF3], + [0xD00, 0xD03], + [0xD05, 0xD0C], + [0xD0E, 0xD10], + [0xD12, 0xD39], + [0xD3E, 0xD43], + [0xD46, 0xD48], + [0xD4A, 0xD4E], + [0xD54, 0xD57], + [0xD5F, 0xD61], + [0xD66, 0xD6F], + [0xD7A, 0xD7F], + [0xD81, 0xD83], + [0xD85, 0xD96], + [0xD9A, 0xDB1], + [0xDB3, 0xDBB], + [0xDBD, 0xDBD], + [0xDC0, 0xDC6], + [0xDCA, 0xDCA], + [0xDCF, 0xDD4], + [0xDD6, 0xDD6], + [0xDD8, 0xDDF], + [0xDE6, 0xDEF], + [0xDF2, 0xDF3], + [0xE01, 0xE3A], + [0xE40, 0xE4E], + [0xE50, 0xE59], + [0xE81, 0xE82], + [0xE84, 0xE84], + [0xE86, 0xE88], + [0xE8A, 0xE8A], + [0xE8C, 0xE8D], + [0xE94, 0xE97], + [0xE99, 0xE9F], + [0xEA1, 0xEA3], + [0xEA5, 0xEA5], + [0xEA7, 0xEAB], + [0xEAD, 0xEAE], + [0xEB0, 0xEB9], + [0xEBB, 0xEBD], + [0xEC0, 0xEC4], + [0xEC6, 0xEC6], + [0xEC8, 0xECE], + [0xED0, 0xED9], + [0xEDC, 0xEDF], + [0xF00, 0xF00], + [0xF18, 0xF19], + [0xF20, 0xF29], + [0xF35, 0xF35], + [0xF37, 0xF37], + [0xF39, 0xF39], + [0xF3E, 0xF47], + [0xF49, 0xF6C], + [0xF71, 0xF84], + [0xF86, 0xF95], + [0xF97, 0xF97], + [0xF99, 0xFB7], + [0xFB9, 0xFB9], + [0xFC6, 0xFC6], + [0x1000, 0x1049], + [0x1050, 0x109D], + [0x10A0, 0x10C5], + [0x10C7, 0x10C7], + [0x10CD, 0x10CD], + [0x10D0, 0x10FA], + [0x10FC, 0x1248], + [0x124A, 0x124D], + [0x1250, 0x1256], + [0x1258, 0x1258], + [0x125A, 0x125D], + [0x1260, 0x1288], + [0x128A, 0x128D], + [0x1290, 0x12B0], + [0x12B2, 0x12B5], + [0x12B8, 0x12BE], + [0x12C0, 0x12C0], + [0x12C2, 0x12C5], + [0x12C8, 0x12D6], + [0x12D8, 0x1310], + [0x1312, 0x1315], + [0x1318, 0x135A], + [0x135D, 0x135F], + [0x1369, 0x1371], + [0x1380, 0x138F], + [0x13A0, 0x13F5], + [0x13F8, 0x13FD], + [0x1401, 0x166C], + [0x166F, 0x167F], + [0x1681, 0x169A], + [0x16A0, 0x16EA], + [0x16EE, 0x16F8], + [0x1700, 0x1715], + [0x171F, 0x1734], + [0x1740, 0x1753], + [0x1760, 0x176C], + [0x176E, 0x1770], + [0x1772, 0x1773], + [0x1780, 0x17D3], + [0x17D7, 0x17D7], + [0x17DC, 0x17DD], + [0x17E0, 0x17E9], + [0x180B, 0x180D], + [0x180F, 0x1819], + [0x1820, 0x1878], + [0x1880, 0x18AA], + [0x18B0, 0x18F5], + [0x1900, 0x191E], + [0x1920, 0x192B], + [0x1930, 0x193B], + [0x1946, 0x196D], + [0x1970, 0x1974], + [0x1980, 0x19AB], + [0x19B0, 0x19C9], + [0x19D0, 0x19DA], + [0x1A00, 0x1A1B], + [0x1A20, 0x1A5E], + [0x1A60, 0x1A7C], + [0x1A7F, 0x1A89], + [0x1A90, 0x1A99], + [0x1AA7, 0x1AA7], + [0x1AB0, 0x1ABD], + [0x1ABF, 0x1ACE], + [0x1B00, 0x1B4C], + [0x1B50, 0x1B59], + [0x1B6B, 0x1B73], + [0x1B80, 0x1BF3], + [0x1C00, 0x1C37], + [0x1C40, 0x1C49], + [0x1C4D, 0x1C7D], + [0x1C80, 0x1C88], + [0x1C90, 0x1CBA], + [0x1CBD, 0x1CBF], + [0x1CD0, 0x1CD2], + [0x1CD4, 0x1CFA], + [0x1D00, 0x1EF9], + [0x1F00, 0x1F15], + [0x1F18, 0x1F1D], + [0x1F20, 0x1F45], + [0x1F48, 0x1F4D], + [0x1F50, 0x1F57], + [0x1F59, 0x1F59], + [0x1F5B, 0x1F5B], + [0x1F5D, 0x1F5D], + [0x1F5F, 0x1F7D], + [0x1F80, 0x1FB4], + [0x1FB6, 0x1FBC], + [0x1FBE, 0x1FBE], + [0x1FC2, 0x1FC4], + [0x1FC6, 0x1FCC], + [0x1FD0, 0x1FD3], + [0x1FD6, 0x1FDB], + [0x1FE0, 0x1FEC], + [0x1FF2, 0x1FF4], + [0x1FF6, 0x1FFC], + [0x200B, 0x200D], + [0x202A, 0x202E], + [0x203F, 0x2040], + [0x2054, 0x2054], + [0x2060, 0x2071], + [0x207F, 0x207F], + [0x2090, 0x209C], + [0x20D0, 0x20DC], + [0x20E1, 0x20E1], + [0x20E5, 0x20F0], + [0x2102, 0x2102], + [0x2107, 0x2107], + [0x210A, 0x2113], + [0x2115, 0x2115], + [0x2118, 0x211D], + [0x2124, 0x2124], + [0x2126, 0x2126], + [0x2128, 0x2128], + [0x212A, 0x2138], + [0x213C, 0x213F], + [0x2145, 0x2149], + [0x214E, 0x214E], + [0x2160, 0x2188], + [0x2460, 0x24FF], + [0x2776, 0x2793], + [0x2C00, 0x2CE4], + [0x2CEB, 0x2CF3], + [0x2D00, 0x2D25], + [0x2D27, 0x2D27], + [0x2D2D, 0x2D2D], + [0x2D30, 0x2D67], + [0x2D6F, 0x2D6F], + [0x2D7F, 0x2D96], + [0x2DA0, 0x2DA6], + [0x2DA8, 0x2DAE], + [0x2DB0, 0x2DB6], + [0x2DB8, 0x2DBE], + [0x2DC0, 0x2DC6], + [0x2DC8, 0x2DCE], + [0x2DD0, 0x2DD6], + [0x2DD8, 0x2DDE], + [0x2DE0, 0x2DFF], + [0x2E80, 0x2FFF], + [0x3004, 0x3007], + [0x3021, 0x302F], + [0x3031, 0x303C], + [0x3041, 0x3096], + [0x3099, 0x309F], + [0x30A1, 0x30FC], + [0x3105, 0x312F], + [0x3131, 0x318E], + [0x31A0, 0x31BF], + [0x31F0, 0x31FF], + [0x3400, 0x4DBF], + [0x4E00, 0xA48C], + [0xA4D0, 0xA4FD], + [0xA500, 0xA60C], + [0xA610, 0xA62B], + [0xA640, 0xA66F], + [0xA674, 0xA67D], + [0xA67F, 0xA6F1], + [0xA717, 0xA71F], + [0xA722, 0xA788], + [0xA78B, 0xA7CA], + [0xA7D0, 0xA7D1], + [0xA7D3, 0xA7D3], + [0xA7D5, 0xA7D9], + [0xA7F2, 0xA827], + [0xA82C, 0xA82C], + [0xA840, 0xA873], + [0xA880, 0xA8C5], + [0xA8D0, 0xA8D9], + [0xA8E0, 0xA8F7], + [0xA8FB, 0xA8FB], + [0xA8FD, 0xA92D], + [0xA930, 0xA953], + [0xA960, 0xA97C], + [0xA980, 0xA9C0], + [0xA9CF, 0xA9D9], + [0xA9E0, 0xA9FE], + [0xAA00, 0xAA36], + [0xAA40, 0xAA4D], + [0xAA50, 0xAA59], + [0xAA60, 0xAA76], + [0xAA7A, 0xAAC2], + [0xAADB, 0xAADD], + [0xAAE0, 0xAAEF], + [0xAAF2, 0xAAF6], + [0xAB01, 0xAB06], + [0xAB09, 0xAB0E], + [0xAB11, 0xAB16], + [0xAB20, 0xAB26], + [0xAB28, 0xAB2E], + [0xAB30, 0xAB5A], + [0xAB5C, 0xAB69], + [0xAB70, 0xABEA], + [0xABEC, 0xABED], + [0xABF0, 0xABF9], + [0xAC00, 0xD7A3], + [0xD7B0, 0xD7C6], + [0xD7CB, 0xD7FB], + [0xF900, 0xFA6D], + [0xFA70, 0xFAD9], + [0xFB00, 0xFB06], + [0xFB13, 0xFB17], + [0xFB1D, 0xFB28], + [0xFB2A, 0xFB36], + [0xFB38, 0xFB3C], + [0xFB3E, 0xFB3E], + [0xFB40, 0xFB41], + [0xFB43, 0xFB44], + [0xFB46, 0xFBB1], + [0xFBD3, 0xFC5D], + [0xFC64, 0xFD3D], + [0xFD40, 0xFD8F], + [0xFD92, 0xFDC7], + [0xFDF0, 0xFDF9], + [0xFE00, 0xFE0F], + [0xFE20, 0xFE2F], + [0xFE33, 0xFE34], + [0xFE47, 0xFE4F], + [0xFE71, 0xFE71], + [0xFE73, 0xFE73], + [0xFE77, 0xFE77], + [0xFE79, 0xFE79], + [0xFE7B, 0xFE7B], + [0xFE7D, 0xFE7D], + [0xFE7F, 0xFEFC], + [0xFF10, 0xFF19], + [0xFF21, 0xFF3A], + [0xFF3F, 0xFF3F], + [0xFF41, 0xFF5A], + [0xFF65, 0xFFBE], + [0xFFC2, 0xFFC7], + [0xFFCA, 0xFFCF], + [0xFFD2, 0xFFD7], + [0xFFDA, 0xFFDC], + [0x10000, 0x1000B], + [0x1000D, 0x10026], + [0x10028, 0x1003A], + [0x1003C, 0x1003D], + [0x1003F, 0x1004D], + [0x10050, 0x1005D], + [0x10080, 0x100FA], + [0x10140, 0x10174], + [0x101FD, 0x101FD], + [0x10280, 0x1029C], + [0x102A0, 0x102D0], + [0x102E0, 0x102E0], + [0x10300, 0x1031F], + [0x1032D, 0x1034A], + [0x10350, 0x1037A], + [0x10380, 0x1039D], + [0x103A0, 0x103C3], + [0x103C8, 0x103CF], + [0x103D1, 0x103D5], + [0x10400, 0x1049D], + [0x104A0, 0x104A9], + [0x104B0, 0x104D3], + [0x104D8, 0x104FB], + [0x10500, 0x10527], + [0x10530, 0x10563], + [0x10570, 0x1057A], + [0x1057C, 0x1058A], + [0x1058C, 0x10592], + [0x10594, 0x10595], + [0x10597, 0x105A1], + [0x105A3, 0x105B1], + [0x105B3, 0x105B9], + [0x105BB, 0x105BC], + [0x10600, 0x10736], + [0x10740, 0x10755], + [0x10760, 0x10767], + [0x10780, 0x10785], + [0x10787, 0x107B0], + [0x107B2, 0x107BA], + [0x10800, 0x10805], + [0x10808, 0x10808], + [0x1080A, 0x10835], + [0x10837, 0x10838], + [0x1083C, 0x1083C], + [0x1083F, 0x10855], + [0x10860, 0x10876], + [0x10880, 0x1089E], + [0x108E0, 0x108F2], + [0x108F4, 0x108F5], + [0x10900, 0x10915], + [0x10920, 0x10939], + [0x10980, 0x109B7], + [0x109BE, 0x109BF], + [0x10A00, 0x10A03], + [0x10A05, 0x10A06], + [0x10A0C, 0x10A13], + [0x10A15, 0x10A17], + [0x10A19, 0x10A35], + [0x10A38, 0x10A3A], + [0x10A3F, 0x10A3F], + [0x10A60, 0x10A7C], + [0x10A80, 0x10A9C], + [0x10AC0, 0x10AC7], + [0x10AC9, 0x10AE6], + [0x10B00, 0x10B35], + [0x10B40, 0x10B55], + [0x10B60, 0x10B72], + [0x10B80, 0x10B91], + [0x10C00, 0x10C48], + [0x10C80, 0x10CB2], + [0x10CC0, 0x10CF2], + [0x10D00, 0x10D27], + [0x10D30, 0x10D39], + [0x10E80, 0x10EA9], + [0x10EAB, 0x10EAC], + [0x10EB0, 0x10EB1], + [0x10EFD, 0x10F1C], + [0x10F27, 0x10F27], + [0x10F30, 0x10F50], + [0x10F70, 0x10F85], + [0x10FB0, 0x10FC4], + [0x10FE0, 0x10FF6], + [0x11000, 0x11046], + [0x11066, 0x11075], + [0x1107F, 0x110BA], + [0x110C2, 0x110C2], + [0x110D0, 0x110E8], + [0x110F0, 0x110F9], + [0x11100, 0x11134], + [0x11136, 0x1113F], + [0x11144, 0x11147], + [0x11150, 0x11173], + [0x11176, 0x11176], + [0x11180, 0x111C4], + [0x111C9, 0x111CC], + [0x111CE, 0x111DA], + [0x111DC, 0x111DC], + [0x11200, 0x11211], + [0x11213, 0x11237], + [0x1123E, 0x11241], + [0x11280, 0x11286], + [0x11288, 0x11288], + [0x1128A, 0x1128D], + [0x1128F, 0x1129D], + [0x1129F, 0x112A8], + [0x112B0, 0x112EA], + [0x112F0, 0x112F9], + [0x11300, 0x11303], + [0x11305, 0x1130C], + [0x1130F, 0x11310], + [0x11313, 0x11328], + [0x1132A, 0x11330], + [0x11332, 0x11333], + [0x11335, 0x11339], + [0x1133B, 0x11344], + [0x11347, 0x11348], + [0x1134B, 0x1134D], + [0x11350, 0x11350], + [0x11357, 0x11357], + [0x1135D, 0x11363], + [0x11366, 0x1136C], + [0x11370, 0x11374], + [0x11400, 0x1144A], + [0x11450, 0x11459], + [0x1145E, 0x11461], + [0x11480, 0x114C5], + [0x114C7, 0x114C7], + [0x114D0, 0x114D9], + [0x11580, 0x115B5], + [0x115B8, 0x115C0], + [0x115D8, 0x115DD], + [0x11600, 0x11640], + [0x11644, 0x11644], + [0x11650, 0x11659], + [0x11680, 0x116B8], + [0x116C0, 0x116C9], + [0x11700, 0x1171A], + [0x1171D, 0x1172B], + [0x11730, 0x11739], + [0x11740, 0x11746], + [0x11800, 0x1183A], + [0x118A0, 0x118E9], + [0x118FF, 0x11906], + [0x11909, 0x11909], + [0x1190C, 0x11913], + [0x11915, 0x11916], + [0x11918, 0x11935], + [0x11937, 0x11938], + [0x1193B, 0x11943], + [0x11950, 0x11959], + [0x119A0, 0x119A7], + [0x119AA, 0x119D7], + [0x119DA, 0x119E1], + [0x119E3, 0x119E4], + [0x11A00, 0x11A3E], + [0x11A47, 0x11A47], + [0x11A50, 0x11A99], + [0x11A9D, 0x11A9D], + [0x11AB0, 0x11AF8], + [0x11C00, 0x11C08], + [0x11C0A, 0x11C36], + [0x11C38, 0x11C40], + [0x11C50, 0x11C59], + [0x11C72, 0x11C8F], + [0x11C92, 0x11CA7], + [0x11CA9, 0x11CB6], + [0x11D00, 0x11D06], + [0x11D08, 0x11D09], + [0x11D0B, 0x11D36], + [0x11D3A, 0x11D3A], + [0x11D3C, 0x11D3D], + [0x11D3F, 0x11D47], + [0x11D50, 0x11D59], + [0x11D60, 0x11D65], + [0x11D67, 0x11D68], + [0x11D6A, 0x11D8E], + [0x11D90, 0x11D91], + [0x11D93, 0x11D98], + [0x11DA0, 0x11DA9], + [0x11EE0, 0x11EF6], + [0x11F00, 0x11F10], + [0x11F12, 0x11F3A], + [0x11F3E, 0x11F42], + [0x11F50, 0x11F59], + [0x11FB0, 0x11FB0], + [0x12000, 0x12399], + [0x12400, 0x1246E], + [0x12480, 0x12543], + [0x12F90, 0x12FF0], + [0x13000, 0x1342F], + [0x13440, 0x13455], + [0x14400, 0x14646], + [0x16800, 0x16A38], + [0x16A40, 0x16A5E], + [0x16A60, 0x16A69], + [0x16A70, 0x16ABE], + [0x16AC0, 0x16AC9], + [0x16AD0, 0x16AED], + [0x16AF0, 0x16AF4], + [0x16B00, 0x16B36], + [0x16B40, 0x16B43], + [0x16B50, 0x16B59], + [0x16B63, 0x16B77], + [0x16B7D, 0x16B8F], + [0x16E40, 0x16E7F], + [0x16F00, 0x16F4A], + [0x16F4F, 0x16F87], + [0x16F8F, 0x16F9F], + [0x16FE0, 0x16FE1], + [0x16FE3, 0x16FE4], + [0x16FF0, 0x16FF1], + [0x17000, 0x187F7], + [0x18800, 0x18CD5], + [0x18D00, 0x18D08], + [0x1AFF0, 0x1AFF3], + [0x1AFF5, 0x1AFFB], + [0x1AFFD, 0x1AFFE], + [0x1B000, 0x1B122], + [0x1B132, 0x1B132], + [0x1B150, 0x1B152], + [0x1B155, 0x1B155], + [0x1B164, 0x1B167], + [0x1B170, 0x1B2FB], + [0x1BC00, 0x1BC6A], + [0x1BC70, 0x1BC7C], + [0x1BC80, 0x1BC88], + [0x1BC90, 0x1BC99], + [0x1BC9D, 0x1BC9E], + [0x1CF00, 0x1CF2D], + [0x1CF30, 0x1CF46], + [0x1D165, 0x1D169], + [0x1D16D, 0x1D172], + [0x1D17B, 0x1D182], + [0x1D185, 0x1D18B], + [0x1D1AA, 0x1D1AD], + [0x1D242, 0x1D244], + [0x1D400, 0x1D454], + [0x1D456, 0x1D49C], + [0x1D49E, 0x1D49F], + [0x1D4A2, 0x1D4A2], + [0x1D4A5, 0x1D4A6], + [0x1D4A9, 0x1D4AC], + [0x1D4AE, 0x1D4B9], + [0x1D4BB, 0x1D4BB], + [0x1D4BD, 0x1D4C3], + [0x1D4C5, 0x1D505], + [0x1D507, 0x1D50A], + [0x1D50D, 0x1D514], + [0x1D516, 0x1D51C], + [0x1D51E, 0x1D539], + [0x1D53B, 0x1D53E], + [0x1D540, 0x1D544], + [0x1D546, 0x1D546], + [0x1D54A, 0x1D550], + [0x1D552, 0x1D6A5], + [0x1D6A8, 0x1D6C0], + [0x1D6C2, 0x1D6DA], + [0x1D6DC, 0x1D6FA], + [0x1D6FC, 0x1D714], + [0x1D716, 0x1D734], + [0x1D736, 0x1D74E], + [0x1D750, 0x1D76E], + [0x1D770, 0x1D788], + [0x1D78A, 0x1D7A8], + [0x1D7AA, 0x1D7C2], + [0x1D7C4, 0x1D7CB], + [0x1D7CE, 0x1D7FF], + [0x1DA00, 0x1DA36], + [0x1DA3B, 0x1DA6C], + [0x1DA75, 0x1DA75], + [0x1DA84, 0x1DA84], + [0x1DA9B, 0x1DA9F], + [0x1DAA1, 0x1DAAF], + [0x1DF00, 0x1DF1E], + [0x1DF25, 0x1DF2A], + [0x1E000, 0x1E006], + [0x1E008, 0x1E018], + [0x1E01B, 0x1E021], + [0x1E023, 0x1E024], + [0x1E026, 0x1E02A], + [0x1E030, 0x1E06D], + [0x1E08F, 0x1E08F], + [0x1E100, 0x1E12C], + [0x1E130, 0x1E13D], + [0x1E140, 0x1E149], + [0x1E14E, 0x1E14E], + [0x1E290, 0x1E2AE], + [0x1E2C0, 0x1E2F9], + [0x1E4D0, 0x1E4F9], + [0x1E7E0, 0x1E7E6], + [0x1E7E8, 0x1E7EB], + [0x1E7ED, 0x1E7EE], + [0x1E7F0, 0x1E7FE], + [0x1E800, 0x1E8C4], + [0x1E8D0, 0x1E8D6], + [0x1E900, 0x1E94B], + [0x1E950, 0x1E959], + [0x1EE00, 0x1EE03], + [0x1EE05, 0x1EE1F], + [0x1EE21, 0x1EE22], + [0x1EE24, 0x1EE24], + [0x1EE27, 0x1EE27], + [0x1EE29, 0x1EE32], + [0x1EE34, 0x1EE37], + [0x1EE39, 0x1EE39], + [0x1EE3B, 0x1EE3B], + [0x1EE42, 0x1EE42], + [0x1EE47, 0x1EE47], + [0x1EE49, 0x1EE49], + [0x1EE4B, 0x1EE4B], + [0x1EE4D, 0x1EE4F], + [0x1EE51, 0x1EE52], + [0x1EE54, 0x1EE54], + [0x1EE57, 0x1EE57], + [0x1EE59, 0x1EE59], + [0x1EE5B, 0x1EE5B], + [0x1EE5D, 0x1EE5D], + [0x1EE5F, 0x1EE5F], + [0x1EE61, 0x1EE62], + [0x1EE64, 0x1EE64], + [0x1EE67, 0x1EE6A], + [0x1EE6C, 0x1EE72], + [0x1EE74, 0x1EE77], + [0x1EE79, 0x1EE7C], + [0x1EE7E, 0x1EE7E], + [0x1EE80, 0x1EE89], + [0x1EE8B, 0x1EE9B], + [0x1EEA1, 0x1EEA3], + [0x1EEA5, 0x1EEA9], + [0x1EEAB, 0x1EEBB], + [0x1FBF0, 0x1FBF9], + [0x20000, 0x2B739], + [0x2B740, 0x2B81D], + [0x2B820, 0x2CEA1], + [0x2CEB0, 0x2EBE0], + [0x2EBF0, 0x2EE5D], + [0x2F800, 0x2FA1D], + [0x30000, 0x323AF], + [0x40000, 0x4FFFD], + [0x50000, 0x5FFFD], + [0x60000, 0x6FFFD], + [0x70000, 0x7FFFD], + [0x80000, 0x8FFFD], + [0x90000, 0x9FFFD], + [0xA0000, 0xAFFFD], + [0xB0000, 0xBFFFD], + [0xC0000, 0xCFFFD], + [0xD0000, 0xDFFFD], + [0xE0000, 0xE01EF], +]; diff --git a/dmd/dmangle.d b/dmd/dmangle.d index 3640b67c7a9..d4eecb94a97 100644 --- a/dmd/dmangle.d +++ b/dmd/dmangle.d @@ -72,12 +72,14 @@ void mangleToBuffer(TemplateInstance ti, ref OutBuffer buf) /// Returns: `true` if the given character is a valid mangled character package bool isValidMangling(dchar c) nothrow { + import dmd.common.charactertables; + return c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c >= '0' && c <= '9' || c != 0 && strchr("$%().:?@[]_", c) || - isUniAlpha(c); + isAnyIdentifierCharacter(c); } // valid mangled characters diff --git a/dmd/doc.d b/dmd/doc.d index c00c1cc8f40..a8b43da625b 100644 --- a/dmd/doc.d +++ b/dmd/doc.d @@ -2106,43 +2106,13 @@ int getMarkdownIndent(ref OutBuffer buf, size_t from, size_t to) @safe return indent; } -/************************************************ - * Scan forward to one of: - * start of identifier - * beginning of next line - * end of buf - */ -size_t skiptoident(ref OutBuffer buf, size_t i) @safe -{ - const slice = buf[]; - while (i < slice.length) - { - dchar c; - size_t oi = i; - if (utf_decodeChar(slice, i, c)) - { - /* Ignore UTF errors, but still consume input - */ - break; - } - if (c >= 0x80) - { - if (!isUniAlpha(c)) - continue; - } - else if (!(isalpha(c) || c == '_' || c == '\n')) - continue; - i = oi; - break; - } - return i; -} - /************************************************ * Scan forward past end of identifier. */ size_t skippastident(ref OutBuffer buf, size_t i) @safe { + import dmd.common.charactertables; + const slice = buf[]; while (i < slice.length) { @@ -2156,7 +2126,8 @@ size_t skippastident(ref OutBuffer buf, size_t i) @safe } if (c >= 0x80) { - if (isUniAlpha(c)) + // we don't care if it is start/continue here + if (isAnyIdentifierCharacter(c)) continue; } else if (isalnum(c) || c == '_') @@ -2173,6 +2144,8 @@ size_t skippastident(ref OutBuffer buf, size_t i) @safe */ size_t skipPastIdentWithDots(ref OutBuffer buf, size_t i) @safe { + import dmd.common.charactertables; + const slice = buf[]; bool lastCharWasDot; while (i < slice.length) @@ -2203,7 +2176,8 @@ size_t skipPastIdentWithDots(ref OutBuffer buf, size_t i) @safe { if (c >= 0x80) { - if (isUniAlpha(c)) + // we don't care if it is start/continue here + if (isAnyIdentifierCharacter(c)) { lastCharWasDot = false; continue; @@ -5249,6 +5223,8 @@ bool isCVariadicArg(const(char)[] p) @nogc nothrow pure @safe @trusted bool isIdStart(const(char)* p) @nogc nothrow pure { + import dmd.common.charactertables; + dchar c = *p; if (isalpha(c) || c == '_') return true; @@ -5257,7 +5233,7 @@ bool isIdStart(const(char)* p) @nogc nothrow pure size_t i = 0; if (utf_decodeChar(p[0 .. 4], i, c)) return false; // ignore errors - if (isUniAlpha(c)) + if (isAnyStart(c)) return true; } return false; @@ -5269,6 +5245,8 @@ bool isIdStart(const(char)* p) @nogc nothrow pure @trusted bool isIdTail(const(char)* p) @nogc nothrow pure { + import dmd.common.charactertables; + dchar c = *p; if (isalnum(c) || c == '_') return true; @@ -5277,7 +5255,7 @@ bool isIdTail(const(char)* p) @nogc nothrow pure size_t i = 0; if (utf_decodeChar(p[0 .. 4], i, c)) return false; // ignore errors - if (isUniAlpha(c)) + if (isAnyContinue(c)) return true; } return false; diff --git a/dmd/frontend.h b/dmd/frontend.h index 307f930c1c9..a2c9d2faf02 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -6118,6 +6118,15 @@ enum class CHECKACTION : uint8_t context = 3u, }; +enum class CLIIdentifierTable : uint8_t +{ + default_ = 0u, + C99 = 1u, + C11 = 2u, + UAX31 = 3u, + All = 4u, +}; + enum class JsonFieldFlags : uint32_t { none = 0u, @@ -6137,6 +6146,8 @@ struct CompileEnv final bool previewIn; bool ddocOutput; bool masm; + IdentifierCharLookup cCharLookupTable; + IdentifierCharLookup dCharLookupTable; CompileEnv() : versionNumber(), date(), @@ -6145,10 +6156,12 @@ struct CompileEnv final timestamp(), previewIn(), ddocOutput(), - masm() + masm(), + cCharLookupTable(), + dCharLookupTable() { } - CompileEnv(uint32_t versionNumber, _d_dynamicArray< const char > date = {}, _d_dynamicArray< const char > time = {}, _d_dynamicArray< const char > vendor = {}, _d_dynamicArray< const char > timestamp = {}, bool previewIn = false, bool ddocOutput = false, bool masm = false) : + CompileEnv(uint32_t versionNumber, _d_dynamicArray< const char > date = {}, _d_dynamicArray< const char > time = {}, _d_dynamicArray< const char > vendor = {}, _d_dynamicArray< const char > timestamp = {}, bool previewIn = false, bool ddocOutput = false, bool masm = false, IdentifierCharLookup cCharLookupTable = IdentifierCharLookup(), IdentifierCharLookup dCharLookupTable = IdentifierCharLookup()) : versionNumber(versionNumber), date(date), time(time), @@ -6156,7 +6169,9 @@ struct CompileEnv final timestamp(timestamp), previewIn(previewIn), ddocOutput(ddocOutput), - masm(masm) + masm(masm), + cCharLookupTable(cCharLookupTable), + dCharLookupTable(dCharLookupTable) {} }; @@ -7804,6 +7819,56 @@ extern _d_real cimagl(complex_t x); extern void browse(const char* url); +enum class IdentifierTable +{ + UAX31 = 0, + C99 = 1, + C11 = 2, + LR = 3, +}; + +struct IdentifierCharLookup final +{ + bool(*isStart)(char32_t ); + bool(*isContinue)(char32_t ); + static IdentifierCharLookup forTable(IdentifierTable table); + IdentifierCharLookup() : + isStart(), + isContinue() + { + } + IdentifierCharLookup(bool(*isStart)(char32_t ), bool(*isContinue)(char32_t ) = nullptr) : + isStart(isStart), + isContinue(isContinue) + {} +}; + +extern bool isAnyIdentifierCharacter(char32_t c); + +extern bool isAnyStart(char32_t c); + +extern bool isAnyContinue(char32_t c); + +enum : int32_t { LS = 8232 }; + +enum : int32_t { PS = 8233 }; + +extern bool isoctal(const char c); + +extern bool ishex(const char c); + +extern bool isidchar(const char c); + +extern bool isZeroSecond(const char c); + +extern bool isDigitSecond(const char c); + +extern bool issinglechar(const char c); + +extern bool c_isxdigit(const int32_t c); + +extern bool c_isalnum(const int32_t c); + extern void error(const Loc& loc, const char* format, ...); extern void error(const char* filename, uint32_t linnum, uint32_t charnum, const char* format, ...); @@ -8013,6 +8078,8 @@ struct Param final CHECKENABLE useSwitchError; CHECKENABLE boundscheck; CHECKACTION checkAction; + CLIIdentifierTable dIdentifierTable; + CLIIdentifierTable cIdentifierTable; _d_dynamicArray< const char > argv0; Array modFileAliasStrings; Array imppath; @@ -8088,6 +8155,8 @@ struct Param final useSwitchError((CHECKENABLE)0u), boundscheck((CHECKENABLE)0u), checkAction((CHECKACTION)0u), + dIdentifierTable((CLIIdentifierTable)0u), + cIdentifierTable((CLIIdentifierTable)0u), argv0(), modFileAliasStrings(), imppath(), @@ -8119,7 +8188,7 @@ struct Param final mapfile() { } - Param(bool obj, bool multiobj = false, bool trace = false, bool tracegc = false, bool vcg_ast = false, DiagnosticReporting useDeprecated = (DiagnosticReporting)1u, bool useUnitTests = false, bool useInline = false, bool release = false, bool preservePaths = false, DiagnosticReporting warnings = (DiagnosticReporting)2u, bool cov = false, uint8_t covPercent = 0u, bool ctfe_cov = false, bool ignoreUnsupportedPragmas = true, bool useModuleInfo = true, bool useTypeInfo = true, bool useExceptions = true, bool useGC = true, bool betterC = false, bool addMain = false, bool allInst = false, bool bitfields = false, CppStdRevision cplusplus = (CppStdRevision)201103u, Help help = Help(), Verbose v = Verbose(), FeatureState useDIP25 = (FeatureState)2u, FeatureState useDIP1000 = (FeatureState)0u, bool ehnogc = false, bool useDIP1021 = false, FeatureState fieldwise = (FeatureState)0u, bool fixAliasThis = false, FeatureState rvalueRefParam = (FeatureState)0u, FeatureState noSharedAccess = (FeatureState)0u, bool previewIn = false, bool inclusiveInContracts = false, bool shortenedMethods = true, bool fixImmutableConv = false, bool fix16997 = true, FeatureState dtorFields = (FeatureState)0u, FeatureState systemVariables = (FeatureState)0u, CHECKENABLE useInvariants = (CHECKENABLE)0u, CHECKENABLE useIn = (CHECKENABLE)0u, CHECKENABLE useOut = (CHECKENABLE)0u, CHECKENABLE useArrayBounds = (CHECKENABLE)0u, CHECKENABLE useAssert = (CHECKENABLE)0u, CHECKENABLE useSwitchError = (CHECKENABLE)0u, CHECKENABLE boundscheck = (CHECKENABLE)0u, CHECKACTION checkAction = (CHECKACTION)0u, _d_dynamicArray< const char > argv0 = {}, Array modFileAliasStrings = Array(), Array imppath = Array(), Array fileImppath = Array(), _d_dynamicArray< const char > objdir = {}, _d_dynamicArray< const char > objname = {}, _d_dynamicArray< const char > libname = {}, Output ddoc = Output(), Output dihdr = Output(), Output cxxhdr = Output(), Output json = Output(), JsonFieldFlags jsonFieldFlags = (JsonFieldFlags)0u, Output makeDeps = Output(), Output mixinOut = Output(), Output moduleDeps = Output(), uint32_t debuglevel = 0u, uint32_t versionlevel = 0u, bool run = false, Array runargs = Array(), Array cppswitches = Array(), const char* cpp = nullptr, Array objfiles = Array(), Array linkswitches = Array(), Array linkswitchIsForCC = Array(), Array libfiles = Array(), Array dllfiles = Array(), _d_dynamicArray< const char > deffile = {}, _d_dynamicArray< const char > resfile = {}, _d_dynamicArray< const char > exefile = {}, _d_dynamicArray< const char > mapfile = {}) : + Param(bool obj, bool multiobj = false, bool trace = false, bool tracegc = false, bool vcg_ast = false, DiagnosticReporting useDeprecated = (DiagnosticReporting)1u, bool useUnitTests = false, bool useInline = false, bool release = false, bool preservePaths = false, DiagnosticReporting warnings = (DiagnosticReporting)2u, bool cov = false, uint8_t covPercent = 0u, bool ctfe_cov = false, bool ignoreUnsupportedPragmas = true, bool useModuleInfo = true, bool useTypeInfo = true, bool useExceptions = true, bool useGC = true, bool betterC = false, bool addMain = false, bool allInst = false, bool bitfields = false, CppStdRevision cplusplus = (CppStdRevision)201103u, Help help = Help(), Verbose v = Verbose(), FeatureState useDIP25 = (FeatureState)2u, FeatureState useDIP1000 = (FeatureState)0u, bool ehnogc = false, bool useDIP1021 = false, FeatureState fieldwise = (FeatureState)0u, bool fixAliasThis = false, FeatureState rvalueRefParam = (FeatureState)0u, FeatureState noSharedAccess = (FeatureState)0u, bool previewIn = false, bool inclusiveInContracts = false, bool shortenedMethods = true, bool fixImmutableConv = false, bool fix16997 = true, FeatureState dtorFields = (FeatureState)0u, FeatureState systemVariables = (FeatureState)0u, CHECKENABLE useInvariants = (CHECKENABLE)0u, CHECKENABLE useIn = (CHECKENABLE)0u, CHECKENABLE useOut = (CHECKENABLE)0u, CHECKENABLE useArrayBounds = (CHECKENABLE)0u, CHECKENABLE useAssert = (CHECKENABLE)0u, CHECKENABLE useSwitchError = (CHECKENABLE)0u, CHECKENABLE boundscheck = (CHECKENABLE)0u, CHECKACTION checkAction = (CHECKACTION)0u, CLIIdentifierTable dIdentifierTable = (CLIIdentifierTable)0u, CLIIdentifierTable cIdentifierTable = (CLIIdentifierTable)0u, _d_dynamicArray< const char > argv0 = {}, Array modFileAliasStrings = Array(), Array imppath = Array(), Array fileImppath = Array(), _d_dynamicArray< const char > objdir = {}, _d_dynamicArray< const char > objname = {}, _d_dynamicArray< const char > libname = {}, Output ddoc = Output(), Output dihdr = Output(), Output cxxhdr = Output(), Output json = Output(), JsonFieldFlags jsonFieldFlags = (JsonFieldFlags)0u, Output makeDeps = Output(), Output mixinOut = Output(), Output moduleDeps = Output(), uint32_t debuglevel = 0u, uint32_t versionlevel = 0u, bool run = false, Array runargs = Array(), Array cppswitches = Array(), const char* cpp = nullptr, Array objfiles = Array(), Array linkswitches = Array(), Array linkswitchIsForCC = Array(), Array libfiles = Array(), Array dllfiles = Array(), _d_dynamicArray< const char > deffile = {}, _d_dynamicArray< const char > resfile = {}, _d_dynamicArray< const char > exefile = {}, _d_dynamicArray< const char > mapfile = {}) : obj(obj), multiobj(multiobj), trace(trace), @@ -8169,6 +8238,8 @@ struct Param final useSwitchError(useSwitchError), boundscheck(boundscheck), checkAction(checkAction), + dIdentifierTable(dIdentifierTable), + cIdentifierTable(cIdentifierTable), argv0(argv0), modFileAliasStrings(modFileAliasStrings), imppath(imppath), diff --git a/dmd/globals.d b/dmd/globals.d index 358696fd833..f8291fa5ce7 100644 --- a/dmd/globals.d +++ b/dmd/globals.d @@ -72,6 +72,16 @@ enum FeatureState : ubyte enabled = 2, /// Specified as `-preview=` } +/// Different identifier tables specifiable by CLI +enum CLIIdentifierTable : ubyte +{ + default_ = 0, /// Not specified by user + C99 = 1, /// Tables from C99 standard + C11 = 2, /// Tables from C11 standard + UAX31 = 3, /// Tables from the Unicode Standard Annex 31: UNICODE IDENTIFIERS AND SYNTAX + All = 4, /// The least restrictive set of all other tables +} + extern(C++) struct Output { bool doOutput; // Output is enabled @@ -199,6 +209,9 @@ extern (C++) struct Param CHECKACTION checkAction = CHECKACTION.D; // action to take when bounds, asserts or switch defaults are violated + CLIIdentifierTable dIdentifierTable = CLIIdentifierTable.default_; + CLIIdentifierTable cIdentifierTable = CLIIdentifierTable.default_; + const(char)[] argv0; // program name Array!(const(char)*) modFileAliasStrings; // array of char*'s of -I module filename alias strings Array!(const(char)*) imppath; // array of char*'s of where to look for import modules diff --git a/dmd/globals.h b/dmd/globals.h index f553ae6d8a9..ac2b2867fb7 100644 --- a/dmd/globals.h +++ b/dmd/globals.h @@ -13,6 +13,7 @@ #include "root/dcompat.h" #include "root/ctfloat.h" #include "common/outbuffer.h" +#include "common/charactertables.h" #include "root/filename.h" #include "compiler.h" @@ -82,6 +83,16 @@ enum class FeatureState : unsigned char enabled = 2, /// Specified as `-preview=` }; +/// Different identifier tables specifiable by CLI +enum class CLIIdentifierTable : unsigned char +{ + default_ = 0, /// Not specified by user + C99 = 1, /// Tables from C99 standard + C11 = 2, /// Tables from C11 standard + UAX31 = 3, /// Tables from the Unicode Standard Annex 31: UNICODE IDENTIFIERS AND SYNTAX + All = 4, /// The least restrictive set of all other tables +}; + struct Output { /// Configuration for the compiler generator @@ -200,6 +211,9 @@ struct Param CHECKACTION checkAction; // action to take when bounds, asserts or switch defaults are violated + CLIIdentifierTable dIdentifierTable; + CLIIdentifierTable cIdentifierTable; + DString argv0; // program name Array modFileAliasStrings; // array of char*'s of -I module filename alias strings Array imppath; // array of char*'s of where to look for import modules @@ -274,6 +288,9 @@ struct CompileEnv DString timestamp; d_bool previewIn; d_bool ddocOutput; + d_bool masm; + IdentifierCharLookup cCharLookupTable; + IdentifierCharLookup dCharLookupTable; }; struct Global diff --git a/dmd/identifier.d b/dmd/identifier.d index 8b40e9d6bbc..6fd0d3ad5ec 100644 --- a/dmd/identifier.d +++ b/dmd/identifier.d @@ -315,28 +315,83 @@ nothrow: /********************************** * ditto */ - extern (D) static bool isValidIdentifier(const(char)[] str) @safe + extern (D) static bool isValidIdentifier(const(char)[] str) @trusted { + import dmd.common.charactertables; + if (str.length == 0 || (str[0] >= '0' && str[0] <= '9')) // beware of isdigit() on signed chars { return false; } - size_t idx = 0; - while (idx < str.length) + // In a previous implementation this was implemented quite naively, + // by utilizing the libc. + // However we can do better, by copying the lexer approach to identifier validation. + + const(char)* p = &str[0], pEnd = str.ptr + str.length; + + // handle start characters { - dchar dc; - const s = utf_decodeChar(str, idx, dc); - if (s || - !((dc >= 0x80 && isUniAlpha(dc)) || isalnum(dc) || dc == '_')) + const c = *p; + + if (isidchar(c)) + p++; + else if (c & 0x80) { + size_t countDecoded; + dchar decoded; + + if (utf_decodeChar(p[0 .. pEnd - p], countDecoded, decoded) is null || + isAnyStart(decoded)) + p += countDecoded; + else + return false; + } + else return false; + } + + // handle continue characters + while(p !is pEnd) + { + const c = *p; + + if (isidchar(c)) // handles ASCII subset + { + p++; + continue; } + else if (c & 0x80) + { + size_t countDecoded; + dchar decoded; + + if (utf_decodeChar(p[0 .. pEnd - p], countDecoded, decoded) is null || + isAnyContinue(decoded)) + { + p += countDecoded; + continue; + } + else + return false; + } + else + return false; } + return true; } + /// + unittest + { + assert(Identifier.isValidIdentifier("tes123_t".ptr)); + assert(!Identifier.isValidIdentifier("tes123_^t".ptr)); + assert(Identifier.isValidIdentifier("te123s_ğt".ptr)); + assert(!Identifier.isValidIdentifier("t^e123s_ğt".ptr)); + } + extern (D) static Identifier lookup(const(char)* s, size_t len) { return lookup(s[0 .. len]); diff --git a/dmd/lexer.d b/dmd/lexer.d index c9c506e9cf3..b8a4223f884 100644 --- a/dmd/lexer.d +++ b/dmd/lexer.d @@ -22,9 +22,11 @@ import dmd.errorsink; import dmd.id; import dmd.identifier; import dmd.location; +import dmd.common.smallbuffer; +import dmd.common.outbuffer; +import dmd.common.charactertables; import dmd.root.array; import dmd.root.ctfloat; -import dmd.common.outbuffer; import dmd.root.port; import dmd.root.rmem; import dmd.root.utf; @@ -42,6 +44,8 @@ version (DMDLIB) */ struct CompileEnv { + import dmd.common.charactertables; + uint versionNumber; /// __VERSION__ const(char)[] date; /// __DATE__ const(char)[] time; /// __TIME__ @@ -51,6 +55,10 @@ struct CompileEnv bool previewIn; /// `in` means `[ref] scope const`, accepts rvalues bool ddocOutput; /// collect embedded documentation comments bool masm; /// use MASM inline asm syntax + + // these need a default otherwise tests won't work. + IdentifierCharLookup cCharLookupTable; + IdentifierCharLookup dCharLookupTable; } /*********************************************************** @@ -66,6 +74,8 @@ class Lexer Token token; + IdentifierCharLookup charLookup; + // For ImportC bool Ccompile; /// true if compiling ImportC @@ -142,6 +152,8 @@ class Lexer { this.compileEnv.versionNumber = 1; this.compileEnv.vendor = "DLF"; + this.compileEnv.cCharLookupTable = IdentifierCharLookup.forTable(IdentifierTable.LR); + this.compileEnv.dCharLookupTable = IdentifierCharLookup.forTable(IdentifierTable.LR); } //initKeywords(); /* If first line starts with '#!', ignore the line @@ -175,6 +187,16 @@ class Lexer } endOfLine(); } + + // setup the identifier table lookup functions + if (this.Ccompile) + { + charLookup = this.compileEnv.cCharLookupTable; + } + else + { + charLookup = this.compileEnv.dCharLookupTable; + } } /*********************** @@ -306,6 +328,8 @@ class Lexer t.blockComment = null; t.lineComment = null; + size_t universalCharacterName4, universalCharacterName8; + while (1) { t.ptr = p; @@ -395,10 +419,35 @@ class Lexer continue; // skip white space case '\\': - if (Ccompile && (p[1] == '\r' || p[1] == '\n')) + if (Ccompile) { - ++p; // ignore \ followed by new line, like VC does - continue; + if (p[1] == '\r' || p[1] == '\n') + { + ++p; // ignore \ followed by new line, like VC does + continue; + } + else if (p[1] == 'u') + { + // Universal Character Name (C) 2 byte + // \uXXXX + // let the main case handling for identifiers process this + + // case_indent will always increment, so subtract to prevent branching on the fast path + p--; + + goto case_ident; + } + else if (p[1] == 'U') + { + // Universal Character Name (C) 4 byte + // \UXXXXXXXX + // let the main case handling for identifiers process this + + // case_indent will always increment, so subtract to prevent branching on the fast path + p--; + + goto case_ident; + } } goto default; @@ -586,23 +635,161 @@ class Lexer case '_': case_ident: { - while (1) + IdentLoop: while (1) { + // If this is changed, change the decrement in C's universal character name code above + // For syntax \uXXXX and \UXXXXXXXX const c = *++p; + + // Is this the first character of the identifier + // For the universal character name this will line up, + // for the main switch it won't since it wasn't the first, + // for the default it won't either because a decode increments. + const isStartCharacter = t.ptr is p; + if (isidchar(c)) continue; else if (c & 0x80) { const s = p; const u = decodeUTF(); - if (isUniAlpha(u)) - continue; - error(t.loc, "char 0x%04x not allowed in identifier", u); + + if (isStartCharacter) + { + if (charLookup.isStart(u)) + continue; + error(t.loc, "character 0x%04x is not allowed as a start character in an identifier", u); + } + else + { + if (charLookup.isContinue(u)) + continue; + error(t.loc, "character 0x%04x is not allowed as a continue character in an identifier", u); + } + p = s; } + else if (Ccompile && c == '\\') + { + uint times; + const s = p; + p++; + + if (*p == 'u') + { + // Universal Character Name (C) 2 byte + // \uXXXX + p++; + times = 4; + } + else if (*p == 'U') + { + // Universal Character Name (C) 4 byte + // \UXXXXXXXX + p++; + times = 8; + } + else + { + error(t.loc, "char 0x%x is not allowed to follow '\\' expecting a C universal character name in format \\uXXXX or \\UXXXXXXXX with hex digits instead of X with invalid u/U", *p); + p = s; + break; + } + + foreach(_; 0 .. times) + { + const hc = *p; + p++; + + if ((hc >= '0' && hc <= '9') || (hc >= 'a' && hc <= 'f') || (hc >= 'A' && hc <= 'F')) + continue; + + error(t.loc, "char 0x%x is not allowed to follow '\\' expecting a C universal character name in format \\uXXXX or \\UXXXXXXXX with hex digits instead of X with invalid hex digit", hc); + p = s; + break IdentLoop; + } + + continue; + } break; } - Identifier id = Identifier.idPool((cast(char*)t.ptr)[0 .. p - t.ptr], false); + + Identifier id; + + if (universalCharacterName4 > 0 || universalCharacterName8 > 0) + { + auto priorValidation = t.ptr[0 .. p - t.ptr]; + const(char)* priorVPtr = priorValidation.ptr; + const possibleLength = ( + priorValidation.length - ( + (universalCharacterName4 * 6) + + (universalCharacterName8 * 10) + )) + ( + (universalCharacterName4 * 3) + + (universalCharacterName8 * 4) + ); + + char[64] buffer = void; + SmallBuffer!char sb = SmallBuffer!char(possibleLength, buffer[]); + + char[] storage = sb.extent; + size_t offset; + + while(priorVPtr < &priorValidation[$-1] + 1) + { + if (*priorVPtr == '\\') + { + dchar tempDchar = 0; + uint times; + + // universal character name (C) + if (priorVPtr[1] == 'u') + times = 4; + else if (priorVPtr[1] == 'U') + times = 8; + else + assert(0, "ICE: Universal character name is 2 or 4 bytes only"); + priorVPtr += 2; + + foreach(_; 0 .. times) + { + char c = *++priorVPtr; + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'a' && c <= 'f') + c -= 'a' - 10; + else if (c >= 'A' && c <= 'F') + c -= 'A' - 10; + + tempDchar <<= 4; + tempDchar |= c; + } + + utf_encodeChar(&storage[offset], tempDchar); + offset += utf_codeLengthChar(tempDchar); + + // Could be an error instead of a warning, + // but hey it was written specifically so why worry? + if (priorVPtr is priorValidation.ptr) + { + if (!charLookup.isStart(tempDchar)) + warning(t.loc, "char 0x%x is not allowed start character for an identifier", tempDchar); + } + else + { + if (!charLookup.isContinue(tempDchar)) + warning(t.loc, "char 0x%x is not allowed continue character for an identifier", tempDchar); + } + } + else + storage[offset++] = *++priorVPtr; + } + + id = Identifier.idPool(storage[0 .. offset], false); + } + else + id = Identifier.idPool((cast(char*)t.ptr)[0 .. p - t.ptr], false); + t.ident = id; t.value = cast(TOK)id.getValue(); @@ -1174,9 +1361,11 @@ class Lexer if (c & 0x80) { c = decodeUTF(); - // Check for start of unicode identifier - if (isUniAlpha(c)) + + // Check for start of an identifier + if (charLookup.isStart(c)) goto case_ident; + if (c == PS || c == LS) { endOfLine(); @@ -1688,7 +1877,7 @@ class Lexer delimright = ']'; else if (c == '<') delimright = '>'; - else if (isalpha(c) || c == '_' || (c >= 0x80 && isUniAlpha(c))) + else if (isalpha(c) || c == '_' || (c >= 0x80 && charLookup.isStart(c))) { // Start of identifier; must be a heredoc Token tok; @@ -1736,7 +1925,9 @@ class Lexer } else if (c == delimright) goto Ldone; - if (startline && (isalpha(c) || c == '_' || (c >= 0x80 && isUniAlpha(c))) && hereid) + + // we're looking for a new identifier token + if (startline && (isalpha(c) || c == '_' || (c >= 0x80 && charLookup.isStart(c))) && hereid) { Token tok; auto psave = p; @@ -2988,6 +3179,11 @@ class Lexer eSink.deprecation(loc, format, args); } + void warning(T...)(const ref Loc loc, const(char)* format, T args) + { + eSink.warning(loc, format, args); + } + void deprecation(T...)(const(char)* format, T args) { eSink.deprecation(token.loc, format, args); @@ -3416,124 +3612,6 @@ class Lexer } } - -/******************************* Private *****************************************/ - -private: - -private enum LS = 0x2028; // UTF line separator -private enum PS = 0x2029; // UTF paragraph separator - -/******************************************** - * Do our own char maps - */ -private static immutable cmtable = () -{ - ubyte[256] table; - foreach (const c; 0 .. table.length) - { - if ('0' <= c && c <= '7') - table[c] |= CMoctal; - if (c_isxdigit(c)) - table[c] |= CMhex; - if (c_isalnum(c) || c == '_') - table[c] |= CMidchar; - - switch (c) - { - case 'x': case 'X': - case 'b': case 'B': - table[c] |= CMzerosecond; - break; - - case '0': .. case '9': - case 'e': case 'E': - case 'f': case 'F': - case 'l': case 'L': - case 'p': case 'P': - case 'u': case 'U': - case 'i': - case '.': - case '_': - table[c] |= CMzerosecond | CMdigitsecond; - break; - - default: - break; - } - - switch (c) - { - case '\\': - case '\n': - case '\r': - case 0: - case 0x1A: - case '\'': - break; - default: - if (!(c & 0x80)) - table[c] |= CMsinglechar; - break; - } - } - return table; -}(); - -private -{ - enum CMoctal = 0x1; - enum CMhex = 0x2; - enum CMidchar = 0x4; - enum CMzerosecond = 0x8; - enum CMdigitsecond = 0x10; - enum CMsinglechar = 0x20; -} - -private bool isoctal(const char c) pure @nogc @safe -{ - return (cmtable[c] & CMoctal) != 0; -} - -private bool ishex(const char c) pure @nogc @safe -{ - return (cmtable[c] & CMhex) != 0; -} - -private bool isidchar(const char c) pure @nogc @safe -{ - return (cmtable[c] & CMidchar) != 0; -} - -private bool isZeroSecond(const char c) pure @nogc @safe -{ - return (cmtable[c] & CMzerosecond) != 0; -} - -private bool isDigitSecond(const char c) pure @nogc @safe -{ - return (cmtable[c] & CMdigitsecond) != 0; -} - -private bool issinglechar(const char c) pure @nogc @safe -{ - return (cmtable[c] & CMsinglechar) != 0; -} - -private bool c_isxdigit(const int c) pure @nogc @safe -{ - return (( c >= '0' && c <= '9') || - ( c >= 'a' && c <= 'f') || - ( c >= 'A' && c <= 'F')); -} - -private bool c_isalnum(const int c) pure @nogc @safe -{ - return (( c >= '0' && c <= '9') || - ( c >= 'a' && c <= 'z') || - ( c >= 'A' && c <= 'Z')); -} - /******************************* Unittest *****************************************/ unittest diff --git a/dmd/main.d b/dmd/main.d index a97a60bae0d..27f79379aba 100644 --- a/dmd/main.d +++ b/dmd/main.d @@ -157,6 +157,8 @@ private: */ private int tryMain(size_t argc, const(char)** argv, ref Param params) { + import dmd.common.charactertables; + Strings files; Strings libmodules; global._init(); @@ -168,6 +170,52 @@ private int tryMain(size_t argc, const(char)** argv, ref Param params) global.compileEnv.previewIn = global.params.previewIn; global.compileEnv.ddocOutput = global.params.ddoc.doOutput; + final switch(global.params.cIdentifierTable) + { + case CLIIdentifierTable.C99: + global.compileEnv.cCharLookupTable = IdentifierCharLookup.forTable(IdentifierTable.C99); + break; + + case CLIIdentifierTable.C11: + case CLIIdentifierTable.default_: + // ImportC is defined against C11, not C23. + // If it was C23 this needs to be changed to UAX31 instead. + global.compileEnv.cCharLookupTable = IdentifierCharLookup.forTable(IdentifierTable.C11); + break; + + case CLIIdentifierTable.UAX31: + global.compileEnv.cCharLookupTable = IdentifierCharLookup.forTable(IdentifierTable.UAX31); + break; + + case CLIIdentifierTable.All: + global.compileEnv.cCharLookupTable = IdentifierCharLookup.forTable(IdentifierTable.LR); + break; + } + + final switch(global.params.dIdentifierTable) + { + case CLIIdentifierTable.C99: + global.compileEnv.dCharLookupTable = IdentifierCharLookup.forTable(IdentifierTable.C99); + break; + + case CLIIdentifierTable.C11: + global.compileEnv.dCharLookupTable = IdentifierCharLookup.forTable(IdentifierTable.C11); + break; + + case CLIIdentifierTable.UAX31: + global.compileEnv.dCharLookupTable = IdentifierCharLookup.forTable(IdentifierTable.UAX31); + break; + + case CLIIdentifierTable.All: + case CLIIdentifierTable.default_: + // @@@DEPRECATED_2.119@@@ + // Change the default to UAX31, + // this is a breaking change as C99 (what D used for ~23 years), + // has characters that are not in UAX31. + global.compileEnv.dCharLookupTable = IdentifierCharLookup.forTable(IdentifierTable.LR); + break; + } + if (params.help.usage) { usage(); diff --git a/dmd/mars.d b/dmd/mars.d index 23f595636cd..06f694e9e47 100644 --- a/dmd/mars.d +++ b/dmd/mars.d @@ -1383,6 +1383,58 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param params.useInline = true; params.dihdr.fullOutput = true; } + else if (startsWith(p + 1, "identifiers-importc")) + { + enum len = "-identifiers-importc=".length; + // Parse: + // -identifiers=table + immutable string msg = "Only `UAX31`, `c99`, `c11`, `all`, allowed for `-identifiers-importc`"; + if (Identifier.isValidIdentifier(p + len)) + { + const ident = p + len; + switch (ident.toDString()) + { + case "c99": params.cIdentifierTable = CLIIdentifierTable.C99; break; + case "c11": params.cIdentifierTable = CLIIdentifierTable.C11; break; + case "UAX31": params.cIdentifierTable = CLIIdentifierTable.UAX31; break; + case "all": params.cIdentifierTable = CLIIdentifierTable.All; break; + default: + errorInvalidSwitch(p, msg); + return false; + } + } + else + { + errorInvalidSwitch(p, msg); + return false; + } + } + else if (startsWith(p + 1, "identifiers")) + { + enum len = "-identifiers=".length; + // Parse: + // -identifiers=table + immutable string msg = "Only `UAX31`, `c99`, `c11`, `all`, allowed for `-identifiers`"; + if (Identifier.isValidIdentifier(p + len)) + { + const ident = p + len; + switch (ident.toDString()) + { + case "c99": params.dIdentifierTable = CLIIdentifierTable.C99; break; + case "c11": params.dIdentifierTable = CLIIdentifierTable.C11; break; + case "UAX31": params.dIdentifierTable = CLIIdentifierTable.UAX31; break; + case "all": params.dIdentifierTable = CLIIdentifierTable.All; break; + default: + errorInvalidSwitch(p, msg); + return false; + } + } + else + { + errorInvalidSwitch(p, msg); + return false; + } + } else if (arg == "-i") includeImports = true; else if (startsWith(p + 1, "i=")) diff --git a/dmd/pragmasem.d b/dmd/pragmasem.d index b52b5516263..4dceb595e54 100644 --- a/dmd/pragmasem.d +++ b/dmd/pragmasem.d @@ -67,6 +67,8 @@ void pragmaDeclSemantic(PragmaDeclaration pd, Scope* sc) } version (all) { + import dmd.common.charactertables; + /* Note: D language specification should not have any assumption about backend * implementation. Ideally pragma(mangle) can accept a string of any content. * @@ -94,7 +96,7 @@ void pragmaDeclSemantic(PragmaDeclaration pd, Scope* sc) .error(pd.loc, "%s `%s` %.*s", pd.kind, pd.toPrettyChars, cast(int)msg.length, msg.ptr); break; } - if (!isUniAlpha(c)) + if (!isAnyIdentifierCharacter(c)) { .error(pd.loc, "%s `%s` char `0x%04x` not allowed in mangled name", pd.kind, pd.toPrettyChars, c); break; diff --git a/dmd/root/utf.d b/dmd/root/utf.d index 7d732f2fbe8..36f0b985c3a 100644 --- a/dmd/root/utf.d +++ b/dmd/root/utf.d @@ -27,281 +27,6 @@ bool utf_isValidDchar(dchar c) return false; } -/******************************* - * Return !=0 if unicode alpha. - * Use table from C99 Appendix D. - */ -bool isUniAlpha(dchar c) -{ - static immutable wchar[2][] ALPHA_TABLE = - [ - [0x00AA, 0x00AA], - [0x00B5, 0x00B5], - [0x00B7, 0x00B7], - [0x00BA, 0x00BA], - [0x00C0, 0x00D6], - [0x00D8, 0x00F6], - [0x00F8, 0x01F5], - [0x01FA, 0x0217], - [0x0250, 0x02A8], - [0x02B0, 0x02B8], - [0x02BB, 0x02BB], - [0x02BD, 0x02C1], - [0x02D0, 0x02D1], - [0x02E0, 0x02E4], - [0x037A, 0x037A], - [0x0386, 0x0386], - [0x0388, 0x038A], - [0x038C, 0x038C], - [0x038E, 0x03A1], - [0x03A3, 0x03CE], - [0x03D0, 0x03D6], - [0x03DA, 0x03DA], - [0x03DC, 0x03DC], - [0x03DE, 0x03DE], - [0x03E0, 0x03E0], - [0x03E2, 0x03F3], - [0x0401, 0x040C], - [0x040E, 0x044F], - [0x0451, 0x045C], - [0x045E, 0x0481], - [0x0490, 0x04C4], - [0x04C7, 0x04C8], - [0x04CB, 0x04CC], - [0x04D0, 0x04EB], - [0x04EE, 0x04F5], - [0x04F8, 0x04F9], - [0x0531, 0x0556], - [0x0559, 0x0559], - [0x0561, 0x0587], - [0x05B0, 0x05B9], - [0x05BB, 0x05BD], - [0x05BF, 0x05BF], - [0x05C1, 0x05C2], - [0x05D0, 0x05EA], - [0x05F0, 0x05F2], - [0x0621, 0x063A], - [0x0640, 0x0652], - [0x0660, 0x0669], - [0x0670, 0x06B7], - [0x06BA, 0x06BE], - [0x06C0, 0x06CE], - [0x06D0, 0x06DC], - [0x06E5, 0x06E8], - [0x06EA, 0x06ED], - [0x06F0, 0x06F9], - [0x0901, 0x0903], - [0x0905, 0x0939], - [0x093D, 0x094D], - [0x0950, 0x0952], - [0x0958, 0x0963], - [0x0966, 0x096F], - [0x0981, 0x0983], - [0x0985, 0x098C], - [0x098F, 0x0990], - [0x0993, 0x09A8], - [0x09AA, 0x09B0], - [0x09B2, 0x09B2], - [0x09B6, 0x09B9], - [0x09BE, 0x09C4], - [0x09C7, 0x09C8], - [0x09CB, 0x09CD], - [0x09DC, 0x09DD], - [0x09DF, 0x09E3], - [0x09E6, 0x09F1], - [0x0A02, 0x0A02], - [0x0A05, 0x0A0A], - [0x0A0F, 0x0A10], - [0x0A13, 0x0A28], - [0x0A2A, 0x0A30], - [0x0A32, 0x0A33], - [0x0A35, 0x0A36], - [0x0A38, 0x0A39], - [0x0A3E, 0x0A42], - [0x0A47, 0x0A48], - [0x0A4B, 0x0A4D], - [0x0A59, 0x0A5C], - [0x0A5E, 0x0A5E], - [0x0A66, 0x0A6F], - [0x0A74, 0x0A74], - [0x0A81, 0x0A83], - [0x0A85, 0x0A8B], - [0x0A8D, 0x0A8D], - [0x0A8F, 0x0A91], - [0x0A93, 0x0AA8], - [0x0AAA, 0x0AB0], - [0x0AB2, 0x0AB3], - [0x0AB5, 0x0AB9], - [0x0ABD, 0x0AC5], - [0x0AC7, 0x0AC9], - [0x0ACB, 0x0ACD], - [0x0AD0, 0x0AD0], - [0x0AE0, 0x0AE0], - [0x0AE6, 0x0AEF], - [0x0B01, 0x0B03], - [0x0B05, 0x0B0C], - [0x0B0F, 0x0B10], - [0x0B13, 0x0B28], - [0x0B2A, 0x0B30], - [0x0B32, 0x0B33], - [0x0B36, 0x0B39], - [0x0B3D, 0x0B43], - [0x0B47, 0x0B48], - [0x0B4B, 0x0B4D], - [0x0B5C, 0x0B5D], - [0x0B5F, 0x0B61], - [0x0B66, 0x0B6F], - [0x0B82, 0x0B83], - [0x0B85, 0x0B8A], - [0x0B8E, 0x0B90], - [0x0B92, 0x0B95], - [0x0B99, 0x0B9A], - [0x0B9C, 0x0B9C], - [0x0B9E, 0x0B9F], - [0x0BA3, 0x0BA4], - [0x0BA8, 0x0BAA], - [0x0BAE, 0x0BB5], - [0x0BB7, 0x0BB9], - [0x0BBE, 0x0BC2], - [0x0BC6, 0x0BC8], - [0x0BCA, 0x0BCD], - [0x0BE7, 0x0BEF], - [0x0C01, 0x0C03], - [0x0C05, 0x0C0C], - [0x0C0E, 0x0C10], - [0x0C12, 0x0C28], - [0x0C2A, 0x0C33], - [0x0C35, 0x0C39], - [0x0C3E, 0x0C44], - [0x0C46, 0x0C48], - [0x0C4A, 0x0C4D], - [0x0C60, 0x0C61], - [0x0C66, 0x0C6F], - [0x0C82, 0x0C83], - [0x0C85, 0x0C8C], - [0x0C8E, 0x0C90], - [0x0C92, 0x0CA8], - [0x0CAA, 0x0CB3], - [0x0CB5, 0x0CB9], - [0x0CBE, 0x0CC4], - [0x0CC6, 0x0CC8], - [0x0CCA, 0x0CCD], - [0x0CDE, 0x0CDE], - [0x0CE0, 0x0CE1], - [0x0CE6, 0x0CEF], - [0x0D02, 0x0D03], - [0x0D05, 0x0D0C], - [0x0D0E, 0x0D10], - [0x0D12, 0x0D28], - [0x0D2A, 0x0D39], - [0x0D3E, 0x0D43], - [0x0D46, 0x0D48], - [0x0D4A, 0x0D4D], - [0x0D60, 0x0D61], - [0x0D66, 0x0D6F], - [0x0E01, 0x0E3A], - [0x0E40, 0x0E5B], - [0x0E81, 0x0E82], - [0x0E84, 0x0E84], - [0x0E87, 0x0E88], - [0x0E8A, 0x0E8A], - [0x0E8D, 0x0E8D], - [0x0E94, 0x0E97], - [0x0E99, 0x0E9F], - [0x0EA1, 0x0EA3], - [0x0EA5, 0x0EA5], - [0x0EA7, 0x0EA7], - [0x0EAA, 0x0EAB], - [0x0EAD, 0x0EAE], - [0x0EB0, 0x0EB9], - [0x0EBB, 0x0EBD], - [0x0EC0, 0x0EC4], - [0x0EC6, 0x0EC6], - [0x0EC8, 0x0ECD], - [0x0ED0, 0x0ED9], - [0x0EDC, 0x0EDD], - [0x0F00, 0x0F00], - [0x0F18, 0x0F19], - [0x0F20, 0x0F33], - [0x0F35, 0x0F35], - [0x0F37, 0x0F37], - [0x0F39, 0x0F39], - [0x0F3E, 0x0F47], - [0x0F49, 0x0F69], - [0x0F71, 0x0F84], - [0x0F86, 0x0F8B], - [0x0F90, 0x0F95], - [0x0F97, 0x0F97], - [0x0F99, 0x0FAD], - [0x0FB1, 0x0FB7], - [0x0FB9, 0x0FB9], - [0x10A0, 0x10C5], - [0x10D0, 0x10F6], - [0x1E00, 0x1E9B], - [0x1EA0, 0x1EF9], - [0x1F00, 0x1F15], - [0x1F18, 0x1F1D], - [0x1F20, 0x1F45], - [0x1F48, 0x1F4D], - [0x1F50, 0x1F57], - [0x1F59, 0x1F59], - [0x1F5B, 0x1F5B], - [0x1F5D, 0x1F5D], - [0x1F5F, 0x1F7D], - [0x1F80, 0x1FB4], - [0x1FB6, 0x1FBC], - [0x1FBE, 0x1FBE], - [0x1FC2, 0x1FC4], - [0x1FC6, 0x1FCC], - [0x1FD0, 0x1FD3], - [0x1FD6, 0x1FDB], - [0x1FE0, 0x1FEC], - [0x1FF2, 0x1FF4], - [0x1FF6, 0x1FFC], - [0x203F, 0x2040], - [0x207F, 0x207F], - [0x2102, 0x2102], - [0x2107, 0x2107], - [0x210A, 0x2113], - [0x2115, 0x2115], - [0x2118, 0x211D], - [0x2124, 0x2124], - [0x2126, 0x2126], - [0x2128, 0x2128], - [0x212A, 0x2131], - [0x2133, 0x2138], - [0x2160, 0x2182], - [0x3005, 0x3007], - [0x3021, 0x3029], - [0x3041, 0x3093], - [0x309B, 0x309C], - [0x30A1, 0x30F6], - [0x30FB, 0x30FC], - [0x3105, 0x312C], - [0x4E00, 0x9FA5], - [0xAC00, 0xD7A3] - ]; - - size_t high = ALPHA_TABLE.length - 1; - // Shortcut search if c is out of range - size_t low = (c < ALPHA_TABLE[0][0] || ALPHA_TABLE[high][1] < c) ? high + 1 : 0; - // Binary search - while (low <= high) - { - const size_t mid = low + ((high - low) >> 1); - if (c < ALPHA_TABLE[mid][0]) - high = mid - 1; - else if (ALPHA_TABLE[mid][1] < c) - low = mid + 1; - else - { - assert(ALPHA_TABLE[mid][0] <= c && c <= ALPHA_TABLE[mid][1]); - return true; - } - } - return false; -} - /** * Returns the code length of c in code units. */ diff --git a/tests/dmd/compilable/ident_UAX31.c b/tests/dmd/compilable/ident_UAX31.c new file mode 100644 index 00000000000..4536031e78c --- /dev/null +++ b/tests/dmd/compilable/ident_UAX31.c @@ -0,0 +1,9 @@ +// REQUIRED_ARGS: -identifiers-importc=UAX31 + +// sppn doesn't support anything newer than c99 +// DISABLED: win32omf + +// verify that the UAX31 identifier set is applied. + +int \u00F8ide\u00F9nt; +int øideùnt2; diff --git a/tests/dmd/compilable/ident_UAX31.d b/tests/dmd/compilable/ident_UAX31.d new file mode 100644 index 00000000000..c8d579a30e0 --- /dev/null +++ b/tests/dmd/compilable/ident_UAX31.d @@ -0,0 +1,5 @@ +// REQUIRED_ARGS: -identifiers=UAX31 + +// verify that the UAX31 identifier set is applied. + +int øideùnt; diff --git a/tests/dmd/compilable/ident_all.c b/tests/dmd/compilable/ident_all.c new file mode 100644 index 00000000000..5c3dee4bbf6 --- /dev/null +++ b/tests/dmd/compilable/ident_all.c @@ -0,0 +1,11 @@ +// REQUIRED_ARGS: -identifiers-importc=all + +// sppn doesn't support anything newer than c99 +// DISABLED: win32omf + +// verify that the All identifier set is applied. + +int \u00F8ide\u00F9nt; +int \u00AAide\u00B5nt; +int \u00A8ide\u00AFnt; +int \u00F8ide\u00F9nt; diff --git a/tests/dmd/compilable/ident_all.d b/tests/dmd/compilable/ident_all.d new file mode 100644 index 00000000000..2dd93167b69 --- /dev/null +++ b/tests/dmd/compilable/ident_all.d @@ -0,0 +1,10 @@ +// REQUIRED_ARGS: -identifiers=all + +// verify that the UAX31 identifier set is applied. + +int øideùnt; +int ªideµnt; +int ¨ide¯nt; + +// just to play it safe, do we support one unicode then another at start? +int øùident; diff --git a/tests/dmd/compilable/ident_c11.c b/tests/dmd/compilable/ident_c11.c new file mode 100644 index 00000000000..5ef3cf3b73b --- /dev/null +++ b/tests/dmd/compilable/ident_c11.c @@ -0,0 +1,9 @@ +// REQUIRED_ARGS: -identifiers-importc=c11 + +// sppn doesn't support anything newer than c99 +// DISABLED: win32omf + +// verify that the C11 identifier set is applied. + +int \u00A8ide\u00AFnt; +int ¨ide¯nt; diff --git a/tests/dmd/compilable/ident_c11.d b/tests/dmd/compilable/ident_c11.d new file mode 100644 index 00000000000..8504a36059f --- /dev/null +++ b/tests/dmd/compilable/ident_c11.d @@ -0,0 +1,5 @@ +// REQUIRED_ARGS: -identifiers=c11 + +// verify that the C11 identifier set is applied. + +int ¨ide¯nt; diff --git a/tests/dmd/compilable/ident_c99.c b/tests/dmd/compilable/ident_c99.c new file mode 100644 index 00000000000..3be11d7fe81 --- /dev/null +++ b/tests/dmd/compilable/ident_c99.c @@ -0,0 +1,6 @@ +// REQUIRED_ARGS: -identifiers-importc=c99 + +// verify that the C99 identifier set is applied. + +int \u00AAide\u00B5nt; +int ªideµnt2; diff --git a/tests/dmd/compilable/ident_c99.d b/tests/dmd/compilable/ident_c99.d new file mode 100644 index 00000000000..2cff082fff9 --- /dev/null +++ b/tests/dmd/compilable/ident_c99.d @@ -0,0 +1,5 @@ +// REQUIRED_ARGS: -identifiers=c99 + +// verify that the C99 identifier set is applied. + +int ªideµnt; diff --git a/tests/dmd/fail_compilation/lexer23465.d b/tests/dmd/fail_compilation/lexer23465.d index 526b7704c88..4ea41a58af1 100644 --- a/tests/dmd/fail_compilation/lexer23465.d +++ b/tests/dmd/fail_compilation/lexer23465.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/lexer23465.d(19): Error: char 0x1f37a not allowed in identifier +fail_compilation/lexer23465.d(19): Error: character 0x1f37a is not allowed as a continue character in an identifier fail_compilation/lexer23465.d(19): Error: character 0x1f37a is not a valid token fail_compilation/lexer23465.d(20): Error: character '\' is not a valid token fail_compilation/lexer23465.d(21): Error: unterminated /+ +/ comment From 9a1b251e7016e3fd182f473d999d34710d83883f Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Mon, 18 Mar 2024 19:26:29 +0100 Subject: [PATCH 033/125] Implement __ctfeWrite builtin (dlang/dmd!16250) * Implement __ctfeWrite builtin Reworked stalled PR dlang/dmd!12412. The __ctfeWrite function is already part of druntime (core.builtins), but was never implemented. The actual implementation is the one used at Weka (slightly different from PR dlang/dmd!12412) and does not require any changes to expression.d (contrary to said PR). * fix file extension of changelog entry * fix changelog file naming * Improve changelog entry * Remove superfluous code and add test case. --- dmd/builtin.d | 22 +++++++++- dmd/frontend.h | 3 ++ dmd/func.d | 3 +- dmd/id.d | 2 + tests/dmd/compilable/test__ctfeWrite.d | 58 ++++++++++++++++++++++++++ 5 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 tests/dmd/compilable/test__ctfeWrite.d diff --git a/dmd/builtin.d b/dmd/builtin.d index 3e4b9e934a2..f54a9131538 100644 --- a/dmd/builtin.d +++ b/dmd/builtin.d @@ -88,7 +88,7 @@ BUILTIN determine_builtin(FuncDeclaration func) // Look for core.math, core.bitop, std.math, and std.math. const id2 = (md.packages.length == 2) ? md.packages[1] : md.id; - if (id2 != Id.math && id2 != Id.bitop) + if (id2 != Id.math && id2 != Id.bitop && id2 != Id.builtinsModuleName) return BUILTIN.unimp; if (md.packages.length != 1 && !(md.packages.length == 2 && id2 == Id.math)) @@ -108,6 +108,12 @@ BUILTIN determine_builtin(FuncDeclaration func) return BUILTIN.unimp; } + if (id1 == Id.core && id2 == Id.builtinsModuleName) + { + if (id3 == Id.ctfeWrite) return BUILTIN.ctfeWrite; + return BUILTIN.unimp; + } + // Math if (id3 == Id.sin) return BUILTIN.sin; if (id3 == Id.cos) return BUILTIN.cos; @@ -167,6 +173,20 @@ Expression eval_unimp(Loc loc, FuncDeclaration fd, Expressions* arguments) return null; } +Expression eval_ctfeWrite(Loc loc, FuncDeclaration fd, Expressions* arguments) +{ + import core.stdc.stdio: fprintf, stderr; + import dmd.expression: CTFEExp; + import dmd.ctfeexpr: resolveSlice; + + Expression e = (*arguments)[0]; + const se = resolveSlice(e).toStringExp(); + assert(se); + const slice = se.peekString(); + fprintf(stderr, "%.*s", cast(int)slice.length, slice.ptr); + return CTFEExp.voidexp; +} + Expression eval_sin(Loc loc, FuncDeclaration fd, Expressions* arguments) { Expression arg0 = (*arguments)[0]; diff --git a/dmd/frontend.h b/dmd/frontend.h index a2c9d2faf02..17500667dd4 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -741,6 +741,7 @@ enum class BUILTIN : uint8_t toPrecFloat = 33u, toPrecDouble = 34u, toPrecReal = 35u, + ctfeWrite = 36u, }; enum class Include : uint8_t @@ -8679,6 +8680,8 @@ struct Id final static Identifier* outp; static Identifier* outpl; static Identifier* outpw; + static Identifier* builtinsModuleName; + static Identifier* ctfeWrite; static Identifier* isAbstractClass; static Identifier* isArithmetic; static Identifier* isAssociativeArray; diff --git a/dmd/func.d b/dmd/func.d index 382710d7839..e39849e467a 100644 --- a/dmd/func.d +++ b/dmd/func.d @@ -109,7 +109,8 @@ enum BUILTIN : ubyte yl2xp1, toPrecFloat, toPrecDouble, - toPrecReal + toPrecReal, + ctfeWrite, } private struct FUNCFLAG diff --git a/dmd/id.d b/dmd/id.d index 5ad324d6052..9e3814ad904 100644 --- a/dmd/id.d +++ b/dmd/id.d @@ -448,6 +448,8 @@ immutable Msgtable[] msgtable = { "outp"}, { "outpl"}, { "outpw"}, + { "builtinsModuleName", "builtins" }, + { "ctfeWrite", "__ctfeWrite" }, // Traits { "isAbstractClass" }, diff --git a/tests/dmd/compilable/test__ctfeWrite.d b/tests/dmd/compilable/test__ctfeWrite.d new file mode 100644 index 00000000000..0f1e55ff0fc --- /dev/null +++ b/tests/dmd/compilable/test__ctfeWrite.d @@ -0,0 +1,58 @@ +/* +TEST_OUTPUT: +--- +Hello +World +from +CTFE +Hello[1 .. 3] = el +S.str +abcdefghij[2 .. 6] = cdef +--- +*/ + +struct S +{ + string str = "S.str"; + alias str this; +} + +int greeting(scope const char[][] values) pure nothrow @safe @nogc +{ + const string newline = "\n"; + + foreach (const val; values) + { + __ctfeWrite(val); + __ctfeWrite(newline); + } + + // Test slices + const val = values[0]; + __ctfeWrite(val[]); + __ctfeWrite(['[','1',' ','.','.',' ','3',']',' ','=',' ']); + __ctfeWrite(val[1 .. 3]); + __ctfeWrite(newline); + + S s; + __ctfeWrite(s); + __ctfeWrite(newline); + + // Test mutable slices + char[10] buffer; + fill(buffer); // Avoid potential shortcuts for literals + __ctfeWrite(buffer[0 .. $]); + __ctfeWrite("[2 .. 6] = "); + __ctfeWrite(buffer[2 .. 6]); + __ctfeWrite(newline); + + return 0; +} + +void fill(ref char[10] buffer) pure nothrow @safe @nogc +{ + foreach (const idx, ref ch; buffer) + ch = cast(char)('a' + idx); +} + +enum forceCTFE = greeting(["Hello", "World", "from", "CTFE"]); From a977154005ce934cfdade73a0aa4ef73a44815c5 Mon Sep 17 00:00:00 2001 From: "richard (rikki) andrew cattermole" Date: Wed, 20 Mar 2024 01:13:18 +1300 Subject: [PATCH 034/125] Fix a unittest for character tables that was missed matching to function --- dmd/common/charactertables.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dmd/common/charactertables.d b/dmd/common/charactertables.d index e0fb7de93d2..951c335817d 100644 --- a/dmd/common/charactertables.d +++ b/dmd/common/charactertables.d @@ -78,7 +78,7 @@ bool isAnyIdentifierCharacter(dchar c) /// unittest { - assert(isAnyContinue('ğ')); + assert(isAnyIdentifierCharacter('ğ')); } /** From 9cf448a136266b129e50b994d386ccfaac7b1519 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Wed, 20 Mar 2024 20:48:42 -0700 Subject: [PATCH 035/125] rename __builtins.di to __importc_builtins.di (dlang/dmd!16317) --- dmd/cparse.d | 4 ++-- dmd/frontend.h | 2 +- dmd/id.d | 2 +- runtime/druntime/src/{__builtins.di => __importc_builtins.di} | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) rename runtime/druntime/src/{__builtins.di => __importc_builtins.di} (96%) diff --git a/dmd/cparse.d b/dmd/cparse.d index aeedb493efc..a7e1ecf9607 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -35,7 +35,7 @@ final class CParser(AST) : Parser!AST AST.Dsymbols* symbols; // symbols declared in current scope bool addFuncName; /// add declaration of __func__ to function symbol table - bool importBuiltins; /// seen use of C compiler builtins, so import __builtins; + bool importBuiltins; /// seen use of C compiler builtins, so import __importc_builtins; private { @@ -125,7 +125,7 @@ final class CParser(AST) : Parser!AST /* Seen references to C builtin functions. * Import their definitions */ - auto s = new AST.Import(Loc.initial, null, Id.builtins, null, false); + auto s = new AST.Import(Loc.initial, null, Id.importc_builtins, null, false); wrap.push(s); } diff --git a/dmd/frontend.h b/dmd/frontend.h index 17500667dd4..22e02c382f2 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -8771,7 +8771,7 @@ struct Id final static Identifier* _deprecated; static Identifier* _align; static Identifier* aligned; - static Identifier* builtins; + static Identifier* importc_builtins; static Identifier* builtin_va_list; static Identifier* builtin_va_arg; static Identifier* va_list_tag; diff --git a/dmd/id.d b/dmd/id.d index 9e3814ad904..9dcf2f5d473 100644 --- a/dmd/id.d +++ b/dmd/id.d @@ -555,7 +555,7 @@ immutable Msgtable[] msgtable = { "_align", "align" }, { "aligned" }, { "__pragma", "pragma" }, - { "builtins", "__builtins" }, + { "importc_builtins", "__importc_builtins" }, { "builtin_va_list", "__builtin_va_list" }, { "builtin_va_arg", "__builtin_va_arg" }, { "va_list_tag", "__va_list_tag" }, diff --git a/runtime/druntime/src/__builtins.di b/runtime/druntime/src/__importc_builtins.di similarity index 96% rename from runtime/druntime/src/__builtins.di rename to runtime/druntime/src/__importc_builtins.di index b4fef091b5f..94247179cd7 100644 --- a/runtime/druntime/src/__builtins.di +++ b/runtime/druntime/src/__importc_builtins.di @@ -3,10 +3,10 @@ * The purpose is to make it unnecessary to hardwire them into the compiler. * As the leading double underscore suggests, this is for internal use only. * - * Copyright: Copyright Digital Mars 2022 + * Copyright: Copyright D Language Foundation 2022-2024 * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Walter Bright - * Source: $(DRUNTIMESRC __builtins.d) + * Source: $(DRUNTIMESRC __importc_builtins.d) */ From 595379c0dc58b5b5fbe7f26a3ebbd4a6513107b9 Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Thu, 21 Mar 2024 20:57:08 +0100 Subject: [PATCH 036/125] core.runtime: Use new alias syntax --- runtime/druntime/src/core/runtime.d | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/runtime/druntime/src/core/runtime.d b/runtime/druntime/src/core/runtime.d index 0212ae69923..e40b86cddb0 100644 --- a/runtime/druntime/src/core/runtime.d +++ b/runtime/druntime/src/core/runtime.d @@ -90,15 +90,15 @@ struct UnitTestResult } /// Legacy module unit test handler -alias bool function() ModuleUnitTester; +alias ModuleUnitTester = bool function(); /// Module unit test handler -alias UnitTestResult function() ExtendedModuleUnitTester; +alias ExtendedModuleUnitTester = UnitTestResult function(); private { - alias bool function(Object) CollectHandler; - alias Throwable.TraceInfo function( void* ptr ) TraceHandler; + alias CollectHandler = bool function(Object); + alias TraceHandler = Throwable.TraceInfo function(void* ptr); - alias void delegate( Throwable ) ExceptionHandler; + alias ExceptionHandler = void delegate(Throwable); extern (C) void _d_print_throwable(Throwable t); extern (C) void* thread_stackBottom() nothrow @nogc; From 7fb0e950ea6f8c943fabf3b9fd5f82331f704d41 Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Fri, 22 Mar 2024 13:16:17 -0600 Subject: [PATCH 037/125] prevent object files from clashing on c99 ident tests --- tests/dmd/compilable/{ident_c99.c => c_ident_c99.c} | 0 tests/dmd/compilable/{ident_c99.d => d_ident_c99.d} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/dmd/compilable/{ident_c99.c => c_ident_c99.c} (100%) rename tests/dmd/compilable/{ident_c99.d => d_ident_c99.d} (100%) diff --git a/tests/dmd/compilable/ident_c99.c b/tests/dmd/compilable/c_ident_c99.c similarity index 100% rename from tests/dmd/compilable/ident_c99.c rename to tests/dmd/compilable/c_ident_c99.c diff --git a/tests/dmd/compilable/ident_c99.d b/tests/dmd/compilable/d_ident_c99.d similarity index 100% rename from tests/dmd/compilable/ident_c99.d rename to tests/dmd/compilable/d_ident_c99.d From 9bb03d594ced4291423258798398994dd82fe91b Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sat, 23 Mar 2024 00:41:01 -0700 Subject: [PATCH 038/125] split off PathCache from File_Manager (dlang/dmd!16329) * split off PathCache from FileManager * split off PathCache from File_Manager --- dmd/file_manager.d | 106 +++++++++++++++++++++++++++++---------------- 1 file changed, 69 insertions(+), 37 deletions(-) diff --git a/dmd/file_manager.d b/dmd/file_manager.d index 946cc131519..07d611f263b 100644 --- a/dmd/file_manager.d +++ b/dmd/file_manager.d @@ -72,44 +72,84 @@ private struct PathStack } } -final class FileManager +/*************************** + * Cache path lookups so the operating system + * is only consulted once for each path. + */ +private struct PathCache { - private StringTable!(const(ubyte)[]) files; // contents of files indexed by file name - private StringTable!(bool) packageStatus; + /* for filespec "a/b/c/d.ext" + * a b and c are directories, a, a/b, a/b/c are paths. + */ - // check if the package path of the given path exists. The input path is - // expected to contain the full path to the module, so the parent - // directories of that path are checked. - private bool packageExists(const(char)[] p) nothrow + StringTable!(bool) pathStatus; // cached value of does a path exist or not + + nothrow: + + /** + * Determine if the path part of path/filename exists. + * Cache the results for the path and each left-justified subpath of the path. + * Params: + * filespec = path/filename + * Returns: + * true if path exists, false if it does not + */ + bool pathExists(const(char)[] filespec) nothrow { - // step 1, look for the closest parent path that is cached + /* look for the longest leftmost parent path that is cached + * by starting at the right and working to the left + */ bool exists = true; - auto st = PathStack(p); + auto st = PathStack(filespec); while (st.up) { - if (auto cached = packageStatus.lookup(st.cur)) { + if (auto cached = pathStatus.lookup(st.cur)) { exists = cached.value; break; } } - // found a parent that is cached (or reached the end of the stack). - // step 2, traverse back up the stack storing either false if the - // parent doesn't exist, or the result of the `exists` call if it does. + /* found a parent path that is cached (or reached the left end of the path). + * Now move right caching the results of those directories. + * Once a directory is found to not exist, all the directories + * to the right of it do not exist + */ while (st.down) { if (!exists) - packageStatus.insert(st.cur, false); + pathStatus.insert(st.cur, false); else - exists = packageStatus.insert(st.cur, FileName.exists(st.cur) == 2).value; + exists = pathStatus.insert(st.cur, FileName.exists(st.cur) == 2).value; } - // at this point, exists should be the answer. return exists; } + /** + * Ask if path ends in a directory. + * Cache result for speed. + * Params: + * path = a path + * Returns: + * true if it's a path, false if not + */ + bool isExistingPath(const char[] path) + { + auto cached = pathStatus.lookup(path); + if (!cached) + cached = pathStatus.insert(path, FileName.exists(path) == 2); + return cached.value; + } +} + +final class FileManager +{ + private StringTable!(const(ubyte)[]) files; // contents of files indexed by file name + + private PathCache pathCache; + /// public this () nothrow { this.files._init(); - this.packageStatus._init(); + this.pathCache.pathStatus._init(); } nothrow: @@ -119,18 +159,18 @@ nothrow: * Does not open the file. * Params: * filename = as supplied by the user - * path = path to look for filename + * paths = paths to look for filename * Returns: * the found file name or * `null` if it is not different from filename. */ - const(char)[] lookForSourceFile(const char[] filename, const char*[] path) + const(char)[] lookForSourceFile(const char[] filename, const char*[] paths) { //printf("lookForSourceFile(`%.*s`)\n", cast(int)filename.length, filename.ptr); - /* Search along path[] for .di file, then .d file. + /* Search along paths[] for .di file, then .d file. */ // see if we should check for the module locally. - bool checkLocal = packageExists(filename); + bool checkLocal = pathCache.pathExists(filename); const sdi = FileName.forceExt(filename, hdr_ext); if (checkLocal && FileName.exists(sdi) == 1) return sdi; @@ -146,12 +186,9 @@ nothrow: if (checkLocal) { - auto cached = packageStatus.lookup(filename); - if (!cached) - cached = packageStatus.insert(filename, FileName.exists(filename) == 2); - if (cached.value) + if (pathCache.isExistingPath(filename)) { - /* The filename exists and it's a directory. + /* The filename exists but it's a directory. * Therefore, the result should be: filename/package.d * iff filename/package.d is a file */ @@ -169,15 +206,15 @@ nothrow: if (FileName.absolute(filename)) return null; - if (!path.length) + if (!paths.length) return null; - foreach (entry; path) + foreach (entry; paths) { const p = entry.toDString(); const(char)[] n = FileName.combine(p, sdi); - if (!packageExists(n)) { + if (!pathCache.pathExists(n)) { FileName.free(n.ptr); continue; // no need to check for anything else. } @@ -196,12 +233,7 @@ nothrow: scope(exit) FileName.free(n.ptr); // also cache this if we are looking for package.d[i] - auto cached = packageStatus.lookup(n); - if (!cached) { - cached = packageStatus.insert(n, FileName.exists(n) == 2); - } - - if (cached.value) + if (pathCache.isExistingPath(n)) { const n2i = FileName.combine(n, package_di); if (FileName.exists(n2i) == 1) @@ -215,7 +247,7 @@ nothrow: } } - /* ImportC: No D modules found, now search along path[] for .i file, then .c file. + /* ImportC: No D modules found, now search along paths[] for .i file, then .c file. */ const si = FileName.forceExt(filename, i_ext); if (FileName.exists(si) == 1) @@ -226,7 +258,7 @@ nothrow: if (FileName.exists(sc) == 1) return sc; scope(exit) FileName.free(sc.ptr); - foreach (entry; path) + foreach (entry; paths) { const p = entry.toDString(); From eb8187b868ee0ce379df5177b7f209e751d21e95 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Sat, 23 Mar 2024 01:57:12 -0600 Subject: [PATCH 039/125] fiber: add the support for MIPS64 N64 ABI --- runtime/druntime/src/core/thread/fiber.d | 46 +++++++++++++++ runtime/druntime/src/core/threadasm.S | 72 ++++++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/runtime/druntime/src/core/thread/fiber.d b/runtime/druntime/src/core/thread/fiber.d index 22b1506276e..08e84edf5a3 100644 --- a/runtime/druntime/src/core/thread/fiber.d +++ b/runtime/druntime/src/core/thread/fiber.d @@ -100,6 +100,14 @@ private version = AsmExternal; } } + else version (MIPS_N64) + { + version (Posix) + { + version = AsmMIPS_N64_Posix; + version = AsmExternal; + } + } else version (AArch64) { version (Posix) @@ -1386,6 +1394,44 @@ private: pstack -= ABOVE; *cast(size_t*)(pstack - SZ_RA) = cast(size_t)&fiber_entryPoint; } + else version (AsmMIPS_N64_Posix) + { + version (StackGrowsDown) {} + else static assert(0); + + /* We keep the FP registers and the return address below + * the stack pointer, so they don't get scanned by the + * GC. The last frame before swapping the stack pointer is + * organized like the following. + * + * |-----------|<= frame pointer + * | $fp/$gp | + * | $s0-7 | + * |-----------|<= stack pointer + * | $ra | + * | $f24-31 | + * |-----------| + * + */ + enum SZ_GP = 10 * size_t.sizeof; // $fp + $gp + $s0-7 + enum SZ_RA = size_t.sizeof; // $ra + version (MIPS_HardFloat) + { + enum SZ_FP = 8 * double.sizeof; // $f24-31 + } + else + { + enum SZ_FP = 0; + } + + enum BELOW = SZ_FP + SZ_RA; + enum ABOVE = SZ_GP; + enum SZ = BELOW + ABOVE; + + (cast(ubyte*)pstack - SZ)[0 .. SZ] = 0; + pstack -= ABOVE; + *cast(size_t*)(pstack - SZ_RA) = cast(size_t)&fiber_entryPoint; + } else version (AsmLoongArch64_Posix) { // Like others, FP registers and return address ($r1) are kept diff --git a/runtime/druntime/src/core/threadasm.S b/runtime/druntime/src/core/threadasm.S index a357965aee9..cef64f65f01 100644 --- a/runtime/druntime/src/core/threadasm.S +++ b/runtime/druntime/src/core/threadasm.S @@ -229,6 +229,78 @@ fiber_switchContext: jr $ra // return +#elif defined(__mips64) && _MIPS_SIM == _ABI64 +/************************************************************************************ + * MIPS 64 ASM BITS + * $a0 - void** - ptr to old stack pointer + * $a1 - void* - new stack pointer + * + */ +.text +.globl fiber_switchContext +fiber_switchContext: + .cfi_startproc + daddiu $sp, $sp, -(10 * 8) + + // fp regs and return address are stored below the stack + // because we don't want the GC to scan them. + +#ifdef __mips_hard_float +#define BELOW (8 * 8 + 8) + s.d $f24, (0 * 8 - BELOW)($sp) # save F24 + s.d $f25, (1 * 8 - BELOW)($sp) # save F25 + s.d $f26, (2 * 8 - BELOW)($sp) # save F26 + s.d $f27, (3 * 8 - BELOW)($sp) # save F27 + s.d $f28, (4 * 8 - BELOW)($sp) # save F28 + s.d $f29, (5 * 8 - BELOW)($sp) # save F29 + s.d $f30, (6 * 8 - BELOW)($sp) # save F30 + s.d $f31, (7 * 8 - BELOW)($sp) # save F31 +#endif + sd $ra, -8($sp) + + sd $s0, (0 * 8)($sp) # save S0 + sd $s1, (1 * 8)($sp) # save S1 + sd $s2, (2 * 8)($sp) # save S2 + sd $s3, (3 * 8)($sp) # save S3 + sd $s4, (4 * 8)($sp) # save S4 + sd $s5, (5 * 8)($sp) # save S5 + sd $s6, (6 * 8)($sp) # save S6 + sd $s7, (7 * 8)($sp) # save S7 + sd $gp, (8 * 8)($sp) # save GP + sd $fp, (9 * 8)($sp) # save FP + + // swap stack pointer + sd $sp, 0($a0) + move $sp, $a1 + +#ifdef __mips_hard_float + l.d $f24, (0 * 8 - BELOW)($sp) # restore F24 + l.d $f25, (1 * 8 - BELOW)($sp) # restore F25 + l.d $f26, (2 * 8 - BELOW)($sp) # restore F26 + l.d $f27, (3 * 8 - BELOW)($sp) # restore F27 + l.d $f28, (4 * 8 - BELOW)($sp) # restore F28 + l.d $f29, (5 * 8 - BELOW)($sp) # restore F29 + l.d $f30, (6 * 8 - BELOW)($sp) # restore F30 + l.d $f31, (7 * 8 - BELOW)($sp) # restore F31 +#endif + ld $ra, -8($sp) + + ld $s0, (0 * 8)($sp) + ld $s1, (1 * 8)($sp) + ld $s2, (2 * 8)($sp) + ld $s3, (3 * 8)($sp) + ld $s4, (4 * 8)($sp) + ld $s5, (5 * 8)($sp) + ld $s6, (6 * 8)($sp) + ld $s7, (7 * 8)($sp) + ld $gp, (8 * 8)($sp) + ld $fp, (9 * 8)($sp) + + daddiu $sp, $sp, (10 * 8) + + jr $ra // return + .cfi_endproc + #elif defined(__loongarch64) /************************************************************************************ * LoongArch64 ASM BITS From d27d7e1801cf9097f970d261abed12fb4c297244 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Mon, 25 Mar 2024 14:01:43 +0000 Subject: [PATCH 040/125] Fix Bugzilla 24450 - apply VRP to foreach indices when array is of known length --- dmd/statementsem.d | 12 +++++++- .../fail_compilation/foreach_index_overflow.d | 28 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 tests/dmd/fail_compilation/foreach_index_overflow.d diff --git a/dmd/statementsem.d b/dmd/statementsem.d index 084f8d9e6e3..cbe583592a9 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -989,7 +989,17 @@ Statement statementSemanticVisit(Statement s, Scope* sc) (tn.ty != tv.ty && tn.ty.isSomeChar && tv.ty.isSomeChar)) && !Type.tsize_t.implicitConvTo(tindex)) { - deprecation(fs.loc, "foreach: loop index implicitly converted from `size_t` to `%s`", + bool over = true; // overflow possible + if (tab.isTypeDArray()) + { + const maxLen = IntRange.fromType(tindex).imax.value + 1; + if (auto ale = fs.aggr.isArrayLiteralExp()) + over = ale.elements.length > maxLen; + else if (auto se = fs.aggr.isSliceExp()) + over = !(se.upr && se.upr.isConst() && se.upr.toInteger() <= maxLen); + } + if (over) + deprecation(fs.loc, "foreach: loop index implicitly converted from `size_t` to `%s`", tindex.toChars()); } } diff --git a/tests/dmd/fail_compilation/foreach_index_overflow.d b/tests/dmd/fail_compilation/foreach_index_overflow.d new file mode 100644 index 00000000000..c472e885317 --- /dev/null +++ b/tests/dmd/fail_compilation/foreach_index_overflow.d @@ -0,0 +1,28 @@ +/* +REQUIRED_ARGS: -de +TEST_OUTPUT: +--- +fail_compilation/foreach_index_overflow.d(19): Deprecation: foreach: loop index implicitly converted from `size_t` to `int` +fail_compilation/foreach_index_overflow.d(21): Deprecation: foreach: loop index implicitly converted from `size_t` to `uint` +fail_compilation/foreach_index_overflow.d(24): Deprecation: foreach: loop index implicitly converted from `size_t` to `ubyte` +fail_compilation/foreach_index_overflow.d(26): Deprecation: foreach: loop index implicitly converted from `size_t` to `byte` +--- +*/ + +void main() +{ + enum { red, green, blue } + foreach (int i, color; [red, green, blue]) {} // OK + + int[] arr; + foreach (int index, element; arr[0 .. 0x8000_0000]) {} // OK + foreach (int index, element; arr[0 .. 0x8000_0001]) {} // error + foreach (uint index, element; arr[0 .. 0x1_0000_0000]) {} // OK + foreach (uint index, element; arr[0 .. 0x1_0000_0001]) {} // error + + int[257] data; + foreach (ubyte i, x; data[]) {} // error + foreach (ubyte i, x; data[0..256]) {} // OK + foreach (byte i, x; data[0..0x81]) {} // error + foreach (byte i, x; data[0..0x80]) {} // OK +} From a1523cd1436c42db4c40d609045fde983af0d88a Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Mon, 25 Mar 2024 15:06:56 +0000 Subject: [PATCH 041/125] Long literal not allowed as array index on x86 --- tests/dmd/fail_compilation/foreach_index_overflow.d | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/dmd/fail_compilation/foreach_index_overflow.d b/tests/dmd/fail_compilation/foreach_index_overflow.d index c472e885317..fe5421196c3 100644 --- a/tests/dmd/fail_compilation/foreach_index_overflow.d +++ b/tests/dmd/fail_compilation/foreach_index_overflow.d @@ -3,7 +3,7 @@ REQUIRED_ARGS: -de TEST_OUTPUT: --- fail_compilation/foreach_index_overflow.d(19): Deprecation: foreach: loop index implicitly converted from `size_t` to `int` -fail_compilation/foreach_index_overflow.d(21): Deprecation: foreach: loop index implicitly converted from `size_t` to `uint` +fail_compilation/foreach_index_overflow.d(21): Deprecation: foreach: loop index implicitly converted from `size_t` to `ushort` fail_compilation/foreach_index_overflow.d(24): Deprecation: foreach: loop index implicitly converted from `size_t` to `ubyte` fail_compilation/foreach_index_overflow.d(26): Deprecation: foreach: loop index implicitly converted from `size_t` to `byte` --- @@ -17,8 +17,8 @@ void main() int[] arr; foreach (int index, element; arr[0 .. 0x8000_0000]) {} // OK foreach (int index, element; arr[0 .. 0x8000_0001]) {} // error - foreach (uint index, element; arr[0 .. 0x1_0000_0000]) {} // OK - foreach (uint index, element; arr[0 .. 0x1_0000_0001]) {} // error + foreach (ushort index, element; arr[0 .. 0x1_0000]) {} // OK + foreach (ushort index, element; arr[0 .. 0x1_0001]) {} // error int[257] data; foreach (ubyte i, x; data[]) {} // error From bc6e32d5b039e77e98e49ac8d2fc80787d504baf Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Mon, 25 Mar 2024 17:00:02 +0000 Subject: [PATCH 042/125] Require -m64 for test, impossible to get $?:64= to work --- tests/dmd/fail_compilation/foreach_index_overflow.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/dmd/fail_compilation/foreach_index_overflow.d b/tests/dmd/fail_compilation/foreach_index_overflow.d index fe5421196c3..aa6baa1274b 100644 --- a/tests/dmd/fail_compilation/foreach_index_overflow.d +++ b/tests/dmd/fail_compilation/foreach_index_overflow.d @@ -1,5 +1,5 @@ /* -REQUIRED_ARGS: -de +REQUIRED_ARGS: -de -m64 TEST_OUTPUT: --- fail_compilation/foreach_index_overflow.d(19): Deprecation: foreach: loop index implicitly converted from `size_t` to `int` From 2aa1046babc9a853270eafd5f06ccb89aeb903a5 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Mon, 25 Mar 2024 17:57:39 +0000 Subject: [PATCH 043/125] Rename variable --- dmd/statementsem.d | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/dmd/statementsem.d b/dmd/statementsem.d index cbe583592a9..b79b47ff7f4 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -989,16 +989,17 @@ Statement statementSemanticVisit(Statement s, Scope* sc) (tn.ty != tv.ty && tn.ty.isSomeChar && tv.ty.isSomeChar)) && !Type.tsize_t.implicitConvTo(tindex)) { - bool over = true; // overflow possible + bool err = true; if (tab.isTypeDArray()) { + // check if overflow is possible const maxLen = IntRange.fromType(tindex).imax.value + 1; if (auto ale = fs.aggr.isArrayLiteralExp()) - over = ale.elements.length > maxLen; + err = ale.elements.length > maxLen; else if (auto se = fs.aggr.isSliceExp()) - over = !(se.upr && se.upr.isConst() && se.upr.toInteger() <= maxLen); + err = !(se.upr && se.upr.isConst() && se.upr.toInteger() <= maxLen); } - if (over) + if (err) deprecation(fs.loc, "foreach: loop index implicitly converted from `size_t` to `%s`", tindex.toChars()); } From 03c20e7c7849da5b66e2df0abf32c5a34023ae39 Mon Sep 17 00:00:00 2001 From: "Richard (Rikki) Andrew Cattermole" Date: Tue, 26 Mar 2024 21:54:00 +1300 Subject: [PATCH 044/125] Attempt to prevent a race condition with dmd test runner when same file but different extension is used (dlang/dmd!16330) --- .../extra-files/objdump-postscript.sh | 6 +++-- .../compilable/{c_ident_c99.c => ident_c99.c} | 0 tests/dmd/tools/d_do_test.d | 22 ++++++++++++++----- 3 files changed, 21 insertions(+), 7 deletions(-) rename tests/dmd/compilable/{c_ident_c99.c => ident_c99.c} (100%) diff --git a/tests/dmd/compilable/extra-files/objdump-postscript.sh b/tests/dmd/compilable/extra-files/objdump-postscript.sh index e2daeecf22e..1743ace1eac 100644 --- a/tests/dmd/compilable/extra-files/objdump-postscript.sh +++ b/tests/dmd/compilable/extra-files/objdump-postscript.sh @@ -3,7 +3,9 @@ source tools/common_funcs.sh expect_file=${EXTRA_FILES}/${TEST_NAME}.out -obj_file=${OUTPUT_BASE}_0.o +# We are only used by cdcmp, which is a D test, +# otherwise we could use ${OUTPUT_BASE} and then figure out language. +obj_file=${RESULTS_TEST_DIR}/d/${TEST_NAME}_0.o echo Creating objdump objdump --disassemble --disassembler-options=intel "${obj_file}" > "${obj_file}.dump" @@ -14,4 +16,4 @@ echo SANITIZING Objdump... diff -up --strip-trailing-cr "${expect_file}" "${obj_file}.dump.sanitized" -rm_retry "${OUTPUT_BASE}.o"{,.dump,.dump,.sanitized} +rm_retry ${obj_file}{,.dump,.dump,.sanitized} diff --git a/tests/dmd/compilable/c_ident_c99.c b/tests/dmd/compilable/ident_c99.c similarity index 100% rename from tests/dmd/compilable/c_ident_c99.c rename to tests/dmd/compilable/ident_c99.c diff --git a/tests/dmd/tools/d_do_test.d b/tests/dmd/tools/d_do_test.d index 551e0ed72f6..c30cb023278 100755 --- a/tests/dmd/tools/d_do_test.d +++ b/tests/dmd/tools/d_do_test.d @@ -1598,10 +1598,22 @@ int tryMain(string[] args) const input_dir = input_file.dirName(); const test_base_name = input_file.baseName(); const test_name = test_base_name.stripExtension(); - - const result_path = envData.results_dir ~ envData.sep; - const output_dir = result_path ~ input_dir; - const output_file = result_path ~ input_file ~ ".out"; + const test_ext = test_base_name.extension(); + + // Previously we did not take into account the test file extension, + // because of ImportC we now have the ability to have conflicting source file basenames (when ignoring extension). + // This can cause a race condition with the concurrent test runner, + // in which whilst one test is running another will try to clobber it and fail. + // Unfortunately dshell does not take into account our output directory, + // at least not in a way that'll allow us to take advantage of the test extension. + // However since its not really an issue for clobbering, we'll ignore it. + const result_path = envData.results_dir ~ envData.sep; + const output_dir_base = result_path ~ input_dir; + const output_dir = (input_dir == "dshell" ? output_dir_base : (output_dir_base ~ envData.sep ~ test_ext[1 .. $])); + const output_file = result_path ~ input_file ~ ".out"; + + // We are potentially creating a test extension specific directory + mkdirRecurse(output_dir); TestArgs testArgs; switch (input_dir) @@ -1622,7 +1634,7 @@ int tryMain(string[] args) return 1; } - if (test_base_name.extension() == ".sh") + if (test_ext == ".sh") { string file = cast(string) std.file.read(input_file); string disabledPlatforms; From eb0c73aaf94b259d38869398172d0238ca66f59e Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Wed, 27 Mar 2024 14:21:24 +0100 Subject: [PATCH 045/125] Start implementation of editions --- dmd/astenums.d | 9 +++++++++ dmd/dmodule.d | 3 +++ dmd/dscope.d | 10 ++++++++-- dmd/frontend.h | 9 +++++++++ dmd/id.d | 3 +++ dmd/module.h | 9 +++++++++ dmd/parse.d | 9 +++++++++ tests/dmd/fail_compilation/editions.d | 16 ++++++++++++++++ 8 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 tests/dmd/fail_compilation/editions.d diff --git a/dmd/astenums.d b/dmd/astenums.d index 3a2d65735cd..1e30b9f8fc2 100644 --- a/dmd/astenums.d +++ b/dmd/astenums.d @@ -18,6 +18,15 @@ enum Sizeok : ubyte done, /// size of aggregate is set correctly } +/// D Language version +enum Edition : ubyte +{ + none, + legacy, /// Before the introduction of editions + v2024, /// Experimental first new edition + latest = v2024 /// Newest edition that this compiler knows of +} + enum Baseok : ubyte { none, /// base classes not computed yet diff --git a/dmd/dmodule.d b/dmd/dmodule.d index 4f84eec7c0d..a1a337bd3a5 100644 --- a/dmd/dmodule.d +++ b/dmd/dmodule.d @@ -358,6 +358,7 @@ extern (C++) final class Module : Package FileType filetype; // source file type bool hasAlwaysInlines; // contains references to functions that must be inlined bool isPackageFile; // if it is a package.d + Edition edition; // language edition that this module is compiled with Package pkg; // if isPackageFile is true, the Package that contains this package.d Strings contentImportedFiles; // array of files whose content was imported int needmoduleinfo; @@ -477,6 +478,8 @@ extern (C++) final class Module : Package setDocfile(); if (doHdrGen) hdrfile = setOutfilename(global.params.dihdr.name, global.params.dihdr.dir, arg, hdr_ext); + + this.edition = Edition.legacy; } extern (D) this(const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen) diff --git a/dmd/dscope.d b/dmd/dscope.d index 76a26a245fb..47195295547 100644 --- a/dmd/dscope.d +++ b/dmd/dscope.d @@ -830,12 +830,18 @@ extern (C++) struct Scope /// Returns: whether to raise DIP1000 warnings (FeatureStabe.default) or errors (FeatureState.enabled) extern (D) FeatureState useDIP1000() { - return (flags & SCOPE.dip1000) ? FeatureState.enabled : FeatureState.disabled; + return (flags & SCOPE.dip1000 || hasEdition(Edition.v2024)) ? FeatureState.enabled : FeatureState.disabled; } /// Returns: whether to raise DIP25 warnings (FeatureStabe.default) or errors (FeatureState.enabled) extern (D) FeatureState useDIP25() { - return (flags & SCOPE.dip25) ? FeatureState.enabled : FeatureState.disabled; + return (flags & SCOPE.dip25 || hasEdition(Edition.v2024)) ? FeatureState.enabled : FeatureState.disabled; + } + + /// Returns: whether this scope compiles with `edition` or later + extern (D) bool hasEdition(Edition edition) + { + return _module && _module.edition >= edition; } } diff --git a/dmd/frontend.h b/dmd/frontend.h index 22e02c382f2..0bbabafbcf3 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -834,6 +834,14 @@ enum class FileType : uint8_t c = 3u, }; +enum class Edition : uint8_t +{ + none = 0u, + legacy = 1u, + v2024 = 2u, + latest = 2u, +}; + struct OutBuffer final { private: @@ -7073,6 +7081,7 @@ class Module final : public Package FileType filetype; bool hasAlwaysInlines; bool isPackageFile; + Edition edition; Package* pkg; Array contentImportedFiles; int32_t needmoduleinfo; diff --git a/dmd/id.d b/dmd/id.d index 9dcf2f5d473..6dbc60b020c 100644 --- a/dmd/id.d +++ b/dmd/id.d @@ -531,6 +531,9 @@ immutable Msgtable[] msgtable = { "udaMustUse", "mustuse" }, { "udaStandalone", "standalone" }, + // Editions + { "__edition_latest_do_not_use", }, + // C names, for undefined identifier error messages { "NULL" }, { "TRUE" }, diff --git a/dmd/module.h b/dmd/module.h index 379e8e6973a..7f02bec2f3a 100644 --- a/dmd/module.h +++ b/dmd/module.h @@ -28,6 +28,14 @@ enum PKG PKGpackage // already determined that's an actual package }; +enum class Edition : unsigned char +{ + none = 0u, + legacy = 1u, + v2024 = 2u, + latest = 2u, +}; + class Package : public ScopeDsymbol { public: @@ -75,6 +83,7 @@ class Module final : public Package FileType filetype; // source file type d_bool hasAlwaysInlines; // contains references to functions that must be inlined d_bool isPackageFile; // if it is a package.d + Edition edition; // language edition that this module is compiled with Package *pkg; // if isPackageFile is true, the Package that contains this package.d Strings contentImportedFiles; // array of files whose content was imported int needmoduleinfo; diff --git a/dmd/parse.d b/dmd/parse.d index 646c4b7d3db..f3a844dff2e 100644 --- a/dmd/parse.d +++ b/dmd/parse.d @@ -233,6 +233,15 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } else { + static if (is(typeof(mod.edition))) + if (exps && exps.length > 0) + if (auto id = (*exps)[0].isIdentifierExp()) + if (id.ident == Id.__edition_latest_do_not_use) + { + mod.edition = Edition.latest; + continue; + } + udas = AST.UserAttributeDeclaration.concat(udas, exps); } if (stc) diff --git a/tests/dmd/fail_compilation/editions.d b/tests/dmd/fail_compilation/editions.d new file mode 100644 index 00000000000..869ee22c11c --- /dev/null +++ b/tests/dmd/fail_compilation/editions.d @@ -0,0 +1,16 @@ +/** +Test language editions (currently experimental) + +TEST_OUTPUT: +--- +fail_compilation/editions.d(15): Error: scope parameter `x` may not be returned +--- +*/ +@__edition_latest_do_not_use +module editions; + +@safe: +int* f(scope int* x) +{ + return x; +} From ae01d98321f6233d86a5ffe5376324ad1cae0da3 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Fri, 29 Mar 2024 21:17:10 -0700 Subject: [PATCH 046/125] splitLines: move to root/string.d --- dmd/errors.d | 2 +- dmd/file_manager.d | 99 ---------------------------------------------- dmd/root/string.d | 99 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 100 deletions(-) diff --git a/dmd/errors.d b/dmd/errors.d index 31d600327d9..704bb9d77e7 100644 --- a/dmd/errors.d +++ b/dmd/errors.d @@ -663,7 +663,7 @@ private void verrorPrint(const(char)* format, va_list ap, ref ErrorInfo info) const fileName = FileName(loc.filename.toDString); if (auto text = global.fileManager.getFileContents(fileName)) { - auto range = global.fileManager.splitLines(cast(const(char[])) text); + auto range = dmd.root.string.splitLines(cast(const(char[])) text); size_t linnum; foreach (line; range) { diff --git a/dmd/file_manager.d b/dmd/file_manager.d index 07d611f263b..6ad3d621708 100644 --- a/dmd/file_manager.d +++ b/dmd/file_manager.d @@ -317,105 +317,6 @@ nothrow: return fb; } - /********************************** - * Take `text` and turn it into an InputRange that emits - * slices into `text` for each line. - * Params: - * text = array of characters - * Returns: - * InputRange accessing `text` as a sequence of lines - * Reference: - * `std.string.splitLines()` - */ - auto splitLines(const char[] text) - { - struct Range - { - @safe: - @nogc: - nothrow: - pure: - private: - - const char[] text; - size_t index; // index of start of line - size_t eolIndex; // index of end of line before newline characters - size_t nextIndex; // index past end of line - - public this(const char[] text) - { - this.text = text; - } - - public bool empty() { return index == text.length; } - - public void popFront() { advance(); index = nextIndex; } - - public const(char)[] front() { advance(); return text[index .. eolIndex]; } - - private void advance() - { - if (index != nextIndex) // if already advanced - return; - - for (size_t i = index; i < text.length; ++i) - { - switch (text[i]) - { - case '\v', '\f', '\n': - eolIndex = i; - nextIndex = i + 1; - return; - - case '\r': - if (i + 1 < text.length && text[i + 1] == '\n') // decode "\r\n" - { - eolIndex = i; - nextIndex = i + 2; - return; - } - eolIndex = i; - nextIndex = i + 1; - return; - - /* Manually decode: - * NEL is C2 85 - */ - case 0xC2: - if (i + 1 < text.length && text[i + 1] == 0x85) - { - eolIndex = i; - nextIndex = i + 2; - return; - } - break; - - /* Manually decode: - * lineSep is E2 80 A8 - * paraSep is E2 80 A9 - */ - case 0xE2: - if (i + 2 < text.length && - text[i + 1] == 0x80 && - (text[i + 2] == 0xA8 || text[i + 2] == 0xA9) - ) - { - eolIndex = i; - nextIndex = i + 3; - return; - } - break; - - default: - break; - } - } - } - } - - return Range(text); - } - /** * Adds the contents of a file to the table. * Params: diff --git a/dmd/root/string.d b/dmd/root/string.d index 59ec1aa6909..ea801024e13 100644 --- a/dmd/root/string.d +++ b/dmd/root/string.d @@ -306,3 +306,102 @@ unittest assert(ptr.startsWith("123")); assert(!ptr.startsWith("1234")); } + +/********************************** + * Take `text` and turn it into an InputRange that emits + * slices into `text` for each line. + * Params: + * text = array of characters + * Returns: + * InputRange accessing `text` as a sequence of lines + * Reference: + * `std.string.splitLines()` + */ +auto splitLines(const char[] text) +{ + struct Range + { + @safe: + @nogc: + nothrow: + pure: + private: + + const char[] text; + size_t index; // index of start of line + size_t eolIndex; // index of end of line before newline characters + size_t nextIndex; // index past end of line + + public this(const char[] text) + { + this.text = text; + } + + public bool empty() { return index == text.length; } + + public void popFront() { advance(); index = nextIndex; } + + public const(char)[] front() { advance(); return text[index .. eolIndex]; } + + private void advance() + { + if (index != nextIndex) // if already advanced + return; + + for (size_t i = index; i < text.length; ++i) + { + switch (text[i]) + { + case '\v', '\f', '\n': + eolIndex = i; + nextIndex = i + 1; + return; + + case '\r': + if (i + 1 < text.length && text[i + 1] == '\n') // decode "\r\n" + { + eolIndex = i; + nextIndex = i + 2; + return; + } + eolIndex = i; + nextIndex = i + 1; + return; + + /* Manually decode: + * NEL is C2 85 + */ + case 0xC2: + if (i + 1 < text.length && text[i + 1] == 0x85) + { + eolIndex = i; + nextIndex = i + 2; + return; + } + break; + + /* Manually decode: + * lineSep is E2 80 A8 + * paraSep is E2 80 A9 + */ + case 0xE2: + if (i + 2 < text.length && + text[i + 1] == 0x80 && + (text[i + 2] == 0xA8 || text[i + 2] == 0xA9) + ) + { + eolIndex = i; + nextIndex = i + 3; + return; + } + break; + + default: + break; + } + } + } + } + + return Range(text); +} From b6ed032df4fd9e80573a66ab75b9a3d73ad98e0e Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Fri, 29 Mar 2024 12:24:22 +0000 Subject: [PATCH 047/125] Fix Bugzilla 21718 - Preview switches have insufficient descriptions Add links. --- dmd/cli.d | 60 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/dmd/cli.d b/dmd/cli.d index 24cf50729b2..82e1270d6b5 100644 --- a/dmd/cli.d +++ b/dmd/cli.d @@ -892,6 +892,7 @@ dmd -cov -unittest myprog.d string name; /// name of the feature string paramName; // internal transition parameter name string helpText; // detailed description of the feature + string link; // link for more info bool documented = true; // whether this option should be shown in the documentation bool deprecated_; /// whether the feature is still in use } @@ -901,7 +902,7 @@ dmd -cov -unittest myprog.d Feature("field", "v.field", "list all non-mutable fields which occupy an object instance"), Feature("complex", "v.complex", - "give deprecation messages about all usages of complex or imaginary types", true, true), + "give deprecation messages about all usages of complex or imaginary types", "", true, true), Feature("tls", "v.tls", "list all variables going into thread local storage"), Feature("in", "v.vin", @@ -910,45 +911,64 @@ dmd -cov -unittest myprog.d /// Returns all available reverts static immutable reverts = [ - Feature("dip25", "useDIP25", "revert DIP25 changes https://github.com/dlang/DIPs/blob/master/DIPs/archive/DIP25.md", true, true), + Feature("dip25", "useDIP25", "revert DIP25 changes", + "https://github.com/dlang/DIPs/blob/master/DIPs/archive/DIP25.md", true, true), Feature("dip1000", "useDIP1000", - "revert DIP1000 changes https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1000.md (Scoped Pointers)"), + "revert DIP1000 changes (Scoped Pointers)", + "https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1000.md"), Feature("intpromote", "fix16997", "revert integral promotions for unary + - ~ operators"), Feature("dtorfields", "dtorFields", "don't destruct fields of partially constructed objects"), ]; /// Returns all available previews static immutable previews = [ + // Note: generally changelog entries should be added to the spec Feature("dip25", "useDIP25", - "implement https://github.com/dlang/DIPs/blob/master/DIPs/archive/DIP25.md (Sealed references)", true, true), + "implement Sealed References DIP", + "https://github.com/dlang/DIPs/blob/master/DIPs/archive/DIP25.md", true, true), Feature("dip1000", "useDIP1000", - "implement https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1000.md (Scoped Pointers)"), + "implement Scoped Pointers DIP", + "https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1000.md"), Feature("dip1008", "ehnogc", - "implement https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1008.md (@nogc Throwable)"), + "implement @nogc Throwable DIP", + "https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1008.md"), Feature("dip1021", "useDIP1021", - "implement https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md (Mutable function arguments)"), - Feature("bitfields", "bitfields", "add bitfields https://github.com/dlang/dlang.org/pull/3190"), - Feature("fieldwise", "fieldwise", "use fieldwise comparisons for struct equality"), + "implement Mutable Function Arguments DIP", + "https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md"), + Feature("bitfields", "bitfields", "add C-like bitfields", + "https://github.com/dlang/dlang.org/pull/3190"), + Feature("fieldwise", "fieldwise", "use fieldwise comparisons for struct equality", + "https://dlang.org/changelog/2.085.0.html#no-cmpsb"), Feature("fixAliasThis", "fixAliasThis", - "when a symbol is resolved, check alias this scope before going to upper scopes"), + "when a symbol is resolved, check alias this scope before going to upper scopes", + "https://github.com/dlang/dmd/pull/8885"), Feature("intpromote", "fix16997", - "fix integral promotions for unary + - ~ operators", false, true), + "fix integral promotions for unary + - ~ operators", + "https://dlang.org/changelog/2.078.0.html#fix16997", false, true), Feature("dtorfields", "dtorFields", - "destruct fields of partially constructed objects", false, false), + "destruct fields of partially constructed objects", + "https://dlang.org/changelog/2.098.0.html#dtorfileds", false, false), Feature("rvaluerefparam", "rvalueRefParam", - "enable rvalue arguments to ref parameters"), + "enable rvalue arguments to ref parameters", + "https://github.com/dlang/DIPs/blob/master/DIPs/rejected/DIP1016.md"), Feature("nosharedaccess", "noSharedAccess", - "disable access to shared memory objects"), + "disable access to shared memory objects", + "https://dlang.org/spec/const3.html#shared"), Feature("in", "previewIn", - "`in` on parameters means `scope const [ref]` and accepts rvalues"), + "`in` on parameters means `scope const [ref]` and accepts rvalues", + "https://dlang.org/spec/function.html#in-params"), Feature("inclusiveincontracts", "inclusiveInContracts", - "'in' contracts of overridden methods must be a superset of parent contract"), + "'in' contracts of overridden methods must be a superset of parent contract", + "https://dlang.org/changelog/2.095.0.html#inclusive-incontracts"), Feature("shortenedMethods", "shortenedMethods", - "allow use of => for methods and top-level functions in addition to lambdas", false, true), + "allow use of => for methods and top-level functions in addition to lambdas", + "https://dlang.org/spec/function.html#ShortenedFunctionBody", false, true), Feature("fixImmutableConv", "fixImmutableConv", - "disallow unsound immutable conversions that were formerly incorrectly permitted"), + "disallow functions with a mutable `void[]` parameter to be strongly pure", + "https://dlang.org/changelog/2.101.0.html#dmd.fix-immutable-conv"), Feature("systemVariables", "systemVariables", - "disable access to variables marked '@system' from @safe code"), + "disable access to variables marked '@system' from @safe code", + "https://dlang.org/spec/attribute.html#system-variables"), ]; } @@ -1026,6 +1046,8 @@ struct CLIUsage buf ~= t.helpText; if (t.deprecated_) buf ~= " [DEPRECATED]"; + if (t.link) + buf ~= " (" ~ t.link ~ ")"; buf ~= "\n"; } return buf; From d577c0bb38866ee1433bb3b1864c99e8e35ff8dd Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Fri, 29 Mar 2024 14:35:23 +0000 Subject: [PATCH 048/125] Update tests; fix link check --- dmd/cli.d | 2 +- tests/dmd/compilable/previewhelp.d | 26 +++++++++++++------------- tests/dmd/compilable/reverthelp.d | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/dmd/cli.d b/dmd/cli.d index 82e1270d6b5..2131a016954 100644 --- a/dmd/cli.d +++ b/dmd/cli.d @@ -1046,7 +1046,7 @@ struct CLIUsage buf ~= t.helpText; if (t.deprecated_) buf ~= " [DEPRECATED]"; - if (t.link) + if (t.link.length) buf ~= " (" ~ t.link ~ ")"; buf ~= "\n"; } diff --git a/tests/dmd/compilable/previewhelp.d b/tests/dmd/compilable/previewhelp.d index f71a3dd72ec..85b1d9a53bf 100644 --- a/tests/dmd/compilable/previewhelp.d +++ b/tests/dmd/compilable/previewhelp.d @@ -5,18 +5,18 @@ TEST_OUTPUT: ---- Upcoming language changes listed by -preview=name: =all Enables all available upcoming language changes - =dip25 implement https://github.com/dlang/DIPs/blob/master/DIPs/archive/DIP25.md (Sealed references) [DEPRECATED] - =dip1000 implement https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1000.md (Scoped Pointers) - =dip1008 implement https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1008.md (@nogc Throwable) - =dip1021 implement https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md (Mutable function arguments) - =bitfields add bitfields https://github.com/dlang/dlang.org/pull/3190 - =fieldwise use fieldwise comparisons for struct equality - =fixAliasThis when a symbol is resolved, check alias this scope before going to upper scopes - =rvaluerefparam enable rvalue arguments to ref parameters - =nosharedaccess disable access to shared memory objects - =in `in` on parameters means `scope const [ref]` and accepts rvalues - =inclusiveincontracts 'in' contracts of overridden methods must be a superset of parent contract - =fixImmutableConv disallow unsound immutable conversions that were formerly incorrectly permitted - =systemVariables disable access to variables marked '@system' from @safe code + =dip25 implement Sealed References DIP [DEPRECATED] (https://github.com/dlang/DIPs/blob/master/DIPs/archive/DIP25.md) + =dip1000 implement Scoped Pointers DIP (https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1000.md) + =dip1008 implement @nogc Throwable DIP (https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1008.md) + =dip1021 implement Mutable Function Arguments DIP (https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md) + =bitfields add C-like bitfields (https://github.com/dlang/dlang.org/pull/3190) + =fieldwise use fieldwise comparisons for struct equality (https://dlang.org/changelog/2.085.0.html#no-cmpsb) + =fixAliasThis when a symbol is resolved, check alias this scope before going to upper scopes (https://github.com/dlang/dmd/pull/8885) + =rvaluerefparam enable rvalue arguments to ref parameters (https://github.com/dlang/DIPs/blob/master/DIPs/rejected/DIP1016.md) + =nosharedaccess disable access to shared memory objects (https://dlang.org/spec/const3.html#shared) + =in `in` on parameters means `scope const [ref]` and accepts rvalues (https://dlang.org/spec/function.html#in-params) + =inclusiveincontracts 'in' contracts of overridden methods must be a superset of parent contract (https://dlang.org/changelog/2.095.0.html#inclusive-incontracts) + =fixImmutableConv disallow functions with a mutable `void[]` parameter to be strongly pure (https://dlang.org/changelog/2.101.0.html#dmd.fix-immutable-conv) + =systemVariables disable access to variables marked '@system' from @safe code (https://dlang.org/spec/attribute.html#system-variables) ---- */ diff --git a/tests/dmd/compilable/reverthelp.d b/tests/dmd/compilable/reverthelp.d index 127baef5544..febbba1445a 100644 --- a/tests/dmd/compilable/reverthelp.d +++ b/tests/dmd/compilable/reverthelp.d @@ -5,8 +5,8 @@ TEST_OUTPUT: ---- Revertable language changes listed by -revert=name: =all Enables all available revertable language changes - =dip25 revert DIP25 changes https://github.com/dlang/DIPs/blob/master/DIPs/archive/DIP25.md [DEPRECATED] - =dip1000 revert DIP1000 changes https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1000.md (Scoped Pointers) + =dip25 revert DIP25 changes [DEPRECATED] (https://github.com/dlang/DIPs/blob/master/DIPs/archive/DIP25.md) + =dip1000 revert DIP1000 changes (Scoped Pointers) (https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1000.md) =intpromote revert integral promotions for unary + - ~ operators =dtorfields don't destruct fields of partially constructed objects ---- From ea65a8371fc19f36bf2092753cc48f76cc842d99 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Sat, 30 Mar 2024 09:16:50 +0000 Subject: [PATCH 049/125] Fix rvalue ref link Co-authored-by: Dennis --- dmd/cli.d | 2 +- tests/dmd/compilable/previewhelp.d | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dmd/cli.d b/dmd/cli.d index 2131a016954..b69fed52ae5 100644 --- a/dmd/cli.d +++ b/dmd/cli.d @@ -950,7 +950,7 @@ dmd -cov -unittest myprog.d "https://dlang.org/changelog/2.098.0.html#dtorfileds", false, false), Feature("rvaluerefparam", "rvalueRefParam", "enable rvalue arguments to ref parameters", - "https://github.com/dlang/DIPs/blob/master/DIPs/rejected/DIP1016.md"), + "https://gist.github.com/andralex/e5405a5d773f07f73196c05f8339435a"), Feature("nosharedaccess", "noSharedAccess", "disable access to shared memory objects", "https://dlang.org/spec/const3.html#shared"), diff --git a/tests/dmd/compilable/previewhelp.d b/tests/dmd/compilable/previewhelp.d index 85b1d9a53bf..c50118a8a25 100644 --- a/tests/dmd/compilable/previewhelp.d +++ b/tests/dmd/compilable/previewhelp.d @@ -12,7 +12,7 @@ Upcoming language changes listed by -preview=name: =bitfields add C-like bitfields (https://github.com/dlang/dlang.org/pull/3190) =fieldwise use fieldwise comparisons for struct equality (https://dlang.org/changelog/2.085.0.html#no-cmpsb) =fixAliasThis when a symbol is resolved, check alias this scope before going to upper scopes (https://github.com/dlang/dmd/pull/8885) - =rvaluerefparam enable rvalue arguments to ref parameters (https://github.com/dlang/DIPs/blob/master/DIPs/rejected/DIP1016.md) + =rvaluerefparam enable rvalue arguments to ref parameters (https://gist.github.com/andralex/e5405a5d773f07f73196c05f8339435a) =nosharedaccess disable access to shared memory objects (https://dlang.org/spec/const3.html#shared) =in `in` on parameters means `scope const [ref]` and accepts rvalues (https://dlang.org/spec/function.html#in-params) =inclusiveincontracts 'in' contracts of overridden methods must be a superset of parent contract (https://dlang.org/changelog/2.095.0.html#inclusive-incontracts) From 378f531a6bc3fdc85244d0f78d6c14c346f3d38b Mon Sep 17 00:00:00 2001 From: FeepingCreature <540727+FeepingCreature@users.noreply.github.com> Date: Sat, 30 Mar 2024 21:59:13 +0100 Subject: [PATCH 050/125] Fix Bugzilla issue 24452: Add `--DRT-covopt="disable:1"` to suppress writing coverage at runtime. (dlang/dmd!16336) This is useful if you don't want to rebuild a binary just to have coverage on or off. --- runtime/druntime/src/rt/cover.d | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runtime/druntime/src/rt/cover.d b/runtime/druntime/src/rt/cover.d index 251d80faa3c..ef1d505d8f5 100644 --- a/runtime/druntime/src/rt/cover.d +++ b/runtime/druntime/src/rt/cover.d @@ -69,6 +69,7 @@ private { string srcpath; string dstpath; + bool disable; bool merge; @nogc nothrow: @@ -83,6 +84,7 @@ private { string s = "Code coverage options are specified as whitespace separated assignments: merge:0|1 - 0 overwrites existing reports, 1 merges current run with existing coverage reports (default: %d) + disable:0|1 - 1 disables writing coverage report even if binary is compiled with coverage dstpath: - writes code coverage reports to (default: current working directory) srcpath: - sets the path where the source files are located to @@ -222,7 +224,7 @@ shared static this() shared static ~this() { - if (!gdata.length) return; + if (!gdata.length || config.disable) return; const NUMLINES = 16384 - 1; const NUMCHARS = 16384 * 16 - 1; From c87c76056a5c11300f299ae7c05e5ef46397cbf4 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sat, 30 Mar 2024 22:06:32 -0700 Subject: [PATCH 051/125] fix versions in core.stdc.limits.d --- runtime/druntime/src/core/stdc/limits.d | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/runtime/druntime/src/core/stdc/limits.d b/runtime/druntime/src/core/stdc/limits.d index f3f88006a12..2684b22bfcd 100644 --- a/runtime/druntime/src/core/stdc/limits.d +++ b/runtime/druntime/src/core/stdc/limits.d @@ -90,8 +90,7 @@ version (Darwin) /// enum PIPE_BUF = 512; } - -version (DragonFlyBSD) +else version (DragonFlyBSD) { /// enum MAX_CANON = 255; @@ -182,3 +181,5 @@ else version (Windows) /// enum PIPE_BUF = 5120; } +else + static assert(0, "unsupported OS"); From 8dcef8210abcb810de99ae2846733bdf7e46a446 Mon Sep 17 00:00:00 2001 From: Denis Feklushkin Date: Mon, 1 Apr 2024 21:20:39 +0700 Subject: [PATCH 052/125] enum PAGESIZE eliminated inside of unittest (dlang/dmd!16351) Co-authored-by: Denis Feklushkin --- runtime/druntime/src/core/internal/gc/pooltable.d | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime/druntime/src/core/internal/gc/pooltable.d b/runtime/druntime/src/core/internal/gc/pooltable.d index 096633825a2..f9ec3d2a22b 100644 --- a/runtime/druntime/src/core/internal/gc/pooltable.d +++ b/runtime/druntime/src/core/internal/gc/pooltable.d @@ -181,9 +181,10 @@ package: unittest { + import core.internal.gc.impl.conservative.gc : PAGESIZE; + enum NPOOLS = 6; enum NPAGES = 10; - enum PAGESIZE = 4096; static struct MockPool { From 13a00fb62a2c4be74db84c7c61f592bd3ae782b7 Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Wed, 28 Jun 2023 12:12:25 +0200 Subject: [PATCH 053/125] Fix 20148 - void initializated bool can be both true and false --- dmd/dstruct.d | 9 ++-- dmd/dsymbolsem.d | 12 +++-- dmd/frontend.h | 13 ++--- dmd/mtype.d | 19 ++++--- dmd/mtype.h | 8 +-- .../systemvariables_void_init.d | 51 +++++++++++++++++-- 6 files changed, 86 insertions(+), 26 deletions(-) diff --git a/dmd/dstruct.d b/dmd/dstruct.d index e4a33dd9815..339b22323af 100644 --- a/dmd/dstruct.d +++ b/dmd/dstruct.d @@ -223,7 +223,7 @@ extern (C++) class StructDeclaration : AggregateDeclaration bool hasCopyCtor; // copy constructor bool hasPointerField; // members with indirections bool hasVoidInitPointers; // void-initialized unsafe fields - bool hasSystemFields; // @system members + bool hasUnsafeBitpatterns; // @system members, pointers, bool bool hasFieldWithInvariant; // invariants bool computedTypeProperties;// the above 3 fields are computed // Even if struct is defined as non-root symbol, some built-in operations @@ -396,13 +396,16 @@ extern (C++) class StructDeclaration : AggregateDeclaration foreach (vd; fields) { if (vd.storage_class & STC.ref_ || vd.hasPointers()) + { hasPointerField = true; + hasUnsafeBitpatterns = true; + } if (vd._init && vd._init.isVoidInitializer() && vd.type.hasPointers()) hasVoidInitPointers = true; - if (vd.storage_class & STC.system || vd.type.hasSystemFields()) - hasSystemFields = true; + if (vd.storage_class & STC.system || vd.type.hasUnsafeBitpatterns()) + hasUnsafeBitpatterns = true; if (!vd._init && vd.type.hasVoidInitPointers()) hasVoidInitPointers = true; diff --git a/dmd/dsymbolsem.d b/dmd/dsymbolsem.d index 19c5c51ad81..e83def58c17 100644 --- a/dmd/dsymbolsem.d +++ b/dmd/dsymbolsem.d @@ -1072,8 +1072,11 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor // Calculate type size + safety checks if (sc && sc.func) { - if (dsym._init && dsym._init.isVoidInitializer()) + if (dsym._init && dsym._init.isVoidInitializer() && !(dsym.storage_class & STC.temp)) { + // Don't do these checks for STC.temp vars because the generated `opAssign` + // for a struct with postblit and destructor void initializes a temporary + // __swap variable, which can be trusted if (dsym.type.hasPointers()) // also computes type size sc.setUnsafe(false, dsym.loc, @@ -1081,9 +1084,12 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor else if (dsym.type.hasInvariant()) sc.setUnsafe(false, dsym.loc, "`void` initializers for structs with invariants are not allowed in safe functions"); - else if (dsym.type.hasSystemFields()) + else if (dsym.type.toBasetype().ty == Tbool) sc.setUnsafePreview(global.params.systemVariables, false, dsym.loc, - "`void` initializers for `@system` variables not allowed in safe functions"); + "a `bool` must be 0 or 1, so void intializing it is not allowed in safe functions"); + else if (dsym.type.hasUnsafeBitpatterns()) + sc.setUnsafePreview(global.params.systemVariables, false, dsym.loc, + "`void` initializers for types with unsafe bit patterns are not allowed in safe functions"); } else if (!dsym._init && !(dsym.storage_class & (STC.static_ | STC.extern_ | STC.gshared | STC.manifest | STC.field | STC.parameter)) && diff --git a/dmd/frontend.h b/dmd/frontend.h index 22e02c382f2..558948d7149 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -1863,7 +1863,7 @@ class Type : public ASTNode virtual bool isZeroInit(const Loc& loc); virtual int32_t hasWild() const; virtual bool hasVoidInitPointers(); - virtual bool hasSystemFields(); + virtual bool hasUnsafeBitpatterns(); virtual bool hasInvariant(); virtual Type* nextOf(); Type* baseElemOf(); @@ -4242,6 +4242,7 @@ class TypeBasic final : public Type bool isunsigned() override; MATCH implicitConvTo(Type* to) override; bool isZeroInit(const Loc& loc) override; + bool hasUnsafeBitpatterns() override; TypeBasic* isTypeBasic() override; void accept(Visitor* v) override; }; @@ -4329,7 +4330,7 @@ class TypeEnum final : public Type MATCH constConv(Type* to) override; bool isZeroInit(const Loc& loc) override; bool hasVoidInitPointers() override; - bool hasSystemFields() override; + bool hasUnsafeBitpatterns() override; bool hasInvariant() override; Type* nextOf() override; void accept(Visitor* v) override; @@ -4568,7 +4569,7 @@ class TypeSArray final : public TypeArray MATCH constConv(Type* to) override; MATCH implicitConvTo(Type* to) override; Expression* defaultInitLiteral(const Loc& loc) override; - bool hasSystemFields() override; + bool hasUnsafeBitpatterns() override; bool hasVoidInitPointers() override; bool hasInvariant() override; bool needsDestruction() override; @@ -4607,7 +4608,7 @@ class TypeStruct final : public Type bool needsCopyOrPostblit() override; bool needsNested() override; bool hasVoidInitPointers() override; - bool hasSystemFields() override; + bool hasUnsafeBitpatterns() override; bool hasInvariant() override; MATCH implicitConvTo(Type* to) override; MATCH constConv(Type* to) override; @@ -7311,8 +7312,8 @@ class StructDeclaration : public AggregateDeclaration bool hasPointerField(bool v); bool hasVoidInitPointers() const; bool hasVoidInitPointers(bool v); - bool hasSystemFields() const; - bool hasSystemFields(bool v); + bool hasUnsafeBitpatterns() const; + bool hasUnsafeBitpatterns(bool v); bool hasFieldWithInvariant() const; bool hasFieldWithInvariant(bool v); bool computedTypeProperties() const; diff --git a/dmd/mtype.d b/dmd/mtype.d index d296a51361a..75827d714d8 100644 --- a/dmd/mtype.d +++ b/dmd/mtype.d @@ -1412,7 +1412,7 @@ extern (C++) abstract class Type : ASTNode * Returns: * true if so */ - bool hasSystemFields() + bool hasUnsafeBitpatterns() { return false; } @@ -2340,6 +2340,11 @@ extern (C++) final class TypeBasic : Type } } + override bool hasUnsafeBitpatterns() + { + return ty == Tbool; + } + // For eliminating dynamic_cast override TypeBasic isTypeBasic() { @@ -2657,9 +2662,9 @@ extern (C++) final class TypeSArray : TypeArray return ae; } - override bool hasSystemFields() + override bool hasUnsafeBitpatterns() { - return next.hasSystemFields(); + return next.hasUnsafeBitpatterns(); } override bool hasVoidInitPointers() @@ -3976,11 +3981,11 @@ extern (C++) final class TypeStruct : Type return sym.hasVoidInitPointers; } - override bool hasSystemFields() + override bool hasUnsafeBitpatterns() { sym.size(Loc.initial); // give error for forward references sym.determineTypeProperties(); - return sym.hasSystemFields; + return sym.hasUnsafeBitpatterns; } override bool hasInvariant() @@ -4239,9 +4244,9 @@ extern (C++) final class TypeEnum : Type return memType().hasVoidInitPointers(); } - override bool hasSystemFields() + override bool hasUnsafeBitpatterns() { - return memType().hasSystemFields(); + return memType().hasUnsafeBitpatterns(); } override bool hasInvariant() diff --git a/dmd/mtype.h b/dmd/mtype.h index ef47d9eed69..1121711a99f 100644 --- a/dmd/mtype.h +++ b/dmd/mtype.h @@ -276,7 +276,7 @@ class Type : public ASTNode virtual bool isZeroInit(const Loc &loc = Loc()); // if initializer is 0 virtual int hasWild() const; virtual bool hasVoidInitPointers(); - virtual bool hasSystemFields(); + virtual bool hasUnsafeBitpatterns(); virtual bool hasInvariant(); virtual Type *nextOf(); Type *baseElemOf(); @@ -421,7 +421,7 @@ class TypeSArray final : public TypeArray MATCH constConv(Type *to) override; MATCH implicitConvTo(Type *to) override; Expression *defaultInitLiteral(const Loc &loc) override; - bool hasSystemFields() override; + bool hasUnsafeBitpatterns() override; bool hasVoidInitPointers() override; bool hasInvariant() override; bool needsDestruction() override; @@ -739,7 +739,7 @@ class TypeStruct final : public Type bool needsCopyOrPostblit() override; bool needsNested() override; bool hasVoidInitPointers() override; - bool hasSystemFields() override; + bool hasUnsafeBitpatterns() override; bool hasInvariant() override; MATCH implicitConvTo(Type *to) override; MATCH constConv(Type *to) override; @@ -775,7 +775,7 @@ class TypeEnum final : public Type MATCH constConv(Type *to) override; bool isZeroInit(const Loc &loc) override; bool hasVoidInitPointers() override; - bool hasSystemFields() override; + bool hasUnsafeBitpatterns() override; bool hasInvariant() override; Type *nextOf() override; diff --git a/tests/dmd/fail_compilation/systemvariables_void_init.d b/tests/dmd/fail_compilation/systemvariables_void_init.d index 6f44093a8af..ea0e55daff3 100644 --- a/tests/dmd/fail_compilation/systemvariables_void_init.d +++ b/tests/dmd/fail_compilation/systemvariables_void_init.d @@ -2,9 +2,13 @@ REQUIRED_ARGS: -preview=systemVariables TEST_OUTPUT: --- -fail_compilation/systemvariables_void_init.d(29): Error: `void` initializers for `@system` variables not allowed in safe functions -fail_compilation/systemvariables_void_init.d(30): Error: `void` initializers for `@system` variables not allowed in safe functions -fail_compilation/systemvariables_void_init.d(31): Error: `void` initializers for `@system` variables not allowed in safe functions +fail_compilation/systemvariables_void_init.d(48): Error: `void` initializers for types with unsafe bit patterns are not allowed in safe functions +fail_compilation/systemvariables_void_init.d(49): Error: `void` initializers for types with unsafe bit patterns are not allowed in safe functions +fail_compilation/systemvariables_void_init.d(50): Error: `void` initializers for types with unsafe bit patterns are not allowed in safe functions +fail_compilation/systemvariables_void_init.d(51): Error: a `bool` must be 0 or 1, so void intializing it is not allowed in safe functions +fail_compilation/systemvariables_void_init.d(52): Error: a `bool` must be 0 or 1, so void intializing it is not allowed in safe functions +fail_compilation/systemvariables_void_init.d(53): Error: `void` initializers for types with unsafe bit patterns are not allowed in safe functions +fail_compilation/systemvariables_void_init.d(54): Error: `void` initializers for types with unsafe bit patterns are not allowed in safe functions --- */ @@ -24,9 +28,50 @@ enum E : C x = C.init, } +enum B : bool +{ + x, +} + +struct SB +{ + bool x; +} + +struct SSB +{ + SB sb; +} + void main() @safe { S s = void; C c = void; E e = void; + const bool b = void; + B bb = void; + SB sb = void; + SSB ssb = void; +} + +// The following test is reduced from Phobos. The compiler generates this `opAssign`: +// (CopyPreventer __swap2 = void;) , __swap2 = this , (this = p , __swap2.~this()); +// The compiler would give an error about void initialization a struct with a bool, +// but it can be trusted in this case because it's a compiler generated temporary. +auto staticArray(T)(T a) @safe +{ + T c; + c = a; +} + +void assignmentTest() @safe +{ + static struct CopyPreventer + { + bool on; + this(this) @safe {} + ~this() { } + } + + staticArray(CopyPreventer()); } From f627b48c7c3d13484e5df08b29f1302159ea1289 Mon Sep 17 00:00:00 2001 From: Andrei Horodniceanu Date: Mon, 1 Apr 2024 09:08:53 +0300 Subject: [PATCH 054/125] Prefix test that requires GDB with gdb Signed-off-by: Andrei Horodniceanu --- tests/dmd/runnable/{b18504.d => gdb18504.d} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/dmd/runnable/{b18504.d => gdb18504.d} (100%) diff --git a/tests/dmd/runnable/b18504.d b/tests/dmd/runnable/gdb18504.d similarity index 100% rename from tests/dmd/runnable/b18504.d rename to tests/dmd/runnable/gdb18504.d From f6a9383b60a25233ad55ce9536b2ab369365e527 Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Mon, 1 Apr 2024 23:35:52 +0200 Subject: [PATCH 055/125] Fix bugzilla 24477 - Union access of bool shouldn't be allowed in `@safe` --- dmd/safe.d | 13 ++++++++++- .../systemvariables_bool_union.d | 22 +++++++++++++++++++ tests/dmd/runnable/test15.d | 2 +- 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 tests/dmd/fail_compilation/systemvariables_bool_union.d diff --git a/dmd/safe.d b/dmd/safe.d index 741f979184e..5064ac2d9cd 100644 --- a/dmd/safe.d +++ b/dmd/safe.d @@ -75,6 +75,7 @@ bool checkUnsafeAccess(Scope* sc, Expression e, bool readonly, bool printmsg) if (ad.sizeok != Sizeok.done) ad.determineSize(ad.loc); + import dmd.globals : FeatureState; const hasPointers = v.type.hasPointers(); if (hasPointers) { @@ -87,7 +88,6 @@ bool checkUnsafeAccess(Scope* sc, Expression e, bool readonly, bool printmsg) } else { - import dmd.globals : FeatureState; // @@@DEPRECATED_2.116@@@ // https://issues.dlang.org/show_bug.cgi?id=20655 // Inferring `@system` because of union access breaks code, @@ -111,6 +111,17 @@ bool checkUnsafeAccess(Scope* sc, Expression e, bool readonly, bool printmsg) } } + // @@@DEPRECATED_2.119@@@ + // https://issues.dlang.org/show_bug.cgi?id=24477 + // Should probably be turned into an error in a new edition + if (v.type.hasUnsafeBitpatterns() && v.overlapped && sc.setUnsafePreview( + FeatureState.default_, !printmsg, e.loc, + "cannot access overlapped field `%s.%s` with unsafe bit patterns in `@safe` code", ad, v) + ) + { + return true; + } + if (readonly || !e.type.isMutable()) return false; diff --git a/tests/dmd/fail_compilation/systemvariables_bool_union.d b/tests/dmd/fail_compilation/systemvariables_bool_union.d new file mode 100644 index 00000000000..ca6e620a51a --- /dev/null +++ b/tests/dmd/fail_compilation/systemvariables_bool_union.d @@ -0,0 +1,22 @@ +/* +REQUIRED_ARGS: -de +TEST_OUTPUT: +--- +fail_compilation/systemvariables_bool_union.d(21): Deprecation: cannot access overlapped field `Box.b` with unsafe bit patterns in `@safe` code +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=24477 + +bool schrodingersCat() @safe +{ + union Box + { + bool b; + ubyte y; + } + + Box u; + u.y = 2; + return u.b; +} diff --git a/tests/dmd/runnable/test15.d b/tests/dmd/runnable/test15.d index b4acc235289..bb9fc875f7b 100644 --- a/tests/dmd/runnable/test15.d +++ b/tests/dmd/runnable/test15.d @@ -1420,7 +1420,7 @@ void test19758() /************************************/ // https://issues.dlang.org/show_bug.cgi?id=19968 -@safe void test19968() +void test19968() { int[2] array = [16, 678]; union U { int i; bool b; } From e8675aaf8b8206c6de01ba64249979338acf338b Mon Sep 17 00:00:00 2001 From: Andrei Horodniceanu Date: Mon, 1 Apr 2024 09:15:11 +0300 Subject: [PATCH 056/125] stdcheaders.c: Don't include signal.h on aarch64 linux due to int128 Signed-off-by: Andrei Horodniceanu --- tests/dmd/compilable/stdcheaders.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/dmd/compilable/stdcheaders.c b/tests/dmd/compilable/stdcheaders.c index a1a4dc7ffb4..07fbb73cdc4 100644 --- a/tests/dmd/compilable/stdcheaders.c +++ b/tests/dmd/compilable/stdcheaders.c @@ -30,7 +30,9 @@ float x = NAN; #include #endif +#if !(defined(__linux__) && defined(__aarch64__)) // /usr/include/linux/types.h(12): Error: __int128 not supported #include +#endif #ifndef __DMC__ // no stdalign.h #include From a32ed302ab4252bf57f0397ed82239b5e7cf85bf Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Thu, 4 Apr 2024 23:48:23 -0700 Subject: [PATCH 057/125] fileSize() return value misused --- dmd/common/file.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dmd/common/file.d b/dmd/common/file.d index 80677f66ff8..98ee13e031b 100644 --- a/dmd/common/file.d +++ b/dmd/common/file.d @@ -566,7 +566,7 @@ else version (Windows) private ulong fileSize(HANDLE fd) { ulong result; - if (GetFileSizeEx(fd, cast(LARGE_INTEGER*) &result) == 0) + if (GetFileSizeEx(fd, cast(LARGE_INTEGER*) &result)) return result; return ulong.max; } From c8896315d9ddd25fee3a356f5f3733af2b2e4107 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sat, 6 Apr 2024 05:58:07 -0600 Subject: [PATCH 058/125] Add mqueue declarations for FreeBSD. The glibc declarations are unchanged, but they had to be put in a proper version block instead of using : with the version statement. --- .../druntime/src/core/sys/freebsd/mqueue.d | 19 + runtime/druntime/src/core/sys/posix/mqueue.d | 370 ++++++++++-------- 2 files changed, 222 insertions(+), 167 deletions(-) create mode 100644 runtime/druntime/src/core/sys/freebsd/mqueue.d diff --git a/runtime/druntime/src/core/sys/freebsd/mqueue.d b/runtime/druntime/src/core/sys/freebsd/mqueue.d new file mode 100644 index 00000000000..448ead71d0f --- /dev/null +++ b/runtime/druntime/src/core/sys/freebsd/mqueue.d @@ -0,0 +1,19 @@ +//Written in the D programming language + +/++ + D header file for FreeBSD's ifaddrs.h. + + Copyright: Copyright 2024 + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + Authors: $(HTTP jmdavisprog.com, Jonathan M Davis) + +/ +module core.sys.freebsd.mqueue; + +public import core.sys.posix.mqueue; + +version (FreeBSD): +extern(C): +@nogc: +nothrow: + +int mq_getfd_np(mqd_t mqd); diff --git a/runtime/druntime/src/core/sys/posix/mqueue.d b/runtime/druntime/src/core/sys/posix/mqueue.d index eae50d27d83..f8b4b150944 100644 --- a/runtime/druntime/src/core/sys/posix/mqueue.d +++ b/runtime/druntime/src/core/sys/posix/mqueue.d @@ -28,195 +28,231 @@ import core.sys.posix.signal; import core.sys.posix.time; version (Posix): -version (CRuntime_Glibc): extern (C): @nogc nothrow: +version (CRuntime_Glibc) +{ + /// Message queue descriptor. + alias int mqd_t; -/// Message queue descriptor. -alias int mqd_t; + /** + * Used in getting and setting the attributes of a message queue. + */ + struct mq_attr + { + /// Message queue flags. + c_long mq_flags; + /// Maximum number of messages. + c_long mq_maxmsg; + /// Maximum message size. + c_long mq_msgsize; + /// Number of messages currently queued. + c_long mq_curmsgs; + } -/** - * Used in getting and setting the attributes of a message queue. - */ -struct mq_attr -{ - /// Message queue flags. - c_long mq_flags; - /// Maximum number of messages. - c_long mq_maxmsg; - /// Maximum message size. - c_long mq_msgsize; - /// Number of messages currently queued. - c_long mq_curmsgs; -} + /** + * Establish connection between a process and a message queue `name`. + * + * Note: + * Linux prototypes are: + * mqd_t mq_open (const(char)* name, int oflag); + * mqd_t mq_open (const(char)* name, int oflag, mode_t mode, mq_attr* attr); + * + * Params: + * name = Name of the message queue to open. + * oflag = determines the type of access used. + * If `O_CREAT` is on `oflag`, the third argument is taken as a + * `mode_t`, the mode of the created message queue. + * If `O_CREAT` is on `oflag`, the fourth argument is taken as + * a pointer to a `mq_attr' (message queue attributes). + * If the fourth argument is `null`, default attributes are used. + * ... = varargs matching the function prototypes + * + * Returns: + * Message queue descriptor or (mqd_t) -1 on error. + */ + mqd_t mq_open(const(char)* name, int oflag, ...); -/** - * Establish connection between a process and a message queue `name`. - * - * Note: - * Linux prototypes are: - * mqd_t mq_open (const(char)* name, int oflag); - * mqd_t mq_open (const(char)* name, int oflag, mode_t mode, mq_attr* attr); - * - * Params: - * name = Name of the message queue to open. - * oflag = determines the type of access used. - * If `O_CREAT` is on `oflag`, the third argument is taken as a - * `mode_t`, the mode of the created message queue. - * If `O_CREAT` is on `oflag`, the fourth argument is taken as - * a pointer to a `mq_attr' (message queue attributes). - * If the fourth argument is `null`, default attributes are used. - * ... = varargs matching the function prototypes - * - * Returns: - * Message queue descriptor or (mqd_t) -1 on error. - */ -mqd_t mq_open(const(char)* name, int oflag, ...); + /** + * Closes the message queue descriptor mqdes. + * + * Params: + * mqdes = Message queue descriptor to close. + * + * Returns: + * On success mq_close() returns 0; on error, -1 is returned, with errno + * set to indicate the error. + */ + int mq_close (mqd_t mqdes); -/** - * Closes the message queue descriptor mqdes. - * - * Params: - * mqdes = Message queue descriptor to close. - * - * Returns: - * On success mq_close() returns 0; on error, -1 is returned, with errno - * set to indicate the error. - */ -int mq_close (mqd_t mqdes); + /** + * Query status and attributes of message queue `mqdes`. + * + * Params: + * mqdes = Message queue descriptor. + * mqstat = Buffer to fill with the message queue's attributes. + * + * Returns: + * On success mq_getattr() return 0; on error, -1 is returned, with errno + * set to indicate the error. + */ + int mq_getattr (mqd_t mqdes, mq_attr* mqstat); -/** - * Query status and attributes of message queue `mqdes`. - * - * Params: - * mqdes = Message queue descriptor. - * mqstat = Buffer to fill with the message queue's attributes. - * - * Returns: - * On success mq_getattr() return 0; on error, -1 is returned, with errno - * set to indicate the error. - */ -int mq_getattr (mqd_t mqdes, mq_attr* mqstat); + /* + * Set attributes associated with message queue `mqdes` + * + * Params: + * mqdes = Message queue descriptor. + * newstat = non-null pointer to fill with attributes for `mqdes`. + * oldstat = if not `null` it is filled with the old attributes. + * + * Returns: + * On success mq_setattr() return 0; on error, -1 is returned, with errno + * set to indicate the error. + */ + int mq_setattr (mqd_t mqdes, const(mq_attr)* newstat, mq_attr* oldstat); -/* - * Set attributes associated with message queue `mqdes` - * - * Params: - * mqdes = Message queue descriptor. - * newstat = non-null pointer to fill with attributes for `mqdes`. - * oldstat = if not `null` it is filled with the old attributes. - * - * Returns: - * On success mq_setattr() return 0; on error, -1 is returned, with errno - * set to indicate the error. - */ -int mq_setattr (mqd_t mqdes, const(mq_attr)* newstat, mq_attr* oldstat); + /** + * Remove the specified message queue `name`. + * + * Params: + * name = Name of the queue to remove. + * + * Returns: + * On success mq_unlink() returns 0; on error, -1 is returned, with errno + * set to indicate the error. + */ + int mq_unlink (const(char)* name); -/** - * Remove the specified message queue `name`. - * - * Params: - * name = Name of the queue to remove. - * - * Returns: - * On success mq_unlink() returns 0; on error, -1 is returned, with errno - * set to indicate the error. - */ -int mq_unlink (const(char)* name); + /** + * Register for notification when a message is available + * + * Params: + * mqdes = Message queue descriptor. + * notification = See `man 3 mq_notify` for details. + * + * Returns: + * On success mq_notify() returns 0; on error, -1 is returned, with errno + * set to indicate the error. + */ + int mq_notify (mqd_t mqdes, const(sigevent)* notification); -/** - * Register for notification when a message is available - * - * Params: - * mqdes = Message queue descriptor. - * notification = See `man 3 mq_notify` for details. - * - * Returns: - * On success mq_notify() returns 0; on error, -1 is returned, with errno - * set to indicate the error. - */ -int mq_notify (mqd_t mqdes, const(sigevent)* notification); + /** + * Receive the oldest message with the highest priority the message queue + * + * Params: + * mqdes = Message queue descriptor. + * msg_ptr = Buffer to write the message to + * msg_len = Size of the buffer provided as `msg_ptr`. Must be greater + * than the mq_msgsize attribute of the queue. + * msg_prio = If not `null`, set to the priority of this message. + * + * Returns: + * On success, mq_receive() returns the number of bytes in the received + * message; on error, -1 is returned, with errno set to indicate the error + */ + ssize_t mq_receive (mqd_t mqdes, char* msg_ptr, size_t msg_len, uint* msg_prio); -/** - * Receive the oldest message with the highest priority the message queue - * - * Params: - * mqdes = Message queue descriptor. - * msg_ptr = Buffer to write the message to - * msg_len = Size of the buffer provided as `msg_ptr`. Must be greater - * than the mq_msgsize attribute of the queue. - * msg_prio = If not `null`, set to the priority of this message. - * - * Returns: - * On success, mq_receive() returns the number of bytes in the received - * message; on error, -1 is returned, with errno set to indicate the error - */ -ssize_t mq_receive (mqd_t mqdes, char* msg_ptr, size_t msg_len, uint* msg_prio); + /** + * Receive the oldest message with the highest priority the message queue, + * wait up to a certain timeout. + * + * Params: + * mqdes = Message queue descriptor. + * msg_ptr = Buffer to write the message to + * msg_len = Size of the buffer provided as `msg_ptr`. Must be greater + * than the mq_msgsize attribute of the queue. + * msg_prio = If not `null`, set to the priority of this message. + * abs_timeout = Specify a ceiling on the time to block if the queue is empty. + * + * Returns: + * On success, mq_receive() returns the number of bytes in the received + * message; on error, -1 is returned, with errno set to indicate the error + */ + pragma(mangle, muslRedirTime64Mangle!("mq_timedreceive", "__mq_timedreceive_time64")) + ssize_t mq_timedreceive (mqd_t mqdes, char* msg_ptr, size_t msg_len, + uint* msg_prio, const(timespec)* abs_timeout); -/** - * Receive the oldest message with the highest priority the message queue, - * wait up to a certain timeout. - * - * Params: - * mqdes = Message queue descriptor. - * msg_ptr = Buffer to write the message to - * msg_len = Size of the buffer provided as `msg_ptr`. Must be greater - * than the mq_msgsize attribute of the queue. - * msg_prio = If not `null`, set to the priority of this message. - * abs_timeout = Specify a ceiling on the time to block if the queue is empty. - * - * Returns: - * On success, mq_receive() returns the number of bytes in the received - * message; on error, -1 is returned, with errno set to indicate the error - */ -pragma(mangle, muslRedirTime64Mangle!("mq_timedreceive", "__mq_timedreceive_time64")) -ssize_t mq_timedreceive (mqd_t mqdes, char* msg_ptr, size_t msg_len, - uint* msg_prio, const(timespec)* abs_timeout); + /** + * Add a message to a message queue. + * + * Params: + * mqdes = Message queue descriptor. + * msg_ptr = Buffer to read the message from + * msg_len = Size of the message provided via `msg_ptr`. Must be lower + * or equal to the mq_msgsize attribute of the queue. + * msg_prio = Priority of this message. + * + * Returns: + * On success, mq_send() return zero; on error, -1 is returned, with errno + * set to indicate the error. + */ + int mq_send (mqd_t mqdes, const(char)* msg_ptr, size_t msg_len, uint msg_prio); -/** - * Add a message to a message queue. - * - * Params: - * mqdes = Message queue descriptor. - * msg_ptr = Buffer to read the message from - * msg_len = Size of the message provided via `msg_ptr`. Must be lower - * or equal to the mq_msgsize attribute of the queue. - * msg_prio = Priority of this message. - * - * Returns: - * On success, mq_send() return zero; on error, -1 is returned, with errno - * set to indicate the error. - */ -int mq_send (mqd_t mqdes, const(char)* msg_ptr, size_t msg_len, uint msg_prio); + /** + * Add a message to a message queue, block up to a certain time if the queue + * is full. + * + * Params: + * mqdes = Message queue descriptor. + * msg_ptr = Buffer to read the message from + * msg_len = Size of the message provided via `msg_ptr`. Must be lower + * or equal to the mq_msgsize attribute of the queue. + * msg_prio = Priority of this message. + * abs_timeout = Specify a ceiling on the time to block if the queue is empty. + * + * Returns: + * On success, mq_timedsend() return zero; on error, -1 is returned, + * with errno set to indicate the error. + * + */ + pragma(mangle, muslRedirTime64Mangle!("mq_timedsend", "__mq_timedsend_time64")) + int mq_timedsend (mqd_t mqdes, const(char)* msg_ptr, size_t msg_len, + uint msg_prio, const(timespec)* abs_timeout); +} +else version (FreeBSD) +{ + private struct __mq; -/** - * Add a message to a message queue, block up to a certain time if the queue - * is full. - * - * Params: - * mqdes = Message queue descriptor. - * msg_ptr = Buffer to read the message from - * msg_len = Size of the message provided via `msg_ptr`. Must be lower - * or equal to the mq_msgsize attribute of the queue. - * msg_prio = Priority of this message. - * abs_timeout = Specify a ceiling on the time to block if the queue is empty. - * - * Returns: - * On success, mq_timedsend() return zero; on error, -1 is returned, - * with errno set to indicate the error. - * - */ -pragma(mangle, muslRedirTime64Mangle!("mq_timedsend", "__mq_timedsend_time64")) -int mq_timedsend (mqd_t mqdes, const(char)* msg_ptr, size_t msg_len, - uint msg_prio, const(timespec)* abs_timeout); + alias mqd_t = __mq*; + + struct mq_attr + { + c_long mq_flags; + c_long mq_maxmsg; + c_long mq_msgsize; + c_long mq_curmsgs; + c_long[4] __reserved; + } + + int mq_close(mqd_t); + + int mq_getattr(mqd_t, mq_attr*); + + int mq_notify(mqd_t, const sigevent*); + + mqd_t mq_open(const char*, int, ...); + + ssize_t mq_receive(mqd_t, char*, size_t, uint*); + + int mq_send(mqd_t, const char*, size_t, uint); + + int mq_setattr(mqd_t, const mq_attr*, mq_attr*); + + ssize_t mq_timedreceive(mqd_t, char*, size_t, uint*, const timespec*); + + int mq_timedsend(mqd_t, const char*, size_t, uint, const timespec*); + + int mq_unlink(const char*); +} From 1e635dc8857a91677fcaefcde9cdaeac8bc5bff1 Mon Sep 17 00:00:00 2001 From: Bastiaan Veelo Date: Sun, 7 Apr 2024 21:28:33 +0200 Subject: [PATCH 059/125] Eliminate expressionsem import from attrib.d. In pursuit of https://github.com/orgs/dlang/projects/41. --- dmd/README.md | 1 + dmd/attrib.d | 53 ----------------------------- dmd/attrib.h | 6 +++- dmd/attribsem.d | 87 +++++++++++++++++++++++++++++++++++++++++++++++ dmd/cxxfrontend.d | 10 ++++++ dmd/dsymbolsem.d | 1 + dmd/frontend.h | 1 - dmd/mustuse.d | 2 +- dmd/objc.d | 1 + dmd/traits.d | 1 + 10 files changed, 107 insertions(+), 56 deletions(-) create mode 100644 dmd/attribsem.d diff --git a/dmd/README.md b/dmd/README.md index 282e8183783..d784d07df21 100644 --- a/dmd/README.md +++ b/dmd/README.md @@ -109,6 +109,7 @@ Note that these groups have no strict meaning, the category assignments are a bi | File | Purpose | |-------------------------------------------------------------------------------------------|-------------------------------------------------------------------| +| [attribsem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/attribsem.d) | Attribute semantics | | [dsymbolsem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dsymbolsem.d) | Do semantic 1 pass (symbol identifiers/types) | | [enumsem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/enumsem.d) | Enum semantics | | [funcsem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/funcsem.d) | Function semantics | diff --git a/dmd/attrib.d b/dmd/attrib.d index d7d3eca6f8d..1c8eb577261 100644 --- a/dmd/attrib.d +++ b/dmd/attrib.d @@ -35,7 +35,6 @@ import dmd.dsymbol; import dmd.dsymbolsem; import dmd.errors; import dmd.expression; -import dmd.expressionsem; import dmd.func; import dmd.globals; import dmd.hdrgen : visibilityToBuffer; @@ -1121,21 +1120,6 @@ extern (C++) final class UserAttributeDeclaration : AttribDeclaration return udas; } - Expressions* getAttributes() - { - if (auto sc = _scope) - { - _scope = null; - arrayExpressionSemantic(atts.peekSlice(), sc); - } - auto exps = new Expressions(); - if (userAttribDecl && userAttribDecl !is this) - exps.push(new TupleExp(Loc.initial, userAttribDecl.getAttributes())); - if (atts && atts.length) - exps.push(new TupleExp(Loc.initial, atts)); - return exps; - } - override const(char)* kind() const { return "UserAttribute"; @@ -1231,43 +1215,6 @@ bool isCoreUda(Dsymbol sym, Identifier ident) return _module && _module.isCoreModule(Id.attribute); } -/** - * Iterates the UDAs attached to the given symbol. - * - * Params: - * sym = the symbol to get the UDAs from - * sc = scope to use for semantic analysis of UDAs - * 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) -{ - if (!sym.userAttribDecl) - return 0; - - auto udas = sym.userAttribDecl.getAttributes(); - arrayExpressionSemantic(udas.peekSlice(), sc, true); - - return udas.each!((uda) { - if (!uda.isTupleExp()) - return 0; - - auto exps = uda.isTupleExp().exps; - - return exps.each!((e) { - assert(e); - - if (auto result = dg(e)) - return result; - - return 0; - }); - }); -} - /** * Iterates the UDAs attached to the given symbol, without performing semantic * analysis. diff --git a/dmd/attrib.h b/dmd/attrib.h index 344a7e929a7..d4c41ec94f3 100644 --- a/dmd/attrib.h +++ b/dmd/attrib.h @@ -17,6 +17,11 @@ class Expression; class Condition; class StaticForeach; +namespace dmd +{ + Expressions *getAttributes(UserAttributeDeclaration *a); +} + /**************************************************************/ class AttribDeclaration : public Dsymbol @@ -226,7 +231,6 @@ class UserAttributeDeclaration final : public AttribDeclaration UserAttributeDeclaration *syntaxCopy(Dsymbol *s) override; Scope *newScope(Scope *sc) override; - Expressions *getAttributes(); const char *kind() const override; void accept(Visitor *v) override { v->visit(this); } }; diff --git a/dmd/attribsem.d b/dmd/attribsem.d new file mode 100644 index 00000000000..44647eee62b --- /dev/null +++ b/dmd/attribsem.d @@ -0,0 +1,87 @@ +/** + * Does semantic analysis for attributes. + * + * The term 'attribute' refers to things that can apply to a larger scope than a single declaration. + * Among them are: + * - Alignment (`align(8)`) + * - User defined attributes (`@UDA`) + * - Function Attributes (`@safe`) + * - Storage classes (`static`, `__gshared`) + * - Mixin declarations (`mixin("int x;")`) + * - Conditional compilation (`static if`, `static foreach`) + * - Linkage (`extern(C)`) + * - Anonymous structs / unions + * - Protection (`private`, `public`) + * - Deprecated declarations (`@deprecated`) + * + * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) + * 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/attribsem.d, _attrib.d) + * Documentation: https://dlang.org/phobos/dmd_attribsem.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/attribsem.d + */ + +module dmd.attribsem; + +import dmd.arraytypes; +import dmd.attrib; +import dmd.dscope; +import dmd.dsymbol; +import dmd.expression; +import dmd.expressionsem; +import dmd.location; +import dmd.root.array; // for each + + +Expressions* getAttributes(UserAttributeDeclaration a) +{ + if (auto sc = a._scope) + { + a._scope = null; + arrayExpressionSemantic(a.atts.peekSlice(), sc); + } + auto exps = new Expressions(); + if (a.userAttribDecl && a.userAttribDecl !is a) + exps.push(new TupleExp(Loc.initial, a.userAttribDecl.getAttributes())); + if (a.atts && a.atts.length) + exps.push(new TupleExp(Loc.initial, a.atts)); + return exps; +} + +/** + * Iterates the UDAs attached to the given symbol. + * + * Params: + * sym = the symbol to get the UDAs from + * sc = scope to use for semantic analysis of UDAs + * 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) +{ + if (!sym.userAttribDecl) + return 0; + + auto udas = sym.userAttribDecl.getAttributes(); + arrayExpressionSemantic(udas.peekSlice(), sc, true); + + return udas.each!((uda) { + if (!uda.isTupleExp()) + return 0; + + auto exps = uda.isTupleExp().exps; + + return exps.each!((e) { + assert(e); + + if (auto result = dg(e)) + return result; + + return 0; + }); + }); +} diff --git a/dmd/cxxfrontend.d b/dmd/cxxfrontend.d index a0432d2e1b4..c0805b838ff 100644 --- a/dmd/cxxfrontend.d +++ b/dmd/cxxfrontend.d @@ -13,6 +13,7 @@ module dmd.cxxfrontend; import dmd.aggregate : AggregateDeclaration; import dmd.arraytypes; import dmd.astenums; +import dmd.attrib; import dmd.common.outbuffer : OutBuffer; import dmd.denum : EnumDeclaration; import dmd.dmodule /*: Module*/; @@ -34,6 +35,15 @@ import dmd.statement : Statement, AsmStatement, GccAsmStatement; // NB: At some point in the future, we can switch to shortened function syntax. extern (C++, "dmd"): +/*********************************************************** + * atrtibsem.d + */ +Expressions* getAttributes(UserAttributeDeclaration a) +{ + import dmd.attribsem; + return dmd.attribsem.getAttributes(a); +} + /*********************************************************** * cppmangle.d */ diff --git a/dmd/dsymbolsem.d b/dmd/dsymbolsem.d index e83def58c17..c3181e4b381 100644 --- a/dmd/dsymbolsem.d +++ b/dmd/dsymbolsem.d @@ -21,6 +21,7 @@ import dmd.arraytypes; import dmd.astcodegen; import dmd.astenums; import dmd.attrib; +import dmd.attribsem; import dmd.clone; import dmd.cond; import dmd.dcast; diff --git a/dmd/frontend.h b/dmd/frontend.h index 424db3c3627..c4394042077 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -6470,7 +6470,6 @@ class UserAttributeDeclaration final : public AttribDeclaration Array* atts; UserAttributeDeclaration* syntaxCopy(Dsymbol* s) override; Scope* newScope(Scope* sc) override; - Array* getAttributes(); const char* kind() const override; void accept(Visitor* v) override; static bool isGNUABITag(Expression* e); diff --git a/dmd/mustuse.d b/dmd/mustuse.d index c2fa5fbd023..fc7618bfc0e 100644 --- a/dmd/mustuse.d +++ b/dmd/mustuse.d @@ -202,7 +202,7 @@ private bool isIncrementOrDecrement(Expression e) */ private bool hasMustUseAttribute(Dsymbol sym, Scope* sc) { - import dmd.attrib : foreachUda; + import dmd.attribsem : foreachUda; bool result = false; diff --git a/dmd/objc.d b/dmd/objc.d index 2f36d5d13da..624210bf527 100644 --- a/dmd/objc.d +++ b/dmd/objc.d @@ -17,6 +17,7 @@ import dmd.aggregate; import dmd.arraytypes; import dmd.astenums; import dmd.attrib; +import dmd.attribsem; import dmd.cond; import dmd.dclass; import dmd.declaration; diff --git a/dmd/traits.d b/dmd/traits.d index be7aa9923c0..fc41ffb71a1 100644 --- a/dmd/traits.d +++ b/dmd/traits.d @@ -20,6 +20,7 @@ import dmd.arraytypes; import dmd.astcodegen; import dmd.astenums; import dmd.attrib; +import dmd.attribsem; import dmd.canthrow; import dmd.dclass; import dmd.declaration; From 88c8391b5df52aa130342fd65d2a1e55025ee74b Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Mon, 8 Apr 2024 17:46:49 -0700 Subject: [PATCH 060/125] trivial file edits --- dmd/mars.d | 5 +++-- dmd/root/file.d | 2 +- dmd/root/filename.d | 2 ++ dmd/root/rmem.d | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/dmd/mars.d b/dmd/mars.d index 06f694e9e47..6d4af13dda0 100644 --- a/dmd/mars.d +++ b/dmd/mars.d @@ -586,9 +586,9 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param version (none) { - for (size_t i = 0; i < arguments.length; i++) + foreach (i, arg; arguments[]) { - printf("arguments[%d] = '%s'\n", i, arguments[i]); + printf("arguments[%d] = '%s'\n", cast(int)i, arguments[i]); } } @@ -614,6 +614,7 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param return false; } } + //printf("push %s\n", p); files.push(p); continue; } diff --git a/dmd/root/file.d b/dmd/root/file.d index 034dba61b13..ee7170e230c 100644 --- a/dmd/root/file.d +++ b/dmd/root/file.d @@ -92,7 +92,7 @@ nothrow: version (Posix) { - //printf("File::read('%s')\n",name); + //printf("File::read('%.*s')\n", cast(int)name.length, name.ptr); int fd = name.toCStringThen!(slice => open(slice.ptr, O_RDONLY)); if (fd == -1) { diff --git a/dmd/root/filename.d b/dmd/root/filename.d index 67baeebb451..1fbe0ae3609 100644 --- a/dmd/root/filename.d +++ b/dmd/root/filename.d @@ -13,6 +13,7 @@ module dmd.root.filename; import core.stdc.ctype; import core.stdc.errno; +import core.stdc.stdio; import core.stdc.string; import dmd.common.file; @@ -858,6 +859,7 @@ nothrow: { if (!name.length) return 0; + //static int count; printf("count: %d %.*s\n", ++count, cast(int)name.length, name.ptr); version (Posix) { stat_t st; diff --git a/dmd/root/rmem.d b/dmd/root/rmem.d index 19652072376..c6986c0b56f 100644 --- a/dmd/root/rmem.d +++ b/dmd/root/rmem.d @@ -318,7 +318,7 @@ Params: Returns: A null-terminated copy of the input array. */ -extern (D) char[] xarraydup(const(char)[] s) pure nothrow +extern (D) char[] xarraydup(scope const(char)[] s) pure nothrow { if (!s) return null; From 12b59703ab7bf4d9f18e452fd839b0fbf53fb5f7 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Mon, 8 Apr 2024 22:06:49 -0700 Subject: [PATCH 061/125] fix bugzilla Issue 21854 - @live breaks foreach over integers --- dmd/ob.d | 23 +++++++++++++++++++---- tests/dmd/compilable/ob1.d | 16 ++++++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/dmd/ob.d b/dmd/ob.d index 0a5981589c6..9a14100facd 100644 --- a/dmd/ob.d +++ b/dmd/ob.d @@ -7,6 +7,8 @@ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/ob.d, _ob.d) * Documentation: https://dlang.org/phobos/dmd_escape.html * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/ob.d + * Bug reports: use 'live' keyword: + * https://issues.dlang.org/buglist.cgi?bug_status=NEW&bug_status=REOPENED&keywords=live */ module dmd.ob; @@ -32,6 +34,7 @@ import dmd.expression; import dmd.foreachvar; import dmd.func; import dmd.globals; +import dmd.hdrgen; import dmd.identifier; import dmd.init; import dmd.location; @@ -40,6 +43,7 @@ import dmd.printast; import dmd.statement; import dmd.stmtstate; import dmd.tokens; +import dmd.typesem; import dmd.visitor; import dmd.root.bitarray; @@ -846,7 +850,7 @@ void toObNodes(ref ObNodes obnodes, Statement s) case STMT.Mixin: case STMT.Peel: case STMT.Synchronized: - debug printf("s: %s\n", s.toChars()); + debug printf("s: %s\n", toChars(s)); assert(0); // should have been rewritten } } @@ -1251,7 +1255,7 @@ void genKill(ref ObState obstate, ObNode* ob) { enum log = false; if (log) - printf("-----------computeGenKill()-----------\n"); + printf("-----------computeGenKill() %d -----------\n", ob.index); /*************** * Assigning result of expression `e` to variable `v`. @@ -1721,6 +1725,15 @@ void genKill(ref ObState obstate, ObNode* ob) } foreachExp(ob, ob.exp); + + if (log) + { + printf(" gen:\n"); + foreach (i, ref pvs2; ob.gen[]) + { + printf(" %s: ", obstate.vars[i].toChars()); pvs2.print(obstate.vars[]); + } + } } /*************************************** @@ -2460,7 +2473,7 @@ void checkObErrors(ref ObState obstate) { static if (log) { - printf("%d: %s\n", obi, ob.exp ? ob.exp.toChars() : "".ptr); + printf("%d: %s\n", cast(int) obi, ob.exp ? ob.exp.toChars() : "".ptr); printf(" input:\n"); foreach (i, ref pvs; ob.input[]) { @@ -2490,7 +2503,9 @@ void checkObErrors(ref ObState obstate) if (s1 != s2 && (s1 == PtrState.Owner || s2 == PtrState.Owner)) { auto v = obstate.vars[i]; - .error(ob.exp ? ob.exp.loc : v.loc, "%s `%s` is both %s and %s", v.kind, v.toPrettyChars, PtrStateToChars(s1), PtrStateToChars(s2)); + // Don't worry about non-pointers + if (hasPointers(v.type)) + .error(ob.exp ? ob.exp.loc : v.loc, "%s `%s` is both %s and %s", v.kind, v.toPrettyChars, PtrStateToChars(s1), PtrStateToChars(s2)); } pvs1.combine(*pvs2, i, ob.gen); } diff --git a/tests/dmd/compilable/ob1.d b/tests/dmd/compilable/ob1.d index 720c765eda3..bbc862e0f14 100644 --- a/tests/dmd/compilable/ob1.d +++ b/tests/dmd/compilable/ob1.d @@ -147,3 +147,19 @@ struct S { int i; int* p; } S* s = cast(S*)malloc(); free(s.p); // consumes s } + +/******************************* + * https://issues.dlang.org/show_bug.cgi?id=21854 + */ + +@live void test21854() +{ + foreach(int tmp; 0..10) { } + + int key = 0; + int limit = 10; + for (; key < limit; key += 1) + { + int tmp = key; + } +} From 06995c8465946bbc72ec3f5f20beeb72540269dd Mon Sep 17 00:00:00 2001 From: Razvan Nitu Date: Wed, 10 Apr 2024 15:44:40 +0300 Subject: [PATCH 062/125] Fix Bugzilla 24485 - Invalid implicit ref return reinterpret cast for structs with copy constructor (dlang/dmd!16364) --- dmd/semantic3.d | 16 ++++++------- tests/dmd/fail_compilation/fail24485.d | 32 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 8 deletions(-) create mode 100644 tests/dmd/fail_compilation/fail24485.d diff --git a/dmd/semantic3.d b/dmd/semantic3.d index 7277874b9ee..8a35cd7b16e 100644 --- a/dmd/semantic3.d +++ b/dmd/semantic3.d @@ -909,26 +909,26 @@ private extern(C++) final class Semantic3Visitor : Visitor } } - const hasCopyCtor = exp.type.ty == Tstruct && (cast(TypeStruct)exp.type).sym.hasCopyCtor; - // if a copy constructor is present, the return type conversion will be handled by it - if (!(hasCopyCtor && exp.isLvalue())) + // Function returns a reference + if (f.isref) { - if (f.isref && !MODimplicitConv(exp.type.mod, tret.mod) && !tret.isTypeSArray()) + if (!MODimplicitConv(exp.type.mod, tret.mod) && !tret.isTypeSArray()) error(exp.loc, "expression `%s` of type `%s` is not implicitly convertible to return type `ref %s`", exp.toChars(), exp.type.toChars(), tret.toChars()); else exp = exp.implicitCastTo(sc2, tret); - } - if (f.isref) - { - // Function returns a reference exp = exp.toLvalue(sc2, "`ref` return"); checkReturnEscapeRef(sc2, exp, false); exp = exp.optimize(WANTvalue, /*keepLvalue*/ true); } else { + // if a copy constructor is present, the return type conversion will be handled by it + const hasCopyCtor = exp.type.ty == Tstruct && (cast(TypeStruct)exp.type).sym.hasCopyCtor; + if (!hasCopyCtor || !exp.isLvalue()) + exp = exp.implicitCastTo(sc2, tret); + exp = exp.optimize(WANTvalue); /* https://issues.dlang.org/show_bug.cgi?id=10789 diff --git a/tests/dmd/fail_compilation/fail24485.d b/tests/dmd/fail_compilation/fail24485.d new file mode 100644 index 00000000000..15547cdcca7 --- /dev/null +++ b/tests/dmd/fail_compilation/fail24485.d @@ -0,0 +1,32 @@ +// https://issues.dlang.org/show_bug.cgi?id=24485 +/* +TEST_OUTPUT: +--- +fail_compilation/fail24485.d(25): Error: cannot implicitly convert expression `*a` of type `A` to `B` +fail_compilation/fail24485.d(31): Error: cannot implicitly convert expression `this.a` of type `A` to `B` + +--- +*/ + +struct A +{ + int i = 43; + this(ref A rhs) {} +} + +struct B +{ + int i = 42; +} + +ref B foo() +{ + auto a = new A; + return *a; +} + +struct C +{ + A a; + @property ref B b() { return a; } +} From 0d4a36c531bbd531a5ffbd9f4c5b5aca9e694b34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matheus=20C=2E=20Fran=C3=A7a?= Date: Mon, 15 Apr 2024 14:39:11 -0300 Subject: [PATCH 063/125] fix: mmap64 error (dlang/dmd!16361) ref.: https://git.alpinelinux.org/aports/tree/community/ldc/lfs64.patch --- runtime/druntime/src/core/sys/posix/config.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/druntime/src/core/sys/posix/config.d b/runtime/druntime/src/core/sys/posix/config.d index ae6752f220e..6b80d1ff0e6 100644 --- a/runtime/druntime/src/core/sys/posix/config.d +++ b/runtime/druntime/src/core/sys/posix/config.d @@ -88,7 +88,7 @@ else version (CRuntime_Musl) enum __REDIRECT = false; // Those three are irrelevant for Musl as it always uses 64 bits off_t - enum __USE_FILE_OFFSET64 = _FILE_OFFSET_BITS == 64; + enum __USE_FILE_OFFSET64 = false; enum __USE_LARGEFILE = __USE_FILE_OFFSET64 && !__REDIRECT; enum __USE_LARGEFILE64 = __USE_FILE_OFFSET64 && !__REDIRECT; From 3cbd60a343e5fb74a0e1d36453eb420ff23c4d54 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Mon, 15 Apr 2024 14:46:23 -0700 Subject: [PATCH 064/125] fix bugzilla Issue 21923 - @live does not take destructor code into account (dlang/dmd!16371) --- dmd/ob.d | 57 ++++++++++++++++++++++++++++++-- tests/dmd/fail_compilation/ob1.d | 28 ++++++++++++++++ 2 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 tests/dmd/fail_compilation/ob1.d diff --git a/dmd/ob.d b/dmd/ob.d index 9a14100facd..de0416b8352 100644 --- a/dmd/ob.d +++ b/dmd/ob.d @@ -230,6 +230,8 @@ struct PtrVarState * are being merged * Params: * pvs = path to be merged with `this` + * vi = variable's index into gen[] + * gen = array of variable states */ void combine(ref PtrVarState pvs, size_t vi, PtrVarState[] gen) { @@ -284,6 +286,9 @@ struct PtrVarState } /*********************** + * Print a bracketed list of all the variables that depend on 'this' + * Params: + * vars = variables that depend on 'this' */ void print(VarDeclaration[] vars) { @@ -1117,8 +1122,8 @@ bool isTrackableVar(VarDeclaration v) /* Assume types with a destructor are doing their own tracking, * such as being a ref counted type */ - if (v.needsScopeDtor()) - return false; +// if (v.needsScopeDtor()) +// return false; /* Not tracking function parameters that are not mutable */ @@ -1235,7 +1240,9 @@ void allocStates(ref ObState obstate) */ bool isBorrowedPtr(VarDeclaration v) { - return v.isScope() && !v.isowner && v.type.nextOf().isMutable(); + return v.isScope() && !v.isowner && + (!v.type.nextOf() || // could be a struct type with a pointer field + v.type.nextOf().isMutable()); } /****************************** @@ -1439,6 +1446,46 @@ void genKill(ref ObState obstate, ObNode* ob) assert(t.ty == Tdelegate); tf = t.nextOf().isTypeFunction(); assert(tf); + + } + + if (auto dve = ce.e1.isDotVarExp()) + { + if (!t.isTypeDelegate() && dve.e1.isVarExp()) + { + //printf("dve: %s\n", dve.toChars()); + EscapeByResults er; + escapeByValue(dve.e1, &er, true); + + void byf(VarDeclaration v) + { + //printf("byf v: %s\n", v.ident.toChars()); + if (!isTrackableVar(v)) + return; + + const vi = obstate.vars.find(v); + if (vi == size_t.max) + return; + + auto fd = dve.var.isFuncDeclaration(); + if (fd && fd.storage_class & STC.scope_) + { + // borrow + obstate.varStack.push(vi); + obstate.mutableStack.push(isMutableRef(dve.e1.type.toBasetype())); + } + else + { + // move (i.e. consume arg) + makeUndefined(vi, ob.gen); + } + } + + foreach (VarDeclaration v2; er.byvalue) + byf(v2); + foreach (VarDeclaration v2; er.byref) + byf(v2); + } } // j=1 if _arguments[] is first argument @@ -1462,6 +1509,7 @@ void genKill(ref ObState obstate, ObNode* ob) void by(VarDeclaration v) { + //printf("by v: %s\n", v.ident.toChars()); if (!isTrackableVar(v)) return; @@ -2664,6 +2712,9 @@ void makeChildrenUndefined(size_t vi, PtrVarState[] gen) /******************** * Recursively make Undefined vi undefined and all who list vi as a dependency + * Params: + * vi = variable's index + * gen = array of the states of variables */ void makeUndefined(size_t vi, PtrVarState[] gen) { diff --git a/tests/dmd/fail_compilation/ob1.d b/tests/dmd/fail_compilation/ob1.d new file mode 100644 index 00000000000..a3428f13f6d --- /dev/null +++ b/tests/dmd/fail_compilation/ob1.d @@ -0,0 +1,28 @@ +/* REQUIRED_ARGS: -preview=dip1021 +TEST_OUTPUT: +--- +fail_compilation/ob1.d(23): Error: variable `ob1.mars.t` has undefined state and cannot be read +--- + https://issues.dlang.org/show_bug.cgi?id=21923 +*/ + +@live: + +struct Handle +{ + private void* _handle; + + this(int n); + ~this(); + scope void bar(); + static void fido(ref Handle); +} + +void mars() +{ + auto t = Handle(10); + t.bar(); + Handle.fido(t); // moves t to fido(), then destructor runs, causing error + + scope u = Handle(10); +} From 6ca16b68dafde82297abf39e74cd18f494ebc87f Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Mon, 15 Apr 2024 16:53:37 -0700 Subject: [PATCH 065/125] add find_files() to common/file.d (dlang/dmd!16365) --- dmd/common/file.d | 169 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/dmd/common/file.d b/dmd/common/file.d index 98ee13e031b..52da6ee0792 100644 --- a/dmd/common/file.d +++ b/dmd/common/file.d @@ -14,15 +14,23 @@ module dmd.common.file; +import core.stdc.stdio; +import core.stdc.stdlib; +import core.stdc.string; +import core.stdc.limits; + import core.stdc.errno : errno; import core.stdc.stdio : fprintf, remove, rename, stderr; import core.stdc.stdlib; import core.stdc.string : strerror, strlen, memcpy; import dmd.common.smallbuffer; +import dmd.root.filename; +import dmd.root.rmem; version (Windows) { + import core.stdc.wchar_; import core.sys.windows.winbase; import core.sys.windows.winnls : CP_ACP; import core.sys.windows.winnt; @@ -32,6 +40,7 @@ version (Windows) } else version (Posix) { + import core.sys.posix.dirent; import core.sys.posix.fcntl; import core.sys.posix.sys.mman; import core.sys.posix.sys.stat; @@ -587,3 +596,163 @@ private auto ref fakePure(F)(scope F fun) pure mixin("alias PureFun = " ~ F.stringof ~ " pure;"); return (cast(PureFun) fun)(); } + +/*********************************** + * Recursively search all the directories and files under dir_path + * for files that match one of the extensions in exts[]. + * Pass the matches to sink. + * Params: + * dir_path = root of directories to search + * exts = array of filename extensions to match + * recurse = go into subdirectories + * filenameSink = accepts the resulting matches + * Returns: + * true for failed to open the directory + */ +bool findFiles(const char* dir_path, const char[][] exts, bool recurse, void delegate(const(char)[]) nothrow filenameSink) +{ + enum log = false; + if (log) printf("findFiles() dir_path: %s\n", dir_path); + version (Windows) + { + debug + enum BufLength = 10; // trigger any reallocation bugs + else + enum BufLength = 100; + char[BufLength + 1] buf = void; + char* fullPath = buf.ptr; + size_t fullPathLength = BufLength; + + // fullPath = dir_path \ *.* + const dir_pathLength = strlen(dir_path); + auto count = dir_pathLength + 1 + 3; + if (count > fullPathLength) + { + fullPathLength = count; + fullPath = cast(char*)Mem.xrealloc_noscan(fullPath == buf.ptr ? null : fullPath, fullPathLength + 1); + } + memcpy(fullPath, dir_path, dir_pathLength); + strcpy(fullPath + dir_pathLength, "\\*.*".ptr); + + if (log) printf("fullPath: %s\n", fullPath); + + WIN32_FIND_DATAW ffd = void; + HANDLE hFind = fullPath[0 .. strlen(fullPath)].extendedPathThen!(p => FindFirstFileW(p.ptr, &ffd)); + if (hFind == INVALID_HANDLE_VALUE) + return true; + + do + { + if (log) wprintf("ffd.cFileName: %s\n", ffd.cFileName.ptr); + if (ffd.cFileName[0] == 0) + continue; // ignore + + if (ffd.cFileName[0] == '.') + continue; // ignore files that start with a ., also ignore . and .. directories + + const(char)[] name = toNarrowStringz(ffd.cFileName[0 .. wcslen(ffd.cFileName.ptr)], null); + if (log) printf("name: %s\n", name.ptr); + + // fullPath = dir_path \ name.ptr + count = dir_pathLength + 1 + name.length; + if (count > fullPathLength) + { + fullPathLength = count; + fullPath = cast(char*)Mem.xrealloc_noscan(fullPath == buf.ptr ? null : fullPath, fullPathLength + 1); + } + strcpy(fullPath + dir_pathLength + 1, name.ptr); + + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (recurse) + findFiles(fullPath, exts, recurse, filenameSink); + } + else + { + const(char)[] nameExt = FileName.ext(name); + foreach (ext; exts[]) + { + if (nameExt == ext) + { + if (log) printf("adding %s\n", fullPath); + filenameSink(fullPath[0 .. count]); + } + } + } + mem.xfree(cast(void*)name.ptr); + + } while (FindNextFileW(hFind, &ffd) != 0); + + if (fullPath != buf.ptr) + mem.xfree(fullPath); + FindClose(hFind); + if (log) printf("findFiles() exit\n"); + return false; + } + else version (Posix) + { + DIR* dir = opendir(dir_path); + if (!dir) + return true; + + debug + enum BufLength = 10; // trigger any reallocation bugs + else + enum BufLength = 100; + char[BufLength + 1] buf = void; + char* fullPath = buf.ptr; + size_t fullPathLength = BufLength; + + dirent* entry; + while ((entry = readdir(dir)) != null) + { + //printf("entry: %s\n", entry.d_name.ptr); + if (entry.d_name[0] == '.') + continue; // ignore files that start with a . + + // fullPath = dir_path / entry.d_name.ptr + const dir_pathLength = strlen(dir_path); + const count = dir_pathLength + 1 + strlen(entry.d_name.ptr); + if (count > fullPathLength) + { + fullPathLength = count; + fullPath = cast(char*)Mem.xrealloc_noscan(fullPath == buf.ptr ? null : fullPath, fullPathLength + 1); + } + memcpy(fullPath, dir_path, dir_pathLength); + fullPath[dir_pathLength] = '/'; + strcpy(fullPath + dir_pathLength + 1, entry.d_name.ptr); + + stat_t statbuf; + if (lstat(fullPath, &statbuf) == -1) + continue; + + const(char)[] name = entry.d_name.ptr[0 .. strlen(entry.d_name.ptr)]; // convert to D string + if (!name.length) + continue; // ignore + + if (S_ISDIR(statbuf.st_mode)) + { + if (recurse && !(name == "." || name == "..")) + findFiles(fullPath, exts, recurse, filenameSink); + } + else if (S_ISREG(statbuf.st_mode)) + { + foreach (ext; exts) + { + if (FileName.ext(name) == ext) + { + //printf("%s\n", fullPath); + filenameSink(fullPath[0 .. count]); + } + } + } + } + + if (fullPath != buf.ptr) + mem.xfree(fullPath); + closedir(dir); + return false; + } + else + static assert(0); +} From 13b802b99b2dd856ed6eaf98022c7e0ead6ec19f Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Tue, 16 Apr 2024 12:27:54 -0700 Subject: [PATCH 066/125] add DIP info to obj.d (dlang/dmd!16389) --- dmd/ob.d | 1 + 1 file changed, 1 insertion(+) diff --git a/dmd/ob.d b/dmd/ob.d index de0416b8352..b6acef2de50 100644 --- a/dmd/ob.d +++ b/dmd/ob.d @@ -9,6 +9,7 @@ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/ob.d * Bug reports: use 'live' keyword: * https://issues.dlang.org/buglist.cgi?bug_status=NEW&bug_status=REOPENED&keywords=live + * References: https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md Argument Ownership and Function Calls */ module dmd.ob; From 68bfb31077eb874553a5891f459fa5f58ec764be Mon Sep 17 00:00:00 2001 From: Dennis Date: Thu, 18 Apr 2024 09:09:52 +0200 Subject: [PATCH 067/125] dtoh: export `extern(Windows)` and `extern(System)` functions (dlang/dmd!16394) Co-authored-by: Dennis Korpel --- dmd/dtoh.d | 39 ++++++++++++++++-- tests/dmd/compilable/dtoh_windows.d | 63 +++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 tests/dmd/compilable/dtoh_windows.d diff --git a/dmd/dtoh.d b/dmd/dtoh.d index 2e2ced4b0d1..8427c4479b4 100644 --- a/dmd/dtoh.d +++ b/dmd/dtoh.d @@ -131,6 +131,17 @@ struct _d_dynamicArray final } }; #endif +`); + + if (v.hasExternSystem) + buf.writestring(` +#ifndef _WIN32 +#define EXTERN_SYSTEM_AFTER __stdcall +#define EXTERN_SYSTEM_BEFORE +#else +#define EXTERN_SYSTEM_AFTER +#define EXTERN_SYSTEM_BEFORE extern "C" +#endif `); if (v.hasReal) @@ -250,6 +261,10 @@ public: /// The generated header uses `real` emitted as `_d_real`? bool hasReal; + /// The generated header has extern(System) functions, + /// which needs support macros in the header + bool hasExternSystem; + /// The generated header should contain comments for skipped declarations? const bool printIgnored; @@ -745,7 +760,7 @@ public: // Note that tf might be null for templated (member) functions auto tf = cast(AST.TypeFunction)fd.type; - if ((tf && (tf.linkage != LINK.c || adparent) && tf.linkage != LINK.cpp) || (!tf && fd.isPostBlitDeclaration())) + if ((tf && (tf.linkage != LINK.c || adparent) && tf.linkage != LINK.cpp && tf.linkage != LINK.windows) || (!tf && fd.isPostBlitDeclaration())) { ignored("function %s because of linkage", fd.toPrettyChars()); return checkFunctionNeedsPlaceholder(fd); @@ -793,8 +808,17 @@ public: writeProtection(fd.visibility.kind); - if (tf && tf.linkage == LINK.c) + if (fd._linkage == LINK.system) + { + hasExternSystem = true; + buf.writestring("EXTERN_SYSTEM_BEFORE "); + } + else if (tf && tf.linkage == LINK.c) buf.writestring("extern \"C\" "); + else if (tf && tf.linkage == LINK.windows) + { + // __stdcall is printed after return type + } else if (!adparent) buf.writestring("extern "); if (adparent && fd.isStatic()) @@ -2261,8 +2285,8 @@ public: * Writes the function signature to `buf`. * * Params: - * fd = the function to print * tf = fd's type + * fd = the function to print */ private void funcToBuffer(AST.TypeFunction tf, AST.FuncDeclaration fd) { @@ -2297,6 +2321,15 @@ public: if (tf.isref) buf.writeByte('&'); buf.writeByte(' '); + + if (fd._linkage == LINK.system) + { + buf.writestring("EXTERN_SYSTEM_AFTER "); + } + else if (tf.linkage == LINK.windows) + { + buf.writestring("__stdcall "); + } writeIdentifier(fd); } diff --git a/tests/dmd/compilable/dtoh_windows.d b/tests/dmd/compilable/dtoh_windows.d new file mode 100644 index 00000000000..7a8d988453f --- /dev/null +++ b/tests/dmd/compilable/dtoh_windows.d @@ -0,0 +1,63 @@ +/++ +REQUIRED_ARGS: -HC -o- +TEST_OUTPUT: +--- +// Automatically generated by Digital Mars D Compiler + +#pragma once + +#include +#include +#include +#include + +#ifdef CUSTOM_D_ARRAY_TYPE +#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE +#else +/// Represents a D [] array +template +struct _d_dynamicArray final +{ + size_t length; + T *ptr; + + _d_dynamicArray() : length(0), ptr(NULL) { } + + _d_dynamicArray(size_t length_in, T *ptr_in) + : length(length_in), ptr(ptr_in) { } + + T& operator[](const size_t idx) { + assert(idx < length); + return ptr[idx]; + } + + const T& operator[](const size_t idx) const { + assert(idx < length); + return ptr[idx]; + } +}; +#endif + +#ifndef _WIN32 +#define EXTERN_SYSTEM_AFTER __stdcall +#define EXTERN_SYSTEM_BEFORE +#else +#define EXTERN_SYSTEM_AFTER +#define EXTERN_SYSTEM_BEFORE extern "C" +#endif + +EXTERN_SYSTEM_BEFORE int32_t EXTERN_SYSTEM_AFTER exSystem(int32_t x); + +int32_t __stdcall exWindows(int32_t y); +--- +++/ + +extern(System) int exSystem(int x) +{ + return x; +} + +extern(Windows) int exWindows(int y) +{ + return y; +} From a86be560694d617e411ac6f7c6f68d912950933e Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Thu, 18 Apr 2024 08:26:20 +0100 Subject: [PATCH 068/125] [editions] Make `body` after contract an error (dlang/dmd!16382) * [editions] Make `body` after contract an error * Mention --help in build.d module comment * Add ASTBase.Module.edition property, always Edition.legacy for now --- dmd/mars.d | 3 ++- dmd/parse.d | 4 ++-- tests/dmd/fail_compilation/obsolete_body.d | 11 +++++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 tests/dmd/fail_compilation/obsolete_body.d diff --git a/dmd/mars.d b/dmd/mars.d index 6d4af13dda0..767d16ffc0b 100644 --- a/dmd/mars.d +++ b/dmd/mars.d @@ -1212,9 +1212,10 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param params.warnings = DiagnosticReporting.inform; else if (arg == "-wo") // https://dlang.org/dmd.html#switch-wo { - // Obsolete features has been obsoleted until a DIP for "additions" + // Obsolete features has been obsoleted until a DIP for "editions" // has been drafted and ratified in the language spec. // Rather, these old features will just be accepted without warning. + // See also: @__edition_latest_do_not_use } else if (arg == "-O") // https://dlang.org/dmd.html#switch-O driverParams.optimize = true; diff --git a/dmd/parse.d b/dmd/parse.d index f3a844dff2e..a459b89e520 100644 --- a/dmd/parse.d +++ b/dmd/parse.d @@ -9589,9 +9589,9 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer void usageOfBodyKeyword() { - version (none) // disable obsolete warning + if (mod.edition >= Edition.v2024) { - eSink.warning(token.loc, "usage of identifer `body` as a keyword is obsolete. Use `do` instead."); + eSink.error(token.loc, "usage of identifer `body` as a keyword is obsolete. Use `do` instead."); } } } diff --git a/tests/dmd/fail_compilation/obsolete_body.d b/tests/dmd/fail_compilation/obsolete_body.d new file mode 100644 index 00000000000..86d8bbc5d32 --- /dev/null +++ b/tests/dmd/fail_compilation/obsolete_body.d @@ -0,0 +1,11 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/obsolete_body.d(11): Error: usage of identifer `body` as a keyword is obsolete. Use `do` instead. +--- +*/ +@__edition_latest_do_not_use +module m; + +void test() +in { } body { } From d1828b0a024678464536643653ced945ee4a5ac1 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Thu, 18 Apr 2024 05:40:20 -0600 Subject: [PATCH 069/125] Fix bugzilla issue 24493: FreeBSD_14 version identifier missing (dlang/dmd!16368) Apparently, there were two problems that needed fixing. 1. The code for the FreeBSD_14 version identifier had not been added yet. 2. The build did nothing to detect the version of FreeBSD, which makes it very easy to build for the wrong version. The CI apparently sets the version - e.g. TARGET_FREEBSD13 - but unless one of those TARGET_FREEBSD* versions is set, the build defaults to FreeBSD 11, which isn't even supported any longer. So, this adds the code for the FreeBSD_14 identifier, and it makes it so that the build queries the OS version on FreeBSD if TARGET_FREEBSD* has not been set. So, anything that sets the TARGET_FREEBSD* version when building will control the target version as before, but if it's not set, then it will target whatever the current system is. --- dmd/target.d | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/dmd/target.d b/dmd/target.d index a82123123e3..e43287f87c8 100644 --- a/dmd/target.d +++ b/dmd/target.d @@ -79,6 +79,8 @@ ubyte defaultTargetOSMajor() @safe return 12; else version (TARGET_FREEBSD13) return 13; + else version (TARGET_FREEBSD14) + return 14; else return 0; } @@ -200,13 +202,14 @@ void addPredefinedGlobalIdentifiers(const ref Target tgt) case OS.FreeBSD: { predef("FreeBSD"); - switch (tgt.osMajor) + + if(tgt.osMajor != 0) { - case 10: predef("FreeBSD_10"); break; - case 11: predef("FreeBSD_11"); break; - case 12: predef("FreeBSD_12"); break; - case 13: predef("FreeBSD_13"); break; - default: predef("FreeBSD_11"); break; + import core.stdc.stdio : snprintf; + + char["FreeBSD_100".length + 1] buffer; + immutable len = snprintf(buffer.ptr, buffer.length, "FreeBSD_%u", uint(tgt.osMajor)); + predef(buffer[0 .. len]); } break; } From 96535c3865fcfd74e8ebf5e17d8c8381dfaf213e Mon Sep 17 00:00:00 2001 From: Dennis Date: Thu, 18 Apr 2024 14:36:28 +0200 Subject: [PATCH 070/125] dtoh: emit _d_dynamicArray only when needed (dlang/dmd!16397) --- dmd/dtoh.d | 12 ++++++--- tests/dmd/compilable/dtoh_21217.d | 27 ------------------- tests/dmd/compilable/dtoh_AliasDeclaration.d | 27 ------------------- .../dmd/compilable/dtoh_AliasDeclaration_98.d | 27 ------------------- tests/dmd/compilable/dtoh_AnonDeclaration.d | 27 ------------------- .../compilable/dtoh_CPPNamespaceDeclaration.d | 27 ------------------- tests/dmd/compilable/dtoh_ClassDeclaration.d | 27 ------------------- .../dmd/compilable/dtoh_TemplateDeclaration.d | 27 ------------------- tests/dmd/compilable/dtoh_UnionDeclaration.d | 27 ------------------- tests/dmd/compilable/dtoh_enum.d | 27 ------------------- tests/dmd/compilable/dtoh_enum_cpp98.d | 27 ------------------- tests/dmd/compilable/dtoh_expressions.d | 27 ------------------- tests/dmd/compilable/dtoh_forwarding.d | 27 ------------------- tests/dmd/compilable/dtoh_functions.d | 27 ------------------- .../dmd/compilable/dtoh_invalid_identifiers.d | 27 ------------------- tests/dmd/compilable/dtoh_mangling.d | 27 ------------------- tests/dmd/compilable/dtoh_names.d | 27 ------------------- tests/dmd/compilable/dtoh_protection.d | 27 ------------------- tests/dmd/compilable/dtoh_required_symbols.d | 27 ------------------- tests/dmd/compilable/dtoh_special_enum.d | 27 ------------------- tests/dmd/compilable/dtoh_unittest_block.d | 27 ------------------- tests/dmd/compilable/dtoh_verbose.d | 27 ------------------- tests/dmd/compilable/dtoh_windows.d | 27 ------------------- 23 files changed, 9 insertions(+), 597 deletions(-) diff --git a/dmd/dtoh.d b/dmd/dtoh.d index 8427c4479b4..883091be9e3 100644 --- a/dmd/dtoh.d +++ b/dmd/dtoh.d @@ -104,7 +104,8 @@ void genCppHdrFiles(ref Modules ms) // Emit array compatibility because extern(C++) types may have slices // as members (as opposed to function parameters) - buf.writestring(` + if (v.hasDArray) + buf.writestring(` #ifdef CUSTOM_D_ARRAY_TYPE #define _d_dynamicArray CUSTOM_D_ARRAY_TYPE #else @@ -259,11 +260,14 @@ public: OutBuffer* buf; /// The generated header uses `real` emitted as `_d_real`? - bool hasReal; + bool hasReal = false; /// The generated header has extern(System) functions, /// which needs support macros in the header - bool hasExternSystem; + bool hasExternSystem = false; + + /// There are functions taking slices, which need a compatibility struct for C++ + bool hasDArray = false; /// The generated header should contain comments for skipped declarations? const bool printIgnored; @@ -2082,6 +2086,8 @@ public: { debug (Debug_DtoH) mixin(traceVisit!t); + hasDArray = true; + if (t.isConst() || t.isImmutable()) buf.writestring("const "); buf.writestring("_d_dynamicArray< "); diff --git a/tests/dmd/compilable/dtoh_21217.d b/tests/dmd/compilable/dtoh_21217.d index 3e535d24a06..439ff3612d5 100644 --- a/tests/dmd/compilable/dtoh_21217.d +++ b/tests/dmd/compilable/dtoh_21217.d @@ -12,33 +12,6 @@ TEST_OUTPUT: #include #include -#ifdef CUSTOM_D_ARRAY_TYPE -#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE -#else -/// Represents a D [] array -template -struct _d_dynamicArray final -{ - size_t length; - T *ptr; - - _d_dynamicArray() : length(0), ptr(NULL) { } - - _d_dynamicArray(size_t length_in, T *ptr_in) - : length(length_in), ptr(ptr_in) { } - - T& operator[](const size_t idx) { - assert(idx < length); - return ptr[idx]; - } - - const T& operator[](const size_t idx) const { - assert(idx < length); - return ptr[idx]; - } -}; -#endif - struct Foo final { int32_t a; diff --git a/tests/dmd/compilable/dtoh_AliasDeclaration.d b/tests/dmd/compilable/dtoh_AliasDeclaration.d index 64198f91a21..43bbe975e2e 100644 --- a/tests/dmd/compilable/dtoh_AliasDeclaration.d +++ b/tests/dmd/compilable/dtoh_AliasDeclaration.d @@ -14,33 +14,6 @@ TEST_OUTPUT: #include #include -#ifdef CUSTOM_D_ARRAY_TYPE -#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE -#else -/// Represents a D [] array -template -struct _d_dynamicArray final -{ - size_t length; - T *ptr; - - _d_dynamicArray() : length(0), ptr(NULL) { } - - _d_dynamicArray(size_t length_in, T *ptr_in) - : length(length_in), ptr(ptr_in) { } - - T& operator[](const size_t idx) { - assert(idx < length); - return ptr[idx]; - } - - const T& operator[](const size_t idx) const { - assert(idx < length); - return ptr[idx]; - } -}; -#endif - class C; extern void importFunc(); diff --git a/tests/dmd/compilable/dtoh_AliasDeclaration_98.d b/tests/dmd/compilable/dtoh_AliasDeclaration_98.d index 12edbd23923..b285e6f9e6d 100644 --- a/tests/dmd/compilable/dtoh_AliasDeclaration_98.d +++ b/tests/dmd/compilable/dtoh_AliasDeclaration_98.d @@ -11,33 +11,6 @@ TEST_OUTPUT: #include #include -#ifdef CUSTOM_D_ARRAY_TYPE -#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE -#else -/// Represents a D [] array -template -struct _d_dynamicArray final -{ - size_t length; - T *ptr; - - _d_dynamicArray() : length(0), ptr(NULL) { } - - _d_dynamicArray(size_t length_in, T *ptr_in) - : length(length_in), ptr(ptr_in) { } - - T& operator[](const size_t idx) { - assert(idx < length); - return ptr[idx]; - } - - const T& operator[](const size_t idx) const { - assert(idx < length); - return ptr[idx]; - } -}; -#endif - template struct TS final { diff --git a/tests/dmd/compilable/dtoh_AnonDeclaration.d b/tests/dmd/compilable/dtoh_AnonDeclaration.d index a4c6ce4ce2c..3c5bde32dbe 100644 --- a/tests/dmd/compilable/dtoh_AnonDeclaration.d +++ b/tests/dmd/compilable/dtoh_AnonDeclaration.d @@ -12,33 +12,6 @@ TEST_OUTPUT: #include #include -#ifdef CUSTOM_D_ARRAY_TYPE -#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE -#else -/// Represents a D [] array -template -struct _d_dynamicArray final -{ - size_t length; - T *ptr; - - _d_dynamicArray() : length(0), ptr(NULL) { } - - _d_dynamicArray(size_t length_in, T *ptr_in) - : length(length_in), ptr(ptr_in) { } - - T& operator[](const size_t idx) { - assert(idx < length); - return ptr[idx]; - } - - const T& operator[](const size_t idx) const { - assert(idx < length); - return ptr[idx]; - } -}; -#endif - struct S final { union diff --git a/tests/dmd/compilable/dtoh_CPPNamespaceDeclaration.d b/tests/dmd/compilable/dtoh_CPPNamespaceDeclaration.d index 04363711bd3..2310bfade7e 100644 --- a/tests/dmd/compilable/dtoh_CPPNamespaceDeclaration.d +++ b/tests/dmd/compilable/dtoh_CPPNamespaceDeclaration.d @@ -12,33 +12,6 @@ TEST_OUTPUT: #include #include -#ifdef CUSTOM_D_ARRAY_TYPE -#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE -#else -/// Represents a D [] array -template -struct _d_dynamicArray final -{ - size_t length; - T *ptr; - - _d_dynamicArray() : length(0), ptr(NULL) { } - - _d_dynamicArray(size_t length_in, T *ptr_in) - : length(length_in), ptr(ptr_in) { } - - T& operator[](const size_t idx) { - assert(idx < length); - return ptr[idx]; - } - - const T& operator[](const size_t idx) const { - assert(idx < length); - return ptr[idx]; - } -}; -#endif - namespace nameSpace { extern void fn(); diff --git a/tests/dmd/compilable/dtoh_ClassDeclaration.d b/tests/dmd/compilable/dtoh_ClassDeclaration.d index 169b7b15364..cca4646814a 100644 --- a/tests/dmd/compilable/dtoh_ClassDeclaration.d +++ b/tests/dmd/compilable/dtoh_ClassDeclaration.d @@ -12,33 +12,6 @@ TEST_OUTPUT: #include #include -#ifdef CUSTOM_D_ARRAY_TYPE -#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE -#else -/// Represents a D [] array -template -struct _d_dynamicArray final -{ - size_t length; - T *ptr; - - _d_dynamicArray() : length(0), ptr(NULL) { } - - _d_dynamicArray(size_t length_in, T *ptr_in) - : length(length_in), ptr(ptr_in) { } - - T& operator[](const size_t idx) { - assert(idx < length); - return ptr[idx]; - } - - const T& operator[](const size_t idx) const { - assert(idx < length); - return ptr[idx]; - } -}; -#endif - class ForwardClass; class BaseClass diff --git a/tests/dmd/compilable/dtoh_TemplateDeclaration.d b/tests/dmd/compilable/dtoh_TemplateDeclaration.d index 6fefcceeb97..45b5530831a 100644 --- a/tests/dmd/compilable/dtoh_TemplateDeclaration.d +++ b/tests/dmd/compilable/dtoh_TemplateDeclaration.d @@ -12,33 +12,6 @@ TEST_OUTPUT: #include #include -#ifdef CUSTOM_D_ARRAY_TYPE -#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE -#else -/// Represents a D [] array -template -struct _d_dynamicArray final -{ - size_t length; - T *ptr; - - _d_dynamicArray() : length(0), ptr(NULL) { } - - _d_dynamicArray(size_t length_in, T *ptr_in) - : length(length_in), ptr(ptr_in) { } - - T& operator[](const size_t idx) { - assert(idx < length); - return ptr[idx]; - } - - const T& operator[](const size_t idx) const { - assert(idx < length); - return ptr[idx]; - } -}; -#endif - struct Outer final { int32_t a; diff --git a/tests/dmd/compilable/dtoh_UnionDeclaration.d b/tests/dmd/compilable/dtoh_UnionDeclaration.d index 48fcf72c061..e0648d7c176 100644 --- a/tests/dmd/compilable/dtoh_UnionDeclaration.d +++ b/tests/dmd/compilable/dtoh_UnionDeclaration.d @@ -12,33 +12,6 @@ TEST_OUTPUT: #include #include -#ifdef CUSTOM_D_ARRAY_TYPE -#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE -#else -/// Represents a D [] array -template -struct _d_dynamicArray final -{ - size_t length; - T *ptr; - - _d_dynamicArray() : length(0), ptr(NULL) { } - - _d_dynamicArray(size_t length_in, T *ptr_in) - : length(length_in), ptr(ptr_in) { } - - T& operator[](const size_t idx) { - assert(idx < length); - return ptr[idx]; - } - - const T& operator[](const size_t idx) const { - assert(idx < length); - return ptr[idx]; - } -}; -#endif - union U1 { int32_t a; diff --git a/tests/dmd/compilable/dtoh_enum.d b/tests/dmd/compilable/dtoh_enum.d index 8b3e5aa3873..e8b4a554690 100644 --- a/tests/dmd/compilable/dtoh_enum.d +++ b/tests/dmd/compilable/dtoh_enum.d @@ -12,33 +12,6 @@ TEST_OUTPUT: #include #include -#ifdef CUSTOM_D_ARRAY_TYPE -#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE -#else -/// Represents a D [] array -template -struct _d_dynamicArray final -{ - size_t length; - T *ptr; - - _d_dynamicArray() : length(0), ptr(NULL) { } - - _d_dynamicArray(size_t length_in, T *ptr_in) - : length(length_in), ptr(ptr_in) { } - - T& operator[](const size_t idx) { - assert(idx < length); - return ptr[idx]; - } - - const T& operator[](const size_t idx) const { - assert(idx < length); - return ptr[idx]; - } -}; -#endif - enum : int32_t { Anon = 10 }; enum : bool { Anon2 = true }; diff --git a/tests/dmd/compilable/dtoh_enum_cpp98.d b/tests/dmd/compilable/dtoh_enum_cpp98.d index 6a266d9be77..1314f64b0ed 100644 --- a/tests/dmd/compilable/dtoh_enum_cpp98.d +++ b/tests/dmd/compilable/dtoh_enum_cpp98.d @@ -12,33 +12,6 @@ TEST_OUTPUT: #include #include -#ifdef CUSTOM_D_ARRAY_TYPE -#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE -#else -/// Represents a D [] array -template -struct _d_dynamicArray final -{ - size_t length; - T *ptr; - - _d_dynamicArray() : length(0), ptr(NULL) { } - - _d_dynamicArray(size_t length_in, T *ptr_in) - : length(length_in), ptr(ptr_in) { } - - T& operator[](const size_t idx) { - assert(idx < length); - return ptr[idx]; - } - - const T& operator[](const size_t idx) const { - assert(idx < length); - return ptr[idx]; - } -}; -#endif - static int32_t const Anon = 10; static bool const Anon2 = true; diff --git a/tests/dmd/compilable/dtoh_expressions.d b/tests/dmd/compilable/dtoh_expressions.d index b93c47e0813..7af10256c55 100644 --- a/tests/dmd/compilable/dtoh_expressions.d +++ b/tests/dmd/compilable/dtoh_expressions.d @@ -11,33 +11,6 @@ TEST_OUTPUT: #include #include -#ifdef CUSTOM_D_ARRAY_TYPE -#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE -#else -/// Represents a D [] array -template -struct _d_dynamicArray final -{ - size_t length; - T *ptr; - - _d_dynamicArray() : length(0), ptr(NULL) { } - - _d_dynamicArray(size_t length_in, T *ptr_in) - : length(length_in), ptr(ptr_in) { } - - T& operator[](const size_t idx) { - assert(idx < length); - return ptr[idx]; - } - - const T& operator[](const size_t idx) const { - assert(idx < length); - return ptr[idx]; - } -}; -#endif - extern int32_t foo(); extern int32_t* somePtr; diff --git a/tests/dmd/compilable/dtoh_forwarding.d b/tests/dmd/compilable/dtoh_forwarding.d index c9d5bbc796e..beb779db32e 100644 --- a/tests/dmd/compilable/dtoh_forwarding.d +++ b/tests/dmd/compilable/dtoh_forwarding.d @@ -12,33 +12,6 @@ TEST_OUTPUT: #include #include -#ifdef CUSTOM_D_ARRAY_TYPE -#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE -#else -/// Represents a D [] array -template -struct _d_dynamicArray final -{ - size_t length; - T *ptr; - - _d_dynamicArray() : length(0), ptr(NULL) { } - - _d_dynamicArray(size_t length_in, T *ptr_in) - : length(length_in), ptr(ptr_in) { } - - T& operator[](const size_t idx) { - assert(idx < length); - return ptr[idx]; - } - - const T& operator[](const size_t idx) const { - assert(idx < length); - return ptr[idx]; - } -}; -#endif - struct Child; class Struct; enum class Enum; diff --git a/tests/dmd/compilable/dtoh_functions.d b/tests/dmd/compilable/dtoh_functions.d index 90223cca81e..f5777bc8d21 100644 --- a/tests/dmd/compilable/dtoh_functions.d +++ b/tests/dmd/compilable/dtoh_functions.d @@ -12,33 +12,6 @@ TEST_OUTPUT: #include #include -#ifdef CUSTOM_D_ARRAY_TYPE -#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE -#else -/// Represents a D [] array -template -struct _d_dynamicArray final -{ - size_t length; - T *ptr; - - _d_dynamicArray() : length(0), ptr(NULL) { } - - _d_dynamicArray(size_t length_in, T *ptr_in) - : length(length_in), ptr(ptr_in) { } - - T& operator[](const size_t idx) { - assert(idx < length); - return ptr[idx]; - } - - const T& operator[](const size_t idx) const { - assert(idx < length); - return ptr[idx]; - } -}; -#endif - struct S final { int32_t i; diff --git a/tests/dmd/compilable/dtoh_invalid_identifiers.d b/tests/dmd/compilable/dtoh_invalid_identifiers.d index a8f5b990413..28c79088a26 100644 --- a/tests/dmd/compilable/dtoh_invalid_identifiers.d +++ b/tests/dmd/compilable/dtoh_invalid_identifiers.d @@ -21,33 +21,6 @@ compilable/dtoh_invalid_identifiers.d(145): Warning: function `__attribute__` is #include #include -#ifdef CUSTOM_D_ARRAY_TYPE -#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE -#else -/// Represents a D [] array -template -struct _d_dynamicArray final -{ - size_t length; - T *ptr; - - _d_dynamicArray() : length(0), ptr(NULL) { } - - _d_dynamicArray(size_t length_in, T *ptr_in) - : length(length_in), ptr(ptr_in) { } - - T& operator[](const size_t idx) { - assert(idx < length); - return ptr[idx]; - } - - const T& operator[](const size_t idx) const { - assert(idx < length); - return ptr[idx]; - } -}; -#endif - extern void register(int32_t* ptr); namespace const_cast diff --git a/tests/dmd/compilable/dtoh_mangling.d b/tests/dmd/compilable/dtoh_mangling.d index fda9efa5d34..203f424ae0b 100644 --- a/tests/dmd/compilable/dtoh_mangling.d +++ b/tests/dmd/compilable/dtoh_mangling.d @@ -11,33 +11,6 @@ TEST_OUTPUT: #include #include -#ifdef CUSTOM_D_ARRAY_TYPE -#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE -#else -/// Represents a D [] array -template -struct _d_dynamicArray final -{ - size_t length; - T *ptr; - - _d_dynamicArray() : length(0), ptr(NULL) { } - - _d_dynamicArray(size_t length_in, T *ptr_in) - : length(length_in), ptr(ptr_in) { } - - T& operator[](const size_t idx) { - assert(idx < length); - return ptr[idx]; - } - - const T& operator[](const size_t idx) const { - assert(idx < length); - return ptr[idx]; - } -}; -#endif - extern "C" int32_t freeC(); // Ignored function dtoh_mangling.bar because C++ doesn't support explicit mangling diff --git a/tests/dmd/compilable/dtoh_names.d b/tests/dmd/compilable/dtoh_names.d index a4b055e4097..c6def03ad8f 100644 --- a/tests/dmd/compilable/dtoh_names.d +++ b/tests/dmd/compilable/dtoh_names.d @@ -12,33 +12,6 @@ TEST_OUTPUT: #include #include -#ifdef CUSTOM_D_ARRAY_TYPE -#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE -#else -/// Represents a D [] array -template -struct _d_dynamicArray final -{ - size_t length; - T *ptr; - - _d_dynamicArray() : length(0), ptr(NULL) { } - - _d_dynamicArray(size_t length_in, T *ptr_in) - : length(length_in), ptr(ptr_in) { } - - T& operator[](const size_t idx) { - assert(idx < length); - return ptr[idx]; - } - - const T& operator[](const size_t idx) const { - assert(idx < length); - return ptr[idx]; - } -}; -#endif - struct Outer final { static Outer* outerPtr; diff --git a/tests/dmd/compilable/dtoh_protection.d b/tests/dmd/compilable/dtoh_protection.d index dc07c7b8f8b..458b2d7cc28 100644 --- a/tests/dmd/compilable/dtoh_protection.d +++ b/tests/dmd/compilable/dtoh_protection.d @@ -13,33 +13,6 @@ TEST_OUTPUT: #include #include -#ifdef CUSTOM_D_ARRAY_TYPE -#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE -#else -/// Represents a D [] array -template -struct _d_dynamicArray final -{ - size_t length; - T *ptr; - - _d_dynamicArray() : length(0), ptr(NULL) { } - - _d_dynamicArray(size_t length_in, T *ptr_in) - : length(length_in), ptr(ptr_in) { } - - T& operator[](const size_t idx) { - assert(idx < length); - return ptr[idx]; - } - - const T& operator[](const size_t idx) const { - assert(idx < length); - return ptr[idx]; - } -}; -#endif - struct S1 final { int32_t a; diff --git a/tests/dmd/compilable/dtoh_required_symbols.d b/tests/dmd/compilable/dtoh_required_symbols.d index ab5376424f9..77aee6e2f2a 100644 --- a/tests/dmd/compilable/dtoh_required_symbols.d +++ b/tests/dmd/compilable/dtoh_required_symbols.d @@ -11,33 +11,6 @@ TEST_OUTPUT: #include #include -#ifdef CUSTOM_D_ARRAY_TYPE -#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE -#else -/// Represents a D [] array -template -struct _d_dynamicArray final -{ - size_t length; - T *ptr; - - _d_dynamicArray() : length(0), ptr(NULL) { } - - _d_dynamicArray(size_t length_in, T *ptr_in) - : length(length_in), ptr(ptr_in) { } - - T& operator[](const size_t idx) { - assert(idx < length); - return ptr[idx]; - } - - const T& operator[](const size_t idx) const { - assert(idx < length); - return ptr[idx]; - } -}; -#endif - class ExternDClass; struct ExternDStruct2; struct ExternDStruct3; diff --git a/tests/dmd/compilable/dtoh_special_enum.d b/tests/dmd/compilable/dtoh_special_enum.d index ee86a5e7121..a2982ac9eaa 100644 --- a/tests/dmd/compilable/dtoh_special_enum.d +++ b/tests/dmd/compilable/dtoh_special_enum.d @@ -12,33 +12,6 @@ TEST_OUTPUT: #include #include -#ifdef CUSTOM_D_ARRAY_TYPE -#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE -#else -/// Represents a D [] array -template -struct _d_dynamicArray final -{ - size_t length; - T *ptr; - - _d_dynamicArray() : length(0), ptr(NULL) { } - - _d_dynamicArray(size_t length_in, T *ptr_in) - : length(length_in), ptr(ptr_in) { } - - T& operator[](const size_t idx) { - assert(idx < length); - return ptr[idx]; - } - - const T& operator[](const size_t idx) const { - assert(idx < length); - return ptr[idx]; - } -}; -#endif - enum class __c_not_special; extern "C" void fn_long(long __param_0_); diff --git a/tests/dmd/compilable/dtoh_unittest_block.d b/tests/dmd/compilable/dtoh_unittest_block.d index 7b2943c5fc5..c7aa6b7a7e6 100644 --- a/tests/dmd/compilable/dtoh_unittest_block.d +++ b/tests/dmd/compilable/dtoh_unittest_block.d @@ -12,33 +12,6 @@ TEST_OUTPUT: #include #include -#ifdef CUSTOM_D_ARRAY_TYPE -#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE -#else -/// Represents a D [] array -template -struct _d_dynamicArray final -{ - size_t length; - T *ptr; - - _d_dynamicArray() : length(0), ptr(NULL) { } - - _d_dynamicArray(size_t length_in, T *ptr_in) - : length(length_in), ptr(ptr_in) { } - - T& operator[](const size_t idx) { - assert(idx < length); - return ptr[idx]; - } - - const T& operator[](const size_t idx) const { - assert(idx < length); - return ptr[idx]; - } -}; -#endif - --- */ diff --git a/tests/dmd/compilable/dtoh_verbose.d b/tests/dmd/compilable/dtoh_verbose.d index 891ff0ebd48..e4b268b8a69 100644 --- a/tests/dmd/compilable/dtoh_verbose.d +++ b/tests/dmd/compilable/dtoh_verbose.d @@ -14,33 +14,6 @@ TEST_OUTPUT: #include #include -#ifdef CUSTOM_D_ARRAY_TYPE -#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE -#else -/// Represents a D [] array -template -struct _d_dynamicArray final -{ - size_t length; - T *ptr; - - _d_dynamicArray() : length(0), ptr(NULL) { } - - _d_dynamicArray(size_t length_in, T *ptr_in) - : length(length_in), ptr(ptr_in) { } - - T& operator[](const size_t idx) { - assert(idx < length); - return ptr[idx]; - } - - const T& operator[](const size_t idx) const { - assert(idx < length); - return ptr[idx]; - } -}; -#endif - extern void importFunc(); // Ignored function dtoh_verbose.foo because of linkage diff --git a/tests/dmd/compilable/dtoh_windows.d b/tests/dmd/compilable/dtoh_windows.d index 7a8d988453f..1cbe3248ce1 100644 --- a/tests/dmd/compilable/dtoh_windows.d +++ b/tests/dmd/compilable/dtoh_windows.d @@ -11,33 +11,6 @@ TEST_OUTPUT: #include #include -#ifdef CUSTOM_D_ARRAY_TYPE -#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE -#else -/// Represents a D [] array -template -struct _d_dynamicArray final -{ - size_t length; - T *ptr; - - _d_dynamicArray() : length(0), ptr(NULL) { } - - _d_dynamicArray(size_t length_in, T *ptr_in) - : length(length_in), ptr(ptr_in) { } - - T& operator[](const size_t idx) { - assert(idx < length); - return ptr[idx]; - } - - const T& operator[](const size_t idx) const { - assert(idx < length); - return ptr[idx]; - } -}; -#endif - #ifndef _WIN32 #define EXTERN_SYSTEM_AFTER __stdcall #define EXTERN_SYSTEM_BEFORE From b79247cdc7a4ec1243708a5acb325654eda07de1 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Fri, 19 Apr 2024 15:55:31 +0100 Subject: [PATCH 071/125] =?UTF-8?q?Fix=20Bugzilla=2014128=20-=20AliasDecla?= =?UTF-8?q?ration=20allows=20expressions,=20causing=20false=20=E2=80=A6=20?= =?UTF-8?q?(dlang/dmd!15863)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix Issue 14128 - AliasDeclaration allows expressions, causing false code for ThisExp * Add test case * Fix deprecation comment * Add changelog * Use dummy out param, not null * Change to error with __edition_latest_do_not_use --- dmd/dsymbolsem.d | 18 ++++++++++++ .../fail_compilation/alias_instance_member.d | 28 +++++++++++++++++++ .../fail_compilation/alias_instance_member2.d | 21 ++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 tests/dmd/fail_compilation/alias_instance_member.d create mode 100644 tests/dmd/fail_compilation/alias_instance_member2.d diff --git a/dmd/dsymbolsem.d b/dmd/dsymbolsem.d index c3181e4b381..f3ae3e0f105 100644 --- a/dmd/dsymbolsem.d +++ b/dmd/dsymbolsem.d @@ -5301,6 +5301,24 @@ void aliasSemantic(AliasDeclaration ds, Scope* sc) // Detect `alias sym = sym;` to prevent creating loops in overload overnext lists. if (auto tident = ds.type.isTypeIdentifier()) { + if (sc.hasEdition(Edition.v2024) && tident.idents.length) + { + alias mt = tident; + Dsymbol pscopesym; + Dsymbol s = sc.search(ds.loc, mt.ident, pscopesym); + // detect `alias a = var1.member_var;` which confusingly resolves to + // `typeof(var1).member_var`, which can be valid inside the aggregate type + if (s && s.isVarDeclaration() && + mt.ident != Id.This && mt.ident != Id._super) + { + s = tident.toDsymbol(sc); + if (s && s.isVarDeclaration()) { + error(mt.loc, "cannot alias member of variable `%s`", mt.ident.toChars()); + errorSupplemental(mt.loc, "Use `typeof(%s)` instead to preserve behaviour", + mt.ident.toChars()); + } + } + } // Selective imports are allowed to alias to the same name `import mod : sym=sym`. if (!ds._import) { diff --git a/tests/dmd/fail_compilation/alias_instance_member.d b/tests/dmd/fail_compilation/alias_instance_member.d new file mode 100644 index 00000000000..9f3729ed23f --- /dev/null +++ b/tests/dmd/fail_compilation/alias_instance_member.d @@ -0,0 +1,28 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/alias_instance_member.d(18): Error: cannot alias member of variable `that` +fail_compilation/alias_instance_member.d(18): Use `typeof(that)` instead to preserve behaviour +--- +*/ + +@__edition_latest_do_not_use +module aim; + +struct Foo +{ + int v; + void test(Foo that) const + { + alias a = this.v; // OK + alias b = that.v; + assert(&a is &b); + } +} + +void main() +{ + Foo a = Foo(1); + Foo b = Foo(2); + a.test(b); +} diff --git a/tests/dmd/fail_compilation/alias_instance_member2.d b/tests/dmd/fail_compilation/alias_instance_member2.d new file mode 100644 index 00000000000..752ef140e54 --- /dev/null +++ b/tests/dmd/fail_compilation/alias_instance_member2.d @@ -0,0 +1,21 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/alias_instance_member2.d(20): Error: cannot alias member of variable `f` +fail_compilation/alias_instance_member2.d(20): Use `typeof(f)` instead to preserve behaviour +--- +*/ + +@__edition_latest_do_not_use +module aim; + +struct Foo +{ + int v; +} + +struct Bar +{ + Foo f; + alias v = f.v; +} From 51a42f652ff59b86984c6f469caa40612a923bed Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sat, 20 Apr 2024 23:22:19 -0700 Subject: [PATCH 072/125] generalize isBorrowedPtr and isReadonlyPtr (dlang/dmd!16402) --- dmd/ob.d | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dmd/ob.d b/dmd/ob.d index b6acef2de50..bd985cfefba 100644 --- a/dmd/ob.d +++ b/dmd/ob.d @@ -1242,8 +1242,7 @@ void allocStates(ref ObState obstate) bool isBorrowedPtr(VarDeclaration v) { return v.isScope() && !v.isowner && - (!v.type.nextOf() || // could be a struct type with a pointer field - v.type.nextOf().isMutable()); + v.type.hasPointersToMutableFields(); } /****************************** @@ -1253,7 +1252,7 @@ bool isBorrowedPtr(VarDeclaration v) */ bool isReadonlyPtr(VarDeclaration v) { - return v.isScope() && !v.type.nextOf().isMutable(); + return v.isScope() && !v.type.hasPointersToMutableFields(); } /*************************************** From 267f32a50c65382662e2df06fda85a7a6fe90246 Mon Sep 17 00:00:00 2001 From: Dennis Date: Mon, 22 Apr 2024 16:28:07 +0200 Subject: [PATCH 073/125] Limit the number of deprecation messages by default (dlang/dmd!16403) --- dmd/cli.d | 2 +- dmd/errors.d | 8 +++++-- dmd/frontend.h | 5 ++++- dmd/globals.d | 1 + dmd/globals.h | 1 + dmd/main.d | 15 +++++++++++++ tests/dmd/compilable/deprecationlimit.d | 22 ++++++++++++++++++++ tests/dmd/compilable/sw_transition_complex.d | 2 +- tests/dmd/runnable/template10.d | 2 +- 9 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 tests/dmd/compilable/deprecationlimit.d diff --git a/dmd/cli.d b/dmd/cli.d index b69fed52ae5..71651d6572b 100644 --- a/dmd/cli.d +++ b/dmd/cli.d @@ -818,7 +818,7 @@ dmd -cov -unittest myprog.d "limit the number of supplemental messages for each error (0 means unlimited)" ), Option("verrors=", - "limit the number of error messages (0 means unlimited)" + "limit the number of error/deprecation messages (0 means unlimited)" ), Option("verrors=context", "show error messages with the context of the erroring source line" diff --git a/dmd/errors.d b/dmd/errors.d index 704bb9d77e7..4dd000409fd 100644 --- a/dmd/errors.d +++ b/dmd/errors.d @@ -478,8 +478,12 @@ extern (C++) void verrorReport(const ref Loc loc, const(char)* format, va_list a { if (!global.gag) { - info.headerColor = Classification.deprecation; - verrorPrint(format, ap, info); + global.deprecations++; + if (global.params.v.errorLimit == 0 || global.deprecations <= global.params.v.errorLimit) + { + info.headerColor = Classification.deprecation; + verrorPrint(format, ap, info); + } } else { diff --git a/dmd/frontend.h b/dmd/frontend.h index c4394042077..d1c1d6358b9 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -8294,6 +8294,7 @@ struct Global final CompileEnv compileEnv; Param params; uint32_t errors; + uint32_t deprecations; uint32_t warnings; uint32_t gag; uint32_t gaggedErrors; @@ -8324,6 +8325,7 @@ struct Global final compileEnv(), params(), errors(), + deprecations(), warnings(), gag(), gaggedErrors(), @@ -8339,7 +8341,7 @@ struct Global final preprocess() { } - Global(_d_dynamicArray< const char > inifilename, _d_dynamicArray< const char > copyright = { 73, "Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved" }, _d_dynamicArray< const char > written = { 24, "written by Walter Bright" }, Array path = Array(), Array filePath = Array(), CompileEnv compileEnv = CompileEnv(), Param params = Param(), uint32_t errors = 0u, uint32_t warnings = 0u, uint32_t gag = 0u, uint32_t gaggedErrors = 0u, uint32_t gaggedWarnings = 0u, void* console = nullptr, Array versionids = Array(), Array debugids = Array(), bool hasMainFunction = false, uint32_t varSequenceNumber = 1u, FileManager* fileManager = nullptr, ErrorSink* errorSink = nullptr, ErrorSink* errorSinkNull = nullptr, DArray(*preprocess)(FileName , const Loc& , OutBuffer& ) = nullptr) : + Global(_d_dynamicArray< const char > inifilename, _d_dynamicArray< const char > copyright = { 73, "Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved" }, _d_dynamicArray< const char > written = { 24, "written by Walter Bright" }, Array path = Array(), Array filePath = Array(), CompileEnv compileEnv = CompileEnv(), Param params = Param(), uint32_t errors = 0u, uint32_t deprecations = 0u, uint32_t warnings = 0u, uint32_t gag = 0u, uint32_t gaggedErrors = 0u, uint32_t gaggedWarnings = 0u, void* console = nullptr, Array versionids = Array(), Array debugids = Array(), bool hasMainFunction = false, uint32_t varSequenceNumber = 1u, FileManager* fileManager = nullptr, ErrorSink* errorSink = nullptr, ErrorSink* errorSinkNull = nullptr, DArray(*preprocess)(FileName , const Loc& , OutBuffer& ) = nullptr) : inifilename(inifilename), copyright(copyright), written(written), @@ -8348,6 +8350,7 @@ struct Global final compileEnv(compileEnv), params(params), errors(errors), + deprecations(deprecations), warnings(warnings), gag(gag), gaggedErrors(gaggedErrors), diff --git a/dmd/globals.d b/dmd/globals.d index f8291fa5ce7..c97aeb6d4d0 100644 --- a/dmd/globals.d +++ b/dmd/globals.d @@ -284,6 +284,7 @@ extern (C++) struct Global Param params; /// command line parameters uint errors; /// number of errors reported so far + uint deprecations; /// number of deprecations reported so far uint warnings; /// number of warnings reported so far uint gag; /// !=0 means gag reporting of errors & warnings uint gaggedErrors; /// number of errors reported while gagged diff --git a/dmd/globals.h b/dmd/globals.h index ac2b2867fb7..bd28d7be7b8 100644 --- a/dmd/globals.h +++ b/dmd/globals.h @@ -307,6 +307,7 @@ struct Global Param params; unsigned errors; // number of errors reported so far + unsigned deprecations; // number of deprecations reported so far unsigned warnings; // number of warnings reported so far unsigned gag; // !=0 means gag reporting of errors & warnings unsigned gaggedErrors; // number of errors reported while gagged diff --git a/dmd/main.d b/dmd/main.d index 27f79379aba..0485b191aa5 100644 --- a/dmd/main.d +++ b/dmd/main.d @@ -250,6 +250,18 @@ private int tryMain(size_t argc, const(char)** argv, ref Param params) errorSupplemental(Loc.initial, "Use -wi if you wish to treat warnings only as informational."); } + // In case deprecation messages were omitted, inform the user about it + static void mentionOmittedDeprecations() + { + if (global.params.v.errorLimit != 0 && + global.deprecations > global.params.v.errorLimit) + { + const omitted = global.deprecations - global.params.v.errorLimit; + message(Loc.initial, "%d deprecation warning%s omitted, use `-verrors=0` to show all", + omitted, omitted == 1 ? "".ptr : "s".ptr); + } + } + /* Generates code to check for all `params` whether any usage page has been requested. @@ -582,6 +594,9 @@ private int tryMain(size_t argc, const(char)** argv, ref Param params) if (global.warnings) errorOnWarning(); + if (global.params.useDeprecated == DiagnosticReporting.inform) + mentionOmittedDeprecations(); + // Do not attempt to generate output files if errors or warnings occurred if (global.errors || global.warnings) removeHdrFilesAndFail(params, modules); diff --git a/tests/dmd/compilable/deprecationlimit.d b/tests/dmd/compilable/deprecationlimit.d new file mode 100644 index 00000000000..dcdc9e118ca --- /dev/null +++ b/tests/dmd/compilable/deprecationlimit.d @@ -0,0 +1,22 @@ +/* +REQUIRED_ARGS: -verrors=3 +TEST_OUTPUT: +--- +compilable/deprecationlimit.d(18): Deprecation: function `deprecationlimit.f` is deprecated +compilable/deprecationlimit.d(19): Deprecation: function `deprecationlimit.f` is deprecated +compilable/deprecationlimit.d(20): Deprecation: function `deprecationlimit.f` is deprecated +1 deprecation warning omitted, use `-verrors=0` to show all +--- +*/ + +deprecated void f() +{ +} + +void main() +{ + f(); + f(); + f(); + f(); +} diff --git a/tests/dmd/compilable/sw_transition_complex.d b/tests/dmd/compilable/sw_transition_complex.d index b6dbc8a3460..3b273560332 100644 --- a/tests/dmd/compilable/sw_transition_complex.d +++ b/tests/dmd/compilable/sw_transition_complex.d @@ -1,5 +1,5 @@ // PERMUTE_ARGS: -// REQUIRED_ARGS: -unittest +// REQUIRED_ARGS: -unittest -verrors=0 /* TEST_OUTPUT: diff --git a/tests/dmd/runnable/template10.d b/tests/dmd/runnable/template10.d index cd6f6fe8b83..e0bec9be781 100644 --- a/tests/dmd/runnable/template10.d +++ b/tests/dmd/runnable/template10.d @@ -1,4 +1,5 @@ // PERMUTE_ARGS: -inline +// REQUIRED_ARGS: -verrors=0 /* TEST_OUTPUT: --- runnable/template10.d(89): Deprecation: function `template10.test1b.f0.f!(a).f` function requires a dual-context, which is deprecated @@ -58,7 +59,6 @@ runnable/template10.d(741): instantiated from here: `fun!(n)` */ /********************************************/ - void test1a() { int a = 1; From 2657da9245331b7420f4e4abe6eb5a0fb0e30d33 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Mon, 22 Apr 2024 07:48:00 -0600 Subject: [PATCH 074/125] Fix bugzilla issue 24517: druntime tests crash on FreeBSD 14 FreeBSD 14 changed the signature of qsort_r to be POSIX-compliant with POSIX, making it so that our binding for it no longer matches, resulting in a crash when it's used. This implements a fix similar to what the FreeBSD headers do to avoid breaking code (they provide a static inline extern(C++) overload for the old signature). This provides a deprecated extern(D) overload for the old signature. The extern(C) overload now matches the new signature. The changes have been versioned so that they only affect FreeBSD 14 and newer. Technically, if someone used Cmp when declaring their function for qsort_r, this would still break them (though with a compilation error that should be easy to fix rather than silent breakage or a crash), but I don't really see a way around that, and Cmp is not part of the POSIX API, so no one would have a clue that it was a thing without digging through the bindings. Arguably, we should make it private, since it's not part of POSIX, but I haven't done anything with that in this commit. My guess is that in reality, no D programs are both written to use qsort_r and run on FreeBSD (outside of the druntime tests), but this way, they won't break unless they use Cmp to declare their comparator function. They'll just get a deprecation message that they should update their code. Regardless, we have to change the signature for FreeBSD 14 for it to work, and this does that. --- runtime/druntime/src/core/internal/qsort.d | 46 ++++++++++++++++++---- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/runtime/druntime/src/core/internal/qsort.d b/runtime/druntime/src/core/internal/qsort.d index ada914c9ef4..0040f6b4609 100644 --- a/runtime/druntime/src/core/internal/qsort.d +++ b/runtime/druntime/src/core/internal/qsort.d @@ -56,17 +56,49 @@ static if (Glibc_Qsort_R) } else version (FreeBSD) { - alias extern (C) int function(scope void *, scope const void *, scope const void *) Cmp; - extern (C) void qsort_r(scope void *base, size_t nmemb, size_t size, scope void *thunk, Cmp cmp); + import core.sys.freebsd.config : __FreeBSD_version; - extern (C) void[] _adSort(return scope void[] a, TypeInfo ti) + static if (__FreeBSD_version >= 1400000) { - extern (C) int cmp(scope void* ti, scope const void* p1, scope const void* p2) + alias extern (C) int function(scope const void*, scope const void*, scope void*) Cmp; + extern (C) void qsort_r(scope void* base, size_t nmemb, size_t size, Cmp cmp, scope void* thunk); + + // https://cgit.freebsd.org/src/tree/include/stdlib.h?h=stable/14#n350 + pragma(mangle, "qsort_r@FBSD_1.0") + private extern (C) void __qsort_r_compat(scope void* base, size_t nmemb, size_t size, scope void* thunk, OldCmp cmp); + alias extern (C) int function(scope void*, scope const void*, scope const void*) OldCmp; + + deprecated("In FreeBSD 14, qsort_r's signature was fixed to match POSIX. This extern(D) overload has been " ~ + "provided to avoid breaking code, but code should be updated to use the POSIX version.") + extern (D) void qsort_r(scope void* base, size_t nmemb, size_t size, scope void* thunk, OldCmp cmp) { - return (cast(TypeInfo)ti).compare(p1, p2); + __qsort_r_compat(base, nmemb, size, thunk, cmp); + } + + extern (C) void[] _adSort(return scope void[] a, TypeInfo ti) + { + extern (C) int cmp(scope const void* p1, scope const void* p2, scope void* ti) + { + return (cast(TypeInfo)ti).compare(p1, p2); + } + qsort_r(a.ptr, a.length, ti.tsize, &cmp, cast(void*)ti); + return a; + } + } + else + { + alias extern (C) int function(scope void *, scope const void *, scope const void *) Cmp; + extern (C) void qsort_r(scope void* base, size_t nmemb, size_t size, scope void* thunk, Cmp cmp); + + extern (C) void[] _adSort(return scope void[] a, TypeInfo ti) + { + extern (C) int cmp(scope void* ti, scope const void* p1, scope const void* p2) + { + return (cast(TypeInfo)ti).compare(p1, p2); + } + qsort_r(a.ptr, a.length, ti.tsize, cast(void*)ti, &cmp); + return a; } - qsort_r(a.ptr, a.length, ti.tsize, cast(void*)ti, &cmp); - return a; } } else version (DragonFlyBSD) From 0606485b28af208a320232b096012cf80bb0ab9a Mon Sep 17 00:00:00 2001 From: Steven Schveighoffer Date: Tue, 23 Apr 2024 03:06:21 -0400 Subject: [PATCH 075/125] Remove the interface function fullCollectNoStack from the gcinterface (dlang/dmd!16401) * Remove the interface function fullCollectNoStack from the gcinterface * Fix tests that depend on the GC not scanning the stack. * Remove all nostack remnants * Add changelog entry --- runtime/druntime/src/core/gc/gcinterface.d | 5 -- .../core/internal/gc/impl/conservative/gc.d | 73 ++++++------------- .../src/core/internal/gc/impl/manual/gc.d | 4 - .../src/core/internal/gc/impl/proto/gc.d | 4 - runtime/druntime/src/core/internal/gc/proxy.d | 13 +--- .../exceptions/src/invalid_memory_operation.d | 3 +- .../test/valgrind/src/no_use_after_gc.d | 15 +++- 7 files changed, 40 insertions(+), 77 deletions(-) diff --git a/runtime/druntime/src/core/gc/gcinterface.d b/runtime/druntime/src/core/gc/gcinterface.d index 5560c6229ca..fa3d859ce36 100644 --- a/runtime/druntime/src/core/gc/gcinterface.d +++ b/runtime/druntime/src/core/gc/gcinterface.d @@ -53,11 +53,6 @@ interface GC */ void collect() nothrow; - /** - * - */ - void collectNoStack() nothrow; - /** * minimize free space usage */ diff --git a/runtime/druntime/src/core/internal/gc/impl/conservative/gc.d b/runtime/druntime/src/core/internal/gc/impl/conservative/gc.d index cb8df47507f..b1b270799aa 100644 --- a/runtime/druntime/src/core/internal/gc/impl/conservative/gc.d +++ b/runtime/druntime/src/core/internal/gc/impl/conservative/gc.d @@ -1252,12 +1252,6 @@ class ConservativeGC : GC } - void collectNoStack() nothrow - { - fullCollectNoStack(); - } - - /** * Begins a full collection, scanning all stack segments for roots. * @@ -1290,21 +1284,6 @@ class ConservativeGC : GC } - /** - * Begins a full collection while ignoring all stack segments for roots. - */ - void fullCollectNoStack() nothrow - { - // Since a finalizer could launch a new thread, we always need to lock - // when collecting. - static size_t go(Gcx* gcx) nothrow - { - return gcx.fullcollect(true, true, true); // standard stop the world - } - runLocked!go(gcx); - } - - /** * Minimize free space usage. */ @@ -2556,14 +2535,11 @@ struct Gcx } // collection step 2: mark roots and heap - void markAll(alias markFn)(bool nostack) nothrow + void markAll(alias markFn)() nothrow { - if (!nostack) - { - debug(COLLECT_PRINTF) printf("\tscan stacks.\n"); - // Scan stacks and registers for each paused thread - thread_scanAll(&markFn); - } + debug(COLLECT_PRINTF) printf("\tscan stacks.\n"); + // Scan stacks registers, and TLS for each paused thread + thread_scanAll(&markFn); // Scan roots[] debug(COLLECT_PRINTF) printf("\tscan roots[]\n"); @@ -2584,14 +2560,11 @@ struct Gcx } version (COLLECT_PARALLEL) - void collectAllRoots(bool nostack) nothrow + void collectAllRoots() nothrow { - if (!nostack) - { - debug(COLLECT_PRINTF) printf("\tcollect stacks.\n"); - // Scan stacks and registers for each paused thread - thread_scanAll(&collectRoots); - } + debug(COLLECT_PRINTF) printf("\tcollect stacks.\n"); + // Scan stacks registers and TLS for each paused thread + thread_scanAll(&collectRoots); // Scan roots[] debug(COLLECT_PRINTF) printf("\tcollect roots[]\n"); @@ -2920,7 +2893,7 @@ struct Gcx } version (COLLECT_FORK) - ChildStatus markFork(bool nostack, bool block, bool doParallel) nothrow + ChildStatus markFork(bool block, bool doParallel) nothrow { // Forking is enabled, so we fork() and start a new concurrent mark phase // in the child. If the collection should not block, the parent process @@ -2936,11 +2909,11 @@ struct Gcx int child_mark() scope { if (doParallel) - markParallel(nostack); + markParallel(); else if (ConservativeGC.isPrecise) - markAll!(markPrecise!true)(nostack); + markAll!(markPrecise!true)(); else - markAll!(markConservative!true)(nostack); + markAll!(markConservative!true)(); return 0; } @@ -2999,11 +2972,11 @@ struct Gcx // do the marking in this thread disableFork(); if (doParallel) - markParallel(nostack); + markParallel(); else if (ConservativeGC.isPrecise) - markAll!(markPrecise!false)(nostack); + markAll!(markPrecise!false)(); else - markAll!(markConservative!false)(nostack); + markAll!(markConservative!false)(); } else { assert(r == ChildStatus.done); assert(r != ChildStatus.running); @@ -3016,7 +2989,7 @@ struct Gcx * Return number of full pages free'd. * The collection is done concurrently only if block and isFinal are false. */ - size_t fullcollect(bool nostack = false, bool block = false, bool isFinal = false) nothrow + size_t fullcollect(bool block = false, bool isFinal = false) nothrow { // It is possible that `fullcollect` will be called from a thread which // is not yet registered in runtime (because allocating `new Thread` is @@ -3098,7 +3071,7 @@ Lmark: { version (COLLECT_FORK) { - auto forkResult = markFork(nostack, block, doParallel); + auto forkResult = markFork(block, doParallel); final switch (forkResult) { case ChildStatus.error: @@ -3125,14 +3098,14 @@ Lmark: else if (doParallel) { version (COLLECT_PARALLEL) - markParallel(nostack); + markParallel(); } else { if (ConservativeGC.isPrecise) - markAll!(markPrecise!false)(nostack); + markAll!(markPrecise!false)(); else - markAll!(markConservative!false)(nostack); + markAll!(markConservative!false)(); } thread_processGCMarks(&isMarked); @@ -3184,7 +3157,7 @@ Lmark: updateCollectThresholds(); if (doFork && isFinal) - return fullcollect(true, true, false); + return fullcollect(true, false); return freedPages; } @@ -3300,10 +3273,10 @@ Lmark: shared uint stoppedThreads; bool stopGC; - void markParallel(bool nostack) nothrow + void markParallel() nothrow { toscanRoots.clear(); - collectAllRoots(nostack); + collectAllRoots(); if (toscanRoots.empty) return; diff --git a/runtime/druntime/src/core/internal/gc/impl/manual/gc.d b/runtime/druntime/src/core/internal/gc/impl/manual/gc.d index b820adda1a2..36add7a2d6a 100644 --- a/runtime/druntime/src/core/internal/gc/impl/manual/gc.d +++ b/runtime/druntime/src/core/internal/gc/impl/manual/gc.d @@ -79,10 +79,6 @@ class ManualGC : GC { } - void collectNoStack() nothrow - { - } - void minimize() nothrow { } diff --git a/runtime/druntime/src/core/internal/gc/impl/proto/gc.d b/runtime/druntime/src/core/internal/gc/impl/proto/gc.d index 2286d17d9ce..dbe86007115 100644 --- a/runtime/druntime/src/core/internal/gc/impl/proto/gc.d +++ b/runtime/druntime/src/core/internal/gc/impl/proto/gc.d @@ -76,10 +76,6 @@ class ProtoGC : GC { } - void collectNoStack() nothrow - { - } - void minimize() nothrow { } diff --git a/runtime/druntime/src/core/internal/gc/proxy.d b/runtime/druntime/src/core/internal/gc/proxy.d index abc8c6ac2ea..c7dab714e6c 100644 --- a/runtime/druntime/src/core/internal/gc/proxy.d +++ b/runtime/druntime/src/core/internal/gc/proxy.d @@ -106,18 +106,7 @@ extern (C) case "none": break; case "collect": - // NOTE: There may be daemons threads still running when this routine is - // called. If so, cleaning memory out from under then is a good - // way to make them crash horribly. This probably doesn't matter - // much since the app is supposed to be shutting down anyway, but - // I'm disabling cleanup for now until I can think about it some - // more. - // - // NOTE: Due to popular demand, this has been re-enabled. It still has - // the problems mentioned above though, so I guess we'll see. - - instance.collectNoStack(); // not really a 'collect all' -- still scans - // static data area, roots, and ranges. + instance.collect(); break; case "finalize": instance.runFinalizers((cast(ubyte*)null)[0 .. size_t.max]); diff --git a/runtime/druntime/test/exceptions/src/invalid_memory_operation.d b/runtime/druntime/test/exceptions/src/invalid_memory_operation.d index 1cf1a9fd4ba..bd30eebe425 100644 --- a/runtime/druntime/test/exceptions/src/invalid_memory_operation.d +++ b/runtime/druntime/test/exceptions/src/invalid_memory_operation.d @@ -8,5 +8,6 @@ struct S void main() { - new S; + foreach(i; 0 .. 100) + new S; } diff --git a/runtime/druntime/test/valgrind/src/no_use_after_gc.d b/runtime/druntime/test/valgrind/src/no_use_after_gc.d index 1bb45628ff0..527af535d52 100644 --- a/runtime/druntime/test/valgrind/src/no_use_after_gc.d +++ b/runtime/druntime/test/valgrind/src/no_use_after_gc.d @@ -16,7 +16,7 @@ struct S __gshared int result; // Trick the optimizer -int main() +int makeBadInstances() { auto a = new S; auto b = new S; @@ -24,3 +24,16 @@ int main() b.other = a; return result; } + +int destroyStack() +{ + int[1000] x = result; + return x[123]; +} + +int main() +{ + int x = makeBadInstances(); + x += destroyStack(); + return x; +} From 6a3a82850a513948f988ca486e8b07d5d8908f5f Mon Sep 17 00:00:00 2001 From: Dennis Date: Wed, 24 Apr 2024 13:35:14 +0200 Subject: [PATCH 076/125] Demangle symbols in linker errors (dlang/dmd!16021) * Demangle symbols in linker errors Fix bugzilla issue 5573 * Fix test output regex of needspkgmod --------- Co-authored-by: Dennis Korpel --- dmd/root/string.d | 77 ++++++++++++++++++++++++ tests/dmd/fail_compilation/needspkgmod.d | 4 +- 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/dmd/root/string.d b/dmd/root/string.d index ea801024e13..4b4c3e1f645 100644 --- a/dmd/root/string.d +++ b/dmd/root/string.d @@ -294,6 +294,15 @@ do return true; } +///ditto +nothrow @nogc pure @safe +bool startsWith(scope const(char)[] str, scope const(char)[] prefix) +{ + if (str.length < prefix.length) + return false; + return str[0 .. prefix.length] == prefix; +} + /// @system pure nothrow @nogc unittest @@ -405,3 +414,71 @@ auto splitLines(const char[] text) return Range(text); } + +private struct FindSplit +{ +@nogc nothrow pure @safe: + const(char)[][3] elem; + + ref const(char)[] opIndex(size_t i) scope return { return elem[i]; } + bool opCast() const scope { return elem[1].length > 0; } +} + +/** +Find a substring in a string and split the string into before and after parts. +Params: + str = string to look into + needle = substring to find in str (must not be empty) +Returns: + a `FindSplit` object that casts to `true` iff `needle` was found inside `str`. + In that case, `split[1]` is the needle, and `split[0]`/`split[2]` are before/after the needle. +*/ +FindSplit findSplit(return scope const(char)[] str, scope const(char)[] needle) +{ + if (needle.length > str.length) + return FindSplit([str, null, null]); + + foreach (i; 0 .. str.length - needle.length + 1) + { + if (str[i .. i+needle.length] == needle[]) + return FindSplit([ str[0 .. i], str[i .. i+needle.length], str[i+needle.length .. $] ]); + } + return FindSplit([str, null, null]); +} + +unittest +{ + auto s = findSplit("a b c", "c"); + assert(s[0] == "a b "); + assert(s[1] == "c"); + assert(s[2] == ""); + auto s1 = findSplit("a b c", "b"); + assert(s1[0] == "a "); + assert(s1[1] == "b"); + assert(s1[2] == " c"); + assert(!findSplit("a b c", "d")); + assert(!findSplit("", "d")); +} + +/** +Find a string inbetween two substrings +Params: + str = string to look into + l = substring to find on the left + r = substring to find on the right +Returns: + substring of `str` inbetween `l` and `r` +*/ +const(char)[] findBetween(const(char)[] str, const(char)[] l, const(char)[] r) +{ + if (auto s0 = str.findSplit(l)) + if (auto s1 = s0[2].findSplit(r)) + return s1[0]; + return null; +} + +unittest +{ + assert(findBetween("a b c", "a ", " c") == "b"); + assert(findBetween("a b c", "a ", " d") == null); +} diff --git a/tests/dmd/fail_compilation/needspkgmod.d b/tests/dmd/fail_compilation/needspkgmod.d index 5f441b3c9be..7d97bf76b07 100644 --- a/tests/dmd/fail_compilation/needspkgmod.d +++ b/tests/dmd/fail_compilation/needspkgmod.d @@ -8,9 +8,7 @@ /* TEST_OUTPUT: ---- -$r:.+_D7imports9pkgmod3133mod3barFZv.*$ -Error: $r:.+$ exited with status $n$ -$r:.+$ +$r:.+_D7imports9pkgmod3133mod3barFZv.*Error: linker exited with status.+$ ---- */ import imports.pkgmod313.mod; From 2381be1b1b250215d9bf2c3d4bdc08013fd1dd33 Mon Sep 17 00:00:00 2001 From: "richard (rikki) andrew cattermole" Date: Fri, 26 Apr 2024 08:12:25 +1200 Subject: [PATCH 077/125] Fix lookup of ImportC character tables --- dmd/cparse.d | 3 +++ dmd/lexer.d | 17 ++++++----------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/dmd/cparse.d b/dmd/cparse.d index 527879122b7..7fbcd6d9bc1 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -89,6 +89,9 @@ final class CParser(AST) : Parser!AST this.wchar_tsize = target.wchar_tsize; // C `char` is always unsigned in ImportC + + // We know that we are parsing out C, due the parent not knowing this, we have to setup tables here. + charLookup = compileEnv.cCharLookupTable; } /******************************************** diff --git a/dmd/lexer.d b/dmd/lexer.d index b8a4223f884..26a56c2b1fd 100644 --- a/dmd/lexer.d +++ b/dmd/lexer.d @@ -57,8 +57,8 @@ struct CompileEnv bool masm; /// use MASM inline asm syntax // these need a default otherwise tests won't work. - IdentifierCharLookup cCharLookupTable; - IdentifierCharLookup dCharLookupTable; + IdentifierCharLookup cCharLookupTable; /// C identifier table (set to the lexer by the C parser) + IdentifierCharLookup dCharLookupTable; /// D identifier table } /*********************************************************** @@ -74,7 +74,7 @@ class Lexer Token token; - IdentifierCharLookup charLookup; + IdentifierCharLookup charLookup; /// Character table for identifiers // For ImportC bool Ccompile; /// true if compiling ImportC @@ -189,14 +189,9 @@ class Lexer } // setup the identifier table lookup functions - if (this.Ccompile) - { - charLookup = this.compileEnv.cCharLookupTable; - } - else - { - charLookup = this.compileEnv.dCharLookupTable; - } + // C tables are setup in its parser constructor + // Due to us not knowing if we're in C at this point in time. + charLookup = this.compileEnv.dCharLookupTable; } /*********************** From c7f201966c8b124de4bbf2bc9b6faf93932a6510 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Thu, 25 Apr 2024 17:57:35 -0700 Subject: [PATCH 078/125] add const to toHash() overrides (dlang/dmd!16414) --- runtime/druntime/src/core/internal/hash.d | 2 +- runtime/druntime/src/object.d | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/druntime/src/core/internal/hash.d b/runtime/druntime/src/core/internal/hash.d index ef9f1e525c0..80fdc320032 100644 --- a/runtime/druntime/src/core/internal/hash.d +++ b/runtime/druntime/src/core/internal/hash.d @@ -36,7 +36,7 @@ private enum hasCallableToHash(T) = __traits(compiles, @nogc nothrow pure @safe unittest { - static struct S { size_t toHash() { return 4; } } + static struct S { size_t toHash() const { return 4; } } assert(hasCallableToHash!S); assert(!hasCallableToHash!(shared const S)); } diff --git a/runtime/druntime/src/object.d b/runtime/druntime/src/object.d index 4264ecf6fd6..9bc929ce8ef 100644 --- a/runtime/druntime/src/object.d +++ b/runtime/druntime/src/object.d @@ -3614,7 +3614,7 @@ if (is(typeof(create()) : V) && (is(typeof(update(aa[K.init])) : V) || is(typeof @safe const: // stubs bool opEquals(S rhs) { assert(0); } - size_t toHash() { assert(0); } + size_t toHash() const { assert(0); } } int[string] aai; From b922779ff0d2da03fa09621218802991a063162d Mon Sep 17 00:00:00 2001 From: chloekek <50083900+chloekek@users.noreply.github.com> Date: Fri, 26 Apr 2024 09:57:35 +0200 Subject: [PATCH 079/125] Mark Thread.sleep as @trusted (dlang/dmd!16411) I am fairly convinced that no possible arguments to or global state at the time of any call to Thread.sleep can result in memory corruption. There is a precondition on Thread.sleep, that the duration must be non-negative. On Windows, Thread.sleep calls Sleep, which takes an unsigned integer. On POSIX, Thread.sleep calls nanosleep, which is specified to handle negative durations gracefully. As such, violating this precondition should not be a source of undefined behavior. --- runtime/druntime/src/core/thread/osthread.d | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/runtime/druntime/src/core/thread/osthread.d b/runtime/druntime/src/core/thread/osthread.d index 0bf14db0a86..d7f50b994ed 100644 --- a/runtime/druntime/src/core/thread/osthread.d +++ b/runtime/druntime/src/core/thread/osthread.d @@ -911,7 +911,7 @@ class Thread : ThreadBase * * ------------------------------------------------------------------------ */ - static void sleep( Duration val ) @nogc nothrow + static void sleep( Duration val ) @nogc nothrow @trusted in { assert( !val.isNegative ); @@ -1176,6 +1176,12 @@ unittest thread_resumeAll(); } +@nogc @safe nothrow +unittest +{ + Thread.sleep(1.msecs); +} + /////////////////////////////////////////////////////////////////////////////// // GC Support Routines /////////////////////////////////////////////////////////////////////////////// From d5abe98f2d84c1b07b418d674e9cea72e3c389fa Mon Sep 17 00:00:00 2001 From: Dennis Date: Fri, 26 Apr 2024 11:55:50 +0200 Subject: [PATCH 080/125] escape.d: use callbacks for escapeByXXX instead of arrays (dlang/dmd!16412) * escape.d: use callbacks for escapeByXXX instead of arrays * Fix null access --- dmd/escape.d | 330 ++++++++++++++++++++++----------------------------- dmd/ob.d | 79 ++++-------- 2 files changed, 170 insertions(+), 239 deletions(-) diff --git a/dmd/escape.d b/dmd/escape.d index 102b015af53..5a1312bd435 100644 --- a/dmd/escape.d +++ b/dmd/escape.d @@ -93,7 +93,8 @@ bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf, struct EscapeBy { - EscapeByResults er; + VarDeclarations byref; + VarDeclarations byvalue; Parameter param; // null if no Parameter for this argument bool isMutable; // true if reference to mutable } @@ -151,14 +152,21 @@ bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf, refs = true; auto var = outerVars[i - (len - outerVars.length)]; eb.isMutable = var.type.isMutable(); - eb.er.pushRef(var, false); + eb.byref.push(var); continue; } + void onRef(VarDeclaration v, bool transition) { eb.byref.push(v); } + void onValue(VarDeclaration v) { eb.byvalue.push(v); } + void onFunc(FuncDeclaration fd) {} + void onExp(Expression e, bool transition) {} + + scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp); + if (refs) - escapeByRef(arg, &eb.er); + escapeByRef(arg, &er); else - escapeByValue(arg, &eb.er); + escapeByValue(arg, &er); } void checkOnePair(size_t i, ref EscapeBy eb, ref EscapeBy eb2, @@ -194,7 +202,7 @@ bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf, void escape(size_t i, ref EscapeBy eb, bool byval) { - foreach (VarDeclaration v; byval ? eb.er.byvalue : eb.er.byref) + foreach (VarDeclaration v; byval ? eb.byvalue : eb.byref) { if (log) { @@ -205,7 +213,7 @@ bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf, continue; foreach (ref eb2; escapeBy[i + 1 .. $]) { - foreach (VarDeclaration v2; byval ? eb2.er.byvalue : eb2.er.byref) + foreach (VarDeclaration v2; byval ? eb2.byvalue : eb2.byref) { checkOnePair(i, eb, eb2, v, v2, byval); } @@ -336,22 +344,6 @@ bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Identifier parId, if (!arg.type.hasPointers()) return false; - EscapeByResults er; - - escapeByValue(arg, &er); - - if (parStc & STC.scope_) - { - // These errors only apply to non-scope parameters - // When the parameter is `scope`, only `checkScopeVarAddr` on `er.byref` is needed - er.byfunc.setDim(0); - er.byvalue.setDim(0); - er.byexp.setDim(0); - } - - if (!er.byref.length && !er.byvalue.length && !er.byfunc.length && !er.byexp.length) - return false; - bool result = false; /* 'v' is assigned unsafely to 'par' @@ -383,11 +375,11 @@ bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Identifier parId, } } - foreach (VarDeclaration v; er.byvalue) + void onValue(VarDeclaration v) { if (log) printf("byvalue %s\n", v.toChars()); - if (v.isDataseg()) - continue; + if (parStc & STC.scope_ || v.isDataseg()) + return; Dsymbol p = v.toParent2(); @@ -403,11 +395,11 @@ bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Identifier parId, } } - foreach (VarDeclaration v; er.byref) + void onRef(VarDeclaration v, bool retRefTransition) { if (log) printf("byref %s\n", v.toChars()); if (v.isDataseg()) - continue; + return; Dsymbol p = v.toParent2(); @@ -415,19 +407,21 @@ bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Identifier parId, if (checkScopeVarAddr(v, arg, sc, gag)) { result = true; - continue; + return; } if (p == sc.func && !(parStc & STC.scope_)) { unsafeAssign!"reference to local variable"(v); - continue; + return; } } - foreach (FuncDeclaration fd; er.byfunc) + void onFunc(FuncDeclaration fd) { //printf("fd = %s, %d\n", fd.toChars(), fd.tookAddressOf); + if (parStc & STC.scope_) + return; VarDeclarations vars; findAllOuterAccessedVariables(fd, &vars); @@ -443,16 +437,15 @@ bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Identifier parId, if ((v.isReference() || v.isScope()) && p == sc.func) { unsafeAssign!"reference to local"(v); - continue; + return; } } } - if (!sc.func) - return result; - - foreach (Expression ee; er.byexp) + void onExp(Expression ee, bool retRefTransition) { + if (parStc & STC.scope_) + return; const(char)* msg = parId ? "reference to stack allocated value returned by `%s` assigned to non-scope parameter `%s`" : "reference to stack allocated value returned by `%s` assigned to non-scope anonymous parameter"; @@ -460,6 +453,8 @@ bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Identifier parId, result |= sc.setUnsafeDIP1000(gag, ee.loc, msg, ee, parId); } + scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp); + escapeByValue(arg, &er); return result; } @@ -647,16 +642,6 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) return false; VarDeclaration va = expToVariable(e1); - EscapeByResults er; - - if (byRef) - escapeByRef(e2, &er); - else - escapeByValue(e2, &er); - - if (!er.byref.length && !er.byvalue.length && !er.byfunc.length && !er.byexp.length) - return false; - if (va && e.op == EXP.concatenateElemAssign) { @@ -686,7 +671,6 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) FuncDeclaration fd = sc.func; - // Determine if va is a `ref` parameter, so it has a lifetime exceding the function scope const bool vaIsRef = va && va.isParameter() && va.isReference(); if (log && vaIsRef) printf("va is ref `%s`\n", va.toChars()); @@ -702,7 +686,10 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) vaIsFirstRef = va == fd.vthis; break; case ReturnParamDest.firstArg: - vaIsFirstRef = (*fd.parameters)[0] == va; + // While you'd expect fd.parameters[0] to exist in this case, the compiler-generated + // expression that initializes an `out int* p = null` is analyzed before fd.parameters + // is created, so we still do a null and length check + vaIsFirstRef = fd.parameters && 0 < fd.parameters.length && (*fd.parameters)[0] == va; break; case ReturnParamDest.returnVal: break; @@ -711,14 +698,14 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) if (log && vaIsFirstRef) printf("va is first ref `%s`\n", va.toChars()); bool result = false; - foreach (VarDeclaration v; er.byvalue) + void onValue(VarDeclaration v) { if (log) printf("byvalue: %s\n", v.toChars()); if (v.isDataseg()) - continue; + return; if (v == va) - continue; + return; Dsymbol p = v.toParent2(); @@ -730,7 +717,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) /* Add v to va's list of dependencies */ va.addMaybe(v); - continue; + return; } if (vaIsFirstRef && p == fd) @@ -747,7 +734,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) { // va=v, where v is `return scope` if (inferScope(va)) - continue; + return; } // If va's lifetime encloses v's, then error @@ -762,7 +749,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) break; case EnclosedBy.longerScope: if (v.storage_class & STC.temp) - continue; + return; msg = "scope variable `%s` assigned to `%s` with longer lifetime"; break; case EnclosedBy.refVar: @@ -776,7 +763,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) if (sc.setUnsafeDIP1000(gag, ae.loc, msg, v, va)) { result = true; - continue; + return; } } @@ -794,14 +781,14 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) if (isRefReturnScope(va.storage_class)) va.storage_class |= STC.returnScope; } - continue; + return; } result |= sc.setUnsafeDIP1000(gag, ae.loc, "scope variable `%s` assigned to non-scope `%s`", v, e1); } else if (v.isTypesafeVariadicArray && p == fd) { if (inferScope(va)) - continue; + return; result |= sc.setUnsafeDIP1000(gag, ae.loc, "variadic variable `%s` assigned to non-scope `%s`", v, e1); } else @@ -814,16 +801,16 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) } } - foreach (VarDeclaration v; er.byref) + void onRef(VarDeclaration v, bool retRefTransition) { if (log) printf("byref: %s\n", v.toChars()); if (v.isDataseg()) - continue; + return; if (checkScopeVarAddr(v, ae, sc, gag)) { result = true; - continue; + return; } if (va && va.isScope() && !v.isReference()) @@ -853,7 +840,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) if (sc.setUnsafeDIP1000(gag, ae.loc, "address of variable `%s` assigned to `%s` with longer lifetime", v, va)) { result = true; - continue; + return; } } @@ -861,21 +848,21 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) notMaybeScope(v, e); if (p != sc.func) - continue; + return; if (inferScope(va)) { if (v.isReturn() && !va.isReturn()) va.storage_class |= STC.return_ | STC.returninferred; - continue; + return; } if (e1.op == EXP.structLiteral) - continue; + return; result |= sc.setUnsafeDIP1000(gag, ae.loc, "reference to local variable `%s` assigned to non-scope `%s`", v, e1); } - foreach (FuncDeclaration func; er.byfunc) + void onFunc(FuncDeclaration func) { if (log) printf("byfunc: %s, %d\n", func.toChars(), func.tookAddressOf); VarDeclarations vars; @@ -900,7 +887,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) notMaybeScope(v, e); if (!(v.isReference() || v.isScope()) || p != fd) - continue; + return; if (va && !va.isDataseg() && (va.isScope() || va.maybeScope)) { @@ -909,14 +896,14 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) */ //if (!va.isScope()) //va.storage_class |= STC.scope_ | STC.scopeinferred; - continue; + return; } result |= sc.setUnsafeDIP1000(gag, ae.loc, "reference to local `%s` assigned to non-scope `%s` in @safe code", v, e1); } } - foreach (Expression ee; er.byexp) + void onExp(Expression ee, bool retRefTransition) { if (log) printf("byexp: %s\n", ee.toChars()); @@ -929,7 +916,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) sc.eSink.deprecation(ee.loc, "slice of static array temporary returned by `%s` assigned to longer lived variable `%s`", ee.toChars(), e1.toChars()); //result = true; - continue; + return; } if (ee.op == EXP.call && ee.type.toBasetype().isTypeStruct() && @@ -938,7 +925,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) if (sc.setUnsafeDIP1000(gag, ee.loc, "address of struct temporary returned by `%s` assigned to longer lived variable `%s`", ee, e1)) { result = true; - continue; + return; } } @@ -948,17 +935,24 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) if (sc.setUnsafeDIP1000(gag, ee.loc, "address of struct literal `%s` assigned to longer lived variable `%s`", ee, e1)) { result = true; - continue; + return; } } if (inferScope(va)) - continue; + return; result |= sc.setUnsafeDIP1000(gag, ee.loc, "reference to stack allocated value returned by `%s` assigned to non-scope `%s`", ee, e1); } + scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp); + + if (byRef) + escapeByRef(e2, &er); + else + escapeByValue(e2, &er); + return result; } @@ -977,32 +971,32 @@ public bool checkThrowEscape(Scope* sc, Expression e, bool gag) { //printf("[%s] checkThrowEscape, e = %s\n", e.loc.toChars(), e.toChars()); - EscapeByResults er; - - escapeByValue(e, &er); - - if (!er.byref.length && !er.byvalue.length && !er.byexp.length) - return false; bool result = false; - foreach (VarDeclaration v; er.byvalue) + void onRef(VarDeclaration v, bool retRefTransition) {} + void onValue(VarDeclaration v) { //printf("byvalue %s\n", v.toChars()); if (v.isDataseg()) - continue; + return; if (v.isScope() && !v.iscatchvar) // special case: allow catch var to be rethrown // despite being `scope` { // https://issues.dlang.org/show_bug.cgi?id=17029 result |= sc.setUnsafeDIP1000(gag, e.loc, "scope variable `%s` may not be thrown", v); - continue; + return; } else { notMaybeScope(v, new ThrowExp(e.loc, e)); } } + void onFunc(FuncDeclaration fd) {} + void onExp(Expression exp, bool retRefTransition) {} + + scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp); + escapeByValue(e, &er); return result; } @@ -1026,19 +1020,13 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag) //printf("[%s] checkNewEscape, e = %s\n", e.loc.toChars(), e.toChars()); enum log = false; if (log) printf("[%s] checkNewEscape, e: `%s`\n", e.loc.toChars(), e.toChars()); - EscapeByResults er; - - escapeByValue(e, &er); - - if (!er.byref.length && !er.byvalue.length && !er.byexp.length) - return false; bool result = false; - foreach (VarDeclaration v; er.byvalue) + void onValue(VarDeclaration v) { if (log) printf("byvalue `%s`\n", v.toChars()); if (v.isDataseg()) - continue; + return; Dsymbol p = v.toParent2(); @@ -1058,7 +1046,7 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag) { // https://issues.dlang.org/show_bug.cgi?id=20868 result |= sc.setUnsafeDIP1000(gag, e.loc, "scope variable `%s` may not be copied into allocated memory", v); - continue; + return; } } else if (v.isTypesafeVariadicArray && p == sc.func) @@ -1073,7 +1061,7 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag) } } - foreach (VarDeclaration v; er.byref) + void onRef(VarDeclaration v, bool retRefTransition) { if (log) printf("byref `%s`\n", v.toChars()); @@ -1088,7 +1076,7 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag) } if (v.isDataseg()) - continue; + return; Dsymbol p = v.toParent2(); @@ -1097,7 +1085,7 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag) if (p == sc.func) { result |= escapingRef(v, sc.useDIP1000); - continue; + return; } } @@ -1105,7 +1093,7 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag) * Infer the addition of 'return', or set result to be the offending expression. */ if (!v.isReference()) - continue; + return; // https://dlang.org/spec/function.html#return-ref-parameters if (p == sc.func) @@ -1113,16 +1101,16 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag) //printf("escaping reference to local ref variable %s\n", v.toChars()); //printf("storage class = x%llx\n", v.storage_class); result |= escapingRef(v, sc.useDIP25); - continue; + return; } // Don't need to be concerned if v's parent does not return a ref FuncDeclaration func = p.isFuncDeclaration(); if (!func || !func.type) - continue; + return; if (auto tf = func.type.isTypeFunction()) { if (!tf.isref) - continue; + return; const(char)* msg = "storing reference to outer local variable `%s` into allocated memory causes it to escape"; if (!gag) @@ -1136,7 +1124,9 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag) } } - foreach (Expression ee; er.byexp) + void onFunc(FuncDeclaration fd) {} + + void onExp(Expression ee, bool retRefTransition) { if (log) printf("byexp %s\n", ee.toChars()); if (!gag) @@ -1145,6 +1135,9 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag) result = true; } + scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp); + escapeByValue(e, &er); + return result; } @@ -1205,22 +1198,13 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) { enum log = false; if (log) printf("[%s] checkReturnEscapeImpl, refs: %d e: `%s`\n", e.loc.toChars(), refs, e.toChars()); - EscapeByResults er; - - if (refs) - escapeByRef(e, &er); - else - escapeByValue(e, &er); - - if (!er.byref.length && !er.byvalue.length && !er.byexp.length) - return false; bool result = false; - foreach (VarDeclaration v; er.byvalue) + void onValue(VarDeclaration v) { if (log) printf("byvalue `%s`\n", v.toChars()); if (v.isDataseg()) - continue; + return; const vsr = buildScopeRef(v.storage_class); @@ -1228,7 +1212,7 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) if (p == sc.func && inferReturn(sc.func, v, /*returnScope:*/ true)) { - continue; + return; } if (v.isScope()) @@ -1238,7 +1222,7 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) if (vsr == ScopeRef.ReturnScope || vsr == ScopeRef.Ref_ReturnScope) { - continue; + return; } auto pfunc = p.isFuncDeclaration(); @@ -1270,14 +1254,14 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) "scope parameter `%s` may not be returned", v.toChars() ); result = true; - continue; + return; } } else { // https://issues.dlang.org/show_bug.cgi?id=17029 result |= sc.setUnsafeDIP1000(gag, e.loc, "scope variable `%s` may not be returned", v); - continue; + return; } } } @@ -1294,7 +1278,7 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) } } - foreach (i, VarDeclaration v; er.byref[]) + void onRef(VarDeclaration v, bool retRefTransition) { if (log) { @@ -1330,7 +1314,7 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) } else { - if (er.refRetRefTransition[i]) + if (retRefTransition) { result |= sc.setUnsafeDIP1000(gag, e.loc, msg, e, v); } @@ -1344,7 +1328,7 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) } if (v.isDataseg()) - continue; + return; const vsr = buildScopeRef(v.storage_class); @@ -1359,7 +1343,7 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) if (checkScopeVarAddr(v, e, sc, gag)) { result = true; - continue; + return; } } @@ -1368,7 +1352,7 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) if (p == sc.func) { escapingRef(v, FeatureState.enabled); - continue; + return; } FuncDeclaration fd = p.isFuncDeclaration(); if (fd && sc.func.returnInprocess) @@ -1395,7 +1379,7 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) if (p == sc.func && (vsr == ScopeRef.Ref || vsr == ScopeRef.RefScope) && inferReturn(sc.func, v, /*returnScope:*/ false)) { - continue; + return; } else { @@ -1406,7 +1390,7 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) //printf("escaping reference to local ref variable %s\n", v.toChars()); //printf("storage class = x%llx\n", v.storage_class); escapingRef(v, sc.useDIP25); - continue; + return; } // Don't need to be concerned if v's parent does not return a ref FuncDeclaration fd = p.isFuncDeclaration(); @@ -1419,7 +1403,7 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) if (!gag) previewErrorFunc(sc.isDeprecated(), sc.useDIP25)(e.loc, msg, v.toChars()); result = true; - continue; + return; } } @@ -1427,10 +1411,12 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) } } - foreach (i, Expression ee; er.byexp[]) + void onFunc(FuncDeclaration fd) {} + + void onExp(Expression ee, bool retRefTransition) { if (log) printf("byexp %s\n", ee.toChars()); - if (er.expRetRefTransition[i]) + if (retRefTransition) { result |= sc.setUnsafeDIP1000(gag, ee.loc, "escaping reference to stack allocated value returned by `%s`", ee); @@ -1442,6 +1428,15 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) result = true; } } + + + scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp); + + if (refs) + escapeByRef(e, &er); + else + escapeByValue(e, &er); + return result; } @@ -1568,7 +1563,7 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re { VarDeclaration v = e.var.isVarDeclaration(); if (v) - er.pushRef(v, retRefTransition); + er.byRef(v, retRefTransition); } void visitVar(VarExp e) @@ -1577,14 +1572,14 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re { if (v.type.hasPointers() || // not tracking non-pointers v.storage_class & STC.lazy_) // lazy variables are actually pointers - er.byvalue.push(v); + er.byValue(v); } } void visitThis(ThisExp e) { if (e.var) - er.byvalue.push(e.var); + er.byValue(e.var); } void visitPtr(PtrExp e) @@ -1609,13 +1604,13 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re escapeByValue(e.e1, er, live, retRefTransition); else escapeByRef(e.e1, er, live, retRefTransition); - er.byfunc.push(e.func); + er.byFunc(e.func); } void visitFunc(FuncExp e) { if (e.fd.tok == TOK.delegate_) - er.byfunc.push(e.fd); + er.byFunc(e.fd); } void visitTuple(TupleExp e) @@ -1688,7 +1683,7 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re return; if (v.isTypesafeVariadicArray) { - er.byvalue.push(v); + er.byValue(v); return; } } @@ -1866,7 +1861,7 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re if (fd && fd.isNested()) { if (tf.isreturn && tf.isScopeQual) - er.pushExp(e, false); + er.byExp(e, false); } } @@ -1887,7 +1882,7 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re if (fd && fd.isNested()) { if (tf.isreturn && tf.isScopeQual) - er.pushExp(e, false); + er.byExp(e, false); } } } @@ -1972,7 +1967,7 @@ void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool retR } } else - er.pushRef(v, retRefTransition); + er.byRef(v, retRefTransition); } } @@ -1981,7 +1976,7 @@ void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool retR if (e.var && e.var.toParent2().isFuncDeclaration().hasDualContext()) escapeByValue(e, er, live, retRefTransition); else if (e.var) - er.pushRef(e.var, retRefTransition); + er.byRef(e.var, retRefTransition); } void visitPtr(PtrExp e) @@ -1997,7 +1992,7 @@ void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool retR VarDeclaration v = ve.var.isVarDeclaration(); if (v && v.isTypesafeVariadicArray) { - er.pushRef(v, retRefTransition); + er.byRef(v, retRefTransition); return; } } @@ -2021,7 +2016,7 @@ void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool retR escapeByRef(ex, er, live, retRefTransition); } } - er.pushExp(e, retRefTransition); + er.byExp(e, retRefTransition); } void visitDotVar(DotVarExp e) @@ -2087,7 +2082,7 @@ void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool retR if (auto de = arg.isDelegateExp()) { if (de.func.isNested()) - er.pushExp(de, false); + er.byExp(de, false); } else escapeByValue(arg, er, live, retRefTransition); @@ -2104,7 +2099,7 @@ void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool retR // https://issues.dlang.org/show_bug.cgi?id=20149#c10 if (dve.var.isCtorDeclaration()) { - er.pushExp(e, false); + er.byExp(e, false); return; } @@ -2129,7 +2124,7 @@ void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool retR { if (fd.isNested() && tf.isreturn) { - er.pushExp(e, false); + er.byExp(e, false); } } } @@ -2147,12 +2142,12 @@ void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool retR if (fd && fd.isNested()) { if (tf.isreturn) - er.pushExp(e, false); + er.byExp(e, false); } } } else - er.pushExp(e, retRefTransition); + er.byExp(e, retRefTransition); } switch (e.op) @@ -2182,15 +2177,8 @@ void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool retR public struct EscapeByResults { - VarDeclarations byref; // array into which variables being returned by ref are inserted - VarDeclarations byvalue; // array into which variables with values containing pointers are inserted - private FuncDeclarations byfunc; // nested functions that are turned into delegates - private Expressions byexp; // array into which temporaries being returned by ref are inserted - - import dmd.root.array: Array; - - /** - * Whether the variable / expression went through a `return (ref) scope` function call + /* + * retRefTransition = Whether the variable / expression went through a `return (ref) scope` function call * * This is needed for the dip1000 by default transition, since the rules for * disambiguating `return scope ref` have changed. Therefore, functions in legacy code @@ -2203,45 +2191,15 @@ struct EscapeByResults * case the code could give false positives even without @safe or dip1000: * https://issues.dlang.org/show_bug.cgi?id=23657 */ - private Array!bool refRetRefTransition; - private Array!bool expRetRefTransition; - - /** Reset arrays so the storage can be used again - */ - void reset() - { - byref.setDim(0); - byvalue.setDim(0); - byfunc.setDim(0); - byexp.setDim(0); - refRetRefTransition.setDim(0); - expRetRefTransition.setDim(0); - } - - /** - * Escape variable `v` by reference - * Params: - * v = variable to escape - * retRefTransition = `v` is escaped through a `return (ref) scope` function call - */ - void pushRef(VarDeclaration v, bool retRefTransition) - { - byref.push(v); - refRetRefTransition.push(retRefTransition); - } - - /** - * Escape a reference to expression `e` - * Params: - * e = expression to escape - * retRefTransition = `e` is escaped through a `return (ref) scope` function call - */ - void pushExp(Expression e, bool retRefTransition) - { - byexp.push(e); - expRetRefTransition.push(retRefTransition); - } + /// called on variables being returned by ref / address + void delegate(VarDeclaration, bool retRefTransition) byRef; + /// called on variables with values containing pointers + void delegate(VarDeclaration) byValue; + /// called on nested functions that are turned into delegates + void delegate(FuncDeclaration) byFunc; + /// called when expression temporaries are being returned by ref / address + void delegate(Expression, bool retRefTransition) byExp; } /************************* diff --git a/dmd/ob.d b/dmd/ob.d index bd985cfefba..fef3a2ed6c3 100644 --- a/dmd/ob.d +++ b/dmd/ob.d @@ -1285,8 +1285,6 @@ void genKill(ref ObState obstate, ObNode* ob) pvs.state = PtrState.Owner; pvs.deps.zero(); - EscapeByResults er; - escapeByValue(e, &er, true); bool any = false; // if any variables are assigned to v void by(VarDeclaration r) @@ -1316,10 +1314,7 @@ void genKill(ref ObState obstate, ObNode* ob) } } - foreach (VarDeclaration v2; er.byvalue) - by(v2); - foreach (VarDeclaration v2; er.byref) - by(v2); + escapeLive(e, &by); /* Make v an Owner for initializations like: * scope v = malloc(); @@ -1454,8 +1449,6 @@ void genKill(ref ObState obstate, ObNode* ob) if (!t.isTypeDelegate() && dve.e1.isVarExp()) { //printf("dve: %s\n", dve.toChars()); - EscapeByResults er; - escapeByValue(dve.e1, &er, true); void byf(VarDeclaration v) { @@ -1481,10 +1474,7 @@ void genKill(ref ObState obstate, ObNode* ob) } } - foreach (VarDeclaration v2; er.byvalue) - byf(v2); - foreach (VarDeclaration v2; er.byref) - byf(v2); + escapeLive(dve.e1, &byf); } } @@ -1501,8 +1491,6 @@ void genKill(ref ObState obstate, ObNode* ob) Parameter p = tf.parameterList[i - j]; auto pt = p.type.toBasetype(); - EscapeByResults er; - escapeByValue(arg, &er, true); if (!(p.storageClass & STC.out_ && arg.isVarExp())) arg.accept(this); @@ -1536,18 +1524,12 @@ void genKill(ref ObState obstate, ObNode* ob) } } - foreach (VarDeclaration v2; er.byvalue) - by(v2); - foreach (VarDeclaration v2; er.byref) - by(v2); + escapeLive(arg, &by); } else // variadic args { arg.accept(this); - EscapeByResults er; - escapeByValue(arg, &er, true); - void byv(VarDeclaration v) { if (!isTrackableVar(v)) @@ -1569,10 +1551,7 @@ void genKill(ref ObState obstate, ObNode* ob) makeUndefined(vi, ob.gen); } - foreach (VarDeclaration v2; er.byvalue) - byv(v2); - foreach (VarDeclaration v2; er.byref) - byv(v2); + escapeLive(arg, &byv); } } @@ -2006,6 +1985,24 @@ void doDataFlowAnalysis(ref ObState obstate) } +/*************************************** + * Check for escaping variables using DIP1000's `escapeByValue`, with `live` set to `true` + * Params: + * e = expression to check + * onVar = gets called for each variable escaped through `e`, either by value or by ref + */ +void escapeLive(Expression e, scope void delegate(VarDeclaration) onVar) +{ + scope EscapeByResults er = EscapeByResults( + (VarDeclaration v, bool) => onVar(v), + onVar, + (FuncDeclaration f) {}, + (Expression e, bool) {}, + ); + + escapeByValue(e, &er, true); +} + /*************************************** * Check for Ownership/Borrowing errors. */ @@ -2037,8 +2034,6 @@ void checkObErrors(ref ObState obstate) } pvs.deps.zero(); - EscapeByResults er; - escapeByValue(e, &er, true); void by(VarDeclaration r) // `v` = `r` { @@ -2076,10 +2071,7 @@ void checkObErrors(ref ObState obstate) } } - foreach (VarDeclaration v2; er.byvalue) - by(v2); - foreach (VarDeclaration v2; er.byref) - by(v2); + escapeLive(e, &by); } else { @@ -2218,8 +2210,6 @@ void checkObErrors(ref ObState obstate) if (!(p.storageClass & STC.out_ && arg.isVarExp())) arg.accept(this); - EscapeByResults er; - escapeByValue(arg, &er, true); void by(VarDeclaration v) { @@ -2253,18 +2243,11 @@ void checkObErrors(ref ObState obstate) } } - foreach (VarDeclaration v2; er.byvalue) - by(v2); - foreach (VarDeclaration v2; er.byref) - by(v2); + escapeLive(arg, &by); } else // variadic args { arg.accept(this); - - EscapeByResults er; - escapeByValue(arg, &er, true); - void byv(VarDeclaration v) { if (!isTrackableVar(v)) @@ -2292,10 +2275,7 @@ void checkObErrors(ref ObState obstate) } } - foreach (VarDeclaration v2; er.byvalue) - byv(v2); - foreach (VarDeclaration v2; er.byref) - byv(v2); + escapeLive(arg, &byv); } } @@ -2585,9 +2565,6 @@ void checkObErrors(ref ObState obstate) if (ob.obtype == ObType.retexp) { - EscapeByResults er; - escapeByValue(ob.exp, &er, true); - void by(VarDeclaration r) // `r` is the rvalue { const ri = obstate.vars.find(r); @@ -2615,11 +2592,7 @@ void checkObErrors(ref ObState obstate) } } } - - foreach (VarDeclaration v2; er.byvalue) - by(v2); - foreach (VarDeclaration v2; er.byref) - by(v2); + escapeLive(ob.exp, &by); } if (ob.obtype == ObType.return_ || ob.obtype == ObType.retexp) From be6d313ff4583e1145370ad68ceeaa2210fac790 Mon Sep 17 00:00:00 2001 From: Dennis Date: Fri, 26 Apr 2024 13:15:04 +0200 Subject: [PATCH 081/125] escape.d: Refactor `EscapeByResults` more (dlang/dmd!16416) escape.d: Refactor `EscapeByResults` more Signed-off-by: Nicholas Wilson Merged-on-behalf-of: Dennis --- dmd/escape.d | 129 ++++++++++++++++++++++++++------------------------- dmd/ob.d | 3 +- 2 files changed, 67 insertions(+), 65 deletions(-) diff --git a/dmd/escape.d b/dmd/escape.d index 5a1312bd435..1b74e762e42 100644 --- a/dmd/escape.d +++ b/dmd/escape.d @@ -164,9 +164,9 @@ bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf, scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp); if (refs) - escapeByRef(arg, &er); + escapeByRef(arg, er); else - escapeByValue(arg, &er); + escapeByValue(arg, er); } void checkOnePair(size_t i, ref EscapeBy eb, ref EscapeBy eb2, @@ -454,7 +454,7 @@ bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Identifier parId, } scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp); - escapeByValue(arg, &er); + escapeByValue(arg, er); return result; } @@ -949,9 +949,9 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp); if (byRef) - escapeByRef(e2, &er); + escapeByRef(e2, er); else - escapeByValue(e2, &er); + escapeByValue(e2, er); return result; } @@ -996,7 +996,7 @@ bool checkThrowEscape(Scope* sc, Expression e, bool gag) void onExp(Expression exp, bool retRefTransition) {} scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp); - escapeByValue(e, &er); + escapeByValue(e, er); return result; } @@ -1136,7 +1136,7 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag) } scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp); - escapeByValue(e, &er); + escapeByValue(e, er); return result; } @@ -1433,9 +1433,9 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp); if (refs) - escapeByRef(e, &er); + escapeByRef(e, er); else - escapeByValue(e, &er); + escapeByValue(e, er); return result; } @@ -1537,11 +1537,10 @@ private bool inferReturn(FuncDeclaration fd, VarDeclaration v, bool returnScope) * Params: * e = expression to be returned by value * er = where to place collected data - * live = if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, etc., all return `p`. * retRefTransition = if `e` is returned through a `return (ref) scope` function call */ public -void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool retRefTransition = false) +void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransition = false) { //printf("[%s] escapeByValue, e: %s\n", e.loc.toChars(), e.toChars()); @@ -1556,7 +1555,7 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re * but it'll be placed in static data so no need to check it. */ if (e.e1.op != EXP.structLiteral) - escapeByRef(e.e1, er, live, retRefTransition); + escapeByRef(e.e1, er, retRefTransition); } void visitSymOff(SymOffExp e) @@ -1584,16 +1583,16 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re void visitPtr(PtrExp e) { - if (live && e.type.hasPointers()) - escapeByValue(e.e1, er, live, retRefTransition); + if (er.live && e.type.hasPointers()) + escapeByValue(e.e1, er, retRefTransition); } void visitDotVar(DotVarExp e) { auto t = e.e1.type.toBasetype(); - if (e.type.hasPointers() && (live || t.ty == Tstruct)) + if (e.type.hasPointers() && (er.live || t.ty == Tstruct)) { - escapeByValue(e.e1, er, live, retRefTransition); + escapeByValue(e.e1, er, retRefTransition); } } @@ -1601,9 +1600,9 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re { Type t = e.e1.type.toBasetype(); if (t.ty == Tclass || t.ty == Tpointer) - escapeByValue(e.e1, er, live, retRefTransition); + escapeByValue(e.e1, er, retRefTransition); else - escapeByRef(e.e1, er, live, retRefTransition); + escapeByRef(e.e1, er, retRefTransition); er.byFunc(e.func); } @@ -1624,11 +1623,11 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re if (tb.ty == Tsarray || tb.ty == Tarray) { if (e.basis) - escapeByValue(e.basis, er, live, retRefTransition); + escapeByValue(e.basis, er, retRefTransition); foreach (el; *e.elements) { if (el) - escapeByValue(el, er, live, retRefTransition); + escapeByValue(el, er, retRefTransition); } } } @@ -1640,7 +1639,7 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re foreach (ex; *e.elements) { if (ex) - escapeByValue(ex, er, live, retRefTransition); + escapeByValue(ex, er, retRefTransition); } } } @@ -1653,7 +1652,7 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re foreach (ex; *e.arguments) { if (ex) - escapeByValue(ex, er, live, retRefTransition); + escapeByValue(ex, er, retRefTransition); } } } @@ -1665,10 +1664,10 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re Type tb = e.type.toBasetype(); if (tb.ty == Tarray && e.e1.type.toBasetype().ty == Tsarray) { - escapeByRef(e.e1, er, live, retRefTransition); + escapeByRef(e.e1, er, retRefTransition); } else - escapeByValue(e.e1, er, live, retRefTransition); + escapeByValue(e.e1, er, retRefTransition); } void visitSlice(SliceExp e) @@ -1693,18 +1692,18 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re { Type tb = e.type.toBasetype(); if (tb.ty != Tsarray) - escapeByRef(e.e1, er, live, retRefTransition); + escapeByRef(e.e1, er, retRefTransition); } else - escapeByValue(e.e1, er, live, retRefTransition); + escapeByValue(e.e1, er, retRefTransition); } void visitIndex(IndexExp e) { if (e.e1.type.toBasetype().ty == Tsarray || - live && e.type.hasPointers()) + er.live && e.type.hasPointers()) { - escapeByValue(e.e1, er, live, retRefTransition); + escapeByValue(e.e1, er, retRefTransition); } } @@ -1713,30 +1712,30 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re Type tb = e.type.toBasetype(); if (tb.ty == Tpointer) { - escapeByValue(e.e1, er, live, retRefTransition); - escapeByValue(e.e2, er, live, retRefTransition); + escapeByValue(e.e1, er, retRefTransition); + escapeByValue(e.e2, er, retRefTransition); } } void visitBinAssign(BinAssignExp e) { - escapeByValue(e.e1, er, live, retRefTransition); + escapeByValue(e.e1, er, retRefTransition); } void visitAssign(AssignExp e) { - escapeByValue(e.e1, er, live, retRefTransition); + escapeByValue(e.e1, er, retRefTransition); } void visitComma(CommaExp e) { - escapeByValue(e.e2, er, live, retRefTransition); + escapeByValue(e.e2, er, retRefTransition); } void visitCond(CondExp e) { - escapeByValue(e.e1, er, live, retRefTransition); - escapeByValue(e.e2, er, live, retRefTransition); + escapeByValue(e.e1, er, retRefTransition); + escapeByValue(e.e2, er, retRefTransition); } void visitCall(CallExp e) @@ -1778,11 +1777,11 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re if (auto fd = dve.var.isFuncDeclaration()) if (fd.isCtorDeclaration() && tf.next.toBasetype().isTypeStruct()) { - escapeByValue(arg, er, live, retRefTransition); + escapeByValue(arg, er, retRefTransition); } } else - escapeByValue(arg, er, live, true); + escapeByValue(arg, er, true); } else if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope) { @@ -1793,10 +1792,10 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re * as: * p; */ - escapeByValue(arg, er, live, retRefTransition); + escapeByValue(arg, er, retRefTransition); } else - escapeByRef(arg, er, live, retRefTransition); + escapeByRef(arg, er, retRefTransition); } } } @@ -1839,7 +1838,7 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope) { if (!tf.isref || tf.isctor) - escapeByValue(dve.e1, er, live, retRefTransition); + escapeByValue(dve.e1, er, retRefTransition); } else if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope) { @@ -1850,10 +1849,10 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re * as: * this; */ - escapeByValue(dve.e1, er, live, retRefTransition); + escapeByValue(dve.e1, er, retRefTransition); } else - escapeByRef(dve.e1, er, live, psr == ScopeRef.ReturnRef_Scope); + escapeByRef(dve.e1, er, psr == ScopeRef.ReturnRef_Scope); } } @@ -1871,7 +1870,7 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re if (t1.isTypeDelegate()) { if (tf.isreturn) - escapeByValue(e.e1, er, live, retRefTransition); + escapeByValue(e.e1, er, retRefTransition); } /* If it's a nested function that is 'return scope' @@ -1936,11 +1935,10 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re * Params: * e = expression to be returned by 'ref' * er = where to place collected data - * live = if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, etc., all return `p`. * retRefTransition = if `e` is returned through a `return (ref) scope` function call */ private -void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool retRefTransition = false) +void escapeByRef(Expression e, ref scope EscapeByResults er, bool retRefTransition = false) { //printf("[%s] escapeByRef, e: %s, retRefTransition: %d\n", e.loc.toChars(), e.toChars(), retRefTransition); void visit(Expression e) @@ -1961,9 +1959,9 @@ void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool retR if (ExpInitializer ez = v._init.isExpInitializer()) { if (auto ce = ez.exp.isConstructExp()) - escapeByRef(ce.e2, er, live, retRefTransition); + escapeByRef(ce.e2, er, retRefTransition); else - escapeByRef(ez.exp, er, live, retRefTransition); + escapeByRef(ez.exp, er, retRefTransition); } } else @@ -1974,14 +1972,14 @@ void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool retR void visitThis(ThisExp e) { if (e.var && e.var.toParent2().isFuncDeclaration().hasDualContext()) - escapeByValue(e, er, live, retRefTransition); + escapeByValue(e, er, retRefTransition); else if (e.var) er.byRef(e.var, retRefTransition); } void visitPtr(PtrExp e) { - escapeByValue(e.e1, er, live, retRefTransition); + escapeByValue(e.e1, er, retRefTransition); } void visitIndex(IndexExp e) @@ -1998,11 +1996,11 @@ void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool retR } if (tb.ty == Tsarray) { - escapeByRef(e.e1, er, live, retRefTransition); + escapeByRef(e.e1, er, retRefTransition); } else if (tb.ty == Tarray) { - escapeByValue(e.e1, er, live, retRefTransition); + escapeByValue(e.e1, er, retRefTransition); } } @@ -2013,7 +2011,7 @@ void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool retR foreach (ex; *e.elements) { if (ex) - escapeByRef(ex, er, live, retRefTransition); + escapeByRef(ex, er, retRefTransition); } } er.byExp(e, retRefTransition); @@ -2023,30 +2021,30 @@ void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool retR { Type t1b = e.e1.type.toBasetype(); if (t1b.ty == Tclass) - escapeByValue(e.e1, er, live, retRefTransition); + escapeByValue(e.e1, er, retRefTransition); else - escapeByRef(e.e1, er, live, retRefTransition); + escapeByRef(e.e1, er, retRefTransition); } void visitBinAssign(BinAssignExp e) { - escapeByRef(e.e1, er, live, retRefTransition); + escapeByRef(e.e1, er, retRefTransition); } void visitAssign(AssignExp e) { - escapeByRef(e.e1, er, live, retRefTransition); + escapeByRef(e.e1, er, retRefTransition); } void visitComma(CommaExp e) { - escapeByRef(e.e2, er, live, retRefTransition); + escapeByRef(e.e2, er, retRefTransition); } void visitCond(CondExp e) { - escapeByRef(e.e1, er, live, retRefTransition); - escapeByRef(e.e2, er, live, retRefTransition); + escapeByRef(e.e1, er, retRefTransition); + escapeByRef(e.e2, er, retRefTransition); } void visitCall(CallExp e) @@ -2076,7 +2074,7 @@ void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool retR const stc = tf.parameterStorageClass(null, p); ScopeRef psr = buildScopeRef(stc); if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope) - escapeByRef(arg, er, live, retRefTransition); + escapeByRef(arg, er, retRefTransition); else if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope) { if (auto de = arg.isDelegateExp()) @@ -2085,7 +2083,7 @@ void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool retR er.byExp(de, false); } else - escapeByValue(arg, er, live, retRefTransition); + escapeByValue(arg, er, retRefTransition); } } } @@ -2115,9 +2113,9 @@ void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool retR const psr = buildScopeRef(stc); if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope) - escapeByRef(dve.e1, er, live, psr == ScopeRef.ReturnRef_Scope); + escapeByRef(dve.e1, er, psr == ScopeRef.ReturnRef_Scope); else if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope) - escapeByValue(dve.e1, er, live, retRefTransition); + escapeByValue(dve.e1, er, retRefTransition); // If it's also a nested function that is 'return ref' if (FuncDeclaration fd = dve.var.isFuncDeclaration()) @@ -2131,7 +2129,7 @@ void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool retR // If it's a delegate, check it too if (e.e1.op == EXP.variable && t1.ty == Tdelegate) { - escapeByValue(e.e1, er, live, retRefTransition); + escapeByValue(e.e1, er, retRefTransition); } /* If it's a nested function that is 'return ref' @@ -2200,6 +2198,9 @@ struct EscapeByResults void delegate(FuncDeclaration) byFunc; /// called when expression temporaries are being returned by ref / address void delegate(Expression, bool retRefTransition) byExp; + + /// if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, etc., all return `p`. + bool live = false; } /************************* diff --git a/dmd/ob.d b/dmd/ob.d index fef3a2ed6c3..e7b341444a6 100644 --- a/dmd/ob.d +++ b/dmd/ob.d @@ -1998,9 +1998,10 @@ void escapeLive(Expression e, scope void delegate(VarDeclaration) onVar) onVar, (FuncDeclaration f) {}, (Expression e, bool) {}, + true, ); - escapeByValue(e, &er, true); + escapeByValue(e, er, true); } /*************************************** From 5bb70ad83d1b22fb9b13d73f578c3b6a7e7f1b3a Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Fri, 26 Apr 2024 18:04:17 +0200 Subject: [PATCH 082/125] Mark some functions `@safe` or `@trusted` --- dmd/declaration.d | 4 ++-- dmd/dsymbol.d | 6 +++--- dmd/func.d | 2 +- dmd/init.d | 14 +++++++------- dmd/mtype.d | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/dmd/declaration.d b/dmd/declaration.d index 75e7e911a69..a1202ed628a 100644 --- a/dmd/declaration.d +++ b/dmd/declaration.d @@ -1384,7 +1384,7 @@ extern (C++) class VarDeclaration : Declaration /************************************* * Return true if we can take the address of this variable. */ - final bool canTakeAddressOf() + final bool canTakeAddressOf() @safe { return !(storage_class & STC.manifest); } @@ -1392,7 +1392,7 @@ extern (C++) class VarDeclaration : Declaration /****************************************** * Return true if variable needs to call the destructor. */ - final bool needsScopeDtor() + final bool needsScopeDtor() @safe { //printf("VarDeclaration::needsScopeDtor() %s %d\n", toChars(), edtor && !(storage_class & STC.nodtor)); return edtor && !(storage_class & STC.nodtor); diff --git a/dmd/dsymbol.d b/dmd/dsymbol.d index b831c32c28b..90b2d9fda6b 100644 --- a/dmd/dsymbol.d +++ b/dmd/dsymbol.d @@ -453,7 +453,7 @@ extern (C++) class Dsymbol : ASTNode * * See also `parent`, `toParent` and `toParent2`. */ - final inout(Dsymbol) pastMixin() inout + final inout(Dsymbol) pastMixin() inout @safe { //printf("Dsymbol::pastMixin() %s\n", toChars()); if (!isTemplateMixin() && !isForwardingAttribDeclaration() && !isForwardingScopeDsymbol()) @@ -503,13 +503,13 @@ extern (C++) class Dsymbol : ASTNode * // s.toParentLocal() == FuncDeclaration('mod.test') * --- */ - final inout(Dsymbol) toParent() inout + final inout(Dsymbol) toParent() inout @safe { return parent ? parent.pastMixin() : null; } /// ditto - final inout(Dsymbol) toParent2() inout + final inout(Dsymbol) toParent2() inout @safe { if (!parent || !parent.isTemplateInstance && !parent.isForwardingAttribDeclaration() && !parent.isForwardingScopeDsymbol()) return parent; diff --git a/dmd/func.d b/dmd/func.d index e39849e467a..f3d7aba26ba 100644 --- a/dmd/func.d +++ b/dmd/func.d @@ -1862,7 +1862,7 @@ extern (C++) class FuncDeclaration : Declaration return this; } - inout(FuncDeclaration) toAliasFunc() inout + inout(FuncDeclaration) toAliasFunc() inout @safe { return this; } diff --git a/dmd/init.d b/dmd/init.d index 62bd41eea93..7d9e3e6033d 100644 --- a/dmd/init.d +++ b/dmd/init.d @@ -51,38 +51,38 @@ extern (C++) class Initializer : ASTNode this.kind = kind; } - final inout(ErrorInitializer) isErrorInitializer() inout @nogc nothrow pure + final inout(ErrorInitializer) isErrorInitializer() inout @nogc nothrow pure @trusted { // Use void* cast to skip dynamic casting call return kind == InitKind.error ? cast(inout ErrorInitializer)cast(void*)this : null; } - final inout(VoidInitializer) isVoidInitializer() inout @nogc nothrow pure + final inout(VoidInitializer) isVoidInitializer() inout @nogc nothrow pure @trusted { return kind == InitKind.void_ ? cast(inout VoidInitializer)cast(void*)this : null; } - final inout(DefaultInitializer) isDefaultInitializer() inout @nogc nothrow pure + final inout(DefaultInitializer) isDefaultInitializer() inout @nogc nothrow pure @trusted { return kind == InitKind.default_ ? cast(inout DefaultInitializer)cast(void*)this : null; } - final inout(StructInitializer) isStructInitializer() inout @nogc nothrow pure + final inout(StructInitializer) isStructInitializer() inout @nogc nothrow pure @trusted { return kind == InitKind.struct_ ? cast(inout StructInitializer)cast(void*)this : null; } - final inout(ArrayInitializer) isArrayInitializer() inout @nogc nothrow pure + final inout(ArrayInitializer) isArrayInitializer() inout @nogc nothrow pure @trusted { return kind == InitKind.array ? cast(inout ArrayInitializer)cast(void*)this : null; } - final inout(ExpInitializer) isExpInitializer() inout @nogc nothrow pure + final inout(ExpInitializer) isExpInitializer() inout @nogc nothrow pure @trusted { return kind == InitKind.exp ? cast(inout ExpInitializer)cast(void*)this : null; } - final inout(CInitializer) isCInitializer() inout @nogc nothrow pure + final inout(CInitializer) isCInitializer() inout @nogc nothrow pure @trusted { return kind == InitKind.C_ ? cast(inout CInitializer)cast(void*)this : null; } diff --git a/dmd/mtype.d b/dmd/mtype.d index 75827d714d8..0f4f91d8473 100644 --- a/dmd/mtype.d +++ b/dmd/mtype.d @@ -1690,7 +1690,7 @@ extern (C++) abstract class TypeNext : Type * type is meant to be inferred, and semantic() hasn't yet ben run * on the function. After semantic(), it must no longer be NULL. */ - override final Type nextOf() + override final Type nextOf() @safe { return next; } @@ -3175,7 +3175,7 @@ extern (C++) final class TypeFunction : TypeNext * Returns: * true if D-style variadic */ - bool isDstyleVariadic() const pure nothrow + bool isDstyleVariadic() const pure nothrow @safe { return linkage == LINK.d && parameterList.varargs == VarArg.variadic; } From 9e4c76ba8f8661054de8d8dfe396d2b7d2123ed4 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Sat, 27 Apr 2024 13:25:20 +0100 Subject: [PATCH 083/125] Make `foreach_reverse` with a delegate an error (dlang/dmd!16418) * Make `foreach_reverse` with a delegate an error Deprecation goes back to at least 2016: https://github.com/dlang/dmd/commit/0c330021536025b5e5036c4dd8541f1d1608c80d#diff-13f50d9dea67df89a0535d6b8957f3145d49f71f6b72fdd3913f329a265e1423R1155 --- dmd/statementsem.d | 2 +- tests/dmd/fail_compilation/deprecate1553.d | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/dmd/statementsem.d b/dmd/statementsem.d index b79b47ff7f4..ab3a86efa00 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -1409,7 +1409,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) } case Tdelegate: if (fs.op == TOK.foreach_reverse_) - deprecation(fs.loc, "cannot use `foreach_reverse` with a delegate"); + error(fs.loc, "cannot use `foreach_reverse` with a delegate"); return retStmt(apply()); case Terror: return retError(); diff --git a/tests/dmd/fail_compilation/deprecate1553.d b/tests/dmd/fail_compilation/deprecate1553.d index 18a7152001a..867616ccac1 100644 --- a/tests/dmd/fail_compilation/deprecate1553.d +++ b/tests/dmd/fail_compilation/deprecate1553.d @@ -1,9 +1,7 @@ -// REQUIRED_ARGS: -de - /* TEST_OUTPUT: --- -fail_compilation/deprecate1553.d(18): Deprecation: cannot use `foreach_reverse` with a delegate +fail_compilation/deprecate1553.d(16): Error: cannot use `foreach_reverse` with a delegate --- */ From 3e4fab13b2f9ee22cec708b7a8e66f21f7c5cbdc Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Sat, 27 Apr 2024 12:49:02 +0200 Subject: [PATCH 084/125] Remove unneeded `@trusted` annotations --- dmd/common/outbuffer.d | 7 +++---- dmd/dtemplate.d | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/dmd/common/outbuffer.d b/dmd/common/outbuffer.d index cb2439ff746..f3801cb69ca 100644 --- a/dmd/common/outbuffer.d +++ b/dmd/common/outbuffer.d @@ -141,7 +141,7 @@ struct OutBuffer memory buffer. The config variables `notlinehead`, `doindent` etc. are not changed. */ - extern (C++) void destroy() pure nothrow @trusted + extern (C++) void destroy() pure nothrow { dtor(); fileMapping = null; @@ -247,7 +247,7 @@ struct OutBuffer /** * Writes a 16 bit value, no reserve check. */ - @trusted nothrow + nothrow void write16n(int v) { auto x = cast(ushort) v; @@ -367,7 +367,6 @@ struct OutBuffer } // Position buffer to accept the specified number of bytes at offset - @trusted void position(size_t where, size_t nbytes) nothrow { if (where + nbytes > data.length) @@ -382,7 +381,7 @@ struct OutBuffer /** * Writes an 8 bit byte, no reserve check. */ - extern (C++) @trusted nothrow + extern (C++) nothrow @safe void writeByten(int b) { this.data[offset++] = cast(ubyte) b; diff --git a/dmd/dtemplate.d b/dmd/dtemplate.d index d181facfc24..fb11821f42c 100644 --- a/dmd/dtemplate.d +++ b/dmd/dtemplate.d @@ -5767,7 +5767,7 @@ struct TemplateInstanceBox assert(this.ti.hash); } - size_t toHash() const @trusted pure nothrow + size_t toHash() const @safe pure nothrow { assert(ti.hash); return ti.hash; From 5afb5a20af41b1d3a5ead902d26d6f5262ff9359 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sat, 27 Apr 2024 20:31:51 -0700 Subject: [PATCH 085/125] escape.d: change Scope* to ref Scope --- dmd/dcast.d | 2 +- dmd/dsymbolsem.d | 2 +- dmd/escape.d | 34 +++++++++++++++++----------------- dmd/expressionsem.d | 34 +++++++++++++++++----------------- dmd/semantic3.d | 4 ++-- dmd/statementsem.d | 6 +++--- 6 files changed, 41 insertions(+), 41 deletions(-) diff --git a/dmd/dcast.d b/dmd/dcast.d index b1c9d01178d..78781f4ced7 100644 --- a/dmd/dcast.d +++ b/dmd/dcast.d @@ -2257,7 +2257,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) Type tb = t.toBasetype(); if (tb.ty == Tarray) { - if (checkArrayLiteralEscape(sc, ae, false)) + if (checkArrayLiteralEscape(*sc, ae, false)) { return ErrorExp.get(); } diff --git a/dmd/dsymbolsem.d b/dmd/dsymbolsem.d index f3ae3e0f105..65a1b043650 100644 --- a/dmd/dsymbolsem.d +++ b/dmd/dsymbolsem.d @@ -1258,7 +1258,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor { import dmd.escape : setUnsafeDIP1000; const inSafeFunc = sc.func && sc.func.isSafeBypassingInference(); // isSafeBypassingInference may call setUnsafe(). - if (sc.setUnsafeDIP1000(false, dsym.loc, "`scope` allocation of `%s` requires that constructor be annotated with `scope`", dsym)) + if (setUnsafeDIP1000(*sc, false, dsym.loc, "`scope` allocation of `%s` requires that constructor be annotated with `scope`", dsym)) errorSupplemental(ne.member.loc, "is the location of the constructor"); } ne.onstack = 1; diff --git a/dmd/escape.d b/dmd/escape.d index 1b74e762e42..3d0465117fc 100644 --- a/dmd/escape.d +++ b/dmd/escape.d @@ -74,7 +74,7 @@ package(dmd) struct EscapeState * `true` if error */ public -bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf, +bool checkMutableArguments(ref Scope sc, FuncDeclaration fd, TypeFunction tf, Expression ethis, Expressions* arguments, bool gag) { enum log = false; @@ -240,7 +240,7 @@ bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf, * `true` if any elements escaped */ public -bool checkArrayLiteralEscape(Scope *sc, ArrayLiteralExp ae, bool gag) +bool checkArrayLiteralEscape(ref Scope sc, ArrayLiteralExp ae, bool gag) { bool errors; if (ae.basis) @@ -264,7 +264,7 @@ bool checkArrayLiteralEscape(Scope *sc, ArrayLiteralExp ae, bool gag) * `true` if any elements escaped */ public -bool checkAssocArrayLiteralEscape(Scope *sc, AssocArrayLiteralExp ae, bool gag) +bool checkAssocArrayLiteralEscape(ref Scope sc, AssocArrayLiteralExp ae, bool gag) { bool errors; foreach (ex; *ae.keys) @@ -333,7 +333,7 @@ void printScopeFailure(E)(E printFunc, VarDeclaration v, int recursionLimit) * `true` if pointers to the stack can escape via assignment */ public -bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Identifier parId, VarDeclaration vPar, STC parStc, Expression arg, bool assertmsg, bool gag) +bool checkParamArgumentEscape(ref Scope sc, FuncDeclaration fdc, Identifier parId, VarDeclaration vPar, STC parStc, Expression arg, bool assertmsg, bool gag) { enum log = false; if (log) printf("checkParamArgumentEscape(arg: %s par: %s parSTC: %llx)\n", @@ -472,7 +472,7 @@ bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Identifier parId, * `true` if assignment to `firstArg` would cause an error */ public -bool checkParamArgumentReturn(Scope* sc, Expression firstArg, Expression arg, Parameter param, bool gag) +bool checkParamArgumentReturn(ref Scope sc, Expression firstArg, Expression arg, Parameter param, bool gag) { enum log = false; if (log) printf("checkParamArgumentReturn(firstArg: %s arg: %s)\n", @@ -508,7 +508,7 @@ bool checkParamArgumentReturn(Scope* sc, Expression firstArg, Expression arg, Pa * `true` if construction would cause an escaping reference error */ public -bool checkConstructorEscape(Scope* sc, CallExp ce, bool gag) +bool checkConstructorEscape(ref Scope sc, CallExp ce, bool gag) { enum log = false; if (log) printf("checkConstructorEscape(%s, %s)\n", ce.toChars(), ce.type.toChars()); @@ -605,7 +605,7 @@ ReturnParamDest returnParamDest(TypeFunction tf, Type tthis) * `true` if pointers to the stack can escape via assignment */ public -bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) +bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) { enum log = false; if (log) printf("checkAssignEscape(e: %s, byRef: %d)\n", e.toChars(), byRef); @@ -968,7 +968,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) * `true` if pointers to the stack can escape */ public -bool checkThrowEscape(Scope* sc, Expression e, bool gag) +bool checkThrowEscape(ref Scope sc, Expression e, bool gag) { //printf("[%s] checkThrowEscape, e = %s\n", e.loc.toChars(), e.toChars()); @@ -1012,7 +1012,7 @@ bool checkThrowEscape(Scope* sc, Expression e, bool gag) * `true` if pointers to the stack can escape */ public -bool checkNewEscape(Scope* sc, Expression e, bool gag) +bool checkNewEscape(ref Scope sc, Expression e, bool gag) { import dmd.globals: FeatureState; import dmd.errors: previewErrorFunc; @@ -1072,7 +1072,7 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag) const(char)* msg = v.isParameter() ? "copying `%s` into allocated memory escapes a reference to parameter `%s`" : "copying `%s` into allocated memory escapes a reference to local variable `%s`"; - return sc.setUnsafePreview(fs, gag, e.loc, msg, e, v); + return setUnsafePreview(&sc, fs, gag, e.loc, msg, e, v); } if (v.isDataseg()) @@ -1154,7 +1154,7 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag) * `true` if pointers to the stack can escape */ public -bool checkReturnEscape(Scope* sc, Expression e, bool gag) +bool checkReturnEscape(ref Scope sc, Expression e, bool gag) { //printf("[%s] checkReturnEscape, e: %s\n", e.loc.toChars(), e.toChars()); return checkReturnEscapeImpl(sc, e, false, gag); @@ -1172,7 +1172,7 @@ bool checkReturnEscape(Scope* sc, Expression e, bool gag) * `true` if references to the stack can escape */ public -bool checkReturnEscapeRef(Scope* sc, Expression e, bool gag) +bool checkReturnEscapeRef(ref Scope sc, Expression e, bool gag) { version (none) { @@ -1194,7 +1194,7 @@ bool checkReturnEscapeRef(Scope* sc, Expression e, bool gag) * Returns: * `true` if references to the stack can escape */ -private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) +private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool gag) { enum log = false; if (log) printf("[%s] checkReturnEscapeImpl, refs: %d e: `%s`\n", e.loc.toChars(), refs, e.toChars()); @@ -1295,7 +1295,7 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) if (v.isParameter() && v.isReference()) { - if (sc.setUnsafePreview(featureState, gag, e.loc, msg, e, v) || + if (setUnsafePreview(&sc, featureState, gag, e.loc, msg, e, v) || sc.func.isSafeBypassingInference()) { result = true; @@ -2550,10 +2550,10 @@ private void addMaybe(VarDeclaration va, VarDeclaration v) // `setUnsafePreview` partially evaluated for dip1000 public -bool setUnsafeDIP1000(Scope* sc, bool gag, Loc loc, const(char)* msg, +bool setUnsafeDIP1000(ref Scope sc, bool gag, Loc loc, const(char)* msg, RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) { - return setUnsafePreview(sc, sc.useDIP1000, gag, loc, msg, arg0, arg1, arg2); + return setUnsafePreview(&sc, sc.useDIP1000, gag, loc, msg, arg0, arg1, arg2); } /*************************************** @@ -2570,7 +2570,7 @@ bool setUnsafeDIP1000(Scope* sc, bool gag, Loc loc, const(char)* msg, * Returns: * true if taking the address of `v` is problematic because of the lack of transitive `scope` */ -private bool checkScopeVarAddr(VarDeclaration v, Expression e, Scope* sc, bool gag) +private bool checkScopeVarAddr(VarDeclaration v, Expression e, ref Scope sc, bool gag) { if (v.storage_class & STC.temp) return false; diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 4c106783210..f5f7297cb5a 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -3222,7 +3222,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, /* Argument value can be assigned to firstArg. * Check arg to see if it matters. */ - err |= checkParamArgumentReturn(sc, firstArg, arg, p, false); + err |= checkParamArgumentReturn(*sc, firstArg, arg, p, false); } // Allow 'lazy' to imply 'scope' - lazy parameters can be passed along // as lazy parameters to the next function, but that isn't escaping. @@ -3236,7 +3236,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, * Check arg to see if it matters. */ VarDeclaration vPar = fd ? (fd.parameters ? (*fd.parameters)[i] : null) : null; - err |= checkParamArgumentEscape(sc, fd, p.ident, vPar, cast(STC) pStc, arg, false, false); + err |= checkParamArgumentEscape(*sc, fd, p.ident, vPar, cast(STC) pStc, arg, false, false); } // Turning heap allocations into stack allocations is dangerous without dip1000, since `scope` inference @@ -3375,7 +3375,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, } err |= arg.checkValue(); err |= arg.checkSharedAccess(sc); - err |= checkParamArgumentEscape(sc, fd, Id.dotdotdot, null, cast(STC) tf.parameterList.stc, arg, false, false); + err |= checkParamArgumentEscape(*sc, fd, Id.dotdotdot, null, cast(STC) tf.parameterList.stc, arg, false, false); arg = arg.optimize(WANTvalue); } (*arguments)[i] = arg; @@ -3575,7 +3575,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, */ if (global.params.useDIP1021 && (tf.trust == TRUST.safe || tf.trust == TRUST.default_) || tf.islive) - err |= checkMutableArguments(sc, fd, tf, ethis, arguments, false); + err |= checkMutableArguments(*sc, fd, tf, ethis, arguments, false); // If D linkage and variadic, add _arguments[] as first argument if (tf.isDstyleVariadic()) @@ -4466,7 +4466,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor semanticTypeInfo(sc, e.type); - if (checkAssocArrayLiteralEscape(sc, e, false)) + if (checkAssocArrayLiteralEscape(*sc, e, false)) return setError(); result = e; @@ -5206,7 +5206,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ foreach (arg; *exp.arguments) { - if (arg && checkNewEscape(sc, arg, false)) + if (arg && checkNewEscape(*sc, arg, false)) return setError(); } } @@ -6317,7 +6317,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor tthis = ue.e1.type; if (!(exp.f.type.ty == Tfunction && (cast(TypeFunction)exp.f.type).isScopeQual)) { - if (checkParamArgumentEscape(sc, exp.f, Id.This, exp.f.vthis, STC.undefined_, ethis, false, false)) + if (checkParamArgumentEscape(*sc, exp.f, Id.This, exp.f.vthis, STC.undefined_, ethis, false, false)) return setError(); } } @@ -8047,7 +8047,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.msg = resolveProperties(sc, exp.msg); exp.msg = exp.msg.implicitCastTo(sc, Type.tchar.constOf().arrayOf()); exp.msg = exp.msg.optimize(WANTvalue); - checkParamArgumentEscape(sc, null, null, null, STC.undefined_, exp.msg, true, false); + checkParamArgumentEscape(*sc, null, null, null, STC.undefined_, exp.msg, true, false); } if (exp.msg && exp.msg.op == EXP.error) @@ -10055,7 +10055,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } semanticTypeInfo(sc, taa); - checkNewEscape(sc, exp.e2, false); + checkNewEscape(*sc, exp.e2, false); exp.type = taa.next; break; @@ -10751,7 +10751,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor dvx.e1 = e1x; auto cx = cast(CallExp)ce.copy(); cx.e1 = dvx; - if (checkConstructorEscape(sc, cx, false)) + if (checkConstructorEscape(*sc, cx, false)) return setError(); Expression e0; @@ -11512,9 +11512,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor * `checkAssignExp` expects only AssignExps. */ if (res == exp) // no `AA[k] = v` rewrite was performed - checkAssignEscape(sc, res, false, false); + checkAssignEscape(*sc, res, false, false); else - checkNewEscape(sc, assignElem, false); // assigning to AA puts it on heap + checkNewEscape(*sc, assignElem, false); // assigning to AA puts it on heap if (auto ae = res.isConstructExp()) { @@ -11862,7 +11862,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (tb2.checkPostblit(exp.e2.loc, sc)) return setError(); - if (checkNewEscape(sc, exp.e2, false)) + if (checkNewEscape(*sc, exp.e2, false)) return setError(); exp = new CatElemAssignExp(exp.loc, exp.type, exp.e1, exp.e2.castTo(sc, tb1next)); @@ -11939,9 +11939,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor auto assignElem = exp.e2; auto res = exp.reorderSettingAAElem(sc); if (res != exp) // `AA[k] = v` rewrite was performed - checkNewEscape(sc, assignElem, false); + checkNewEscape(*sc, assignElem, false); else if (exp.op == EXP.concatenateElemAssign || exp.op == EXP.concatenateDcharAssign) - checkAssignEscape(sc, res, false, false); + checkAssignEscape(*sc, res, false, false); result = res; @@ -12512,7 +12512,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.e2 = exp.e2.implicitCastTo(sc, tb1next); exp.type = tb1next.arrayOf(); L2elem: - if (checkNewEscape(sc, exp.e2, false)) + if (checkNewEscape(*sc, exp.e2, false)) return setError(); result = exp.optimize(WANTvalue); trySetCatExpLowering(result); @@ -12546,7 +12546,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.e1 = exp.e1.implicitCastTo(sc, tb2next); exp.type = tb2next.arrayOf(); L1elem: - if (checkNewEscape(sc, exp.e1, false)) + if (checkNewEscape(*sc, exp.e1, false)) return setError(); result = exp.optimize(WANTvalue); trySetCatExpLowering(result); diff --git a/dmd/semantic3.d b/dmd/semantic3.d index 8a35cd7b16e..d88face9c6c 100644 --- a/dmd/semantic3.d +++ b/dmd/semantic3.d @@ -919,7 +919,7 @@ private extern(C++) final class Semantic3Visitor : Visitor exp = exp.implicitCastTo(sc2, tret); exp = exp.toLvalue(sc2, "`ref` return"); - checkReturnEscapeRef(sc2, exp, false); + checkReturnEscapeRef(*sc2, exp, false); exp = exp.optimize(WANTvalue, /*keepLvalue*/ true); } else @@ -938,7 +938,7 @@ private extern(C++) final class Semantic3Visitor : Visitor exp = doCopyOrMove(sc2, exp, f.next); if (tret.hasPointers()) - checkReturnEscape(sc2, exp, false); + checkReturnEscape(*sc2, exp, false); } exp = checkGC(sc2, exp); diff --git a/dmd/statementsem.d b/dmd/statementsem.d index ab3a86efa00..ae68d6a67e7 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -2663,11 +2663,11 @@ Statement statementSemanticVisit(Statement s, Scope* sc) */ Scope* sc2 = sc.push(); sc2.eSink = global.errorSinkNull; - bool err = checkReturnEscapeRef(sc2, rs.exp, true); + bool err = checkReturnEscapeRef(*sc2, rs.exp, true); sc2.pop(); if (err) - turnOffRef(() { checkReturnEscapeRef(sc, rs.exp, false); }); + turnOffRef(() { checkReturnEscapeRef(*sc, rs.exp, false); }); else if (!rs.exp.type.constConv(tf.next)) turnOffRef( () => rs.loc.errorSupplemental("cannot implicitly convert `%s` of type `%s` to `%s`", @@ -3701,7 +3701,7 @@ public bool throwSemantic(const ref Loc loc, ref Expression exp, Scope* sc) exp.loc.deprecation("cannot throw object of qualified type `%s`", exp.type.toChars()); //return false; } - checkThrowEscape(sc, exp, false); + checkThrowEscape(*sc, exp, false); ClassDeclaration cd = exp.type.toBasetype().isClassHandle(); if (!cd || ((cd != ClassDeclaration.throwable) && !ClassDeclaration.throwable.isBaseOf(cd, null))) From 616980be5bbdd9fb2b341b5bf55d7d9e746f382c Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sat, 27 Apr 2024 20:43:39 -0700 Subject: [PATCH 086/125] builtins.d: use arrays instead of pointers --- dmd/builtin.d | 162 +++++++++++++++++++++++++------------------------- 1 file changed, 81 insertions(+), 81 deletions(-) diff --git a/dmd/builtin.d b/dmd/builtin.d index f54a9131538..be84fbe6e97 100644 --- a/dmd/builtin.d +++ b/dmd/builtin.d @@ -56,7 +56,7 @@ public extern (C++) Expression eval_builtin(const ref Loc loc, FuncDeclaration f static if (e == "unknown") case BUILTIN.unknown: assert(false); else - mixin("case BUILTIN."~e~": return eval_"~e~"(loc, fd, arguments);"); + mixin("case BUILTIN."~e~": return eval_"~e~"(loc, fd, (*arguments)[]);"); } default: assert(0); } @@ -168,18 +168,18 @@ BUILTIN determine_builtin(FuncDeclaration func) } } -Expression eval_unimp(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_unimp(Loc loc, FuncDeclaration fd, Expression[] arguments) { return null; } -Expression eval_ctfeWrite(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_ctfeWrite(Loc loc, FuncDeclaration fd, Expression[] arguments) { import core.stdc.stdio: fprintf, stderr; import dmd.expression: CTFEExp; import dmd.ctfeexpr: resolveSlice; - Expression e = (*arguments)[0]; + Expression e = arguments[0]; const se = resolveSlice(e).toStringExp(); assert(se); const slice = se.peekString(); @@ -187,192 +187,192 @@ Expression eval_ctfeWrite(Loc loc, FuncDeclaration fd, Expressions* arguments) return CTFEExp.voidexp; } -Expression eval_sin(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_sin(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.sin(arg0.toReal()), arg0.type); } -Expression eval_cos(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_cos(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.cos(arg0.toReal()), arg0.type); } -Expression eval_tan(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_tan(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.tan(arg0.toReal()), arg0.type); } -Expression eval_sqrt(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_sqrt(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.sqrt(arg0.toReal()), arg0.type); } -Expression eval_fabs(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_fabs(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.fabs(arg0.toReal()), arg0.type); } -Expression eval_ldexp(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_ldexp(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); - Expression arg1 = (*arguments)[1]; + Expression arg1 = arguments[1]; assert(arg1.op == EXP.int64); return new RealExp(loc, CTFloat.ldexp(arg0.toReal(), cast(int) arg1.toInteger()), arg0.type); } -Expression eval_log(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_log(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.log(arg0.toReal()), arg0.type); } -Expression eval_log2(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_log2(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.log2(arg0.toReal()), arg0.type); } -Expression eval_log10(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_log10(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.log10(arg0.toReal()), arg0.type); } -Expression eval_exp(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_exp(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.exp(arg0.toReal()), arg0.type); } -Expression eval_expm1(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_expm1(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.expm1(arg0.toReal()), arg0.type); } -Expression eval_exp2(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_exp2(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.exp2(arg0.toReal()), arg0.type); } -Expression eval_round(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_round(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.round(arg0.toReal()), arg0.type); } -Expression eval_floor(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_floor(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.floor(arg0.toReal()), arg0.type); } -Expression eval_ceil(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_ceil(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.ceil(arg0.toReal()), arg0.type); } -Expression eval_trunc(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_trunc(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.trunc(arg0.toReal()), arg0.type); } -Expression eval_copysign(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_copysign(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); - Expression arg1 = (*arguments)[1]; + Expression arg1 = arguments[1]; assert(arg1.op == EXP.float64); return new RealExp(loc, CTFloat.copysign(arg0.toReal(), arg1.toReal()), arg0.type); } -Expression eval_pow(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_pow(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); - Expression arg1 = (*arguments)[1]; + Expression arg1 = arguments[1]; assert(arg1.op == EXP.float64); return new RealExp(loc, CTFloat.pow(arg0.toReal(), arg1.toReal()), arg0.type); } -Expression eval_fmin(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_fmin(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); - Expression arg1 = (*arguments)[1]; + Expression arg1 = arguments[1]; assert(arg1.op == EXP.float64); return new RealExp(loc, CTFloat.fmin(arg0.toReal(), arg1.toReal()), arg0.type); } -Expression eval_fmax(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_fmax(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); - Expression arg1 = (*arguments)[1]; + Expression arg1 = arguments[1]; assert(arg1.op == EXP.float64); return new RealExp(loc, CTFloat.fmax(arg0.toReal(), arg1.toReal()), arg0.type); } -Expression eval_fma(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_fma(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); - Expression arg1 = (*arguments)[1]; + Expression arg1 = arguments[1]; assert(arg1.op == EXP.float64); - Expression arg2 = (*arguments)[2]; + Expression arg2 = arguments[2]; assert(arg2.op == EXP.float64); return new RealExp(loc, CTFloat.fma(arg0.toReal(), arg1.toReal(), arg2.toReal()), arg0.type); } -Expression eval_isnan(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_isnan(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return IntegerExp.createBool(CTFloat.isNaN(arg0.toReal())); } -Expression eval_isinfinity(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_isinfinity(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return IntegerExp.createBool(CTFloat.isInfinity(arg0.toReal())); } -Expression eval_isfinite(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_isfinite(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); const value = !CTFloat.isNaN(arg0.toReal()) && !CTFloat.isInfinity(arg0.toReal()); return IntegerExp.createBool(value); } -Expression eval_bsf(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_bsf(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.int64); uinteger_t n = arg0.toInteger(); if (n == 0) @@ -380,9 +380,9 @@ Expression eval_bsf(Loc loc, FuncDeclaration fd, Expressions* arguments) return new IntegerExp(loc, core.bitop.bsf(n), Type.tint32); } -Expression eval_bsr(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_bsr(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.int64); uinteger_t n = arg0.toInteger(); if (n == 0) @@ -390,9 +390,9 @@ Expression eval_bsr(Loc loc, FuncDeclaration fd, Expressions* arguments) return new IntegerExp(loc, core.bitop.bsr(n), Type.tint32); } -Expression eval_bswap(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_bswap(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.int64); uinteger_t n = arg0.toInteger(); TY ty = arg0.type.toBasetype().ty; @@ -402,19 +402,19 @@ Expression eval_bswap(Loc loc, FuncDeclaration fd, Expressions* arguments) return new IntegerExp(loc, core.bitop.bswap(cast(uint) n), arg0.type); } -Expression eval_popcnt(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_popcnt(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.int64); uinteger_t n = arg0.toInteger(); return new IntegerExp(loc, core.bitop.popcnt(n), Type.tint32); } -Expression eval_yl2x(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_yl2x(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); - Expression arg1 = (*arguments)[1]; + Expression arg1 = arguments[1]; assert(arg1.op == EXP.float64); const x = arg0.toReal(); const y = arg1.toReal(); @@ -423,11 +423,11 @@ Expression eval_yl2x(Loc loc, FuncDeclaration fd, Expressions* arguments) return new RealExp(loc, result, arg0.type); } -Expression eval_yl2xp1(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_yl2xp1(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); - Expression arg1 = (*arguments)[1]; + Expression arg1 = arguments[1]; assert(arg1.op == EXP.float64); const x = arg0.toReal(); const y = arg1.toReal(); @@ -436,33 +436,33 @@ Expression eval_yl2xp1(Loc loc, FuncDeclaration fd, Expressions* arguments) return new RealExp(loc, result, arg0.type); } -Expression eval_toPrecFloat(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_toPrecFloat(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; float f = cast(real)arg0.toReal(); return new RealExp(loc, real_t(f), Type.tfloat32); } -Expression eval_toPrecDouble(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_toPrecDouble(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; double d = cast(real)arg0.toReal(); return new RealExp(loc, real_t(d), Type.tfloat64); } -Expression eval_toPrecReal(Loc loc, FuncDeclaration fd, Expressions* arguments) +Expression eval_toPrecReal(Loc loc, FuncDeclaration fd, Expression[] arguments) { - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; return new RealExp(loc, arg0.toReal(), Type.tfloat80); } // These built-ins are reserved for GDC and LDC. -Expression eval_gcc(Loc, FuncDeclaration, Expressions*) +Expression eval_gcc(Loc, FuncDeclaration, Expression[]) { assert(0); } -Expression eval_llvm(Loc, FuncDeclaration, Expressions*) +Expression eval_llvm(Loc, FuncDeclaration, Expression[]) { assert(0); } From f239959fcac4dd39dbb79ffcbc355545fdd88e75 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Sun, 28 Apr 2024 15:44:48 +0100 Subject: [PATCH 087/125] =?UTF-8?q?Fix=20Bugzilla=2024434=20-=20Casting=20?= =?UTF-8?q?away=20const=20with=20cast()=20is=20not=20a=20@safe=20lv?= =?UTF-8?q?=E2=80=A6=20(dlang/dmd!16315)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix Bugzilla 24434 - Casting away const with cast() is not a @safe lvalue * Use `pointerTo` * Use cast to hide lvalue append error * Fix unsafe shared increment * Use setUnsafePreview * Add test for lvalue assign * Fix qualifier cast check * Workaround for safe append Fixes Bugzilla 23530 - casting immutable away allowed in safe. * Update header * Trigger tests --- dmd/expression.d | 1 + dmd/expressionsem.d | 14 +++++++++++++- dmd/frontend.h | 1 + runtime/druntime/src/core/sync/mutex.d | 2 +- tests/dmd/fail_compilation/cast_qual.d | 19 +++++++++++++++++++ 5 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 tests/dmd/fail_compilation/cast_qual.d diff --git a/dmd/expression.d b/dmd/expression.d index 479ad3ade5f..a26f3ab83b9 100644 --- a/dmd/expression.d +++ b/dmd/expression.d @@ -3824,6 +3824,7 @@ extern (C++) final class CastExp : UnaExp { Type to; // type to cast to ubyte mod = cast(ubyte)~0; // MODxxxxx + bool trusted; // assume cast is safe extern (D) this(const ref Loc loc, Expression e, Type t) @safe { diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index f5f7297cb5a..a73084fefdf 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -11865,7 +11865,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (checkNewEscape(*sc, exp.e2, false)) return setError(); - exp = new CatElemAssignExp(exp.loc, exp.type, exp.e1, exp.e2.castTo(sc, tb1next)); + auto ecast = exp.e2.castTo(sc, tb1next); + if (auto ce = ecast.isCastExp()) + ce.trusted = true; + + exp = new CatElemAssignExp(exp.loc, exp.type, exp.e1, ecast); exp.e2 = doCopyOrMove(sc, exp.e2); } else if (tb1.ty == Tarray && @@ -15735,7 +15739,15 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action return visit(_this); } if (_this.isLvalue()) + { + with (_this) + if (!trusted && !e1.type.pointerTo().implicitConvTo(to.pointerTo())) + sc.setUnsafePreview(FeatureState.default_, false, loc, + "cast from `%s` to `%s` cannot be used as an lvalue in @safe code", + e1.type, to); + return _this; + } return visit(_this); } diff --git a/dmd/frontend.h b/dmd/frontend.h index d1c1d6358b9..a208add26a6 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -2435,6 +2435,7 @@ class CastExp final : public UnaExp public: Type* to; uint8_t mod; + bool trusted; CastExp* syntaxCopy() override; bool isLvalue() override; void accept(Visitor* v) override; diff --git a/runtime/druntime/src/core/sync/mutex.d b/runtime/druntime/src/core/sync/mutex.d index e7380c46725..5f547cd04ef 100644 --- a/runtime/druntime/src/core/sync/mutex.d +++ b/runtime/druntime/src/core/sync/mutex.d @@ -317,7 +317,7 @@ unittest cargo = 42; } - void useResource() shared @safe nothrow @nogc + void useResource() shared @trusted nothrow @nogc { mtx.lock_nothrow(); (cast() cargo) += 1; diff --git a/tests/dmd/fail_compilation/cast_qual.d b/tests/dmd/fail_compilation/cast_qual.d new file mode 100644 index 00000000000..19932f81165 --- /dev/null +++ b/tests/dmd/fail_compilation/cast_qual.d @@ -0,0 +1,19 @@ +/* +REQUIRED_ARGS: -preview=dip1000 -de +TEST_OUTPUT: +--- +fail_compilation/cast_qual.d(15): Deprecation: cast from `const(int)` to `int` cannot be used as an lvalue in @safe code +fail_compilation/cast_qual.d(17): Deprecation: cast from `const(int)` to `int` cannot be used as an lvalue in @safe code +--- +*/ + +@safe: + +void main() { + const int i = 3; + int j = cast() i; // OK + int* p = &cast() i; // this should not compile in @safe code + *p = 4; // oops + cast() i = 5; // NG + auto q = &cast(const) j; // OK, int* to const int* +} From ee0db1480ad5fc60246256efd97dcdaacf6c668f Mon Sep 17 00:00:00 2001 From: Dennis Date: Mon, 29 Apr 2024 14:50:02 +0200 Subject: [PATCH 088/125] Fix 22977 - can escape scope pointer returned by nested function (dlang/dmd!14236) --- dmd/escape.d | 52 +++++++++++++++++------- dmd/ob.d | 2 +- tests/dmd/fail_compilation/test22977.d | 56 ++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 16 deletions(-) create mode 100644 tests/dmd/fail_compilation/test22977.d diff --git a/dmd/escape.d b/dmd/escape.d index 3d0465117fc..08bd6fd2fdd 100644 --- a/dmd/escape.d +++ b/dmd/escape.d @@ -158,7 +158,7 @@ bool checkMutableArguments(ref Scope sc, FuncDeclaration fd, TypeFunction tf, void onRef(VarDeclaration v, bool transition) { eb.byref.push(v); } void onValue(VarDeclaration v) { eb.byvalue.push(v); } - void onFunc(FuncDeclaration fd) {} + void onFunc(FuncDeclaration fd, bool called) {} void onExp(Expression e, bool transition) {} scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp); @@ -417,7 +417,7 @@ bool checkParamArgumentEscape(ref Scope sc, FuncDeclaration fdc, Identifier parI } } - void onFunc(FuncDeclaration fd) + void onFunc(FuncDeclaration fd, bool called) { //printf("fd = %s, %d\n", fd.toChars(), fd.tookAddressOf); if (parStc & STC.scope_) @@ -862,7 +862,7 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) result |= sc.setUnsafeDIP1000(gag, ae.loc, "reference to local variable `%s` assigned to non-scope `%s`", v, e1); } - void onFunc(FuncDeclaration func) + void onFunc(FuncDeclaration func, bool called) { if (log) printf("byfunc: %s, %d\n", func.toChars(), func.tookAddressOf); VarDeclarations vars; @@ -992,7 +992,7 @@ bool checkThrowEscape(ref Scope sc, Expression e, bool gag) notMaybeScope(v, new ThrowExp(e.loc, e)); } } - void onFunc(FuncDeclaration fd) {} + void onFunc(FuncDeclaration fd, bool called) {} void onExp(Expression exp, bool retRefTransition) {} scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp); @@ -1124,7 +1124,7 @@ bool checkNewEscape(ref Scope sc, Expression e, bool gag) } } - void onFunc(FuncDeclaration fd) {} + void onFunc(FuncDeclaration fd, bool called) {} void onExp(Expression ee, bool retRefTransition) { @@ -1236,15 +1236,20 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g * return s; // s is inferred as 'scope' but incorrectly tested in foo() * return null; } */ - !(!refs && p.parent == sc.func && pfunc.fes) && + !(!refs && p.parent == sc.func && pfunc.fes) + ) + { /* * auto p(scope string s) { * string scfunc() { return s; } * } */ - !(!refs && sc.func.isFuncDeclaration().getLevel(pfunc, sc.intypeof) > 0) - ) - { + if (sc.func.isFuncDeclaration().getLevel(pfunc, sc.intypeof) > 0 && + inferReturn(sc.func, sc.func.vthis, /*returnScope*/ !refs)) + { + return; + } + if (v.isParameter() && !v.isReturn()) { // https://issues.dlang.org/show_bug.cgi?id=23191 @@ -1411,7 +1416,11 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g } } - void onFunc(FuncDeclaration fd) {} + void onFunc(FuncDeclaration fd, bool called) + { + if (called && fd.isNested()) + result |= sc.setUnsafeDIP1000(gag, e.loc, "escaping local variable through nested function `%s`", fd); + } void onExp(Expression ee, bool retRefTransition) { @@ -1603,13 +1612,13 @@ void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransi escapeByValue(e.e1, er, retRefTransition); else escapeByRef(e.e1, er, retRefTransition); - er.byFunc(e.func); + er.byFunc(e.func, false); } void visitFunc(FuncExp e) { if (e.fd.tok == TOK.delegate_) - er.byFunc(e.fd); + er.byFunc(e.fd, false); } void visitTuple(TupleExp e) @@ -1860,7 +1869,12 @@ void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransi if (fd && fd.isNested()) { if (tf.isreturn && tf.isScopeQual) - er.byExp(e, false); + { + if (tf.isreturnscope) + er.byFunc(fd, true); + else + er.byExp(e, false); + } } } @@ -1881,7 +1895,12 @@ void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransi if (fd && fd.isNested()) { if (tf.isreturn && tf.isScopeQual) - er.byExp(e, false); + { + if (tf.isreturnscope) + er.byFunc(fd, true); + else + er.byExp(e, false); + } } } } @@ -2195,7 +2214,10 @@ struct EscapeByResults /// called on variables with values containing pointers void delegate(VarDeclaration) byValue; /// called on nested functions that are turned into delegates - void delegate(FuncDeclaration) byFunc; + /// When `called` is true, it means the delegate escapes variables + /// from the closure through a call to it, while `false` means the + /// delegate itself escapes. + void delegate(FuncDeclaration, bool called) byFunc; /// called when expression temporaries are being returned by ref / address void delegate(Expression, bool retRefTransition) byExp; diff --git a/dmd/ob.d b/dmd/ob.d index e7b341444a6..756caf870bc 100644 --- a/dmd/ob.d +++ b/dmd/ob.d @@ -1996,7 +1996,7 @@ void escapeLive(Expression e, scope void delegate(VarDeclaration) onVar) scope EscapeByResults er = EscapeByResults( (VarDeclaration v, bool) => onVar(v), onVar, - (FuncDeclaration f) {}, + (FuncDeclaration f, bool) {}, (Expression e, bool) {}, true, ); diff --git a/tests/dmd/fail_compilation/test22977.d b/tests/dmd/fail_compilation/test22977.d new file mode 100644 index 00000000000..87bb19cc711 --- /dev/null +++ b/tests/dmd/fail_compilation/test22977.d @@ -0,0 +1,56 @@ +/* +REQUIRED_ARGS: -preview=dip1000 +TEST_OUTPUT: +--- +fail_compilation/test22977.d(16): Error: escaping local variable through nested function `scfunc` +fail_compilation/test22977.d(22): Error: escaping reference to stack allocated value returned by `scfunc2()` +--- +*/ + +// Issue 22977 - [dip1000] can escape scope pointer returned by nested function +// https://issues.dlang.org/show_bug.cgi?id=22977 + +auto p0(scope string s) @safe +{ + string scfunc() { return s; } + return scfunc(); +} + +auto p1(scope string s) @safe +{ + ref string scfunc2() { return s; } + return scfunc2(); +} + +// Reduced from Mir +struct Tuple(T...) +{ + T expand; +} + +auto autoExpandAndForward(alias value)() +{ + return value.expand[0]; +} + +struct MapIterator +{ + int* p; + int* foo() scope + { + auto t = Tuple!(int*)(p); + return autoExpandAndForward!t; + } +} + +// Reduced from Phobos +float partial(alias fun)() +{ + return fun(); +} + +auto partialFunction() @safe +{ + int function() f = () => 0; + return &partial!(f); +} From 38a55d96d138eff7824e6b8db11f85b8f9880c5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Mon, 29 Apr 2024 19:04:07 +0200 Subject: [PATCH 089/125] Fix linker symbol clash for Android libc++. This adjusts the constructor to be extern(D), analogous to the other C++ exception class definitions. See ldc-developers/ldc#4634 --- runtime/druntime/src/core/stdcpp/new_.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/druntime/src/core/stdcpp/new_.d b/runtime/druntime/src/core/stdcpp/new_.d index de9e10ac47c..5f80b286223 100644 --- a/runtime/druntime/src/core/stdcpp/new_.d +++ b/runtime/druntime/src/core/stdcpp/new_.d @@ -33,7 +33,7 @@ extern (C++, "std") { @nogc: /// - this() { super("bad allocation", 1); } + extern(D) this() { super("bad allocation", 1); } } } From 56059f9e963b513339d455ccd6383f5aa52d69f6 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Mon, 29 Apr 2024 17:17:44 +0100 Subject: [PATCH 090/125] Add tests for bug-prone EmptyStatements Related: https://github.com/dlang/dmd/pull/15409. --- tests/dmd/fail_compilation/empty_statement.d | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tests/dmd/fail_compilation/empty_statement.d diff --git a/tests/dmd/fail_compilation/empty_statement.d b/tests/dmd/fail_compilation/empty_statement.d new file mode 100644 index 00000000000..34070d5f48c --- /dev/null +++ b/tests/dmd/fail_compilation/empty_statement.d @@ -0,0 +1,14 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/empty_statement.d(11): Error: use `{ }` for an empty statement, not `;` +fail_compilation/empty_statement.d(12): Error: use `{ }` for an empty statement, not `;` +fail_compilation/empty_statement.d(13): Error: use `{ }` for an empty statement, not `;` +--- +*/ +void main() +{ + for (;;); + if (0); + while (0); +} From cc3b872b1a363c82c4cafb1bc02c290cf95cbdbe Mon Sep 17 00:00:00 2001 From: dokutoku <3581664+dokutoku@users.noreply.github.com> Date: Tue, 30 Apr 2024 16:20:07 +0900 Subject: [PATCH 091/125] Fix some invalid links (dlang/dmd!16424) Co-authored-by: dokutoku <3729541-dokutoku@users.noreply.gitlab.com> --- dmd/argtypes_aarch64.d | 2 +- dmd/mtype.d | 2 +- runtime/druntime/src/core/memory.d | 8 ++++---- runtime/druntime/src/core/sys/solaris/sys/priocntl.d | 2 +- runtime/druntime/src/core/sys/solaris/sys/procset.d | 2 +- runtime/druntime/src/core/sys/solaris/sys/types.d | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dmd/argtypes_aarch64.d b/dmd/argtypes_aarch64.d index de91d29b663..42c32ac61fb 100644 --- a/dmd/argtypes_aarch64.d +++ b/dmd/argtypes_aarch64.d @@ -18,7 +18,7 @@ import dmd.mtype; * This breaks a type down into 'simpler' types that can be passed to a function * in registers, and returned in registers. * This is the implementation for the AAPCS64 ABI, based on - * https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst. + * $(LINK https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst). * Params: * t = type to break down * Returns: diff --git a/dmd/mtype.d b/dmd/mtype.d index 0f4f91d8473..dcfe1835594 100644 --- a/dmd/mtype.d +++ b/dmd/mtype.d @@ -3492,7 +3492,7 @@ extern (C++) final class TypeDelegate : TypeNext * This is a shell containing a TraitsExp that can be * either resolved to a type or to a symbol. * - * The point is to allow AliasDeclarationY to use `__traits()`, see https://issues.dlang.org/show_bug.cgi?id=7804. + * The point is to allow AliasDeclarationY to use `__traits()`, see $(LINK https://issues.dlang.org/show_bug.cgi?id=7804). */ extern (C++) final class TypeTraits : Type { diff --git a/runtime/druntime/src/core/memory.d b/runtime/druntime/src/core/memory.d index 239d23ff01d..001c3159678 100644 --- a/runtime/druntime/src/core/memory.d +++ b/runtime/druntime/src/core/memory.d @@ -322,7 +322,7 @@ extern(D): This can be used to manually allocate arrays. Initial slice size is 0. Note: The slice's usable size will not match the block size. Use - $(LREF capacity) to retrieve actual usable capacity. + $(REF1 capacity, object) to retrieve actual usable capacity. Example: ---- @@ -611,7 +611,7 @@ extern(C): * Note: * Extend may also be used to extend slices (or memory blocks with * $(LREF APPENDABLE) info). However, use the return value only - * as an indicator of success. $(LREF capacity) should be used to + * as an indicator of success. $(REF1 capacity, object) should be used to * retrieve actual usable slice capacity. */ version (D_ProfileGC) @@ -1197,13 +1197,13 @@ $(UL ) Note: Users should prefer $(REF1 destroy, object) to explicitly finalize objects, -and only resort to $(REF __delete, core,memory) when $(REF destroy, object) +and only resort to $(LREF __delete) when $(REF1 destroy, object) wouldn't be a feasible option. Params: x = aggregate object that should be destroyed -See_Also: $(REF1 destroy, object), $(REF free, core,GC) +See_Also: $(REF1 destroy, object), $(LREF GC.free) History: diff --git a/runtime/druntime/src/core/sys/solaris/sys/priocntl.d b/runtime/druntime/src/core/sys/solaris/sys/priocntl.d index c56be3b00f8..87e3e47f503 100644 --- a/runtime/druntime/src/core/sys/solaris/sys/priocntl.d +++ b/runtime/druntime/src/core/sys/solaris/sys/priocntl.d @@ -2,7 +2,7 @@ * D header file for the Solaris priocntl(2) and priocntlset(2) functions. * * Copyright: Copyright 2014 Jason King. - * License: $(HTTP www.boost.org/LICENSE_1.0.txt, Boost License 1.0). + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Jason King */ diff --git a/runtime/druntime/src/core/sys/solaris/sys/procset.d b/runtime/druntime/src/core/sys/solaris/sys/procset.d index 8bd91157007..9266aee12a1 100644 --- a/runtime/druntime/src/core/sys/solaris/sys/procset.d +++ b/runtime/druntime/src/core/sys/solaris/sys/procset.d @@ -2,7 +2,7 @@ * D header file defining a process set. * * Copyright: Copyright 2014 Jason King. - * License: $(HTTP www.boost.org/LICENSE_1.0.txt, Boost License 1.0). + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Jason King */ diff --git a/runtime/druntime/src/core/sys/solaris/sys/types.d b/runtime/druntime/src/core/sys/solaris/sys/types.d index 4b30a686a92..58558769b71 100644 --- a/runtime/druntime/src/core/sys/solaris/sys/types.d +++ b/runtime/druntime/src/core/sys/solaris/sys/types.d @@ -2,7 +2,7 @@ * D header file that defines Solaris-specific types. * * Copyright: Copyright 2014 Jason King. - * License: $(HTTP www.boost.org/LICENSE_1.0.txt, Boost License 1.0). + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Jason King */ From 47c7ef5597728ad0235e032c8737e6f653340c99 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Wed, 1 May 2024 10:50:03 +0100 Subject: [PATCH 092/125] Fix Bugzilla 24525 - ref lambda not parsed at start of ExpressionStatement --- dmd/parse.d | 12 ++++++++++-- tests/dmd/runnable/funclit.d | 12 ++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/dmd/parse.d b/dmd/parse.d index a459b89e520..a7a930335ad 100644 --- a/dmd/parse.d +++ b/dmd/parse.d @@ -5969,9 +5969,18 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer goto Lexp; goto case; + // FunctionLiteral `auto ref (` + case TOK.auto_: + if (peekNext() == TOK.ref_ && peekNext2() == TOK.leftParenthesis) + goto Lexp; + goto Ldeclaration; + case TOK.ref_: + if (peekNext() == TOK.leftParenthesis) + goto Lexp; + goto Ldeclaration; + case TOK.alias_: case TOK.const_: - case TOK.auto_: case TOK.abstract_: case TOK.extern_: case TOK.align_: @@ -5981,7 +5990,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.deprecated_: case TOK.nothrow_: case TOK.pure_: - case TOK.ref_: case TOK.gshared: case TOK.at: case TOK.struct_: diff --git a/tests/dmd/runnable/funclit.d b/tests/dmd/runnable/funclit.d index c4b70dcb509..253df8feec1 100644 --- a/tests/dmd/runnable/funclit.d +++ b/tests/dmd/runnable/funclit.d @@ -1302,6 +1302,17 @@ void test16271() T!().auf() = 2; assert(T!().x == 2); } +// https://issues.dlang.org/show_bug.cgi?id=24525 +void test24525() +{ + int a; + auto ref () {return a;}() = 1; + assert(a == 1); + + ref () {return a;}() = 2; + assert(a == 2); +} + /***************************************************/ int main() @@ -1361,6 +1372,7 @@ int main() test14745(); test15794(); test16271(); + test24525(); printf("Success\n"); return 0; From 77c0c779cf356753166abdb46eeb9b29bfe0d7f7 Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Fri, 3 May 2024 01:30:55 +0200 Subject: [PATCH 093/125] Make distinction between CC and CXX in the testsuite. (dlang/dmd!16434) Clang makes a distinction between clang and clang++. In particular, clang++ will give a warning when it is passed `.c` source files; and the extra output warning text means that dmd testsuite output checking fails. The warning can be silenced (-Wno-deprecated) but then other tests will fail because `#ifdef __cplusplus` will be true, leading to header file import issues. --- tests/dmd/dshell/cpp_header_gen.d | 10 ++--- tests/dmd/dshell/dll_cxx.d | 10 ++--- tests/dmd/tools/d_do_test.d | 40 ++++++++++++++----- .../tools/dshell_prebuilt/dshell_prebuilt.d | 2 +- 4 files changed, 41 insertions(+), 21 deletions(-) diff --git a/tests/dmd/dshell/cpp_header_gen.d b/tests/dmd/dshell/cpp_header_gen.d index 89a689f3b16..b05ed4141bf 100644 --- a/tests/dmd/dshell/cpp_header_gen.d +++ b/tests/dmd/dshell/cpp_header_gen.d @@ -4,16 +4,16 @@ import dshell; int main() { - if (!CC.length) + if (!CXX.length) { - writeln("CPP header generation test was skipped because $CC is empty!"); + writeln("CPP header generation test was skipped because $CXX is empty!"); return DISABLED; } // DMC cannot compile the generated headers ... version (Windows) { import std.algorithm : canFind; - if (CC.canFind("dmc")) + if (CXX.canFind("dmc")) { writeln("CPP header generation test was skipped because DMC is not supported!"); return DISABLED; @@ -41,9 +41,9 @@ int main() } version (Windows) - run([CC, "/c", "/Fo" ~ Vars.CPP_OBJ, "/I" ~ OUTPUT_BASE, "/I" ~ EXTRA_FILES ~"/../../../src/dmd/root", Vars.SOURCE_DIR ~ "/app.cpp"]); + run([CXX, "/c", "/Fo" ~ Vars.CPP_OBJ, "/I" ~ OUTPUT_BASE, "/I" ~ EXTRA_FILES ~"/../../../src/dmd/root", Vars.SOURCE_DIR ~ "/app.cpp"]); else - run("$CC -m$MODEL -c -o $CPP_OBJ -I$OUTPUT_BASE -I$EXTRA_FILES/../../../src/dmd/root $SOURCE_DIR/app.cpp"); + run("$CXX -m$MODEL -c -o $CPP_OBJ -I$OUTPUT_BASE -I$EXTRA_FILES/../../../src/dmd/root $SOURCE_DIR/app.cpp"); run("$DMD -m$MODEL -conf= -of=$HEADER_EXE $LIB $CPP_OBJ"); run("$HEADER_EXE"); diff --git a/tests/dmd/dshell/dll_cxx.d b/tests/dmd/dshell/dll_cxx.d index 0d7ea2aea65..0102e127931 100644 --- a/tests/dmd/dshell/dll_cxx.d +++ b/tests/dmd/dshell/dll_cxx.d @@ -4,8 +4,8 @@ import std.stdio; int main() { - // Only run this test, if CC has been set. - if (Vars.CC.empty) + // Only run this test, if CXX has been set. + if (Vars.CXX.empty) return DISABLED; version (Windows) @@ -20,20 +20,20 @@ int main() Vars.set(`EXE_NAME`, `$OUTPUT_BASE${SEP}testdll$EXE`); Vars.set(`DLL`, `$OUTPUT_BASE${SEP}mydll$SOEXT`); - string[] dllCmd = [Vars.CC]; + string[] dllCmd = [Vars.CXX]; string mainExtra; version (Windows) { Vars.set(`DLL_LIB`, `$OUTPUT_BASE${SEP}mydll.lib`); if (Vars.MODEL == "32omf") { - // CC should be dmc for win32omf. + // CXX should be dmc for win32omf. dllCmd ~= [`-mn`, `-L/implib:` ~ Vars.DLL_LIB, `-WD`, `-o` ~ Vars.DLL, `kernel32.lib`, `user32.lib`]; mainExtra = `$DLL_LIB`; } else { - // CC should be cl for win32mscoff. + // CXX should be cl for win32mscoff. dllCmd ~= [`/LD`, `/nologo`, `/Fe` ~ Vars.DLL]; mainExtra = `$DLL_LIB`; } diff --git a/tests/dmd/tools/d_do_test.d b/tests/dmd/tools/d_do_test.d index c30cb023278..0ccb641f211 100755 --- a/tests/dmd/tools/d_do_test.d +++ b/tests/dmd/tools/d_do_test.d @@ -60,7 +60,8 @@ void usage() ~ " ARGS: set to execute all combinations of\n" ~ " REQUIRED_ARGS: arguments always passed to the compiler\n" ~ " DMD: compiler to use, ex: ../src/dmd (required)\n" - ~ " CC: C++ compiler to use, ex: dmc, g++\n" + ~ " CC: C compiler to use, ex: dmc, cc\n" + ~ " CXX: C++ compiler to use, ex: dmc, g++\n" ~ " OS: windows, linux, freebsd, osx, netbsd, dragonflybsd\n" ~ " RESULTS_DIR: base directory for test results\n" ~ " MODEL: 32 or 64 (required)\n" @@ -93,7 +94,7 @@ struct TestArgs bool link; /// `LINK`: force linking for `fail_compilation` & `compilable` tests bool clearDflags; /// `DFLAGS`: whether DFLAGS should be cleared before invoking dmd string executeArgs; /// `EXECUTE_ARGS`: arguments passed to the compiled executable (for `runnable[_cxx]`) - string cxxflags; /// `CXXFLAGS`: arguments passed to $CC when compiling `EXTRA_CPP_SOURCES` + string cxxflags; /// `CXXFLAGS`: arguments passed to $CXX when compiling `EXTRA_CPP_SOURCES` string[] sources; /// `EXTRA_SOURCES`: additional D sources (+ main source file) string[] compiledImports; /// `COMPILED_IMPORTS`: files compiled alongside the main source string[] cppSources; /// `EXTRA_CPP_SOURCES`: additional C++ sources @@ -130,7 +131,8 @@ struct EnvData string exe; /// `EXE`: executable file extension (none or `.exe`) string os; /// `OS`: host operating system (`linux`, `windows`, ...) string compiler; /// `HOST_DMD`: host D compiler - string ccompiler; /// `CC`: host C++ compiler + string ccompiler; /// `CC`: host C compiler + string cxxcompiler; /// `CXX`: host C++ compiler string model; /// `MODEL`: target model (`32` or `64`) string required_args; /// `REQUIRED_ARGS`: flags added to the tests `REQUIRED_ARGS` parameter string cxxCompatFlags; /// Additional flags passed to $(compiler) when `EXTRA_CPP_SOURCES` is present @@ -171,6 +173,7 @@ immutable(EnvData) processEnvironment() envData.dmd = replace(envGetRequired("DMD"), "/", envData.sep); envData.compiler = "dmd"; //should be replaced for other compilers envData.ccompiler = environment.get("CC"); + envData.cxxcompiler = environment.get("CXX"); envData.model = envGetRequired("MODEL"); if (envData.os == "windows" && envData.model == "32") { @@ -194,7 +197,7 @@ immutable(EnvData) processEnvironment() if (envData.ccompiler.empty) { if (envData.os != "windows") - envData.ccompiler = "c++"; + envData.ccompiler = "cc"; else if (envData.model == "32omf") envData.ccompiler = "dmc"; else if (envData.model == "64") @@ -203,7 +206,23 @@ immutable(EnvData) processEnvironment() envData.ccompiler = "cl"; else { - writeln("Unknown $OS$MODEL combination: ", envData.os, envData.model); + writeln("Can't determine C compiler (CC). Unknown $OS$MODEL combination: ", envData.os, envData.model); + throw new SilentQuit(); + } + } + if (envData.cxxcompiler.empty) + { + if (envData.os != "windows") + envData.cxxcompiler = "c++"; + else if (envData.model == "32omf") + envData.cxxcompiler = "dmc"; + else if (envData.model == "64") + envData.cxxcompiler = "cl"; + else if (envData.model == "32mscoff") + envData.cxxcompiler = "cl"; + else + { + writeln("Can't determine C++ compiler (CXX). Unknown $OS$MODEL combination: ", envData.os, envData.model); throw new SilentQuit(); } } @@ -1098,14 +1117,15 @@ unittest * Returns: false if a compilation error occurred */ bool collectExtraSources (in string input_dir, in string output_dir, in string[] extraSources, - ref string[] sources, in EnvData envData, in string compiler, - const(char)[] cxxflags, ref File logfile) + ref string[] sources, in EnvData envData, in string ccompiler, + in string cxxcompiler, const(char)[] cxxflags, ref File logfile) { foreach (cur; extraSources) { auto curSrc = input_dir ~ envData.sep ~"extra-files" ~ envData.sep ~ cur; auto curObj = output_dir ~ envData.sep ~ cur ~ envData.obj; - string command = quoteSpaces(compiler); + bool is_cpp_file = cur.extension() == ".cpp"; + string command = quoteSpaces(is_cpp_file ? cxxcompiler : ccompiler); if (envData.model == "32omf") // dmc.exe { command ~= " -c "~curSrc~" -o"~curObj; @@ -1706,10 +1726,10 @@ int tryMain(string[] args) if ( //prepare cpp extra sources - !collectExtraSources(input_dir, output_dir, testArgs.cppSources, testArgs.sources, envData, envData.ccompiler, testArgs.cxxflags, f) || + !collectExtraSources(input_dir, output_dir, testArgs.cppSources, testArgs.sources, envData, envData.ccompiler, envData.cxxcompiler, testArgs.cxxflags, f) || //prepare objc extra sources - !collectExtraSources(input_dir, output_dir, testArgs.objcSources, testArgs.sources, envData, "clang", null, f) + !collectExtraSources(input_dir, output_dir, testArgs.objcSources, testArgs.sources, envData, "clang", "clang++", null, f) ) { writeln(); diff --git a/tests/dmd/tools/dshell_prebuilt/dshell_prebuilt.d b/tests/dmd/tools/dshell_prebuilt/dshell_prebuilt.d index 3bd08d96c60..ec0765e1c16 100644 --- a/tests/dmd/tools/dshell_prebuilt/dshell_prebuilt.d +++ b/tests/dmd/tools/dshell_prebuilt/dshell_prebuilt.d @@ -50,7 +50,7 @@ private alias requiredEnvVars = AliasSeq!( "BUILD" ); private alias optionalEnvVars = AliasSeq!( - "CC", "PIC_FLAG" + "CC", "CXX", "PIC_FLAG" ); private alias allVars = AliasSeq!( requiredEnvVars, From 56eb0f27063a6b6955ad0b2b3e6984e767561cb0 Mon Sep 17 00:00:00 2001 From: "richard (rikki) andrew cattermole" Date: Fri, 3 May 2024 18:24:36 +1200 Subject: [PATCH 094/125] Kill off OMF support --- dmd/cli.d | 6 ------ dmd/mars.d | 11 +++-------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/dmd/cli.d b/dmd/cli.d index 71651d6572b..ab7dcfa6e1e 100644 --- a/dmd/cli.d +++ b/dmd/cli.d @@ -536,12 +536,6 @@ dmd -cov -unittest myprog.d "generate 32 bit code and write MS-COFF object files (deprecated use -m32)", TargetOS.Windows ), - Option("m32omf", - "(deprecated) generate 32 bit code and write OMF object files", - `$(WINDOWS Compile a 32 bit executable. The generated object code is in OMF and is meant to be used with the - $(LINK2 http://www.digitalmars.com/download/freecompiler.html, Digital Mars C/C++ compiler)).`, - TargetOS.Windows - ), Option("m64", "generate 64 bit code", `$(UNIX Compile a 64 bit executable. This is the default for the 64 bit dmd.) diff --git a/dmd/mars.d b/dmd/mars.d index 767d16ffc0b..aea75362535 100644 --- a/dmd/mars.d +++ b/dmd/mars.d @@ -276,7 +276,7 @@ void getenv_setargv(const(char)* envvalue, Strings* args) } /** - * Parse command line arguments for the last instance of -m32, -m64, -m32mscoff or -m32omfobj + * Parse command line arguments for the last instance of -m32, -m64, -m32mscoff * to detect the desired architecture. * * Params: @@ -285,7 +285,7 @@ void getenv_setargv(const(char)* envvalue, Strings* args) * Should be "32" or "64" * * Returns: - * "32", "64" or "32omf" if the "-m32", "-m64", "-m32omf" flags were passed, + * "32", or "64" if the "-m32", "-m64" flags were passed, * respectively. If they weren't, return `arch`. */ const(char)[] parse_arch_arg(Strings* args, const(char)[] arch) @@ -296,7 +296,7 @@ const(char)[] parse_arch_arg(Strings* args, const(char)[] arch) if (arg.length && arg[0] == '-') { - if (arg[1 .. $] == "m32" || arg[1 .. $] == "m32omf" || arg[1 .. $] == "m64") + if (arg[1 .. $] == "m32" || arg[1 .. $] == "m64") arch = arg[2 .. $]; else if (arg[1 .. $] == "m32mscoff") arch = "32"; @@ -901,11 +901,6 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param target.isX86_64 = false; target.omfobj = false; } - else if (arg == "-m32omf") // https://dlang.org/dmd.html#switch-m32omfobj - { - target.isX86_64 = false; - target.omfobj = true; - } else if (startsWith(p + 1, "mscrtlib=")) { driverParams.mscrtlib = arg[10 .. $]; From 671183b295757bc7a14f7bb3f7cbda1e01ee8c03 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Fri, 3 May 2024 10:52:21 +0100 Subject: [PATCH 095/125] Fix out of bounds sequence index error --- dmd/expressionsem.d | 2 +- tests/dmd/fail_compilation/fail125.d | 2 +- tests/dmd/fail_compilation/ice12539.d | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index a73084fefdf..3502a1cecf7 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -10089,7 +10089,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (length <= index) { - error(exp.loc, "array index `[%llu]` is outside array bounds `[0 .. %llu]`", index, cast(ulong)length); + error(exp.loc, "sequence index `[%llu]` is outside bounds `[0 .. %llu]`", index, cast(ulong)length); return setError(); } Expression e; diff --git a/tests/dmd/fail_compilation/fail125.d b/tests/dmd/fail_compilation/fail125.d index 93d176dd4e2..8a4be29fbfb 100644 --- a/tests/dmd/fail_compilation/fail125.d +++ b/tests/dmd/fail_compilation/fail125.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail125.d(15): Error: array index `[2]` is outside array bounds `[0 .. 2]` +fail_compilation/fail125.d(15): Error: sequence index `[2]` is outside bounds `[0 .. 2]` fail_compilation/fail125.d(18): Error: template instance `fail125.main.recMove!(1, a, b)` error instantiating fail_compilation/fail125.d(25): instantiated from here: `recMove!(0, a, b)` --- diff --git a/tests/dmd/fail_compilation/ice12539.d b/tests/dmd/fail_compilation/ice12539.d index 8fab042266c..ad68f23ec25 100644 --- a/tests/dmd/fail_compilation/ice12539.d +++ b/tests/dmd/fail_compilation/ice12539.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/ice12539.d(15): Error: array index `[0]` is outside array bounds `[0 .. 0]` +fail_compilation/ice12539.d(15): Error: sequence index `[0]` is outside bounds `[0 .. 0]` --- */ From 2ba483bd8f89b8cb92c6ad12bc915fd32a19a9dc Mon Sep 17 00:00:00 2001 From: zopsicle Date: Mon, 6 May 2024 11:45:05 +0200 Subject: [PATCH 096/125] Add module core.sys.linux.sys.mount The new module core.sys.linux.sys.mount provides definitions corresponding to those in the header on Linux. --- .../druntime/src/core/sys/linux/sys/mount.d | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 runtime/druntime/src/core/sys/linux/sys/mount.d diff --git a/runtime/druntime/src/core/sys/linux/sys/mount.d b/runtime/druntime/src/core/sys/linux/sys/mount.d new file mode 100644 index 00000000000..8b8511af4b1 --- /dev/null +++ b/runtime/druntime/src/core/sys/linux/sys/mount.d @@ -0,0 +1,63 @@ +/** + * D header file for Linux. + */ +module core.sys.linux.sys.mount; + +version (linux): + +public import core.sys.linux.fs : + BLKROSET, BLKROGET, BLKRRPART, BLKGETSIZE, BLKFLSBUF, BLKRASET, BLKRAGET, + BLKFRASET, BLKFRAGET, BLKSECTSET, BLKSECTGET, BLKSSZGET, BLKBSZGET, + BLKBSZSET, BLKGETSIZE64; + +import core.stdc.config : c_ulong; + +extern (C): +nothrow: +@nogc: + +enum c_ulong MS_RDONLY = 1; +enum c_ulong MS_NOSUID = 2; +enum c_ulong MS_NODEV = 4; +enum c_ulong MS_NOEXEC = 8; +enum c_ulong MS_SYNCHRONOUS = 16; +enum c_ulong MS_REMOUNT = 32; +enum c_ulong MS_MANDLOCK = 64; +enum c_ulong MS_DIRSYNC = 128; +enum c_ulong MS_NOSYMFOLLOW = 256; +enum c_ulong MS_NOATIME = 1024; +enum c_ulong MS_NODIRATIME = 2048; +enum c_ulong MS_BIND = 4096; +enum c_ulong MS_MOVE = 8192; +enum c_ulong MS_REC = 16384; +enum c_ulong MS_SILENT = 32768; +enum c_ulong MS_POSIXACL = 1 << 16; +enum c_ulong MS_UNBINDABLE = 1 << 17; +enum c_ulong MS_PRIVATE = 1 << 18; +enum c_ulong MS_SLAVE = 1 << 19; +enum c_ulong MS_SHARED = 1 << 20; +enum c_ulong MS_RELATIME = 1 << 21; +enum c_ulong MS_KERNMOUNT = 1 << 22; +enum c_ulong MS_I_VERSION = 1 << 23; +enum c_ulong MS_STRICTATIME = 1 << 24; +enum c_ulong MS_LAZYTIME = 1 << 25; +enum c_ulong MS_NOREMOTELOCK = 1 << 27; +enum c_ulong MS_NOSEC = 1 << 28; +enum c_ulong MS_BORN = 1 << 29; +enum c_ulong MS_ACTIVE = 1 << 30; +enum c_ulong MS_NOUSER = 1 << 31; + +enum MS_RMT_MASK = + MS_RDONLY | MS_SYNCHRONOUS | MS_MANDLOCK | MS_I_VERSION | MS_LAZYTIME; + +enum MS_MGC_VAL = 0xC0ED0000; +enum MS_MGC_MSK = 0xFFFF0000; + +enum MNT_FORCE = 1; +enum MNT_DETACH = 2; +enum MNT_EXPIRE = 4; +enum UMOUNT_NOFOLLOW = 8; + +int mount(const(char)*, const(char)*, const(char)*, c_ulong, const(void)*); +int umount(const(char)*); +int umount2(const(char)*, int); From 2842ca6eed49361963957ad89460041b02d6aed1 Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Tue, 7 May 2024 02:02:47 +0200 Subject: [PATCH 097/125] Fix alignment of calloc memory in rt.dwarfeh (dlang/dmd!16446) The signature of calloc is `void* calloc( size_t num, size_t size )`, where `num` is the number of objects and `size` is the byte size per object. (https://en.cppreference.com/w/c/memory/calloc) The number of bytes allocated is the same with this fix, but it solves potential memory alignment issues. --- runtime/druntime/src/rt/dwarfeh.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/druntime/src/rt/dwarfeh.d b/runtime/druntime/src/rt/dwarfeh.d index c3a79ad8a52..419a15b9638 100644 --- a/runtime/druntime/src/rt/dwarfeh.d +++ b/runtime/druntime/src/rt/dwarfeh.d @@ -181,7 +181,7 @@ struct ExceptionHeader auto eh = &ehstorage; if (eh.object) // if in use { - eh = cast(ExceptionHeader*)core.stdc.stdlib.calloc(ExceptionHeader.sizeof, 1); + eh = cast(ExceptionHeader*)core.stdc.stdlib.calloc(1, ExceptionHeader.sizeof); if (!eh) terminate(__LINE__); // out of memory while throwing - not much else can be done } From 2f05674c0d566fa59747bc0dc3cb52d68cd44db7 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Wed, 8 May 2024 08:11:38 +0100 Subject: [PATCH 098/125] Improve `@__future` deprecation message (dlang/dmd!16447) Show location of base method. Remove duplicate content in compilable/future.d. --- dmd/funcsem.d | 5 +++- tests/dmd/compilable/future.d | 47 ----------------------------------- tests/dmd/runnable/future.d | 3 ++- 3 files changed, 6 insertions(+), 49 deletions(-) delete mode 100644 tests/dmd/compilable/future.d diff --git a/dmd/funcsem.d b/dmd/funcsem.d index e058deb0f94..31ed744d927 100644 --- a/dmd/funcsem.d +++ b/dmd/funcsem.d @@ -742,7 +742,10 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl) { if (fdv.isFuture()) { - deprecation(funcdecl.loc, "`@__future` base class method `%s` is being overridden by `%s`; rename the latter", fdv.toPrettyChars(), funcdecl.toPrettyChars()); + deprecation(funcdecl.loc, "method `%s` implicitly overrides `@__future` base class method; rename the former", + funcdecl.toPrettyChars()); + deprecationSupplemental(fdv.loc, "base method `%s` defined here", + fdv.toPrettyChars()); // Treat 'this' as an introducing function, giving it a separate hierarchy in the vtbl[] goto Lintro; } diff --git a/tests/dmd/compilable/future.d b/tests/dmd/compilable/future.d deleted file mode 100644 index 30439847ef4..00000000000 --- a/tests/dmd/compilable/future.d +++ /dev/null @@ -1,47 +0,0 @@ -/* PERMUTE_ARGS: - * TEST_OUTPUT: ---- -compilable/future.d(15): Deprecation: `@__future` base class method `future.A.msg` is being overridden by `future.B.msg`; rename the latter ---- - */ - -class A -{ - @__future char msg() { return 'a'; } -} - -class B : A -{ - char msg() { return 'b'; } -} - -class C : B -{ - override char msg() { return 'c'; } -} - -class D : A -{ - override char msg() { return 'd'; } -} - -int main() -{ - auto a = new A(); - assert(a.msg() == 'a'); - auto b = new B(); - assert(b.msg() == 'b'); - auto c = new C(); - assert(c.msg() == 'c'); - auto d = new D(); - assert(d.msg() == 'd'); - - assert(b.A.msg() == 'a'); - - auto ba = cast(A)b; - assert(ba.msg() == 'a'); - - auto da = cast(A)d; - assert(da.msg() == 'd'); - return 0; -} diff --git a/tests/dmd/runnable/future.d b/tests/dmd/runnable/future.d index 1a91d305357..e0ef4663954 100644 --- a/tests/dmd/runnable/future.d +++ b/tests/dmd/runnable/future.d @@ -1,7 +1,8 @@ /* PERMUTE_ARGS: TEST_OUTPUT: --- -runnable/future.d(15): Deprecation: `@__future` base class method `future.A.msg` is being overridden by `future.B.msg`; rename the latter +runnable/future.d(16): Deprecation: method `future.B.msg` implicitly overrides `@__future` base class method; rename the former +runnable/future.d(11): base method `future.A.msg` defined here --- */ From 6189c0b01c6c9a60b40c8f7297e35facb698141c Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Wed, 8 May 2024 12:55:21 +0100 Subject: [PATCH 099/125] [core.attribute] Add cheat sheet links (dlang/dmd!16457) * [core.attribute] Add cheat sheet links * Stray `)` * Fix row macro * Add links --- runtime/druntime/src/core/attribute.d | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/runtime/druntime/src/core/attribute.d b/runtime/druntime/src/core/attribute.d index 79ad25ab358..95a67ea219d 100644 --- a/runtime/druntime/src/core/attribute.d +++ b/runtime/druntime/src/core/attribute.d @@ -2,6 +2,21 @@ * This module contains UDA's (User Defined Attributes) either used in * the runtime or special UDA's recognized by compiler. * + * $(SCRIPT inhibitQuickIndex = 1;) + * $(BOOKTABLE Cheat Sheet, + * $(THEAD Attribute Name, Linkage, Description) + * $(TROW $(LREF gnuAbiTag), C++, + * Declares an ABI tag on a C++ symbol.) + * $(TROW $(LREF mustuse),, + * Ensures that values of a struct or union type are not discarded.) + * $(TROW $(LREF optional), Objective-C, + * Makes an Objective-C interface method optional.) + * $(TROW $(LREF selector), Objective-C, + * Attaches an Objective-C selector to a method.) + * $(TROW $(LREF weak),, + * Specifies that a global symbol should be emitted with weak linkage.) + * ) + * * Copyright: Copyright Jacob Carlborg 2015. * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) * Authors: Jacob Carlborg From 422f334c8362f904cddc3d5e83fed85d6b204c4a Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Wed, 8 May 2024 04:59:34 -0700 Subject: [PATCH 100/125] bitfields: add bitoffsetof, bitwidth, isBitfield (dlang/dmd!16444) --- dmd/expressionsem.d | 8 ++++-- dmd/frontend.h | 3 +++ dmd/id.d | 3 +++ dmd/traits.d | 11 ++++++++ dmd/typesem.d | 21 +++++++++++++-- tests/dmd/fail_compilation/bitintro.d | 38 +++++++++++++++++++++++++++ 6 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 tests/dmd/fail_compilation/bitintro.d diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 3502a1cecf7..9b6a45019b2 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -14306,14 +14306,16 @@ private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc) if (auto te = exp.e1.isTupleExp()) { - if (exp.ident == Id.offsetof) + if (exp.ident == Id.offsetof || + exp.ident == Id.bitoffsetof || + exp.ident == Id.bitwidth) { /* 'distribute' the .offsetof to each of the tuple elements. */ auto exps = new Expressions(te.exps.length); foreach (i, e; (*te.exps)[]) { - (*exps)[i] = new DotIdExp(e.loc, e, Id.offsetof); + (*exps)[i] = new DotIdExp(e.loc, e, exp.ident); } // Don't evaluate te.e0 in runtime Expression e = new TupleExp(exp.loc, null, exps); @@ -14636,6 +14638,8 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) !cfile && (exp.ident == Id._mangleof || exp.ident == Id.offsetof || + exp.ident == Id.bitoffsetof || + exp.ident == Id.bitwidth || exp.ident == Id._init || exp.ident == Id.stringof) )) diff --git a/dmd/frontend.h b/dmd/frontend.h index a208add26a6..de2156ffe44 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -8407,6 +8407,8 @@ struct Id final static Identifier* dollar; static Identifier* ctfe; static Identifier* offset; + static Identifier* bitoffsetof; + static Identifier* bitwidth; static Identifier* ModuleInfo; static Identifier* ClassInfo; static Identifier* classinfo; @@ -8698,6 +8700,7 @@ struct Id final static Identifier* isAbstractClass; static Identifier* isArithmetic; static Identifier* isAssociativeArray; + static Identifier* isBitfield; static Identifier* isFinalClass; static Identifier* isTemplate; static Identifier* isPOD; diff --git a/dmd/id.d b/dmd/id.d index 6dbc60b020c..dfaf8f5200b 100644 --- a/dmd/id.d +++ b/dmd/id.d @@ -102,6 +102,8 @@ immutable Msgtable[] msgtable = { "ctfe", "__ctfe" }, { "offset" }, { "offsetof" }, + { "bitoffsetof" }, + { "bitwidth" }, { "ModuleInfo" }, { "ClassInfo" }, { "classinfo" }, @@ -455,6 +457,7 @@ immutable Msgtable[] msgtable = { "isAbstractClass" }, { "isArithmetic" }, { "isAssociativeArray" }, + { "isBitfield" }, { "isFinalClass" }, { "isTemplate" }, { "isPOD" }, diff --git a/dmd/traits.d b/dmd/traits.d index 81d42e6e8be..5ec38449123 100644 --- a/dmd/traits.d +++ b/dmd/traits.d @@ -503,6 +503,17 @@ Expression semanticTraits(TraitsExp e, Scope* sc) sm => sm.isTemplateDeclaration() !is null) != 0; }); } + if (e.ident == Id.isBitfield) + { + if (dim != 1) + return dimError(1); + + return isDsymX((s) + { + s = s.toAlias(); + return s.isBitFieldDeclaration() !is null; + }); + } if (e.ident == Id.isPOD) { if (dim != 1) diff --git a/dmd/typesem.d b/dmd/typesem.d index 195fdc723a2..7643dc59c10 100644 --- a/dmd/typesem.d +++ b/dmd/typesem.d @@ -4083,7 +4083,9 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag } if (v) { - if (ident == Id.offsetof) + if (ident == Id.offsetof || + ident == Id.bitoffsetof || + ident == Id.bitwidth) { v.dsymbolSemantic(null); if (v.isField()) @@ -4093,7 +4095,20 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag ad.size(e.loc); if (ad.sizeok != Sizeok.done) return ErrorExp.get(); - return new IntegerExp(e.loc, v.offset, Type.tsize_t); + uint value; + if (ident == Id.offsetof) + value = v.offset; + else // Id.bitoffsetof || Id.bitwidth + { + auto bf = v.isBitFieldDeclaration(); + if (bf) + { + value = ident == Id.bitoffsetof ? bf.bitOffset : bf.fieldWidth; + } + else + error(v.loc, "`%s` is not a bitfield, cannot apply `%s`", v.toChars(), ident.toChars()); + } + return new IntegerExp(e.loc, value, Type.tsize_t); } } else if (ident == Id._init) @@ -4512,6 +4527,8 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag ident != Id._mangleof && ident != Id.stringof && ident != Id.offsetof && + ident != Id.bitoffsetof && + ident != Id.bitwidth && // https://issues.dlang.org/show_bug.cgi?id=15045 // Don't forward special built-in member functions. ident != Id.ctor && diff --git a/tests/dmd/fail_compilation/bitintro.d b/tests/dmd/fail_compilation/bitintro.d new file mode 100644 index 00000000000..d58c3ea56ff --- /dev/null +++ b/tests/dmd/fail_compilation/bitintro.d @@ -0,0 +1,38 @@ +/* REQUIRED_ARGS: -preview=bitfields + */ + +struct S +{ + int a; + int b:5, c:6; +} + +static if (0) +{ + pragma(msg, __traits(isBitfield, S.a)); + pragma(msg, __traits(isBitfield, S.b)); + pragma(msg, S.b.bitoffsetof); + pragma(msg, S.b.bitwidth); + pragma(msg, S.c.bitoffsetof); + pragma(msg, S.c.bitwidth); + pragma(msg, S.a.bitoffsetof); + pragma(msg, S.a.bitwidth); +} + +static assert(__traits(isBitfield, S.a) == false); +static assert(__traits(isBitfield, S.b) == true); +static assert(S.b.bitoffsetof == 0); +static assert(S.b.bitwidth == 5); +static assert(S.c.bitoffsetof == 5); +static assert(S.c.bitwidth == 6); + +/* TEST_OUTPUT: +--- +fail_compilation/bitintro.d(6): Error: `a` is not a bitfield, cannot apply `bitoffsetof` +fail_compilation/bitintro.d(37): while evaluating: `static assert(a.bitoffsetof)` +fail_compilation/bitintro.d(6): Error: `a` is not a bitfield, cannot apply `bitwidth` +fail_compilation/bitintro.d(38): while evaluating: `static assert(a.bitwidth)` +--- +*/ +static assert(S.a.bitoffsetof); +static assert(S.a.bitwidth); From 1e8a8f4e9a2422cbcf99f91cfa82b9a0d558131b Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Thu, 9 May 2024 01:47:24 +0200 Subject: [PATCH 101/125] Rename gdb test case file such that it starts by "gdb" like the other gdb tests. (this enables disabling the test when gdb is not available on the test system) (dlang/dmd!16458) --- tests/dmd/runnable/{b18504.d => gdb18504.d} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/dmd/runnable/{b18504.d => gdb18504.d} (100%) diff --git a/tests/dmd/runnable/b18504.d b/tests/dmd/runnable/gdb18504.d similarity index 100% rename from tests/dmd/runnable/b18504.d rename to tests/dmd/runnable/gdb18504.d From 1ab5405f5f13921f99e45dd148bff1c77905b247 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 18 May 2024 14:46:46 +0200 Subject: [PATCH 102/125] Bump version, frontend version and Phobos --- CMakeLists.txt | 6 +++--- runtime/phobos | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b1e41665c07..226a802d974 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,10 +117,10 @@ include(GetLinuxDistribution) # # Version information -set(LDC_VERSION "1.38.0") # May be overridden by git hash tag +set(LDC_VERSION "1.39.0") # May be overridden by git hash tag set(DMDFE_MAJOR_VERSION 2) -set(DMDFE_MINOR_VERSION 108) -set(DMDFE_PATCH_VERSION 1) +set(DMDFE_MINOR_VERSION 109) +set(DMDFE_PATCH_VERSION 0) set(DMD_VERSION ${DMDFE_MAJOR_VERSION}.${DMDFE_MINOR_VERSION}.${DMDFE_PATCH_VERSION}) diff --git a/runtime/phobos b/runtime/phobos index 2181b216357..7bb24a48818 160000 --- a/runtime/phobos +++ b/runtime/phobos @@ -1 +1 @@ -Subproject commit 2181b216357f238eb262320448549a9da20bff51 +Subproject commit 7bb24a48818d87c9ac4da030ea8d03902dbac175 From 4dd703b733a1f1522cf2b17ddf12d88d39ef6bbb Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 18 May 2024 15:03:24 +0200 Subject: [PATCH 103/125] Adapt to changed readFile() signature --- driver/config.d | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/driver/config.d b/driver/config.d index 8e016a3e4a2..46990a49a93 100644 --- a/driver/config.d +++ b/driver/config.d @@ -99,12 +99,21 @@ class GroupSetting : Setting Setting[] parseConfigFile(const(char)* filename) { + import dmd.common.outbuffer : OutBuffer; + import dmd.errors : fatal; import dmd.location : Loc; import dmd.root.string : toDString; import dmd.utils; + OutBuffer buf; const dFilename = filename.toDString; - auto content = readFile(Loc.initial, dFilename).extractSlice(); + if (readFile(Loc.initial, dFilename, buf)) + { + fatal(); + return null; + } + + auto content = buf.extractSlice(); // skip UTF-8 BOM if (content.length >= 3 && content[0 .. 3] == "\xEF\xBB\xBF") From d0738b6af9f4f3702a586fac2c4b993ec68abd0e Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 18 May 2024 15:04:07 +0200 Subject: [PATCH 104/125] Adapt to getAttributes() now being a free-standing function --- gen/uda.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gen/uda.cpp b/gen/uda.cpp index f96ca25ca8a..897c4848ef2 100644 --- a/gen/uda.cpp +++ b/gen/uda.cpp @@ -92,7 +92,7 @@ StructLiteralExp *getMagicAttribute(Dsymbol *sym, const Identifier *id, return nullptr; // Loop over all UDAs and early return the expression if a match was found. - Expressions *attrs = sym->userAttribDecl()->getAttributes(); + Expressions *attrs = getAttributes(sym->userAttribDecl()); expandTuples(attrs); for (auto attr : *attrs) { if (auto sle = attr->isStructLiteralExp()) @@ -113,7 +113,7 @@ StructLiteralExp *getLastMagicAttribute(Dsymbol *sym, const Identifier *id, // Loop over all UDAs and find the last match StructLiteralExp *lastMatch = nullptr; - Expressions *attrs = sym->userAttribDecl()->getAttributes(); + Expressions *attrs = getAttributes(sym->userAttribDecl()); expandTuples(attrs); for (auto attr : *attrs) { if (auto sle = attr->isStructLiteralExp()) @@ -134,7 +134,7 @@ void callForEachMagicAttribute(Dsymbol &sym, const Identifier *id, return; // Loop over all UDAs and call `action` if a match was found. - Expressions *attrs = sym.userAttribDecl()->getAttributes(); + Expressions *attrs = getAttributes(sym.userAttribDecl()); expandTuples(attrs); for (auto attr : *attrs) { if (auto sle = attr->isStructLiteralExp()) @@ -498,7 +498,7 @@ void applyVarDeclUDAs(VarDeclaration *decl, llvm::GlobalVariable *gvar) { if (!decl->userAttribDecl()) return; - Expressions *attrs = decl->userAttribDecl()->getAttributes(); + Expressions *attrs = getAttributes(decl->userAttribDecl()); expandTuples(attrs); for (auto &attr : *attrs) { auto sle = getLdcAttributesStruct(attr); @@ -541,7 +541,7 @@ void applyFuncDeclUDAs(FuncDeclaration *decl, IrFunction *irFunc) { llvm::Function *func = irFunc->getLLVMFunc(); assert(func); - Expressions *attrs = decl->userAttribDecl()->getAttributes(); + Expressions *attrs = getAttributes(decl->userAttribDecl()); expandTuples(attrs); for (auto &attr : *attrs) { auto sle = getLdcAttributesStruct(attr); @@ -609,7 +609,7 @@ void applyFuncDeclUDAs(FuncDeclaration *decl, IrFunction *irFunc) { if (!param->userAttribDecl) continue; - Expressions *attrs = param->userAttribDecl->getAttributes(); + Expressions *attrs = getAttributes(param->userAttribDecl); expandTuples(attrs); for (auto &attr : *attrs) { auto sle = getLdcAttributesStruct(attr); From 624a4fe1649b3aa49c463ff267088038a2d5a24a Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 18 May 2024 15:28:29 +0200 Subject: [PATCH 105/125] Adapt 2 tests to slightly different error msg wrt. missing object.d --- tests/dmd/fail_compilation/fail17612.d | 9 +++++---- tests/driver/cli_preparsing.d | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/dmd/fail_compilation/fail17612.d b/tests/dmd/fail_compilation/fail17612.d index a14e85935a6..e7f617a81bb 100644 --- a/tests/dmd/fail_compilation/fail17612.d +++ b/tests/dmd/fail_compilation/fail17612.d @@ -1,9 +1,10 @@ /* TEST_OUTPUT: --- -fail_compilation/fail17612.d(16): Error: undefined identifier `string` -fail_compilation/fail17612.d(19): Error: `TypeInfo` not found. object.d may be incorrectly installed or corrupt. -fail_compilation/fail17612.d(19): dmd might not be correctly installed. Run 'dmd -man' for installation instructions. -fail_compilation/fail17612.d(19): config file: not found +fail_compilation/fail17612.d(17): Error: undefined identifier `string` +fail_compilation/fail17612.d(20): Error: `TypeInfo` not found. object.d may be incorrectly installed or corrupt. +fail_compilation/fail17612.d(20): ldc2 might not be correctly installed. +fail_compilation/fail17612.d(20): Please check your ldc2.conf configuration file. +fail_compilation/fail17612.d(20): Installation instructions can be found at http://wiki.dlang.org/LDC. --- */ diff --git a/tests/driver/cli_preparsing.d b/tests/driver/cli_preparsing.d index b50e1e6f975..d8206d25f54 100644 --- a/tests/driver/cli_preparsing.d +++ b/tests/driver/cli_preparsing.d @@ -4,7 +4,7 @@ // RUN: not %ldc -o- -conf= %s 2>&1 | FileCheck --check-prefix=NO_CONF %s // RUN: not %ldc -o- --conf "" %s 2>&1 | FileCheck --check-prefix=NO_CONF %s -// NO_CONF: Error: cannot find source code for runtime library file 'object.d' +// NO_CONF: Error: `object` not found. object.d may be incorrectly installed or corrupt. // RUN: %ldc -v -o- -mtriple x86_64-vendor-windows-msvc %s 2>&1 | FileCheck --check-prefix=TRIPLE %s From 172fd081c8bbce19f1c707a42bf4a9253124725f Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 18 May 2024 21:48:49 +0200 Subject: [PATCH 106/125] Adopt new cmdline options -identifiers[-importc] --- driver/cl_options.cpp | 23 ++++++++++++++++++++++- driver/ldmd.cpp | 8 +++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/driver/cl_options.cpp b/driver/cl_options.cpp index 4cec62a46ba..fb1ca9140a3 100644 --- a/driver/cl_options.cpp +++ b/driver/cl_options.cpp @@ -603,10 +603,31 @@ static cl::opt cl::desc("Implement DIP1008 (@nogc Throwable)"), cl::ReallyHidden); -cl::opt betterC( +static cl::opt betterC( "betterC", cl::ZeroOrMore, cl::location(global.params.betterC), cl::desc("Omit generating some runtime information and helper functions")); +static cl::opt identifiers( + "identifiers", cl::ZeroOrMore, cl::location(global.params.dIdentifierTable), + cl::desc("Specify the non-ASCII tables for D identifiers"), + cl::values(clEnumValN(CLIIdentifierTable::C99, "c99", "C99"), + clEnumValN(CLIIdentifierTable::C11, "c11", "C11"), + clEnumValN(CLIIdentifierTable::UAX31, "UAX31", "UAX31"), + clEnumValN(CLIIdentifierTable::All, "all", + "All, the least restrictive set, which comes all " + "others (default)"))); + +static cl::opt identifiersImportc( + "identifiers-importc", cl::ZeroOrMore, + cl::location(global.params.cIdentifierTable), + cl::desc("Specify the non-ASCII tables for ImportC identifiers"), + cl::values(clEnumValN(CLIIdentifierTable::C99, "c99", "C99"), + clEnumValN(CLIIdentifierTable::C11, "c11", "C11 (default)"), + clEnumValN(CLIIdentifierTable::UAX31, "UAX31", "UAX31"), + clEnumValN(CLIIdentifierTable::All, "all", + "All, the least restrictive set, which comes all " + "others"))); + // `-cov[=|ctfe]` parser. struct CoverageParser : public cl::parser { explicit CoverageParser(cl::Option &O) : cl::parser(O) {} diff --git a/driver/ldmd.cpp b/driver/ldmd.cpp index 472874edf5e..1f9d0747c0e 100644 --- a/driver/ldmd.cpp +++ b/driver/ldmd.cpp @@ -206,6 +206,10 @@ Where:\n\ --help print help and exit\n\ -I= look for imports also in directory\n\ -i[=] include imported modules in the compilation\n\ + -identifiers=
\n\ + specify the non-ASCII tables for D identifiers\n\ + -identifiers-importc=
\n\ + specify the non-ASCII tables for ImportC identifiers\n\ -ignore deprecated flag, unsupported pragmas are always ignored now\n\ -inline do function inlining\n\ -J= look for string imports also in directory\n\ @@ -636,7 +640,9 @@ void translateArgs(const llvm::SmallVectorImpl &ldmdArgs, ldcArgs.push_back("-enable-inlining"); ldcArgs.push_back("-Hkeep-all-bodies"); } - /* -dip25 + /* -identifiers-importc + * -identifiers + * -dip25 * -dip1000 * -dip1008 */ From 1f4368efe0ab45f101c134400fd50113d947d934 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 18 May 2024 22:23:17 +0200 Subject: [PATCH 107/125] dmd.builtin: Streamline signature of extra LDC builtins --- dmd/builtin.d | 112 +++++++++++++++++++++++++------------------------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/dmd/builtin.d b/dmd/builtin.d index 6d0cf4ce277..600d42d92a9 100644 --- a/dmd/builtin.d +++ b/dmd/builtin.d @@ -56,7 +56,7 @@ public extern (C++) Expression eval_builtin(const ref Loc loc, FuncDeclaration f static if (e == "unknown") case BUILTIN.unknown: assert(false); else static if (IN_LLVM && e.length > 5 && e[0..5] == "llvm_") - mixin("case BUILTIN."~e~": return eval_llvm"~e[5..$]~"(loc, fd, arguments);"); + mixin("case BUILTIN."~e~": return eval_llvm"~e[5..$]~"(loc, fd, (*arguments)[]);"); else mixin("case BUILTIN."~e~": return eval_"~e~"(loc, fd, (*arguments)[]);"); } @@ -566,173 +566,173 @@ private int getBitsizeOfType(Loc loc, Type type) return 32; // in case of error } -Expression eval_llvmsin(Loc loc, FuncDeclaration fd, Expressions *arguments) +Expression eval_llvmsin(Loc loc, FuncDeclaration fd, Expression[] arguments) { Type type = getTypeOfOverloadedIntrinsic(fd); - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.sin(arg0.toReal()), type); } -Expression eval_llvmcos(Loc loc, FuncDeclaration fd, Expressions *arguments) +Expression eval_llvmcos(Loc loc, FuncDeclaration fd, Expression[] arguments) { Type type = getTypeOfOverloadedIntrinsic(fd); - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.cos(arg0.toReal()), type); } -Expression eval_llvmsqrt(Loc loc, FuncDeclaration fd, Expressions *arguments) +Expression eval_llvmsqrt(Loc loc, FuncDeclaration fd, Expression[] arguments) { Type type = getTypeOfOverloadedIntrinsic(fd); - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.sqrt(arg0.toReal()), type); } -Expression eval_llvmexp(Loc loc, FuncDeclaration fd, Expressions *arguments) +Expression eval_llvmexp(Loc loc, FuncDeclaration fd, Expression[] arguments) { Type type = getTypeOfOverloadedIntrinsic(fd); - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.exp(arg0.toReal()), type); } -Expression eval_llvmexp2(Loc loc, FuncDeclaration fd, Expressions *arguments) +Expression eval_llvmexp2(Loc loc, FuncDeclaration fd, Expression[] arguments) { Type type = getTypeOfOverloadedIntrinsic(fd); - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.exp2(arg0.toReal()), type); } -Expression eval_llvmlog(Loc loc, FuncDeclaration fd, Expressions *arguments) +Expression eval_llvmlog(Loc loc, FuncDeclaration fd, Expression[] arguments) { Type type = getTypeOfOverloadedIntrinsic(fd); - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.log(arg0.toReal()), type); } -Expression eval_llvmlog2(Loc loc, FuncDeclaration fd, Expressions *arguments) +Expression eval_llvmlog2(Loc loc, FuncDeclaration fd, Expression[] arguments) { Type type = getTypeOfOverloadedIntrinsic(fd); - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.log2(arg0.toReal()), type); } -Expression eval_llvmlog10(Loc loc, FuncDeclaration fd, Expressions *arguments) +Expression eval_llvmlog10(Loc loc, FuncDeclaration fd, Expression[] arguments) { Type type = getTypeOfOverloadedIntrinsic(fd); - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.log10(arg0.toReal()), type); } -Expression eval_llvmfabs(Loc loc, FuncDeclaration fd, Expressions *arguments) +Expression eval_llvmfabs(Loc loc, FuncDeclaration fd, Expression[] arguments) { Type type = getTypeOfOverloadedIntrinsic(fd); - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.fabs(arg0.toReal()), type); } -Expression eval_llvmminnum(Loc loc, FuncDeclaration fd, Expressions *arguments) +Expression eval_llvmminnum(Loc loc, FuncDeclaration fd, Expression[] arguments) { Type type = getTypeOfOverloadedIntrinsic(fd); - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); - Expression arg1 = (*arguments)[1]; + Expression arg1 = arguments[1]; assert(arg1.op == EXP.float64); return new RealExp(loc, CTFloat.fmin(arg0.toReal(), arg1.toReal()), type); } -Expression eval_llvmmaxnum(Loc loc, FuncDeclaration fd, Expressions *arguments) +Expression eval_llvmmaxnum(Loc loc, FuncDeclaration fd, Expression[] arguments) { Type type = getTypeOfOverloadedIntrinsic(fd); - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); - Expression arg1 = (*arguments)[1]; + Expression arg1 = arguments[1]; assert(arg1.op == EXP.float64); return new RealExp(loc, CTFloat.fmax(arg0.toReal(), arg1.toReal()), type); } -Expression eval_llvmfloor(Loc loc, FuncDeclaration fd, Expressions *arguments) +Expression eval_llvmfloor(Loc loc, FuncDeclaration fd, Expression[] arguments) { Type type = getTypeOfOverloadedIntrinsic(fd); - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.floor(arg0.toReal()), type); } -Expression eval_llvmceil(Loc loc, FuncDeclaration fd, Expressions *arguments) +Expression eval_llvmceil(Loc loc, FuncDeclaration fd, Expression[] arguments) { Type type = getTypeOfOverloadedIntrinsic(fd); - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.ceil(arg0.toReal()), type); } -Expression eval_llvmtrunc(Loc loc, FuncDeclaration fd, Expressions *arguments) +Expression eval_llvmtrunc(Loc loc, FuncDeclaration fd, Expression[] arguments) { Type type = getTypeOfOverloadedIntrinsic(fd); - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.trunc(arg0.toReal()), type); } -Expression eval_llvmrint(Loc loc, FuncDeclaration fd, Expressions *arguments) +Expression eval_llvmrint(Loc loc, FuncDeclaration fd, Expression[] arguments) { Type type = getTypeOfOverloadedIntrinsic(fd); - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.rint(arg0.toReal()), type); } -Expression eval_llvmnearbyint(Loc loc, FuncDeclaration fd, Expressions *arguments) +Expression eval_llvmnearbyint(Loc loc, FuncDeclaration fd, Expression[] arguments) { Type type = getTypeOfOverloadedIntrinsic(fd); - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.nearbyint(arg0.toReal()), type); } -Expression eval_llvmround(Loc loc, FuncDeclaration fd, Expressions *arguments) +Expression eval_llvmround(Loc loc, FuncDeclaration fd, Expression[] arguments) { Type type = getTypeOfOverloadedIntrinsic(fd); - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); return new RealExp(loc, CTFloat.round(arg0.toReal()), type); } -Expression eval_llvmfma(Loc loc, FuncDeclaration fd, Expressions *arguments) +Expression eval_llvmfma(Loc loc, FuncDeclaration fd, Expression[] arguments) { Type type = getTypeOfOverloadedIntrinsic(fd); - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); - Expression arg1 = (*arguments)[1]; + Expression arg1 = arguments[1]; assert(arg1.op == EXP.float64); - Expression arg2 = (*arguments)[2]; + Expression arg2 = arguments[2]; assert(arg2.op == EXP.float64); return new RealExp(loc, CTFloat.fma(arg0.toReal(), arg1.toReal(), arg2.toReal()), type); } -Expression eval_llvmcopysign(Loc loc, FuncDeclaration fd, Expressions *arguments) +Expression eval_llvmcopysign(Loc loc, FuncDeclaration fd, Expression[] arguments) { Type type = getTypeOfOverloadedIntrinsic(fd); - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.float64); - Expression arg1 = (*arguments)[1]; + Expression arg1 = arguments[1]; assert(arg1.op == EXP.float64); return new RealExp(loc, CTFloat.copysign(arg0.toReal(), arg1.toReal()), type); } -Expression eval_llvmbswap(Loc loc, FuncDeclaration fd, Expressions *arguments) +Expression eval_llvmbswap(Loc loc, FuncDeclaration fd, Expression[] arguments) { Type type = getTypeOfOverloadedIntrinsic(fd); - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.int64); uinteger_t n = arg0.toInteger(); enum ulong BYTEMASK = 0x00FF00FF00FF00FF; @@ -766,11 +766,11 @@ Expression eval_llvmbswap(Loc loc, FuncDeclaration fd, Expressions *arguments) return new IntegerExp(loc, n, type); } -Expression eval_llvmcttz(Loc loc, FuncDeclaration fd, Expressions *arguments) +Expression eval_llvmcttz(Loc loc, FuncDeclaration fd, Expression[] arguments) { Type type = getTypeOfOverloadedIntrinsic(fd); - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.int64); uinteger_t x = arg0.toInteger(); @@ -778,7 +778,7 @@ Expression eval_llvmcttz(Loc loc, FuncDeclaration fd, Expressions *arguments) if (x == 0) { - if ((*arguments)[1].toInteger()) + if (arguments[1].toInteger()) error(loc, "llvm.cttz.i#(0) is undefined"); } else @@ -796,14 +796,14 @@ Expression eval_llvmcttz(Loc loc, FuncDeclaration fd, Expressions *arguments) return new IntegerExp(loc, n, type); } -Expression eval_llvmctlz(Loc loc, FuncDeclaration fd, Expressions *arguments) +Expression eval_llvmctlz(Loc loc, FuncDeclaration fd, Expression[] arguments) { Type type = getTypeOfOverloadedIntrinsic(fd); - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.int64); uinteger_t x = arg0.toInteger(); - if (x == 0 && (*arguments)[1].toInteger()) + if (x == 0 && arguments[1].toInteger()) error(loc, "llvm.ctlz.i#(0) is undefined"); int n = getBitsizeOfType(loc, type); @@ -816,12 +816,12 @@ Expression eval_llvmctlz(Loc loc, FuncDeclaration fd, Expressions *arguments) return new IntegerExp(loc, n - x, type); } -Expression eval_llvmctpop(Loc loc, FuncDeclaration fd, Expressions *arguments) +Expression eval_llvmctpop(Loc loc, FuncDeclaration fd, Expression[] arguments) { // FIXME Does not work for cent/ucent Type type = getTypeOfOverloadedIntrinsic(fd); - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.int64); uinteger_t n = arg0.toInteger(); int cnt = 0; @@ -833,11 +833,11 @@ Expression eval_llvmctpop(Loc loc, FuncDeclaration fd, Expressions *arguments) return new IntegerExp(loc, cnt, type); } -Expression eval_llvmexpect(Loc loc, FuncDeclaration fd, Expressions *arguments) +Expression eval_llvmexpect(Loc loc, FuncDeclaration fd, Expression[] arguments) { Type type = getTypeOfOverloadedIntrinsic(fd); - Expression arg0 = (*arguments)[0]; + Expression arg0 = arguments[0]; assert(arg0.op == EXP.int64); return new IntegerExp(loc, arg0.toInteger(), type); From 4ee4a850b4bc913e57a4b065435a17cd17ca715e Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 18 May 2024 22:26:24 +0200 Subject: [PATCH 108/125] Fix up new missing CastExp field in DMD C++ header --- dmd/expression.h | 1 + 1 file changed, 1 insertion(+) diff --git a/dmd/expression.h b/dmd/expression.h index 77c80112675..f7afe71b3f8 100644 --- a/dmd/expression.h +++ b/dmd/expression.h @@ -914,6 +914,7 @@ class CastExp final : public UnaExp // Possible to cast to one type while painting to another type Type *to; // type to cast to unsigned char mod; // MODxxxxx + bool trusted; // assume cast is safe CastExp *syntaxCopy() override; bool isLvalue() override; From 55abbe742285fcee0d96a9b2d65646090961777d Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 18 May 2024 22:28:39 +0200 Subject: [PATCH 109/125] Streamline description for -verrors --- driver/cl_options.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/driver/cl_options.cpp b/driver/cl_options.cpp index fb1ca9140a3..af12e65da1e 100644 --- a/driver/cl_options.cpp +++ b/driver/cl_options.cpp @@ -137,7 +137,8 @@ static cl::opt verbose_cg_ast("vcg-ast", cl::ZeroOrMore, cl::Hidden, static cl::opt errorLimit( "verrors", cl::ZeroOrMore, cl::location(global.params.v.errorLimit), - cl::desc("Limit the number of error messages (0 means unlimited)")); + cl::desc( + "Limit the number of error/deprecation messages (0 means unlimited)")); static cl::opt showGaggedErrors("verrors-spec", cl::ZeroOrMore, From 279aba31941e1cc7b6f3051c9b0d857d43275436 Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Sun, 19 May 2024 12:09:09 +0200 Subject: [PATCH 110/125] Fix bugzilla 24546 for musl libc (dlang/dmd!16507) --- runtime/druntime/src/importc.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/runtime/druntime/src/importc.h b/runtime/druntime/src/importc.h index 13891715199..72d62d8e442 100644 --- a/runtime/druntime/src/importc.h +++ b/runtime/druntime/src/importc.h @@ -70,12 +70,18 @@ typedef unsigned long long __uint64_t; * Obsolete detritus */ #define __cdecl +#define __pascal + +/********************* + * DMC-specific extensions, https://digitalmars.com/ctg/pointers16.html + */ +#ifdef __DMC__ #define __ss #define __cs #define __far #define __near #define __handle -#define __pascal +#endif /**************************** * __extension__ is a GNU C extension. It suppresses warnings From bc8110c4047a795b3f918d9ce8e3073225eec1c5 Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Mon, 20 May 2024 00:17:23 +0200 Subject: [PATCH 111/125] Only test druntime exceptions gdb test case (rt_trap_exceptions_drt_gdb) when gdb is available. (dlang/dmd!16513) --- runtime/druntime/test/exceptions/Makefile | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/runtime/druntime/test/exceptions/Makefile b/runtime/druntime/test/exceptions/Makefile index 230013cc650..fbcf0df032b 100644 --- a/runtime/druntime/test/exceptions/Makefile +++ b/runtime/druntime/test/exceptions/Makefile @@ -1,5 +1,9 @@ include ../common.mak +DIFF:=diff +SED:=sed +GDB:=gdb + TESTS=stderr_msg unittest_assert invalid_memory_operation unknown_gc static_dtor \ future_message refcounted rt_trap_exceptions_drt catch_in_finally \ message_with_null @@ -9,7 +13,10 @@ ifeq ($(OS)-$(BUILD),linux-debug) LINE_TRACE_DFLAGS:=-L--export-dynamic endif ifeq ($(OS),linux) - TESTS+=rt_trap_exceptions_drt_gdb + # Only add this test if gdb is available. + ifneq (, $(shell which $(GDB))) + TESTS+=rt_trap_exceptions_drt_gdb + endif endif ifeq ($(OS)-$(BUILD),freebsd-debug) TESTS+=line_trace line_trace_21656 long_backtrace_trunc cpp_demangle @@ -31,10 +38,6 @@ ifeq ($(BUILD),debug) TESTS+=assert_fail endif -DIFF:=diff -SED:=sed -GDB:=gdb - .PHONY: all clean all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) From ce1170733a67aef1176c51ffaade6af31b3c8fa3 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 25 May 2024 13:25:51 +0200 Subject: [PATCH 112/125] Fix: Initialize new global.compileEnv.{c,d}CharLookupTable --- dmd/main.d | 17 +++++++++++------ dmd/mars.h | 2 +- driver/main.cpp | 6 +----- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/dmd/main.d b/dmd/main.d index 18209ed19cf..9b51c4d26ba 100644 --- a/dmd/main.d +++ b/dmd/main.d @@ -177,11 +177,17 @@ private: * Returns: * Application return code */ -version (IN_LLVM) {} else -private int tryMain(size_t argc, const(char)** argv, ref Param params) +// LDC: changed from `private int tryMain(size_t argc, const(char)** argv, ref Param params)` +extern (C++) int mars_tryMain(ref Param params, ref Strings files) { import dmd.common.charactertables; +version (IN_LLVM) +{ + Strings libmodules; +} +else +{ Strings files; Strings libmodules; global._init(); @@ -189,6 +195,7 @@ private int tryMain(size_t argc, const(char)** argv, ref Param params) if (parseCommandlineAndConfig(argc, argv, params, files)) return EXIT_FAILURE; +} global.compileEnv.previewIn = global.params.previewIn; global.compileEnv.ddocOutput = global.params.ddoc.doOutput; @@ -239,6 +246,8 @@ private int tryMain(size_t argc, const(char)** argv, ref Param params) break; } +version (IN_LLVM) {} else +{ if (params.help.usage) { usage(); @@ -250,12 +259,8 @@ private int tryMain(size_t argc, const(char)** argv, ref Param params) logo(); return EXIT_SUCCESS; } - - return mars_mainBody(params, files, libmodules); } -extern (C++) int mars_mainBody(ref Param params, ref Strings files, ref Strings libmodules) -{ /* Prints a supplied usage text to the console and returns the exit code for the help usage page. diff --git a/dmd/mars.h b/dmd/mars.h index d5fae0eb254..01126c4ea29 100644 --- a/dmd/mars.h +++ b/dmd/mars.h @@ -4,7 +4,7 @@ struct Param; -int mars_mainBody(Param ¶ms, Strings &files, Strings &libmodules); +int mars_tryMain(Param ¶ms, Strings &files); void parseTransitionOption(Param ¶ms, const char *name); void parsePreviewOption(Param ¶ms, const char *name); diff --git a/driver/main.cpp b/driver/main.cpp index 9dcceeadf6c..bf732a5e72a 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -1153,9 +1153,6 @@ int cppmain() { fatal(); } - global.compileEnv.previewIn = global.params.previewIn; - global.compileEnv.ddocOutput = global.params.ddoc.doOutput; - if (opts::fTimeTrace) { initializeTimeTrace(opts::fTimeTraceGranularity, 0, opts::allArguments[0]); } @@ -1240,8 +1237,7 @@ int cppmain() { int status; { TimeTraceScope timeScope("ExecuteCompiler"); - Strings libmodules; - status = mars_mainBody(global.params, files, libmodules); + status = mars_tryMain(global.params, files); } // try to remove the temp objects dir if created for -cleanup-obj From 4c45f656e9d8b4f087bb4551ff70f682ac90bb27 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 25 May 2024 13:56:20 +0200 Subject: [PATCH 113/125] Adapt to __builtins.di having been renamed to __importc_builtins.di --- .github/actions/5-install/action.yml | 2 +- runtime/CMakeLists.txt | 2 +- runtime/druntime/src/importc.h | 2 +- runtime/druntime/src/object.d | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/actions/5-install/action.yml b/.github/actions/5-install/action.yml index 0bc682e35b9..52d177e6a91 100644 --- a/.github/actions/5-install/action.yml +++ b/.github/actions/5-install/action.yml @@ -29,7 +29,7 @@ runs: cp build-cross/bin/ldc2_install.conf install/etc/ldc2.conf cp -R ldc/packaging/bash_completion.d install/etc/ mkdir install/import - cp -R ldc/runtime/druntime/src/{core,etc,ldc,object.d,__builtins.di,importc.h} install/import/ + cp -R ldc/runtime/druntime/src/{core,etc,ldc,object.d,__importc_builtins.di,importc.h} install/import/ cp bootstrap-ldc/runtime/import/ldc/gccbuiltins_*.di install/import/ldc/ cp -R ldc/runtime/phobos/etc/c install/import/etc/ rm -rf install/import/etc/c/zlib diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index db1eaa5ecd0..b392acb8bbc 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -873,7 +873,7 @@ endforeach() set(DRUNTIME_PACKAGES core etc ldc) -install(FILES ${RUNTIME_DIR}/src/object.d ${RUNTIME_DIR}/src/__builtins.di ${RUNTIME_DIR}/src/importc.h DESTINATION ${INCLUDE_INSTALL_DIR}) +install(FILES ${RUNTIME_DIR}/src/object.d ${RUNTIME_DIR}/src/__importc_builtins.di ${RUNTIME_DIR}/src/importc.h DESTINATION ${INCLUDE_INSTALL_DIR}) foreach(p ${DRUNTIME_PACKAGES}) install(DIRECTORY ${RUNTIME_DIR}/src/${p} DESTINATION ${INCLUDE_INSTALL_DIR} FILES_MATCHING PATTERN "*.d") install(DIRECTORY ${RUNTIME_DIR}/src/${p} DESTINATION ${INCLUDE_INSTALL_DIR} FILES_MATCHING PATTERN "*.di") diff --git a/runtime/druntime/src/importc.h b/runtime/druntime/src/importc.h index 38ee7944507..7a1405bcfe8 100644 --- a/runtime/druntime/src/importc.h +++ b/runtime/druntime/src/importc.h @@ -85,7 +85,7 @@ typedef unsigned long long __uint64_t; #define __builtin_isnan(x) isnan(x) #define __builtin_isfinite(x) finite(x) -// IN_LLVM: replaced by symbol in __builtins.di +// IN_LLVM: replaced by symbol in __importc_builtins.di //#define __builtin_alloca(x) alloca(x) /******************************** diff --git a/runtime/druntime/src/object.d b/runtime/druntime/src/object.d index be3ce87ae3a..582467ca300 100644 --- a/runtime/druntime/src/object.d +++ b/runtime/druntime/src/object.d @@ -76,7 +76,7 @@ alias string = immutable(char)[]; alias wstring = immutable(wchar)[]; alias dstring = immutable(dchar)[]; -version (LDC) // note: there's a copy for importC in __builtins.di +version (LDC) // note: there's a copy for importC in __importc_builtins.di { version (ARM) version = ARM_Any; version (AArch64) version = ARM_Any; From b39fac702d8319895a2fb371b891fb5b73aaddbb Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 25 May 2024 14:14:09 +0200 Subject: [PATCH 114/125] Minimally adapt a new Windows-only test for LDC --- tests/dmd/dshell/issue24111.d | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/dmd/dshell/issue24111.d b/tests/dmd/dshell/issue24111.d index 175cae47846..fff780e371b 100644 --- a/tests/dmd/dshell/issue24111.d +++ b/tests/dmd/dshell/issue24111.d @@ -8,6 +8,11 @@ int main() run(cmd); import std.process: environment; + version (LDC) + { + // if VSINSTALLDIR is set, LDC assumes INCLUDE is set up too + environment.remove("VSINSTALLDIR"); + } environment.remove("INCLUDE"); run(cmd); } From 3876ff8a7f15c818a6d035b3b661c83eff1e5ad5 Mon Sep 17 00:00:00 2001 From: "Richard (Rikki) Andrew Cattermole" Date: Sat, 25 May 2024 23:54:31 +1200 Subject: [PATCH 115/125] Add missing word to CLI documentation of UAX31 table selection flag --- dmd/cli.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dmd/cli.d b/dmd/cli.d index ab7dcfa6e1e..1dade3e0815 100644 --- a/dmd/cli.d +++ b/dmd/cli.d @@ -473,7 +473,7 @@ dmd -cov -unittest myprog.d $(LI $(I UAX31): UAX31) $(LI $(I c99): C99) $(LI $(I c11): C11) - $(LI $(I all): All, the least restrictive set, which comes all others (default)) + $(LI $(I all): All, the least restrictive set, which comes with all others (default)) )` ), Option("identifiers-importc=
", @@ -483,7 +483,7 @@ dmd -cov -unittest myprog.d $(LI $(I UAX31): UAX31) $(LI $(I c99): C99) $(LI $(I c11): C11 (default)) - $(LI $(I all): All, the least restrictive set, which comes all others) + $(LI $(I all): All, the least restrictive set, which comes with all others) )` ), Option("ignore", From 7de830d0619ea095df06197dd7ae241290da9cbf Mon Sep 17 00:00:00 2001 From: RazvanN7 Date: Fri, 24 May 2024 12:47:02 +0300 Subject: [PATCH 116/125] Fix Bugzilla Issue 24560 - dmd crash on imported function with default parameter containing new --- dmd/expressionsem.d | 15 +++++++++++++++ tests/dmd/compilable/test24560.d | 12 ++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 tests/dmd/compilable/test24560.d diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 9b6a45019b2..481806d392b 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -15286,6 +15286,9 @@ Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc) Expression visitStructLiteral(StructLiteralExp exp) { + if (!exp.elements) + return exp; + foreach (ref element; *exp.elements) { if (element) @@ -15304,6 +15307,9 @@ Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc) if (exp.lowering) exp.lowering = exp.lowering.resolveLoc(loc, sc); + if (!exp.arguments) + return exp; + foreach (ref element; *exp.arguments) { if (element) @@ -15315,6 +15321,9 @@ Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc) Expression visitCall(CallExp exp) { + if (!exp.arguments) + return exp; + foreach (ref element; *exp.arguments) { if (element) @@ -15328,6 +15337,9 @@ Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc) { exp.e1 = exp.e1.resolveLoc(loc, sc); + if (!exp.arguments) + return exp; + foreach (ref element; *exp.arguments) { if (element) @@ -15361,6 +15373,9 @@ Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc) if (exp.basis) exp.basis = exp.basis.resolveLoc(loc, sc); + if (!exp.elements) + return exp; + foreach (ref element; *exp.elements) { if (element) diff --git a/tests/dmd/compilable/test24560.d b/tests/dmd/compilable/test24560.d new file mode 100644 index 00000000000..a4a4926622b --- /dev/null +++ b/tests/dmd/compilable/test24560.d @@ -0,0 +1,12 @@ +// https://issues.dlang.org/show_bug.cgi?id=24560 + +class C { } +struct S +{ + static void fun(C heur = new C) { } +} + +void main() +{ + S.fun(); +} From 0014f2c530b1e7c792ad2df60cc52129126989eb Mon Sep 17 00:00:00 2001 From: Harry Gillanders Date: Sat, 1 Jun 2024 05:58:50 +0100 Subject: [PATCH 117/125] Fix Bugzilla Issue 24504 - ImportC: Enum declarations with a mixture of int and uint literal values cause errors, when targeting Windows, when debug info generation is enabled. (dlang/dmd!16385) --- dmd/enumsem.d | 5 ++++- tests/dmd/compilable/test24504.c | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 tests/dmd/compilable/test24504.c diff --git a/dmd/enumsem.d b/dmd/enumsem.d index 3886ca25e97..c67ac618ec3 100644 --- a/dmd/enumsem.d +++ b/dmd/enumsem.d @@ -325,7 +325,10 @@ void enumSemantic(Scope* sc, EnumDeclaration ed) if (EnumMember em = s.isEnumMember()) { em.type = commonType; - em.value = em.value.castTo(sc, commonType); + // optimize out the cast so that other parts of the compiler can + // assume that an integral enum's members are `IntegerExp`s. + // https://issues.dlang.org/show_bug.cgi?id=24504 + em.value = em.value.castTo(sc, commonType).optimize(WANTvalue); } }); } diff --git a/tests/dmd/compilable/test24504.c b/tests/dmd/compilable/test24504.c new file mode 100644 index 00000000000..7deb64801a8 --- /dev/null +++ b/tests/dmd/compilable/test24504.c @@ -0,0 +1,10 @@ +// REQUIRED_ARGS: -os=windows -g +// DISABLED: osx +// This is disabled on macOS because ld complains about _main being undefined +// when clang attempts to preprocess the C file. + +typedef enum +{ + HasIntAndUIntValuesInt = 0, + HasIntAndUIntValuesUInt = 0x80000000 +} HasIntAndUIntValues; From e67918b0b58113ad357f618ee1c4b620c20bc907 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 1 Jun 2024 12:42:27 +0200 Subject: [PATCH 118/125] Adopt fixed CLI documentation string --- driver/cl_options.cpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/driver/cl_options.cpp b/driver/cl_options.cpp index af12e65da1e..044c9dcab31 100644 --- a/driver/cl_options.cpp +++ b/driver/cl_options.cpp @@ -611,23 +611,25 @@ static cl::opt betterC( static cl::opt identifiers( "identifiers", cl::ZeroOrMore, cl::location(global.params.dIdentifierTable), cl::desc("Specify the non-ASCII tables for D identifiers"), - cl::values(clEnumValN(CLIIdentifierTable::C99, "c99", "C99"), - clEnumValN(CLIIdentifierTable::C11, "c11", "C11"), - clEnumValN(CLIIdentifierTable::UAX31, "UAX31", "UAX31"), - clEnumValN(CLIIdentifierTable::All, "all", - "All, the least restrictive set, which comes all " - "others (default)"))); + cl::values( + clEnumValN(CLIIdentifierTable::C99, "c99", "C99"), + clEnumValN(CLIIdentifierTable::C11, "c11", "C11"), + clEnumValN(CLIIdentifierTable::UAX31, "UAX31", "UAX31"), + clEnumValN(CLIIdentifierTable::All, "all", + "All, the least restrictive set, which comes with all " + "others (default)"))); static cl::opt identifiersImportc( "identifiers-importc", cl::ZeroOrMore, cl::location(global.params.cIdentifierTable), cl::desc("Specify the non-ASCII tables for ImportC identifiers"), - cl::values(clEnumValN(CLIIdentifierTable::C99, "c99", "C99"), - clEnumValN(CLIIdentifierTable::C11, "c11", "C11 (default)"), - clEnumValN(CLIIdentifierTable::UAX31, "UAX31", "UAX31"), - clEnumValN(CLIIdentifierTable::All, "all", - "All, the least restrictive set, which comes all " - "others"))); + cl::values( + clEnumValN(CLIIdentifierTable::C99, "c99", "C99"), + clEnumValN(CLIIdentifierTable::C11, "c11", "C11 (default)"), + clEnumValN(CLIIdentifierTable::UAX31, "UAX31", "UAX31"), + clEnumValN(CLIIdentifierTable::All, "all", + "All, the least restrictive set, which comes with all " + "others"))); // `-cov[=|ctfe]` parser. struct CoverageParser : public cl::parser { From c1383b546abed43c6ffcb10855d07ab25306d7cf Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 1 Jun 2024 13:24:19 +0200 Subject: [PATCH 119/125] Work around silly -os=windows usage in upstream test --- tests/dmd/compilable/test24504.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/dmd/compilable/test24504.c b/tests/dmd/compilable/test24504.c index 7deb64801a8..2b389e322a1 100644 --- a/tests/dmd/compilable/test24504.c +++ b/tests/dmd/compilable/test24504.c @@ -1,4 +1,5 @@ -// REQUIRED_ARGS: -os=windows -g +// (disabled for LDC, would require full triple:) rEQUIRED_ARGS: -os=windows +// REQUIRED_ARGS: -g // DISABLED: osx // This is disabled on macOS because ld complains about _main being undefined // when clang attempts to preprocess the C file. From e56c0917167d3d6fafdba6ce739219633de9408f Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 1 Jun 2024 12:32:13 +0200 Subject: [PATCH 120/125] Fix missing new CastExp field in C++ header --- dmd/expression.h | 1 + 1 file changed, 1 insertion(+) diff --git a/dmd/expression.h b/dmd/expression.h index 1ff6c4c863e..ad792817c1b 100644 --- a/dmd/expression.h +++ b/dmd/expression.h @@ -889,6 +889,7 @@ class CastExp final : public UnaExp // Possible to cast to one type while painting to another type Type *to; // type to cast to unsigned char mod; // MODxxxxx + d_bool trusted; // assume cast is safe CastExp *syntaxCopy() override; bool isLvalue() override; From 657e32adf2e18436ed087296cd2811842728c31b Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 1 Jun 2024 13:31:30 +0200 Subject: [PATCH 121/125] Don't needlessly cross-preprocess in 2 tests When running these tests on non-Windows, the compiler would need to cross-preprocess the .c sources before cross-compiling. DMD doesn't handle that properly (on Linux, just uses the native preprocessor, and failing badly with Apple clang on macOS). And for LDC, that would require an installed C cross-toolchain. That'd be a ridiculous effort for these 2 tests, so just preprocess and compile them like all other tests, even if the original problems were on Windows only. No harm in running these tests on Posix too while targeting Posix. --- tests/dmd/compilable/test24504.c | 5 +---- tests/dmd/compilable/test24511.d | 4 ---- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/tests/dmd/compilable/test24504.c b/tests/dmd/compilable/test24504.c index 7deb64801a8..06e07d00317 100644 --- a/tests/dmd/compilable/test24504.c +++ b/tests/dmd/compilable/test24504.c @@ -1,7 +1,4 @@ -// REQUIRED_ARGS: -os=windows -g -// DISABLED: osx -// This is disabled on macOS because ld complains about _main being undefined -// when clang attempts to preprocess the C file. +// REQUIRED_ARGS: -g typedef enum { diff --git a/tests/dmd/compilable/test24511.d b/tests/dmd/compilable/test24511.d index 4e5b75d845a..85f63bbc432 100644 --- a/tests/dmd/compilable/test24511.d +++ b/tests/dmd/compilable/test24511.d @@ -1,8 +1,4 @@ -// REQUIRED_ARGS: -os=windows // EXTRA_SOURCES: imports/test24511_c.c -// DISABLED: osx -// This is disabled on macOS because ld complains about _main being undefined -// when clang attempts to preprocess the C file. import test24511_c; From c006a2a9234ec0d2b3438e0a1fec75d5ea524347 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 2 Jun 2024 16:59:28 +0200 Subject: [PATCH 122/125] Bump mimalloc and bundled dlang tools / dub / reggae --- packaging/dlang-tools_version | 2 +- packaging/dub_version | 2 +- packaging/mimalloc_version | 2 +- packaging/reggae_version | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packaging/dlang-tools_version b/packaging/dlang-tools_version index 2753e37975c..f6dc3c4ebf7 100644 --- a/packaging/dlang-tools_version +++ b/packaging/dlang-tools_version @@ -1 +1 @@ -v2.108.1 \ No newline at end of file +v2.109.0 \ No newline at end of file diff --git a/packaging/dub_version b/packaging/dub_version index 205d68570d0..f49415ca625 100644 --- a/packaging/dub_version +++ b/packaging/dub_version @@ -1 +1 @@ -v1.37.0 \ No newline at end of file +v1.38.0 \ No newline at end of file diff --git a/packaging/mimalloc_version b/packaging/mimalloc_version index e1fbb880433..89365caf557 100644 --- a/packaging/mimalloc_version +++ b/packaging/mimalloc_version @@ -1 +1 @@ -v1.8.2 \ No newline at end of file +v1.8.7 \ No newline at end of file diff --git a/packaging/reggae_version b/packaging/reggae_version index a3011f59c6c..3a945540e4f 100644 --- a/packaging/reggae_version +++ b/packaging/reggae_version @@ -1 +1 @@ -563fb9247a93a352a0999afebe810cbd740c6502 \ No newline at end of file +286ca85619e2476218fa026442cb6ed658d708be \ No newline at end of file From 45b8756396966521505b7ef126bfbb73a262b9c6 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 2 Jun 2024 17:02:02 +0200 Subject: [PATCH 123/125] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc8d6ece7af..56ead4401dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # LDC master #### Big news +- Frontend, druntime and Phobos are at version [2.109.0](https://dlang.org/changelog/2.109.0.html). (#4660) #### Platform support - Supports LLVM 15 - 18. Support for LLVM 11 - 14 was dropped. The CLI options `-passmanager` and `-opaque-pointers` were removed. From e940daf819f960143a32309762dabcb0662d3d49 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Mon, 3 Jun 2024 15:01:40 +0200 Subject: [PATCH 124/125] Fix unrelated recent compile regression with MSVC++ --- driver/dcomputecodegenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver/dcomputecodegenerator.cpp b/driver/dcomputecodegenerator.cpp index efd8b8b4dc8..534757d4533 100644 --- a/driver/dcomputecodegenerator.cpp +++ b/driver/dcomputecodegenerator.cpp @@ -59,7 +59,7 @@ DComputeCodeGenManager::createComputeTarget(const std::string &s) { #endif } -#define STR(x...) #x +#define STR(...) #__VA_ARGS__ #define XSTR(x) STR(x) error(Loc(), From b88f9919d502cb8314cf4489d4e8817667fbc8e5 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Mon, 3 Jun 2024 15:59:06 +0200 Subject: [PATCH 125/125] Adapt lit-test PGO/functions.d to changed GC cleanup at program termination See https://dlang.org/changelog/2.109.0.html#druntime.removenostackcollect. The test depends on all class objects being finalized, and has regressed on some platforms (one object not being finalized because apparently still referenced in the stack). --- tests/PGO/functions.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PGO/functions.d b/tests/PGO/functions.d index 2c5d24114f0..d3eaafeaad4 100644 --- a/tests/PGO/functions.d +++ b/tests/PGO/functions.d @@ -209,7 +209,7 @@ void main() { call_templatefunc(i); outerfunc(i); testanonymous(i); - auto k = new Klass(i); + scope k = new Klass(i); // `scope` for deterministic finalization k.stdmethod(); Klass.staticmethod(i); auto s = Strukt(i);