diff options
author | Iain Buclaw <ibuclaw@gdcproject.org> | 2024-01-28 12:23:14 +0100 |
---|---|---|
committer | Iain Buclaw <ibuclaw@gdcproject.org> | 2024-02-03 12:03:37 +0100 |
commit | 51c4eb28c192ecff4463c973a0ff089e04a80b89 (patch) | |
tree | 160ac2ef2c9d732681e96577ef0009027092aea6 /gcc/d/dmd | |
parent | 854b8555bd49ad97c336b8df7098e725dc196e4f (diff) | |
download | gcc-51c4eb28c192ecff4463c973a0ff089e04a80b89.zip gcc-51c4eb28c192ecff4463c973a0ff089e04a80b89.tar.gz gcc-51c4eb28c192ecff4463c973a0ff089e04a80b89.tar.bz2 |
d: Merge dmd. druntime e770945277, phobos 6d6e0b9b9
D front-end changes:
- Import latest fixes from dmd v2.107.0-beta.1.
- Hex strings can now be cast to integer arrays.
- Add support for Interpolated Expression Sequences.
D runtime changes:
- Import latest fixes from druntime v2.107.0-beta.1.
- New core.interpolation module to provide run-time support for D
interpolated expression sequence literals.
Phobos changes:
- Import latest fixes from phobos v2.107.0-beta.1.
- `std.range.primitives.isBidirectionalRange', and
`std.range.primitives.isRandomAccessRange' now take an optional
element type.
gcc/d/ChangeLog:
* dmd/MERGE: Merge upstream dmd e770945277.
* Make-lang.in (D_FRONTEND_OBJS): Add d/basicmangle.o, d/enumsem.o,
d/funcsem.o, d/templatesem.o.
* d-builtins.cc (build_frontend_type): Update for new front-end
interface.
* d-codegen.cc (declaration_type): Likewise.
(parameter_type): Likewise.
* d-incpath.cc (add_globalpaths): Likewise.
(add_filepaths): Likewise.
(add_import_paths): Likewise.
* d-lang.cc (d_init_options): Likewise.
(d_handle_option): Likewise.
(d_parse_file): Likewise.
* decl.cc (DeclVisitor::finish_vtable): Likewise.
(DeclVisitor::visit (FuncDeclaration *)): Likewise.
(get_symbol_decl): Likewise.
* expr.cc (ExprVisitor::visit (StringExp *)): Likewise.
Implement support for 8-byte hexadecimal strings.
* typeinfo.cc (create_tinfo_types): Update internal TypeInfo
representation.
(TypeInfoVisitor::visit (TypeInfoConstDeclaration *)): Update for new
front-end interface.
(TypeInfoVisitor::visit (TypeInfoInvariantDeclaration *)): Likewise.
(TypeInfoVisitor::visit (TypeInfoSharedDeclaration *)): Likewise.
(TypeInfoVisitor::visit (TypeInfoWildDeclaration *)): Likewise.
(TypeInfoVisitor::visit (TypeInfoClassDeclaration *)): Move data for
TypeInfo_Class.nameSig to the end of the object.
(create_typeinfo): Update for new front-end interface.
libphobos/ChangeLog:
* libdruntime/MERGE: Merge upstream druntime e770945277.
* libdruntime/Makefile.am (DRUNTIME_SOURCES): Add
core/interpolation.d.
* libdruntime/Makefile.in: Regenerate.
* src/MERGE: Merge upstream phobos 6d6e0b9b9.
Diffstat (limited to 'gcc/d/dmd')
48 files changed, 3358 insertions, 2574 deletions
diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE index 138b0b6..9217c65 100644 --- a/gcc/d/dmd/MERGE +++ b/gcc/d/dmd/MERGE @@ -1,4 +1,4 @@ -bce5c1f7b521d22dcf1ea4e2bc3f76d9d28274fa +e7709452775d374c1e2dfb67566668ada3dec5fc The first line of this file holds the git revision number of the last merge done from the dlang/dmd repository. diff --git a/gcc/d/dmd/README.md b/gcc/d/dmd/README.md index f8ac001..23f3333 100644 --- a/gcc/d/dmd/README.md +++ b/gcc/d/dmd/README.md @@ -110,6 +110,8 @@ Note that these groups have no strict meaning, the category assignments are a bi | File | Purpose | |-------------------------------------------------------------------------------------------|-------------------------------------------------------------------| | [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 | | [semantic2.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/semantic2.d) | Do semantic 2 pass (symbol initializers) | | [semantic3.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/semantic3.d) | Do semantic 3 pass (function bodies) | | [inline.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/inline.d) | Do inline pass (optimization pass that dmd does in the front-end) | @@ -117,6 +119,7 @@ Note that these groups have no strict meaning, the category assignments are a bi | [expressionsem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/expressionsem.d) | Do semantic analysis for expressions | | [statementsem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/statementsem.d) | Do semantic analysis for statements | | [initsem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/initsem.d) | Do semantic analysis for initializers | +| [templatesem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/templatesem.d) | Do semantic analysis for templates | | [templateparamsem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/templateparamsem.d) | Do semantic analysis for template parameters | | [typesem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/typesem.d) | Do semantic analysis for types | @@ -230,6 +233,7 @@ Note that these groups have no strict meaning, the category assignments are a bi |-----------------------------------------------------------------------------------|------------------------------------------------------------------| | [cppmangle.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cppmangle.d) | C++ name mangling | | [cppmanglewin.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cppmanglewin.d) | C++ name mangling for Windows | +| [basicmangle.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/basicmangle.d) | D name mangling for basic types | | [dmangle.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dmangle.d) | D [name mangling](https://dlang.org/spec/abi.html#name_mangling) | ### Linking diff --git a/gcc/d/dmd/aggregate.h b/gcc/d/dmd/aggregate.h index 9abdd09..f466ba6 100644 --- a/gcc/d/dmd/aggregate.h +++ b/gcc/d/dmd/aggregate.h @@ -234,7 +234,8 @@ struct ClassFlags hasTypeInfo = 0x20, isAbstract = 0x40, isCPPclass = 0x80, - hasDtor = 0x100 + hasDtor = 0x100, + hasNameSig = 0x200, }; }; diff --git a/gcc/d/dmd/basicmangle.d b/gcc/d/dmd/basicmangle.d new file mode 100644 index 0000000..52534fa --- /dev/null +++ b/gcc/d/dmd/basicmangle.d @@ -0,0 +1,109 @@ +/** + * Defines the building blocks for creating the mangled names for basic types. + * + * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/basicmangle.d, _basicmangle.d) + * Documentation: https://dlang.org/phobos/dmd_basicmangle.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/basicmangle.d + */ +module dmd.basicmangle; + +import dmd.astenums; +import dmd.common.outbuffer : OutBuffer; + +/// Type mangling mapping for basic, derived and user defined types +immutable char[TMAX] mangleChar = +[ + Tchar : 'a', + Tbool : 'b', + Tcomplex80 : 'c', + Tfloat64 : 'd', + Tfloat80 : 'e', + Tfloat32 : 'f', + Tint8 : 'g', + Tuns8 : 'h', + Tint32 : 'i', + Timaginary80 : 'j', + Tuns32 : 'k', + Tint64 : 'l', + Tuns64 : 'm', + Tnull : 'n', + Timaginary32 : 'o', + Timaginary64 : 'p', + Tcomplex32 : 'q', + Tcomplex64 : 'r', + Tint16 : 's', + Tuns16 : 't', + Twchar : 'u', + Tvoid : 'v', + Tdchar : 'w', + // x // const + // y // immutable + Tint128 : 'z', // zi + Tuns128 : 'z', // zk + + Tarray : 'A', + Ttuple : 'B', + Tclass : 'C', + Tdelegate : 'D', + Tenum : 'E', + Tfunction : 'F', // D function + Tsarray : 'G', + Taarray : 'H', + // I // in + // J // out + // K // ref + // L // lazy + // M // has this, or scope + // N // Nh:vector Ng:wild Nn:noreturn + // O // shared + Tpointer : 'P', + // Q // Type/symbol/identifier backward reference + Treference : 'R', + Tstruct : 'S', + // T // Ttypedef + // U // C function + // W // Windows function + // X // variadic T t...) + // Y // variadic T t,...) + // Z // not variadic, end of parameters + + // '@' shouldn't appear anywhere in the deco'd names + Tnone : '@', + Tident : '@', + Tinstance : '@', + Terror : '@', + Ttypeof : '@', + Tslice : '@', + Treturn : '@', + Tvector : '@', + Ttraits : '@', + Tmixin : '@', + Ttag : '@', + Tnoreturn : '@', // becomes 'Nn' +]; + +unittest +{ + foreach (i, mangle; mangleChar) + { + if (mangle == char.init) + { + import core.stdc.stdio; + fprintf(stderr, "ty = %u\n", cast(uint)i); + assert(0); + } + } +} + +/*********************** + * Mangle basic type ty to buf. + */ +void tyToDecoBuffer(ref OutBuffer buf, int ty) @safe +{ + const c = mangleChar[ty]; + buf.writeByte(c); + if (c == 'z') + buf.writeByte(ty == Tint128 ? 'i' : 'k'); +} diff --git a/gcc/d/dmd/clone.d b/gcc/d/dmd/clone.d index 6fbf8e2..46470ee 100644 --- a/gcc/d/dmd/clone.d +++ b/gcc/d/dmd/clone.d @@ -27,6 +27,7 @@ import dmd.errors; import dmd.expression; import dmd.expressionsem; import dmd.func; +import dmd.funcsem; import dmd.globals; import dmd.id; import dmd.identifier; @@ -905,7 +906,7 @@ void buildDtors(AggregateDeclaration ad, Scope* sc) a.addMember(sc, ad); // temporarily add to symbol table } - sdv.dtor.functionSemantic(); + functionSemantic(sdv.dtor); stc = mergeFuncAttrs(stc, sdv.dtor); if (stc & STC.disable) @@ -1154,7 +1155,7 @@ private DtorDeclaration buildExternDDtor(AggregateDeclaration ad, Scope* sc) ad.members.push(func); func.addMember(sc2, ad); func.dsymbolSemantic(sc2); - func.functionSemantic(); // to infer attributes + functionSemantic(func); // to infer attributes sc2.pop(); return func; @@ -1336,7 +1337,7 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) // perform semantic on the member postblit in order to // be able to aggregate it later on with the rest of the // postblits - sdv.postblit.functionSemantic(); + functionSemantic(sdv.postblit); stc = mergeFuncAttrs(stc, sdv.postblit); stc = mergeFuncAttrs(stc, sdv.dtor); @@ -1401,7 +1402,7 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) */ if (sdv.dtor) { - sdv.dtor.functionSemantic(); + functionSemantic(sdv.dtor); // keep a list of fields that need to be destroyed in case // of a future postblit failure diff --git a/gcc/d/dmd/common/outbuffer.d b/gcc/d/dmd/common/outbuffer.d index cff08ec..cb2439f 100644 --- a/gcc/d/dmd/common/outbuffer.d +++ b/gcc/d/dmd/common/outbuffer.d @@ -756,6 +756,25 @@ struct OutBuffer } /** + * Write an array as a string of hexadecimal digits + * Params: + * data = bytes to write + * upperCase = whether to upper case hex digits A-F + */ + void writeHexString(scope const(ubyte)[] data, bool upperCase) pure nothrow @safe + { + auto slice = this.allocate(2 * data.length); + const a = upperCase ? 'A' : 'a'; + foreach (i, c; data) + { + char hi = (c >> 4) & 0xF; + slice[i * 2] = cast(char)(hi < 10 ? hi + '0' : hi - 10 + a); + char lo = c & 0xF; + slice[i * 2 + 1] = cast(char)(lo < 10 ? lo + '0' : lo - 10 + a); + } + } + + /** Destructively saves the contents of `this` to `filename`. As an optimization, if the file already has identical contents with the buffer, no copying is done. This is because on SSD drives reading is often much @@ -943,3 +962,11 @@ unittest else assert(buf[] == "\nabc\n\n"); } + +unittest +{ + OutBuffer buf; + buf.writeHexString([0xAA, 0xBB], false); + buf.writeHexString([0xCC], true); + assert(buf[] == "aabbCC"); +} diff --git a/gcc/d/dmd/cond.d b/gcc/d/dmd/cond.d index 1b32ff7..e194664 100644 --- a/gcc/d/dmd/cond.d +++ b/gcc/d/dmd/cond.d @@ -548,8 +548,6 @@ extern (C++) final class DebugCondition : DVCondition /// Ditto extern(D) static void addGlobalIdent(const(char)[] ident) { - if (!global.debugids) - global.debugids = new Identifiers(); global.debugids.push(Identifier.idPool(ident)); } @@ -579,7 +577,7 @@ extern (C++) final class DebugCondition : DVCondition bool definedInModule = false; if (ident) { - if (findCondition(mod.debugids, ident)) + if (mod.debugids && findCondition(*mod.debugids, ident)) { inc = Include.yes; definedInModule = true; @@ -830,8 +828,6 @@ extern (C++) final class VersionCondition : DVCondition /// Ditto extern(D) static void addPredefinedGlobalIdent(const(char)[] ident) { - if (!global.versionids) - global.versionids = new Identifiers(); global.versionids.push(Identifier.idPool(ident)); } @@ -861,7 +857,7 @@ extern (C++) final class VersionCondition : DVCondition bool definedInModule = false; if (ident) { - if (findCondition(mod.versionids, ident)) + if (mod.versionids && findCondition(*mod.versionids, ident)) { inc = Include.yes; definedInModule = true; @@ -983,15 +979,12 @@ extern (C++) final class StaticIfCondition : Condition * Returns: * true if found */ -bool findCondition(Identifiers* ids, Identifier ident) @safe nothrow pure +bool findCondition(ref Identifiers ids, Identifier ident) @safe nothrow pure { - if (ids) + foreach (id; ids) { - foreach (id; *ids) - { - if (id == ident) - return true; - } + if (id == ident) + return true; } return false; } diff --git a/gcc/d/dmd/constfold.d b/gcc/d/dmd/constfold.d index cee1f63..41fed9c 100644 --- a/gcc/d/dmd/constfold.d +++ b/gcc/d/dmd/constfold.d @@ -711,7 +711,7 @@ UnionExp Equal(EXP op, const ref Loc loc, Type type, Expression e1, Expression e cmp = 1; // if dim1 winds up being 0 foreach (i; 0 .. dim1) { - uinteger_t c = es1.getCodeUnit(i); + uinteger_t c = es1.getIndex(i); auto ee2 = es2[i]; if (ee2.isConst() != 1) { @@ -1119,7 +1119,7 @@ UnionExp Index(Type type, Expression e1, Expression e2, bool indexIsInBounds) } else { - emplaceExp!(IntegerExp)(&ue, loc, es1.getCodeUnit(cast(size_t) i), type); + emplaceExp!(IntegerExp)(&ue, loc, es1.getIndex(cast(size_t) i), type); } } else if (e1.type.toBasetype().ty == Tsarray && e2.op == EXP.int64) @@ -1282,7 +1282,7 @@ void sliceAssignArrayLiteralFromString(ArrayLiteralExp existingAE, const StringE Type elemType = existingAE.type.nextOf(); foreach (j; 0 .. len) { - const val = newval.getCodeUnit(j); + const val = newval.getIndex(j); (*existingAE.elements)[j + firstIndex] = new IntegerExp(newval.loc, val, elemType); } } diff --git a/gcc/d/dmd/ctfeexpr.d b/gcc/d/dmd/ctfeexpr.d index 5fe1e7d..af83aad 100644 --- a/gcc/d/dmd/ctfeexpr.d +++ b/gcc/d/dmd/ctfeexpr.d @@ -568,6 +568,9 @@ StringExp createBlockDuplicatedStringLiteral(UnionExp* pue, const ref Loc loc, T case 4: (cast(dchar*)s)[elemi] = value; break; + case 8: + (cast(ulong*)s)[elemi] = value; + break; default: assert(0); } @@ -1494,7 +1497,7 @@ Expression ctfeIndex(UnionExp* pue, const ref Loc loc, Type type, Expression e1, error(loc, "string index %llu is out of bounds `[0 .. %llu]`", indx, cast(ulong)es1.len); return CTFEExp.cantexp; } - emplaceExp!IntegerExp(pue, loc, es1.getCodeUnit(cast(size_t) indx), type); + emplaceExp!IntegerExp(pue, loc, es1.getIndex(cast(size_t) indx), type); return pue.exp(); } @@ -1704,7 +1707,7 @@ Expression changeArrayLiteralLength(UnionExp* pue, const ref Loc loc, TypeArray void* s = mem.xcalloc(newlen + 1, oldse.sz); const data = oldse.peekData(); memcpy(s, data.ptr, copylen * oldse.sz); - const defaultValue = cast(uint)defaultElem.toInteger(); + const defaultValue = cast(ulong)defaultElem.toInteger(); foreach (size_t elemi; copylen .. newlen) { switch (oldse.sz) @@ -1718,6 +1721,9 @@ Expression changeArrayLiteralLength(UnionExp* pue, const ref Loc loc, TypeArray case 4: (cast(dchar*)s)[cast(size_t)(indxlo + elemi)] = cast(dchar)defaultValue; break; + case 8: + (cast(ulong*)s)[cast(size_t)(indxlo + elemi)] = cast(ulong)defaultValue; + break; default: assert(0); } diff --git a/gcc/d/dmd/dclass.d b/gcc/d/dmd/dclass.d index 405e817..7e5e7e4 100644 --- a/gcc/d/dmd/dclass.d +++ b/gcc/d/dmd/dclass.d @@ -123,6 +123,7 @@ extern (C++) struct BaseClass } } +// These must match the values in druntime/src/object.d enum ClassFlags : uint { none = 0x0, @@ -135,6 +136,7 @@ enum ClassFlags : uint isAbstract = 0x40, isCPPclass = 0x80, hasDtor = 0x100, + hasNameSig = 0x200, } /*********************************************************** diff --git a/gcc/d/dmd/declaration.h b/gcc/d/dmd/declaration.h index 0e327be..e4efbbc 100644 --- a/gcc/d/dmd/declaration.h +++ b/gcc/d/dmd/declaration.h @@ -30,6 +30,9 @@ class StructDeclaration; struct IntRange; struct AttributeViolation; +bool functionSemantic(FuncDeclaration* fd); +bool functionSemantic3(FuncDeclaration* fd); + //enum STC : ulong from astenums.d: #define STCundefined 0ULL @@ -698,14 +701,12 @@ public: FuncDeclaration *fdensure(FuncDeclaration *fde); Expressions *fdrequireParams(Expressions *fdrp); Expressions *fdensureParams(Expressions *fdep); - bool functionSemantic(); - bool functionSemantic3(); bool equals(const RootObject * const o) const override final; int findVtblIndex(Dsymbols *vtbl, int dim); bool overloadInsert(Dsymbol *s) override; bool inUnittest(); - MATCH leastAsSpecialized(FuncDeclaration *g, Identifiers *names); + static MATCH leastAsSpecialized(FuncDeclaration *f, FuncDeclaration *g, Identifiers *names); LabelDsymbol *searchLabel(Identifier *ident, const Loc &loc); const char *toPrettyChars(bool QualifyTypes = false) override; const char *toFullSignature(); // for diagnostics, e.g. 'int foo(int x, int y) pure' diff --git a/gcc/d/dmd/denum.d b/gcc/d/dmd/denum.d index 5464ff9..9abdebd 100644 --- a/gcc/d/dmd/denum.d +++ b/gcc/d/dmd/denum.d @@ -122,91 +122,6 @@ extern (C++) final class EnumDeclaration : ScopeDsymbol return isSpecialEnumIdent(ident) && memtype; } - Expression getDefaultValue(const ref Loc loc) - { - Expression handleErrors(){ - defaultval = ErrorExp.get(); - return defaultval; - } - //printf("EnumDeclaration::getDefaultValue() %p %s\n", this, toChars()); - // https://issues.dlang.org/show_bug.cgi?id=23904 - // Return defaultval only if it is not ErrorExp. - // A speculative context may set defaultval to ErrorExp; - // subsequent non-speculative contexts need to be able - // to print the error. - if (defaultval && !defaultval.isErrorExp()) - return defaultval; - - if (isCsymbol()) - return memtype.defaultInit(loc, true); - - if (_scope) - dsymbolSemantic(this, _scope); - if (errors) - return handleErrors(); - if (!members) - { - if (isSpecial()) - { - /* Allow these special enums to not need a member list - */ - return defaultval = memtype.defaultInit(loc); - } - - error(loc, "%s `%s` is opaque and has no default initializer", kind, toPrettyChars); - return handleErrors(); - } - - foreach (const i; 0 .. members.length) - { - EnumMember em = (*members)[i].isEnumMember(); - if (em) - { - if (em.semanticRun < PASS.semanticdone) - { - error(loc, "%s `%s` forward reference of `%s.init`", kind, toPrettyChars, toChars()); - return handleErrors(); - } - - defaultval = em.value; - return defaultval; - } - } - return handleErrors(); - } - - Type getMemtype(const ref Loc loc) - { - if (_scope) - { - /* Enum is forward referenced. We don't need to resolve the whole thing, - * just the base type - */ - if (memtype) - { - Loc locx = loc.isValid() ? loc : this.loc; - memtype = memtype.typeSemantic(locx, _scope); - } - else - { - // Run semantic to get the type from a possible first member value - dsymbolSemantic(this, _scope); - } - } - if (!memtype) - { - if (!isAnonymous() && (members || semanticRun >= PASS.semanticdone)) - memtype = Type.tint32; - else - { - Loc locx = loc.isValid() ? loc : this.loc; - error(locx, "is forward referenced looking for base type"); - return Type.terror; - } - } - return memtype; - } - override inout(EnumDeclaration) isEnumDeclaration() inout { return this; diff --git a/gcc/d/dmd/dinterpret.d b/gcc/d/dmd/dinterpret.d index a3b38d9..d8069c6 100644 --- a/gcc/d/dmd/dinterpret.d +++ b/gcc/d/dmd/dinterpret.d @@ -33,6 +33,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; @@ -440,7 +441,7 @@ private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterSta fdError("circular dependency. Functions cannot be interpreted while being compiled"); return CTFEExp.cantexp; } - if (!fd.functionSemantic3()) + if (!functionSemantic3(fd)) return CTFEExp.cantexp; if (fd.semanticRun < PASS.semantic3done) { @@ -6097,11 +6098,35 @@ public: result.type = e.to; return; } + // Disallow array type painting, except for conversions between built-in // types of identical size. if ((e.to.ty == Tsarray || e.to.ty == Tarray) && (e1.type.ty == Tsarray || e1.type.ty == Tarray) && !isSafePointerCast(e1.type.nextOf(), e.to.nextOf())) { + auto se = e1.isStringExp(); + // Allow casting a hex string literal to short[], int[] or long[] + if (se && se.hexString && se.postfix == StringExp.NoPostfix) + { + const sz = cast(size_t) e.to.nextOf().size; + if ((se.len % sz) != 0) + { + error(e.loc, "hex string length %d must be a multiple of %d to cast to `%s`", + cast(int) se.len, cast(int) sz, e.to.toChars()); + result = CTFEExp.cantexp; + return; + } + + auto str = arrayCastBigEndian((cast(const ubyte[]) se.peekString()), sz); + emplaceExp!(StringExp)(pue, e1.loc, str, se.len / sz, cast(ubyte) sz); + result = pue.exp(); + result.type = e.to; + return; + } error(e.loc, "array cast from `%s` to `%s` is not supported at compile time", e1.type.toChars(), e.to.toChars()); + if (se && se.hexString && se.postfix != StringExp.NoPostfix) + errorSupplemental(e.loc, "perhaps remove postfix `%s` from hex string", + (cast(char) se.postfix ~ "\0").ptr); + result = CTFEExp.cantexp; return; } @@ -7719,3 +7744,44 @@ private void removeHookTraceImpl(ref CallExp ce, ref FuncDeclaration fd) if (global.params.v.verbose) message("strip %s =>\n %s", oldCE.toChars(), ce.toChars()); } + +/** + * Cast a `ubyte[]` to an array of larger integers as if we are on a big endian architecture + * Params: + * data = array with big endian data + * size = 1 for ubyte[], 2 for ushort[], 4 for uint[], 8 for ulong[] + * Returns: copy of `data`, with bytes shuffled if compiled for `version(LittleEndian)` + */ +ubyte[] arrayCastBigEndian(const ubyte[] data, size_t size) +{ + ubyte[] impl(T)() + { + auto result = new T[](data.length / T.sizeof); + foreach (i; 0 .. result.length) + { + result[i] = 0; + foreach (j; 0 .. T.sizeof) + { + result[i] |= T(data[i * T.sizeof + j]) << ((T.sizeof - 1 - j) * 8); + } + } + return cast(ubyte[]) result; + } + switch (size) + { + case 1: return data.dup; + case 2: return impl!ushort; + case 4: return impl!uint; + case 8: return impl!ulong; + default: assert(0); + } +} + +unittest +{ + ubyte[] data = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22]; + assert(cast(ulong[]) arrayCastBigEndian(data, 8) == [0xAABBCCDDEEFF1122]); + assert(cast(uint[]) arrayCastBigEndian(data, 4) == [0xAABBCCDD, 0xEEFF1122]); + assert(cast(ushort[]) arrayCastBigEndian(data, 2) == [0xAABB, 0xCCDD, 0xEEFF, 0x1122]); + assert(cast(ubyte[]) arrayCastBigEndian(data, 1) == data); +} diff --git a/gcc/d/dmd/dmangle.d b/gcc/d/dmd/dmangle.d index 15b77ea..5bd1379 100644 --- a/gcc/d/dmd/dmangle.d +++ b/gcc/d/dmd/dmangle.d @@ -137,6 +137,7 @@ import core.stdc.string; import dmd.aggregate; import dmd.arraytypes; import dmd.astenums; +import dmd.basicmangle; import dmd.dclass; import dmd.declaration; import dmd.dinterpret; @@ -161,89 +162,6 @@ import dmd.target; import dmd.tokens; import dmd.visitor; -private immutable char[TMAX] mangleChar = -[ - Tchar : 'a', - Tbool : 'b', - Tcomplex80 : 'c', - Tfloat64 : 'd', - Tfloat80 : 'e', - Tfloat32 : 'f', - Tint8 : 'g', - Tuns8 : 'h', - Tint32 : 'i', - Timaginary80 : 'j', - Tuns32 : 'k', - Tint64 : 'l', - Tuns64 : 'm', - Tnull : 'n', - Timaginary32 : 'o', - Timaginary64 : 'p', - Tcomplex32 : 'q', - Tcomplex64 : 'r', - Tint16 : 's', - Tuns16 : 't', - Twchar : 'u', - Tvoid : 'v', - Tdchar : 'w', - // x // const - // y // immutable - Tint128 : 'z', // zi - Tuns128 : 'z', // zk - - Tarray : 'A', - Ttuple : 'B', - Tclass : 'C', - Tdelegate : 'D', - Tenum : 'E', - Tfunction : 'F', // D function - Tsarray : 'G', - Taarray : 'H', - // I // in - // J // out - // K // ref - // L // lazy - // M // has this, or scope - // N // Nh:vector Ng:wild Nn:noreturn - // O // shared - Tpointer : 'P', - // Q // Type/symbol/identifier backward reference - Treference : 'R', - Tstruct : 'S', - // T // Ttypedef - // U // C function - // W // Windows function - // X // variadic T t...) - // Y // variadic T t,...) - // Z // not variadic, end of parameters - - // '@' shouldn't appear anywhere in the deco'd names - Tnone : '@', - Tident : '@', - Tinstance : '@', - Terror : '@', - Ttypeof : '@', - Tslice : '@', - Treturn : '@', - Tvector : '@', - Ttraits : '@', - Tmixin : '@', - Ttag : '@', - Tnoreturn : '@', // becomes 'Nn' -]; - -unittest -{ - foreach (i, mangle; mangleChar) - { - if (mangle == char.init) - { - fprintf(stderr, "ty = %u\n", cast(uint)i); - assert(0); - } - } -} - /************************************************ * Append the mangling of type `t` to `buf`. * Params: @@ -584,6 +502,20 @@ public: toBuffer(*buf, id.toString(), s); } + void mangleInteger(dinteger_t v) + { + if (cast(sinteger_t) v < 0) + { + buf.writeByte('N'); + buf.print(-v); + } + else + { + buf.writeByte('i'); + buf.print(v); + } + } + //////////////////////////////////////////////////////////////////////////// void mangleDecl(Declaration sthis) { @@ -991,17 +923,7 @@ public: override void visit(IntegerExp e) { - const v = e.toInteger(); - if (cast(sinteger_t)v < 0) - { - buf.writeByte('N'); - buf.print(-v); - } - else - { - buf.writeByte('i'); - buf.print(v); - } + mangleInteger(e.toInteger()); } override void visit(RealExp e) @@ -1028,6 +950,7 @@ public: char m; OutBuffer tmp; const(char)[] q; + /* Write string in UTF-8 format */ switch (e.sz) @@ -1065,7 +988,15 @@ public: q = tmp[]; break; } - + case 8: + // String of size 8 has to be hexstring cast to long[], mangle as array literal + buf.writeByte('A'); + buf.print(e.len); + foreach (i; 0 .. e.len) + { + mangleInteger(e.getIndex(i)); + } + return; default: assert(0); } @@ -1073,14 +1004,7 @@ public: buf.writeByte(m); buf.print(q.length); buf.writeByte('_'); // nbytes <= 11 - auto slice = buf.allocate(2 * q.length); - foreach (i, c; q) - { - char hi = (c >> 4) & 0xF; - slice[i * 2] = cast(char)(hi < 10 ? hi + '0' : hi - 10 + 'a'); - char lo = c & 0xF; - slice[i * 2 + 1] = cast(char)(lo < 10 ? lo + '0' : lo - 10 + 'a'); - } + buf.writeHexString(cast(const(ubyte)[]) q, false); } override void visit(ArrayLiteralExp e) @@ -1168,6 +1092,7 @@ private struct Backref { if (t.isFunction_Delegate_PtrToFunction()) { + import dmd.typesem : merge2; t = t.merge2(); } } @@ -1213,19 +1138,6 @@ private struct Backref AssocArray!(Identifier, size_t) idents; /// Identifier => (offset+1) in buf } - -/*********************** - * Mangle basic type ty to buf. - */ - -private void tyToDecoBuffer(ref OutBuffer buf, int ty) @safe -{ - const c = mangleChar[ty]; - buf.writeByte(c); - if (c == 'z') - buf.writeByte(ty == Tint128 ? 'i' : 'k'); -} - /********************************* * Mangling for mod. */ diff --git a/gcc/d/dmd/dmodule.d b/gcc/d/dmd/dmodule.d index 59d4065..6167e2a 100644 --- a/gcc/d/dmd/dmodule.d +++ b/gcc/d/dmd/dmodule.d @@ -490,7 +490,7 @@ extern (C++) final class Module : Package extern (D) static const(char)[] find(const(char)[] filename) { - return global.fileManager.lookForSourceFile(filename, global.path ? (*global.path)[] : null); + return global.fileManager.lookForSourceFile(filename, global.path[]); } extern (C++) static Module load(const ref Loc loc, Identifiers* packages, Identifier ident) @@ -644,9 +644,9 @@ extern (C++) final class Module : Package { /* Print path */ - if (global.path) + if (global.path.length) { - foreach (i, p; *global.path) + foreach (i, p; global.path[]) fprintf(stderr, "import path[%llu] = %s\n", cast(ulong)i, p); } else diff --git a/gcc/d/dmd/doc.d b/gcc/d/dmd/doc.d index ec3cc91..bcf358c 100644 --- a/gcc/d/dmd/doc.d +++ b/gcc/d/dmd/doc.d @@ -5204,6 +5204,7 @@ void highlightCode2(Scope* sc, Dsymbols* a, ref OutBuffer buf, size_t offset) highlight = "$(D_COMMENT "; break; case TOK.string_: + case TOK.interpolated: highlight = "$(D_STRING "; break; default: @@ -5216,7 +5217,7 @@ void highlightCode2(Scope* sc, Dsymbols* a, ref OutBuffer buf, size_t offset) res.writestring(highlight); size_t o = res.length; highlightCode3(sc, res, tok.ptr, lex.p); - if (tok.value == TOK.comment || tok.value == TOK.string_) + if (tok.value == TOK.comment || tok.value == TOK.string_ || tok.value == TOK.interpolated) /* https://issues.dlang.org/show_bug.cgi?id=7656 * https://issues.dlang.org/show_bug.cgi?id=7715 * https://issues.dlang.org/show_bug.cgi?id=10519 diff --git a/gcc/d/dmd/dstruct.d b/gcc/d/dmd/dstruct.d index 5683d5f..7546fb6 100644 --- a/gcc/d/dmd/dstruct.d +++ b/gcc/d/dmd/dstruct.d @@ -616,7 +616,7 @@ bool _isZeroInit(Expression exp) foreach (i; 0 .. se.len) { - if (se.getCodeUnit(i)) + if (se.getIndex(i) != 0) return false; } return true; diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d index e9cdb94..658beaf 100644 --- a/gcc/d/dmd/dsymbolsem.d +++ b/gcc/d/dmd/dsymbolsem.d @@ -38,11 +38,13 @@ import dmd.dstruct; import dmd.dsymbol; import dmd.dtemplate; import dmd.dversion; +import dmd.enumsem; import dmd.errors; import dmd.escape; import dmd.expression; import dmd.expressionsem; import dmd.func; +import dmd.funcsem; import dmd.globals; import dmd.id; import dmd.identifier; @@ -440,6 +442,15 @@ private bool checkDeprecatedAliasThis(AliasThis at, const ref Loc loc, Scope* sc return false; } +// Save the scope and defer semantic analysis on the Dsymbol. +void deferDsymbolSemantic(Scope* sc, Dsymbol s, Scope *scx) +{ + s._scope = scx ? scx : sc.copy(); + s._scope.setNoFree(); + Module.addDeferredSemantic(s); +} + + private extern(C++) final class DsymbolSemanticVisitor : Visitor { alias visit = Visitor.visit; @@ -450,14 +461,6 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor this.sc = sc; } - // Save the scope and defer semantic analysis on the Dsymbol. - private void deferDsymbolSemantic(Dsymbol s, Scope *scx) - { - s._scope = scx ? scx : sc.copy(); - s._scope.setNoFree(); - Module.addDeferredSemantic(s); - } - override void visit(Dsymbol dsym) { .error(dsym.loc, "%s `%s` %p has no semantic routine", dsym.kind, dsym.toPrettyChars, dsym); @@ -2301,543 +2304,12 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor override void visit(EnumDeclaration ed) { - //printf("EnumDeclaration::semantic(sd = %p, '%s') %s\n", sc.scopesym, sc.scopesym.toChars(), ed.toChars()); - //printf("EnumDeclaration::semantic() %p %s\n", ed, ed.toChars()); - if (ed.semanticRun >= PASS.semanticdone) - return; // semantic() already completed - if (ed.semanticRun == PASS.semantic) - { - assert(ed.memtype); - error(ed.loc, "circular reference to enum base type `%s`", ed.memtype.toChars()); - ed.errors = true; - ed.semanticRun = PASS.semanticdone; - return; - } - Scope* scx = null; - if (ed._scope) - { - sc = ed._scope; - scx = ed._scope; // save so we don't make redundant copies - ed._scope = null; - } - - if (!sc) - return; - - ed.parent = sc.parent; - ed.type = ed.type.typeSemantic(ed.loc, sc); - - ed.visibility = sc.visibility; - if (sc.stc & STC.deprecated_) - ed.isdeprecated = true; - ed.userAttribDecl = sc.userAttribDecl; - ed.cppnamespace = sc.namespace; - - ed.semanticRun = PASS.semantic; - UserAttributeDeclaration.checkGNUABITag(ed, sc.linkage); - checkMustUseReserved(ed); - - if (!ed.members && !ed.memtype) // enum ident; - { - ed.semanticRun = PASS.semanticdone; - return; - } - - if (!ed.symtab) - ed.symtab = new DsymbolTable(); - - /* The separate, and distinct, cases are: - * 1. enum { ... } - * 2. enum : memtype { ... } - * 3. enum ident { ... } - * 4. enum ident : memtype { ... } - * 5. enum ident : memtype; - * 6. enum ident; - */ - - if (ed.memtype) - { - ed.memtype = ed.memtype.typeSemantic(ed.loc, sc); - - /* Check to see if memtype is forward referenced - */ - if (auto te = ed.memtype.isTypeEnum()) - { - auto sym = te.toDsymbol(sc).isEnumDeclaration(); - // Special enums like __c_[u]long[long] are fine to forward reference - // see https://issues.dlang.org/show_bug.cgi?id=20599 - if (!sym.isSpecial() && (!sym.memtype || !sym.members || !sym.symtab || sym._scope)) - { - // memtype is forward referenced, so try again later - deferDsymbolSemantic(ed, scx); - //printf("\tdeferring %s\n", toChars()); - ed.semanticRun = PASS.initial; - return; - } - else - // Ensure that semantic is run to detect. e.g. invalid forward references - sym.dsymbolSemantic(sc); - } - if (ed.memtype.ty == Tvoid) - { - .error(ed.loc, "%s `%s` base type must not be `void`", ed.kind, ed.toPrettyChars); - ed.memtype = Type.terror; - } - if (ed.memtype.ty == Terror) - { - ed.errors = true; - // poison all the members - ed.members.foreachDsymbol( (s) { s.errors = true; } ); - ed.semanticRun = PASS.semanticdone; - return; - } - } - - if (!ed.members) // enum ident : memtype; - { - ed.semanticRun = PASS.semanticdone; - return; - } - - if (ed.members.length == 0) - { - .error(ed.loc, "%s `%s enum `%s` must have at least one member", ed.kind, ed.toPrettyChars, ed.toChars()); - ed.errors = true; - ed.semanticRun = PASS.semanticdone; - return; - } - - if (!(sc.flags & SCOPE.Cfile)) // C enum remains incomplete until members are done - ed.semanticRun = PASS.semanticdone; - - version (none) - { - // @@@DEPRECATED_2.110@@@ https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint - // Deprecated in 2.100 - // Make an error in 2.110 - if (sc.stc & STC.scope_) - deprecation(ed.loc, "`scope` as a type constraint is deprecated. Use `scope` at the usage site."); - } - - Scope* sce; - if (ed.isAnonymous()) - sce = sc; - else - { - sce = sc.push(ed); - sce.parent = ed; - } - sce = sce.startCTFE(); - sce.setNoFree(); // needed for getMaxMinValue() - - /* Each enum member gets the sce scope - */ - ed.members.foreachDsymbol( (s) - { - EnumMember em = s.isEnumMember(); - if (em) - em._scope = sce; - }); - - /* addMember() is not called when the EnumDeclaration appears as a function statement, - * so we have to do what addMember() does and install the enum members in the right symbol - * table - */ - addEnumMembersToSymtab(ed, sc, sc.getScopesym()); - - if (sc.flags & SCOPE.Cfile) - { - /* C11 6.7.2.2 - */ - Type commonType = ed.memtype; - if (!commonType) - commonType = Type.tint32; - ulong nextValue = 0; // C11 6.7.2.2-3 first member value defaults to 0 - - // C11 6.7.2.2-2 value must be representable as an int. - // The sizemask represents all values that int will fit into, - // from 0..uint.max. We want to cover int.min..uint.max. - IntRange ir = IntRange.fromType(commonType); - - void emSemantic(EnumMember em, ref ulong nextValue) - { - static void errorReturn(EnumMember em) - { - em.value = ErrorExp.get(); - em.errors = true; - em.semanticRun = PASS.semanticdone; - } - - em.semanticRun = PASS.semantic; - em.type = commonType; - em._linkage = LINK.c; - em.storage_class |= STC.manifest; - if (em.value) - { - Expression e = em.value; - assert(e.dyncast() == DYNCAST.expression); - - /* To merge the type of e with commonType, add 0 of type commonType - */ - if (!ed.memtype) - e = new AddExp(em.loc, e, new IntegerExp(em.loc, 0, commonType)); - - e = e.expressionSemantic(sc); - e = resolveProperties(sc, e); - e = e.integralPromotions(sc); - e = e.ctfeInterpret(); - if (e.op == EXP.error) - return errorReturn(em); - auto ie = e.isIntegerExp(); - if (!ie) - { - // C11 6.7.2.2-2 - .error(em.loc, "%s `%s` enum member must be an integral constant expression, not `%s` of type `%s`", em.kind, em.toPrettyChars, e.toChars(), e.type.toChars()); - return errorReturn(em); - } - if (ed.memtype && !ir.contains(getIntRange(ie))) - { - // C11 6.7.2.2-2 - .error(em.loc, "%s `%s` enum member value `%s` does not fit in `%s`", em.kind, em.toPrettyChars, e.toChars(), commonType.toChars()); - return errorReturn(em); - } - nextValue = ie.toInteger(); - if (!ed.memtype) - commonType = e.type; - em.value = new IntegerExp(em.loc, nextValue, commonType); - } - else - { - // C11 6.7.2.2-3 add 1 to value of previous enumeration constant - bool first = (em == (*em.ed.members)[0]); - if (!first) - { - Expression max = getProperty(commonType, null, em.loc, Id.max, 0); - if (nextValue == max.toInteger()) - { - .error(em.loc, "%s `%s` initialization with `%s+1` causes overflow for type `%s`", em.kind, em.toPrettyChars, max.toChars(), commonType.toChars()); - return errorReturn(em); - } - nextValue += 1; - } - em.value = new IntegerExp(em.loc, nextValue, commonType); - } - em.type = commonType; - em.semanticRun = PASS.semanticdone; - } - - ed.members.foreachDsymbol( (s) - { - if (EnumMember em = s.isEnumMember()) - emSemantic(em, nextValue); - }); - - if (!ed.memtype) - { - // cast all members to commonType - ed.members.foreachDsymbol( (s) - { - if (EnumMember em = s.isEnumMember()) - { - em.type = commonType; - em.value = em.value.castTo(sc, commonType); - } - }); - } - - ed.memtype = commonType; - ed.semanticRun = PASS.semanticdone; - return; - } - - ed.members.foreachDsymbol( (s) - { - if (EnumMember em = s.isEnumMember()) - em.dsymbolSemantic(em._scope); - }); - //printf("defaultval = %lld\n", defaultval); - - //if (defaultval) printf("defaultval: %s %s\n", defaultval.toChars(), defaultval.type.toChars()); - //printf("members = %s\n", members.toChars()); + enumSemantic(sc, ed); } override void visit(EnumMember em) { - //printf("EnumMember::semantic() %s\n", em.toChars()); - - void errorReturn() - { - em.errors = true; - em.semanticRun = PASS.semanticdone; - } - - if (em.errors || em.semanticRun >= PASS.semanticdone) - return; - if (em.semanticRun == PASS.semantic) - { - .error(em.loc, "%s `%s` circular reference to `enum` member", em.kind, em.toPrettyChars); - return errorReturn(); - } - assert(em.ed); - - em.ed.dsymbolSemantic(sc); - if (em.ed.errors) - return errorReturn(); - if (em.errors || em.semanticRun >= PASS.semanticdone) - return; - - if (em._scope) - sc = em._scope; - if (!sc) - return; - - em.semanticRun = PASS.semantic; - - em.visibility = em.ed.isAnonymous() ? em.ed.visibility : Visibility(Visibility.Kind.public_); - em._linkage = LINK.d; - em.storage_class |= STC.manifest; - - // https://issues.dlang.org/show_bug.cgi?id=9701 - if (em.ed.isAnonymous()) - { - if (em.userAttribDecl) - em.userAttribDecl.userAttribDecl = em.ed.userAttribDecl; - else - em.userAttribDecl = em.ed.userAttribDecl; - } - - // Eval UDA in this same scope. Issues 19344, 20835, 21122 - if (em.userAttribDecl) - { - // Set scope but avoid extra sc.uda attachment inside setScope() - auto inneruda = em.userAttribDecl.userAttribDecl; - em.userAttribDecl.setScope(sc); - em.userAttribDecl.userAttribDecl = inneruda; - em.userAttribDecl.dsymbolSemantic(sc); - } - - // The first enum member is special - bool first = (em == (*em.ed.members)[0]); - - if (em.origType) - { - em.origType = em.origType.typeSemantic(em.loc, sc); - em.type = em.origType; - assert(em.value); // "type id;" is not a valid enum member declaration - } - - if (em.value) - { - Expression e = em.value; - assert(e.dyncast() == DYNCAST.expression); - e = e.expressionSemantic(sc); - e = resolveProperties(sc, e); - e = e.ctfeInterpret(); - if (e.op == EXP.error) - return errorReturn(); - if (first && !em.ed.memtype && !em.ed.isAnonymous()) - { - em.ed.memtype = e.type; - if (em.ed.memtype.ty == Terror) - { - em.ed.errors = true; - return errorReturn(); - } - if (em.ed.memtype.ty != Terror) - { - /* https://issues.dlang.org/show_bug.cgi?id=11746 - * All of named enum members should have same type - * with the first member. If the following members were referenced - * during the first member semantic, their types should be unified. - */ - em.ed.members.foreachDsymbol( (s) - { - EnumMember enm = s.isEnumMember(); - if (!enm || enm == em || enm.semanticRun < PASS.semanticdone || enm.origType) - return; - - //printf("[%d] em = %s, em.semanticRun = %d\n", i, toChars(), em.semanticRun); - Expression ev = enm.value; - ev = ev.implicitCastTo(sc, em.ed.memtype); - ev = ev.ctfeInterpret(); - ev = ev.castTo(sc, em.ed.type); - if (ev.op == EXP.error) - em.ed.errors = true; - enm.value = ev; - }); - - if (em.ed.errors) - { - em.ed.memtype = Type.terror; - return errorReturn(); - } - } - } - - if (em.ed.memtype && !em.origType) - { - e = e.implicitCastTo(sc, em.ed.memtype); - e = e.ctfeInterpret(); - - // save origValue for better json output - em.origValue = e; - - if (!em.ed.isAnonymous()) - { - e = e.castTo(sc, em.ed.type.addMod(e.type.mod)); // https://issues.dlang.org/show_bug.cgi?id=12385 - e = e.ctfeInterpret(); - } - } - else if (em.origType) - { - e = e.implicitCastTo(sc, em.origType); - e = e.ctfeInterpret(); - assert(em.ed.isAnonymous()); - - // save origValue for better json output - em.origValue = e; - } - em.value = e; - } - else if (first) - { - Type t; - if (em.ed.memtype) - t = em.ed.memtype; - else - { - t = Type.tint32; - if (!em.ed.isAnonymous()) - em.ed.memtype = t; - } - const errors = global.startGagging(); - Expression e = new IntegerExp(em.loc, 0, t); - e = e.ctfeInterpret(); - if (global.endGagging(errors)) - { - error(em.loc, "cannot generate 0 value of type `%s` for `%s`", - t.toChars(), em.toChars()); - } - // save origValue for better json output - em.origValue = e; - - if (!em.ed.isAnonymous()) - { - e = e.castTo(sc, em.ed.type); - e = e.ctfeInterpret(); - } - em.value = e; - } - else - { - /* Find the previous enum member, - * and set this to be the previous value + 1 - */ - EnumMember emprev = null; - em.ed.members.foreachDsymbol( (s) - { - if (auto enm = s.isEnumMember()) - { - if (enm == em) - return 1; // found - emprev = enm; - } - return 0; // continue - }); - - assert(emprev); - if (emprev.semanticRun < PASS.semanticdone) // if forward reference - emprev.dsymbolSemantic(emprev._scope); // resolve it - if (emprev.errors) - return errorReturn(); - - auto errors = global.startGagging(); - Expression eprev = emprev.value; - assert(eprev); - // .toHeadMutable() due to https://issues.dlang.org/show_bug.cgi?id=18645 - Type tprev = eprev.type.toHeadMutable().equals(em.ed.type.toHeadMutable()) - ? em.ed.memtype - : eprev.type; - /* - https://issues.dlang.org/show_bug.cgi?id=20777 - Previously this used getProperty, which doesn't consider anything user defined, - this construct does do that and thus fixes the bug. - */ - Expression emax = DotIdExp.create(em.ed.loc, new TypeExp(em.ed.loc, tprev), Id.max); - emax = emax.expressionSemantic(sc); - emax = emax.ctfeInterpret(); - - // check that (eprev != emax) - Expression e = new EqualExp(EXP.equal, em.loc, eprev, emax); - e = e.expressionSemantic(sc); - e = e.ctfeInterpret(); - if (global.endGagging(errors)) - { - // display an introductory error before showing what actually failed - error(em.loc, "cannot check `%s` value for overflow", em.toPrettyChars()); - // rerun to show errors - Expression e2 = DotIdExp.create(em.ed.loc, new TypeExp(em.ed.loc, tprev), Id.max); - e2 = e2.expressionSemantic(sc); - e2 = e2.ctfeInterpret(); - e2 = new EqualExp(EXP.equal, em.loc, eprev, e2); - e2 = e2.expressionSemantic(sc); - e2 = e2.ctfeInterpret(); - } - // now any errors are for generating a value - if (e.toInteger()) - { - auto mt = em.ed.memtype; - if (!mt) - mt = eprev.type; - .error(em.loc, "%s `%s` initialization with `%s.%s+1` causes overflow for type `%s`", em.kind, em.toPrettyChars, - emprev.ed.toChars(), emprev.toChars(), mt.toChars()); - return errorReturn(); - } - errors = global.startGagging(); - // Now set e to (eprev + 1) - e = new AddExp(em.loc, eprev, IntegerExp.literal!1); - e = e.expressionSemantic(sc); - e = e.castTo(sc, eprev.type); - e = e.ctfeInterpret(); - if (global.endGagging(errors)) - { - error(em.loc, "cannot generate value for `%s`", em.toPrettyChars()); - // rerun to show errors - Expression e2 = new AddExp(em.loc, eprev, IntegerExp.literal!1); - e2 = e2.expressionSemantic(sc); - e2 = e2.castTo(sc, eprev.type); - e2 = e2.ctfeInterpret(); - } - // save origValue (without cast) for better json output - if (e.op != EXP.error) // avoid duplicate diagnostics - { - assert(emprev.origValue); - em.origValue = new AddExp(em.loc, emprev.origValue, IntegerExp.literal!1); - em.origValue = em.origValue.expressionSemantic(sc); - em.origValue = em.origValue.ctfeInterpret(); - } - - if (e.op == EXP.error) - return errorReturn(); - if (e.type.isfloating()) - { - // Check that e != eprev (not always true for floats) - Expression etest = new EqualExp(EXP.equal, em.loc, e, eprev); - etest = etest.expressionSemantic(sc); - etest = etest.ctfeInterpret(); - if (etest.toInteger()) - { - .error(em.loc, "%s `%s` has inexact value due to loss of precision", em.kind, em.toPrettyChars); - return errorReturn(); - } - } - em.value = e; - } - if (!em.origType) - em.type = em.value.type; - - assert(em.origValue); - em.semanticRun = PASS.semanticdone; + enumMemberSemantic(sc, em); } override void visit(TemplateDeclaration tempdecl) @@ -3026,7 +2498,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (tm.semanticRun == PASS.initial) // forward reference had occurred { //printf("forward reference - deferring\n"); - return deferDsymbolSemantic(tm, scx); + return deferDsymbolSemantic(sc, tm, scx); } tm.inst = tm; @@ -3740,7 +3212,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor continue; if (cbd.parent && cbd.parent.isTemplateInstance()) { - if (!f2.functionSemantic()) + if (!functionSemantic(f2)) goto Ldone; } may_override = true; @@ -4945,7 +4417,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor sc2.pop(); if (log) printf("\tdeferring %s\n", sd.toChars()); - return deferDsymbolSemantic(sd, scx); + return deferDsymbolSemantic(sc, sd, scx); } /* Look for special member functions. @@ -5336,7 +4808,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor { // Forward referencee of one or more bases, try again later //printf("\tL%d semantic('%s') failed due to forward references\n", __LINE__, toChars()); - return deferDsymbolSemantic(cldec, scx); + return deferDsymbolSemantic(sc, cldec, scx); } cldec.baseok = Baseok.done; @@ -5446,7 +4918,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (tc.sym._scope) Module.addDeferredSemantic(tc.sym); //printf("\tL%d semantic('%s') failed due to forward references\n", __LINE__, toChars()); - return deferDsymbolSemantic(cldec, scx); + return deferDsymbolSemantic(sc, cldec, scx); } } @@ -5563,7 +5035,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor sc2.pop(); //printf("\tdeferring %s\n", toChars()); - return deferDsymbolSemantic(cldec, scx); + return deferDsymbolSemantic(sc, cldec, scx); } /* Look for special member functions. @@ -5905,7 +5377,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (idec.baseok == Baseok.none) { // Forward referencee of one or more bases, try again later - return deferDsymbolSemantic(idec, scx); + return deferDsymbolSemantic(sc, idec, scx); } idec.baseok = Baseok.done; @@ -5942,7 +5414,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor // Forward referencee of one or more bases, try again later if (tc.sym._scope) Module.addDeferredSemantic(tc.sym); - return deferDsymbolSemantic(idec, scx); + return deferDsymbolSemantic(sc, idec, scx); } } @@ -6281,7 +5753,7 @@ private extern(C++) class AddMemberVisitor : Visitor } else { - if (findCondition(m.debugidsNot, ds.ident)) + if (m.debugidsNot && findCondition(*m.debugidsNot, ds.ident)) { .error(ds.loc, "%s `%s` defined after use", ds.kind, ds.toPrettyChars); ds.errors = true; @@ -6319,7 +5791,7 @@ private extern(C++) class AddMemberVisitor : Visitor } else { - if (findCondition(m.versionidsNot, vs.ident)) + if (m.versionidsNot && findCondition(*m.versionidsNot, vs.ident)) { .error(vs.loc, "%s `%s` defined after use", vs.kind, vs.toPrettyChars); vs.errors = true; diff --git a/gcc/d/dmd/dtemplate.d b/gcc/d/dmd/dtemplate.d index 13cc32f..465ae74 100644 --- a/gcc/d/dmd/dtemplate.d +++ b/gcc/d/dmd/dtemplate.d @@ -60,6 +60,7 @@ import dmd.errorsink; import dmd.expression; import dmd.expressionsem; import dmd.func; +import dmd.funcsem; import dmd.globals; import dmd.hdrgen; import dmd.id; @@ -76,6 +77,7 @@ import dmd.common.outbuffer; import dmd.rootobject; import dmd.semantic2; import dmd.semantic3; +import dmd.templatesem; import dmd.tokens; import dmd.typesem; import dmd.visitor; @@ -373,7 +375,7 @@ Lnomatch: /************************************ * Match an array of them. */ -private bool arrayObjectMatch(ref Objects oa1, ref Objects oa2) +bool arrayObjectMatch(ref Objects oa1, ref Objects oa2) { if (&oa1 == &oa2) return true; @@ -586,9 +588,9 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol // threaded list of previous instantiation attempts on stack TemplatePrevious* previous; - private Expression lastConstraint; /// the constraint after the last failed evaluation - private Array!Expression lastConstraintNegs; /// its negative parts - private Objects* lastConstraintTiargs; /// template instance arguments for `lastConstraint` + Expression lastConstraint; /// the constraint after the last failed evaluation + Array!Expression lastConstraintNegs; /// its negative parts + Objects* lastConstraintTiargs; /// template instance arguments for `lastConstraint` extern (D) this(const ref Loc loc, Identifier ident, TemplateParameters* parameters, Expression constraint, Dsymbols* decldefs, bool ismixin = false, bool literal = false) { @@ -743,59 +745,9 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol override const(char)* toChars() const { - return toCharsMaybeConstraints(true); - } - - /**************************** - * Similar to `toChars`, but does not print the template constraints - */ - const(char)* toCharsNoConstraints() const - { - return toCharsMaybeConstraints(false); - } - - // Note: this function is not actually `const`, because iterating the - // function parameter list may run dsymbolsemantic on enum types - const(char)* toCharsMaybeConstraints(bool includeConstraints) const - { - OutBuffer buf; HdrGenState hgs; - - buf.writestring(ident == Id.ctor ? "this" : ident.toString()); - buf.writeByte('('); - foreach (i, const tp; *parameters) - { - if (i) - buf.writestring(", "); - toCBuffer(tp, buf, hgs); - } - buf.writeByte(')'); - - if (onemember) - { - if (const fd = onemember.isFuncDeclaration()) - { - if (TypeFunction tf = cast(TypeFunction)fd.type.isTypeFunction()) - { - // !! Casted away const - buf.writestring(parametersTypeToChars(tf.parameterList)); - if (tf.mod) - { - buf.writeByte(' '); - buf.MODtoBuffer(tf.mod); - } - } - } - } - - if (includeConstraints && - constraint) - { - buf.writestring(" if ("); - toCBuffer(constraint, buf, hgs); - buf.writeByte(')'); - } - + OutBuffer buf; + toCharsMaybeConstraints(this, buf, hgs); return buf.extractChars(); } @@ -805,132 +757,6 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol } /**************************** - * Check to see if constraint is satisfied. - */ - private bool evaluateConstraint(TemplateInstance ti, Scope* sc, Scope* paramscope, Objects* dedargs, FuncDeclaration fd) - { - /* Detect recursive attempts to instantiate this template declaration, - * https://issues.dlang.org/show_bug.cgi?id=4072 - * void foo(T)(T x) if (is(typeof(foo(x)))) { } - * static assert(!is(typeof(foo(7)))); - * Recursive attempts are regarded as a constraint failure. - */ - /* There's a chicken-and-egg problem here. We don't know yet if this template - * instantiation will be a local one (enclosing is set), and we won't know until - * after selecting the correct template. Thus, function we're nesting inside - * is not on the sc scope chain, and this can cause errors in FuncDeclaration.getLevel(). - * Workaround the problem by setting a flag to relax the checking on frame errors. - */ - - for (TemplatePrevious* p = previous; p; p = p.prev) - { - if (!arrayObjectMatch(*p.dedargs, *dedargs)) - continue; - //printf("recursive, no match p.sc=%p %p %s\n", p.sc, this, this.toChars()); - /* It must be a subscope of p.sc, other scope chains are not recursive - * instantiations. - * the chain of enclosing scopes is broken by paramscope (its enclosing - * scope is _scope, but paramscope.callsc is the instantiating scope). So - * it's good enough to check the chain of callsc - */ - for (Scope* scx = paramscope.callsc; scx; scx = scx.callsc) - { - // The first scx might be identical for nested eponymeous templates, e.g. - // template foo() { void foo()() {...} } - if (scx == p.sc && scx !is paramscope.callsc) - return false; - } - /* BUG: should also check for ref param differences - */ - } - - TemplatePrevious pr; - pr.prev = previous; - pr.sc = paramscope.callsc; - pr.dedargs = dedargs; - previous = ≺ // add this to threaded list - - Scope* scx = paramscope.push(ti); - scx.parent = ti; - scx.tinst = null; - scx.minst = null; - // Set SCOPE.constraint before declaring function parameters for the static condition - // (previously, this was immediately before calling evalStaticCondition), so the - // semantic pass knows not to issue deprecation warnings for these throw-away decls. - // https://issues.dlang.org/show_bug.cgi?id=21831 - scx.flags |= SCOPE.constraint; - - assert(!ti.symtab); - if (fd) - { - /* Declare all the function parameters as variables and add them to the scope - * Making parameters is similar to FuncDeclaration.semantic3 - */ - auto tf = fd.type.isTypeFunction(); - - scx.parent = fd; - - Parameters* fparameters = tf.parameterList.parameters; - const nfparams = tf.parameterList.length; - foreach (i, fparam; tf.parameterList) - { - fparam.storageClass &= (STC.IOR | STC.lazy_ | STC.final_ | STC.TYPECTOR | STC.nodtor); - fparam.storageClass |= STC.parameter; - if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nfparams) - { - fparam.storageClass |= STC.variadic; - /* Don't need to set STC.scope_ because this will only - * be evaluated at compile time - */ - } - } - foreach (fparam; *fparameters) - { - if (!fparam.ident) - continue; - // don't add it, if it has no name - auto v = new VarDeclaration(fparam.loc, fparam.type, fparam.ident, null); - fparam.storageClass |= STC.parameter; - v.storage_class = fparam.storageClass; - v.dsymbolSemantic(scx); - if (!ti.symtab) - ti.symtab = new DsymbolTable(); - if (!scx.insert(v)) - .error(loc, "%s `%s` parameter `%s.%s` is already defined", kind, toPrettyChars, toChars(), v.toChars()); - else - v.parent = fd; - } - if (isstatic) - fd.storage_class |= STC.static_; - fd.declareThis(scx); - } - - lastConstraint = constraint.syntaxCopy(); - lastConstraintTiargs = ti.tiargs; - lastConstraintNegs.setDim(0); - - import dmd.staticcond; - - assert(ti.inst is null); - ti.inst = ti; // temporary instantiation to enable genIdent() - bool errors; - const bool result = evalStaticCondition(scx, constraint, lastConstraint, errors, &lastConstraintNegs); - if (result || errors) - { - lastConstraint = null; - lastConstraintTiargs = null; - lastConstraintNegs.setDim(0); - } - ti.inst = null; - ti.symtab = null; - scx = scx.pop(); - previous = pr.prev; // unlink from threaded list - if (errors) - return false; - return result; - } - - /**************************** * Destructively get the error message from the last constraint evaluation * Params: * tip = tip to show after printing all overloads @@ -957,7 +783,7 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol assert(parameters && lastConstraintTiargs); if (parameters.length > 0) { - formatParamsWithTiargs(*lastConstraintTiargs, buf); + formatParamsWithTiargs(*parameters, *lastConstraintTiargs, isVariadic() !is null, buf); buf.writenl(); } if (!full) @@ -990,1300 +816,6 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol return buf.extractChars(); } - private void formatParamsWithTiargs(ref Objects tiargs, ref OutBuffer buf) - { - buf.writestring(" with `"); - - // write usual arguments line-by-line - // skips trailing default ones - they are not present in `tiargs` - const bool variadic = isVariadic() !is null; - const end = cast(int)parameters.length - (variadic ? 1 : 0); - uint i; - for (; i < tiargs.length && i < end; i++) - { - if (i > 0) - { - buf.writeByte(','); - buf.writenl(); - buf.writestring(" "); - } - write(buf, (*parameters)[i]); - buf.writestring(" = "); - write(buf, tiargs[i]); - } - // write remaining variadic arguments on the last line - if (variadic) - { - if (i > 0) - { - buf.writeByte(','); - buf.writenl(); - buf.writestring(" "); - } - write(buf, (*parameters)[end]); - buf.writestring(" = "); - buf.writeByte('('); - if (cast(int)tiargs.length - end > 0) - { - write(buf, tiargs[end]); - foreach (j; parameters.length .. tiargs.length) - { - buf.writestring(", "); - write(buf, tiargs[j]); - } - } - buf.writeByte(')'); - } - buf.writeByte('`'); - } - - /****************************** - * Create a scope for the parameters of the TemplateInstance - * `ti` in the parent scope sc from the ScopeDsymbol paramsym. - * - * If paramsym is null a new ScopeDsymbol is used in place of - * paramsym. - * Params: - * ti = the TemplateInstance whose parameters to generate the scope for. - * sc = the parent scope of ti - * Returns: - * a scope for the parameters of ti - */ - Scope* scopeForTemplateParameters(TemplateInstance ti, Scope* sc) - { - ScopeDsymbol paramsym = new ScopeDsymbol(); - paramsym.parent = _scope.parent; - Scope* paramscope = _scope.push(paramsym); - paramscope.tinst = ti; - paramscope.minst = sc.minst; - paramscope.callsc = sc; - paramscope.stc = 0; - return paramscope; - } - - /*************************************** - * Given that ti is an instance of this TemplateDeclaration, - * deduce the types of the parameters to this, and store - * those deduced types in dedtypes[]. - * Input: - * flag 1: don't do semantic() because of dummy types - * 2: don't change types in matchArg() - * Output: - * dedtypes deduced arguments - * Return match level. - */ - private MATCH matchWithInstance(Scope* sc, TemplateInstance ti, ref Objects dedtypes, ArgumentList argumentList, int flag) - { - enum LOGM = 0; - static if (LOGM) - { - printf("\n+TemplateDeclaration.matchWithInstance(this = %s, ti = %s, flag = %d)\n", toChars(), ti.toChars(), flag); - } - version (none) - { - printf("dedtypes.length = %d, parameters.length = %d\n", dedtypes.length, parameters.length); - if (ti.tiargs.length) - printf("ti.tiargs.length = %d, [0] = %p\n", ti.tiargs.length, (*ti.tiargs)[0]); - } - MATCH nomatch() - { - static if (LOGM) - { - printf(" no match\n"); - } - return MATCH.nomatch; - } - MATCH m; - size_t dedtypes_dim = dedtypes.length; - - dedtypes.zero(); - - if (errors) - return MATCH.nomatch; - - size_t parameters_dim = parameters.length; - int variadic = isVariadic() !is null; - - // If more arguments than parameters, no match - if (ti.tiargs.length > parameters_dim && !variadic) - { - static if (LOGM) - { - printf(" no match: more arguments than parameters\n"); - } - return MATCH.nomatch; - } - - assert(dedtypes_dim == parameters_dim); - assert(dedtypes_dim >= ti.tiargs.length || variadic); - - assert(_scope); - - // Set up scope for template parameters - Scope* paramscope = scopeForTemplateParameters(ti,sc); - - // Attempt type deduction - m = MATCH.exact; - for (size_t i = 0; i < dedtypes_dim; i++) - { - MATCH m2; - TemplateParameter tp = (*parameters)[i]; - Declaration sparam; - - //printf("\targument [%d]\n", i); - static if (LOGM) - { - //printf("\targument [%d] is %s\n", i, oarg ? oarg.toChars() : "null"); - TemplateTypeParameter ttp = tp.isTemplateTypeParameter(); - if (ttp) - printf("\tparameter[%d] is %s : %s\n", i, tp.ident.toChars(), ttp.specType ? ttp.specType.toChars() : ""); - } - - m2 = tp.matchArg(ti.loc, paramscope, ti.tiargs, i, parameters, dedtypes, &sparam); - //printf("\tm2 = %d\n", m2); - if (m2 == MATCH.nomatch) - { - version (none) - { - printf("\tmatchArg() for parameter %i failed\n", i); - } - return nomatch(); - } - - if (m2 < m) - m = m2; - - if (!flag) - sparam.dsymbolSemantic(paramscope); - if (!paramscope.insert(sparam)) // TODO: This check can make more early - { - // in TemplateDeclaration.semantic, and - // then we don't need to make sparam if flags == 0 - return nomatch(); - } - } - - if (!flag) - { - /* Any parameter left without a type gets the type of - * its corresponding arg - */ - foreach (i, ref dedtype; dedtypes) - { - if (!dedtype) - { - assert(i < ti.tiargs.length); - dedtype = cast(Type)(*ti.tiargs)[i]; - } - } - } - - if (m > MATCH.nomatch && constraint && !flag) - { - if (ti.hasNestedArgs(ti.tiargs, this.isstatic)) // TODO: should gag error - ti.parent = ti.enclosing; - else - ti.parent = this.parent; - - // Similar to doHeaderInstantiation - FuncDeclaration fd = onemember ? onemember.isFuncDeclaration() : null; - if (fd) - { - TypeFunction tf = fd.type.isTypeFunction().syntaxCopy(); - if (argumentList.hasNames) - return nomatch(); - Expressions* fargs = argumentList.arguments; - // TODO: Expressions* fargs = tf.resolveNamedArgs(argumentList, null); - // if (!fargs) - // return nomatch(); - - fd = new FuncDeclaration(fd.loc, fd.endloc, fd.ident, fd.storage_class, tf); - fd.parent = ti; - fd.inferRetType = true; - - // Shouldn't run semantic on default arguments and return type. - foreach (ref param; *tf.parameterList.parameters) - param.defaultArg = null; - - tf.next = null; - tf.incomplete = true; - - // Resolve parameter types and 'auto ref's. - tf.fargs = fargs; - uint olderrors = global.startGagging(); - fd.type = tf.typeSemantic(loc, paramscope); - global.endGagging(olderrors); - if (fd.type.ty != Tfunction) - return nomatch(); - fd.originalType = fd.type; // for mangling - } - - // TODO: dedtypes => ti.tiargs ? - if (!evaluateConstraint(ti, sc, paramscope, &dedtypes, fd)) - return nomatch(); - } - - static if (LOGM) - { - // Print out the results - printf("--------------------------\n"); - printf("template %s\n", toChars()); - printf("instance %s\n", ti.toChars()); - if (m > MATCH.nomatch) - { - for (size_t i = 0; i < dedtypes_dim; i++) - { - TemplateParameter tp = (*parameters)[i]; - RootObject oarg; - printf(" [%d]", i); - if (i < ti.tiargs.length) - oarg = (*ti.tiargs)[i]; - else - oarg = null; - tp.print(oarg, (*dedtypes)[i]); - } - } - else - return nomatch(); - } - static if (LOGM) - { - printf(" match = %d\n", m); - } - - paramscope.pop(); - static if (LOGM) - { - printf("-TemplateDeclaration.matchWithInstance(this = %s, ti = %s) = %d\n", toChars(), ti.toChars(), m); - } - return m; - } - - /******************************************** - * Determine partial specialization order of 'this' vs td2. - * Returns: - * match this is at least as specialized as td2 - * 0 td2 is more specialized than this - */ - extern (D) MATCH leastAsSpecialized(Scope* sc, TemplateDeclaration td2, ArgumentList argumentList) - { - enum LOG_LEASTAS = 0; - static if (LOG_LEASTAS) - { - printf("%s.leastAsSpecialized(%s)\n", toChars(), td2.toChars()); - } - - /* This works by taking the template parameters to this template - * declaration and feeding them to td2 as if it were a template - * instance. - * If it works, then this template is at least as specialized - * as td2. - */ - - // Set type arguments to dummy template instance to be types - // generated from the parameters to this template declaration - auto tiargs = new Objects(); - tiargs.reserve(parameters.length); - foreach (tp; *parameters) - { - if (tp.dependent) - break; - RootObject p = tp.dummyArg(); - if (!p) //TemplateTupleParameter - break; - - tiargs.push(p); - } - scope TemplateInstance ti = new TemplateInstance(Loc.initial, ident, tiargs); // create dummy template instance - - // Temporary Array to hold deduced types - Objects dedtypes = Objects(td2.parameters.length); - - // Attempt a type deduction - MATCH m = td2.matchWithInstance(sc, ti, dedtypes, argumentList, 1); - if (m > MATCH.nomatch) - { - /* A non-variadic template is more specialized than a - * variadic one. - */ - TemplateTupleParameter tp = isVariadic(); - if (tp && !tp.dependent && !td2.isVariadic()) - goto L1; - - static if (LOG_LEASTAS) - { - printf(" matches %d, so is least as specialized\n", m); - } - return m; - } - L1: - static if (LOG_LEASTAS) - { - printf(" doesn't match, so is not as specialized\n"); - } - return MATCH.nomatch; - } - - /************************************************* - * Match function arguments against a specific template function. - * - * Params: - * ti = template instance. `ti.tdtypes` will be set to Expression/Type deduced template arguments - * sc = instantiation scope - * fd = Partially instantiated function declaration, which is set to an instantiated function declaration - * tthis = 'this' argument if !NULL - * argumentList = arguments to function - * - * Returns: - * match pair of initial and inferred template arguments - */ - extern (D) MATCHpair deduceFunctionTemplateMatch(TemplateInstance ti, Scope* sc, ref FuncDeclaration fd, Type tthis, ArgumentList argumentList) - { - version (none) - { - printf("\nTemplateDeclaration.deduceFunctionTemplateMatch() %s\n", toChars()); - for (size_t i = 0; i < (fargs ? fargs.length : 0); i++) - { - Expression e = (*fargs)[i]; - printf("\tfarg[%d] is %s, type is %s\n", cast(int) i, e.toChars(), e.type.toChars()); - } - printf("fd = %s\n", fd.toChars()); - printf("fd.type = %s\n", fd.type.toChars()); - if (tthis) - printf("tthis = %s\n", tthis.toChars()); - } - - assert(_scope); - - auto dedargs = new Objects(parameters.length); - dedargs.zero(); - - Objects* dedtypes = &ti.tdtypes; // for T:T*, the dedargs is the T*, dedtypes is the T - dedtypes.setDim(parameters.length); - dedtypes.zero(); - - if (errors || fd.errors) - return MATCHpair(MATCH.nomatch, MATCH.nomatch); - - // Set up scope for parameters - Scope* paramscope = scopeForTemplateParameters(ti,sc); - - MATCHpair nomatch() - { - paramscope.pop(); - //printf("\tnomatch\n"); - return MATCHpair(MATCH.nomatch, MATCH.nomatch); - } - - MATCHpair matcherror() - { - // todo: for the future improvement - paramscope.pop(); - //printf("\terror\n"); - return MATCHpair(MATCH.nomatch, MATCH.nomatch); - } - // Mark the parameter scope as deprecated if the templated - // function is deprecated (since paramscope.enclosing is the - // calling scope already) - paramscope.stc |= fd.storage_class & STC.deprecated_; - - TemplateTupleParameter tp = isVariadic(); - Tuple declaredTuple = null; - - version (none) - { - for (size_t i = 0; i < dedargs.length; i++) - { - printf("\tdedarg[%d] = ", i); - RootObject oarg = (*dedargs)[i]; - if (oarg) - printf("%s", oarg.toChars()); - printf("\n"); - } - } - - size_t ntargs = 0; // array size of tiargs - size_t inferStart = 0; // index of first parameter to infer - const Loc instLoc = ti.loc; - MATCH matchTiargs = MATCH.exact; - - if (auto tiargs = ti.tiargs) - { - // Set initial template arguments - ntargs = tiargs.length; - size_t n = parameters.length; - if (tp) - n--; - if (ntargs > n) - { - if (!tp) - return nomatch(); - - /* The extra initial template arguments - * now form the tuple argument. - */ - auto t = new Tuple(ntargs - n); - assert(parameters.length); - (*dedargs)[parameters.length - 1] = t; - - for (size_t i = 0; i < t.objects.length; i++) - { - t.objects[i] = (*tiargs)[n + i]; - } - declareParameter(paramscope, tp, t); - declaredTuple = t; - } - else - n = ntargs; - - memcpy(dedargs.tdata(), tiargs.tdata(), n * (*dedargs.tdata()).sizeof); - - for (size_t i = 0; i < n; i++) - { - assert(i < parameters.length); - Declaration sparam = null; - MATCH m = (*parameters)[i].matchArg(instLoc, paramscope, dedargs, i, parameters, *dedtypes, &sparam); - //printf("\tdeduceType m = %d\n", m); - if (m == MATCH.nomatch) - return nomatch(); - if (m < matchTiargs) - matchTiargs = m; - - sparam.dsymbolSemantic(paramscope); - if (!paramscope.insert(sparam)) - return nomatch(); - } - if (n < parameters.length && !declaredTuple) - { - inferStart = n; - } - else - inferStart = parameters.length; - //printf("tiargs matchTiargs = %d\n", matchTiargs); - } - version (none) - { - for (size_t i = 0; i < dedargs.length; i++) - { - printf("\tdedarg[%d] = ", i); - RootObject oarg = (*dedargs)[i]; - if (oarg) - printf("%s", oarg.toChars()); - printf("\n"); - } - } - - ParameterList fparameters = fd.getParameterList(); // function parameter list - const nfparams = fparameters.length; // number of function parameters - if (argumentList.hasNames) - return matcherror(); // TODO: resolve named args - Expression[] fargs = argumentList.arguments ? (*argumentList.arguments)[] : null; - - /* Check for match of function arguments with variadic template - * parameter, such as: - * - * void foo(T, A...)(T t, A a); - * void main() { foo(1,2,3); } - */ - size_t fptupindex = IDX_NOTFOUND; - if (tp) // if variadic - { - // TemplateTupleParameter always makes most lesser matching. - matchTiargs = MATCH.convert; - - if (nfparams == 0 && argumentList.length != 0) // if no function parameters - { - if (!declaredTuple) - { - auto t = new Tuple(); - //printf("t = %p\n", t); - (*dedargs)[parameters.length - 1] = t; - declareParameter(paramscope, tp, t); - declaredTuple = t; - } - } - else - { - /* Figure out which of the function parameters matches - * the tuple template parameter. Do this by matching - * type identifiers. - * Set the index of this function parameter to fptupindex. - */ - for (fptupindex = 0; fptupindex < nfparams; fptupindex++) - { - auto fparam = (*fparameters.parameters)[fptupindex]; // fparameters[fptupindex] ? - if (fparam.type.ty != Tident) - continue; - TypeIdentifier tid = fparam.type.isTypeIdentifier(); - if (!tp.ident.equals(tid.ident) || tid.idents.length) - continue; - - if (fparameters.varargs != VarArg.none) // variadic function doesn't - return nomatch(); // go with variadic template - - goto L1; - } - fptupindex = IDX_NOTFOUND; - L1: - } - } - - MATCH match = MATCH.exact; - if (toParent().isModule()) - tthis = null; - if (tthis) - { - bool hasttp = false; - - // Match 'tthis' to any TemplateThisParameter's - foreach (param; *parameters) - { - if (auto ttp = param.isTemplateThisParameter()) - { - hasttp = true; - - Type t = new TypeIdentifier(Loc.initial, ttp.ident); - MATCH m = deduceType(tthis, paramscope, t, *parameters, *dedtypes); - if (m == MATCH.nomatch) - return nomatch(); - if (m < match) - match = m; // pick worst match - } - } - - // Match attributes of tthis against attributes of fd - if (fd.type && !fd.isCtorDeclaration() && !(_scope.stc & STC.static_)) - { - StorageClass stc = _scope.stc | fd.storage_class2; - // Propagate parent storage class, https://issues.dlang.org/show_bug.cgi?id=5504 - Dsymbol p = parent; - while (p.isTemplateDeclaration() || p.isTemplateInstance()) - p = p.parent; - AggregateDeclaration ad = p.isAggregateDeclaration(); - if (ad) - stc |= ad.storage_class; - - ubyte mod = fd.type.mod; - if (stc & STC.immutable_) - mod = MODFlags.immutable_; - else - { - if (stc & (STC.shared_ | STC.synchronized_)) - mod |= MODFlags.shared_; - if (stc & STC.const_) - mod |= MODFlags.const_; - if (stc & STC.wild) - mod |= MODFlags.wild; - } - - ubyte thismod = tthis.mod; - if (hasttp) - mod = MODmerge(thismod, mod); - MATCH m = MODmethodConv(thismod, mod); - if (m == MATCH.nomatch) - return nomatch(); - if (m < match) - match = m; - } - } - - // Loop through the function parameters - { - //printf("%s\n\tnfargs = %d, nfparams = %d, tuple_dim = %d\n", toChars(), nfargs, nfparams, declaredTuple ? declaredTuple.objects.length : 0); - //printf("\ttp = %p, fptupindex = %d, found = %d, declaredTuple = %s\n", tp, fptupindex, fptupindex != IDX_NOTFOUND, declaredTuple ? declaredTuple.toChars() : NULL); - size_t argi = 0; - size_t nfargs2 = fargs.length; // nfargs + supplied defaultArgs - uint inoutMatch = 0; // for debugging only - for (size_t parami = 0; parami < nfparams; parami++) - { - Parameter fparam = fparameters[parami]; - - // Apply function parameter storage classes to parameter types - Type prmtype = fparam.type.addStorageClass(fparam.storageClass); - - Expression farg; - - /* See function parameters which wound up - * as part of a template tuple parameter. - */ - if (fptupindex != IDX_NOTFOUND && parami == fptupindex) - { - TypeIdentifier tid = prmtype.isTypeIdentifier(); - assert(tid); - if (!declaredTuple) - { - /* The types of the function arguments - * now form the tuple argument. - */ - declaredTuple = new Tuple(); - (*dedargs)[parameters.length - 1] = declaredTuple; - - /* Count function parameters with no defaults following a tuple parameter. - * void foo(U, T...)(int y, T, U, double, int bar = 0) {} // rem == 2 (U, double) - */ - size_t rem = 0; - foreach (j; parami + 1 .. nfparams) - { - Parameter p = fparameters[j]; - if (p.defaultArg) - { - break; - } - if (!reliesOnTemplateParameters(p.type, (*parameters)[inferStart .. parameters.length])) - { - Type pt = p.type.syntaxCopy().typeSemantic(fd.loc, paramscope); - if (auto ptt = pt.isTypeTuple()) - rem += ptt.arguments.length; - else - rem += 1; - } - else - { - ++rem; - } - } - - if (nfargs2 - argi < rem) - return nomatch(); - declaredTuple.objects.setDim(nfargs2 - argi - rem); - foreach (i; 0 .. declaredTuple.objects.length) - { - farg = fargs[argi + i]; - - // Check invalid arguments to detect errors early. - if (farg.op == EXP.error || farg.type.ty == Terror) - return nomatch(); - - if (!fparam.isLazy() && farg.type.ty == Tvoid) - return nomatch(); - - Type tt; - MATCH m; - if (ubyte wm = deduceWildHelper(farg.type, &tt, tid)) - { - inoutMatch |= wm; - m = MATCH.constant; - } - else - { - m = deduceTypeHelper(farg.type, tt, tid); - } - if (m == MATCH.nomatch) - return nomatch(); - if (m < match) - match = m; - - /* Remove top const for dynamic array types and pointer types - */ - if ((tt.ty == Tarray || tt.ty == Tpointer) && !tt.isMutable() && (!(fparam.storageClass & STC.ref_) || (fparam.storageClass & STC.auto_) && !farg.isLvalue())) - { - tt = tt.mutableOf(); - } - declaredTuple.objects[i] = tt; - } - declareParameter(paramscope, tp, declaredTuple); - } - else - { - // https://issues.dlang.org/show_bug.cgi?id=6810 - // If declared tuple is not a type tuple, - // it cannot be function parameter types. - for (size_t i = 0; i < declaredTuple.objects.length; i++) - { - if (!isType(declaredTuple.objects[i])) - return nomatch(); - } - } - assert(declaredTuple); - argi += declaredTuple.objects.length; - continue; - } - - // If parameter type doesn't depend on inferred template parameters, - // semantic it to get actual type. - if (!reliesOnTemplateParameters(prmtype, (*parameters)[inferStart .. parameters.length])) - { - // should copy prmtype to avoid affecting semantic result - prmtype = prmtype.syntaxCopy().typeSemantic(fd.loc, paramscope); - - if (TypeTuple tt = prmtype.isTypeTuple()) - { - const tt_dim = tt.arguments.length; - for (size_t j = 0; j < tt_dim; j++, ++argi) - { - Parameter p = (*tt.arguments)[j]; - if (j == tt_dim - 1 && fparameters.varargs == VarArg.typesafe && - parami + 1 == nfparams && argi < fargs.length) - { - prmtype = p.type; - goto Lvarargs; - } - if (argi >= fargs.length) - { - if (p.defaultArg) - continue; - - // https://issues.dlang.org/show_bug.cgi?id=19888 - if (fparam.defaultArg) - break; - - return nomatch(); - } - farg = fargs[argi]; - if (!farg.implicitConvTo(p.type)) - return nomatch(); - } - continue; - } - } - - if (argi >= fargs.length) // if not enough arguments - { - if (!fparam.defaultArg) - goto Lvarargs; - - /* https://issues.dlang.org/show_bug.cgi?id=2803 - * Before the starting of type deduction from the function - * default arguments, set the already deduced parameters into paramscope. - * It's necessary to avoid breaking existing acceptable code. Cases: - * - * 1. Already deduced template parameters can appear in fparam.defaultArg: - * auto foo(A, B)(A a, B b = A.stringof); - * foo(1); - * // at fparam == 'B b = A.string', A is equivalent with the deduced type 'int' - * - * 2. If prmtype depends on default-specified template parameter, the - * default type should be preferred. - * auto foo(N = size_t, R)(R r, N start = 0) - * foo([1,2,3]); - * // at fparam `N start = 0`, N should be 'size_t' before - * // the deduction result from fparam.defaultArg. - */ - if (argi == fargs.length) - { - foreach (ref dedtype; *dedtypes) - { - Type at = isType(dedtype); - if (at && at.ty == Tnone) - { - TypeDeduced xt = cast(TypeDeduced)at; - dedtype = xt.tded; // 'unbox' - } - } - for (size_t i = ntargs; i < dedargs.length; i++) - { - TemplateParameter tparam = (*parameters)[i]; - - RootObject oarg = (*dedargs)[i]; - RootObject oded = (*dedtypes)[i]; - if (oarg) - continue; - - if (oded) - { - if (tparam.specialization() || !tparam.isTemplateTypeParameter()) - { - /* The specialization can work as long as afterwards - * the oded == oarg - */ - (*dedargs)[i] = oded; - MATCH m2 = tparam.matchArg(instLoc, paramscope, dedargs, i, parameters, *dedtypes, null); - //printf("m2 = %d\n", m2); - if (m2 == MATCH.nomatch) - return nomatch(); - if (m2 < matchTiargs) - matchTiargs = m2; // pick worst match - if (!(*dedtypes)[i].equals(oded)) - .error(loc, "%s `%s` specialization not allowed for deduced parameter `%s`", kind, toPrettyChars, kind, toPrettyChars, tparam.ident.toChars()); - } - else - { - if (MATCH.convert < matchTiargs) - matchTiargs = MATCH.convert; - } - (*dedargs)[i] = declareParameter(paramscope, tparam, oded); - } - else - { - oded = tparam.defaultArg(instLoc, paramscope); - if (oded) - (*dedargs)[i] = declareParameter(paramscope, tparam, oded); - } - } - } - nfargs2 = argi + 1; - - /* If prmtype does not depend on any template parameters: - * - * auto foo(T)(T v, double x = 0); - * foo("str"); - * // at fparam == 'double x = 0' - * - * or, if all template parameters in the prmtype are already deduced: - * - * auto foo(R)(R range, ElementType!R sum = 0); - * foo([1,2,3]); - * // at fparam == 'ElementType!R sum = 0' - * - * Deducing prmtype from fparam.defaultArg is not necessary. - */ - if (prmtype.deco || prmtype.syntaxCopy().trySemantic(loc, paramscope)) - { - ++argi; - continue; - } - - // Deduce prmtype from the defaultArg. - farg = fparam.defaultArg.syntaxCopy(); - farg = farg.expressionSemantic(paramscope); - farg = resolveProperties(paramscope, farg); - } - else - { - farg = fargs[argi]; - } - { - // Check invalid arguments to detect errors early. - if (farg.op == EXP.error || farg.type.ty == Terror) - return nomatch(); - - Type att = null; - Lretry: - version (none) - { - printf("\tfarg.type = %s\n", farg.type.toChars()); - printf("\tfparam.type = %s\n", prmtype.toChars()); - } - Type argtype = farg.type; - - if (!fparam.isLazy() && argtype.ty == Tvoid && farg.op != EXP.function_) - return nomatch(); - - // https://issues.dlang.org/show_bug.cgi?id=12876 - // Optimize argument to allow CT-known length matching - farg = farg.optimize(WANTvalue, fparam.isReference()); - //printf("farg = %s %s\n", farg.type.toChars(), farg.toChars()); - - RootObject oarg = farg; - if ((fparam.storageClass & STC.ref_) && (!(fparam.storageClass & STC.auto_) || farg.isLvalue())) - { - /* Allow expressions that have CT-known boundaries and type [] to match with [dim] - */ - bool inferIndexType = (argtype.ty == Tarray) && (prmtype.ty == Tsarray || prmtype.ty == Taarray); - if (auto aaType = prmtype.isTypeAArray()) - { - if (auto indexType = aaType.index.isTypeIdentifier()) - { - inferIndexType = indexType.idents.length == 0; - } - } - if (inferIndexType) - { - if (StringExp se = farg.isStringExp()) - { - argtype = se.type.nextOf().sarrayOf(se.len); - } - else if (ArrayLiteralExp ae = farg.isArrayLiteralExp()) - { - argtype = ae.type.nextOf().sarrayOf(ae.elements.length); - } - else if (SliceExp se = farg.isSliceExp()) - { - if (Type tsa = toStaticArrayType(se)) - argtype = tsa; - } - } - - oarg = argtype; - } - else if ((fparam.storageClass & STC.out_) == 0 && (argtype.ty == Tarray || argtype.ty == Tpointer) && templateParameterLookup(prmtype, parameters) != IDX_NOTFOUND && prmtype.isTypeIdentifier().idents.length == 0) - { - /* The farg passing to the prmtype always make a copy. Therefore, - * we can shrink the set of the deduced type arguments for prmtype - * by adjusting top-qualifier of the argtype. - * - * prmtype argtype ta - * T <- const(E)[] const(E)[] - * T <- const(E[]) const(E)[] - * qualifier(T) <- const(E)[] const(E[]) - * qualifier(T) <- const(E[]) const(E[]) - */ - Type ta = argtype.castMod(prmtype.mod ? argtype.nextOf().mod : 0); - if (ta != argtype) - { - Expression ea = farg.copy(); - ea.type = ta; - oarg = ea; - } - } - - if (fparameters.varargs == VarArg.typesafe && parami + 1 == nfparams && argi + 1 < fargs.length) - goto Lvarargs; - - uint im = 0; - MATCH m = deduceType(oarg, paramscope, prmtype, *parameters, *dedtypes, &im, inferStart); - //printf("\tL%d deduceType m = %d, im = x%x, inoutMatch = x%x\n", __LINE__, m, im, inoutMatch); - inoutMatch |= im; - - /* If no match, see if the argument can be matched by using - * implicit conversions. - */ - if (m == MATCH.nomatch && prmtype.deco) - m = farg.implicitConvTo(prmtype); - - if (m == MATCH.nomatch) - { - AggregateDeclaration ad = isAggregate(farg.type); - if (ad && ad.aliasthis && !isRecursiveAliasThis(att, argtype)) - { - // https://issues.dlang.org/show_bug.cgi?id=12537 - // The isRecursiveAliasThis() call above - - /* If a semantic error occurs while doing alias this, - * eg purity(https://issues.dlang.org/show_bug.cgi?id=7295), - * just regard it as not a match. - * - * We also save/restore sc.func.flags to avoid messing up - * attribute inference in the evaluation. - */ - const oldflags = sc.func ? sc.func.flags : 0; - auto e = resolveAliasThis(sc, farg, true); - if (sc.func) - sc.func.flags = oldflags; - if (e) - { - farg = e; - goto Lretry; - } - } - } - - if (m > MATCH.nomatch && (fparam.storageClass & (STC.ref_ | STC.auto_)) == STC.ref_) - { - if (!farg.isLvalue()) - { - if ((farg.op == EXP.string_ || farg.op == EXP.slice) && (prmtype.ty == Tsarray || prmtype.ty == Taarray)) - { - // Allow conversion from T[lwr .. upr] to ref T[upr-lwr] - } - else if (global.params.rvalueRefParam == FeatureState.enabled) - { - // Allow implicit conversion to ref - } - else - return nomatch(); - } - } - if (m > MATCH.nomatch && (fparam.storageClass & STC.out_)) - { - if (!farg.isLvalue()) - return nomatch(); - if (!farg.type.isMutable()) // https://issues.dlang.org/show_bug.cgi?id=11916 - return nomatch(); - } - if (m == MATCH.nomatch && fparam.isLazy() && prmtype.ty == Tvoid && farg.type.ty != Tvoid) - m = MATCH.convert; - if (m != MATCH.nomatch) - { - if (m < match) - match = m; // pick worst match - argi++; - continue; - } - } - - Lvarargs: - /* The following code for variadic arguments closely - * matches TypeFunction.callMatch() - */ - if (!(fparameters.varargs == VarArg.typesafe && parami + 1 == nfparams)) - return nomatch(); - - /* Check for match with function parameter T... - */ - Type tb = prmtype.toBasetype(); - switch (tb.ty) - { - // 6764 fix - TypeAArray may be TypeSArray have not yet run semantic(). - case Tsarray: - case Taarray: - { - // Perhaps we can do better with this, see TypeFunction.callMatch() - if (TypeSArray tsa = tb.isTypeSArray()) - { - dinteger_t sz = tsa.dim.toInteger(); - if (sz != fargs.length - argi) - return nomatch(); - } - else if (TypeAArray taa = tb.isTypeAArray()) - { - Expression dim = new IntegerExp(instLoc, fargs.length - argi, Type.tsize_t); - - size_t i = templateParameterLookup(taa.index, parameters); - if (i == IDX_NOTFOUND) - { - Expression e; - Type t; - Dsymbol s; - Scope *sco; - - uint errors = global.startGagging(); - /* ref: https://issues.dlang.org/show_bug.cgi?id=11118 - * The parameter isn't part of the template - * ones, let's try to find it in the - * instantiation scope 'sc' and the one - * belonging to the template itself. */ - sco = sc; - taa.index.resolve(instLoc, sco, e, t, s); - if (!e) - { - sco = paramscope; - taa.index.resolve(instLoc, sco, e, t, s); - } - global.endGagging(errors); - - if (!e) - return nomatch(); - - e = e.ctfeInterpret(); - e = e.implicitCastTo(sco, Type.tsize_t); - e = e.optimize(WANTvalue); - if (!dim.equals(e)) - return nomatch(); - } - else - { - // This code matches code in TypeInstance.deduceType() - TemplateParameter tprm = (*parameters)[i]; - TemplateValueParameter tvp = tprm.isTemplateValueParameter(); - if (!tvp) - return nomatch(); - Expression e = cast(Expression)(*dedtypes)[i]; - if (e) - { - if (!dim.equals(e)) - return nomatch(); - } - else - { - Type vt = tvp.valType.typeSemantic(Loc.initial, sc); - MATCH m = dim.implicitConvTo(vt); - if (m == MATCH.nomatch) - return nomatch(); - (*dedtypes)[i] = dim; - } - } - } - goto case Tarray; - } - case Tarray: - { - TypeArray ta = cast(TypeArray)tb; - Type tret = fparam.isLazyArray(); - for (; argi < fargs.length; argi++) - { - Expression arg = fargs[argi]; - assert(arg); - - MATCH m; - /* If lazy array of delegates, - * convert arg(s) to delegate(s) - */ - if (tret) - { - if (ta.next.equals(arg.type)) - { - m = MATCH.exact; - } - else - { - m = arg.implicitConvTo(tret); - if (m == MATCH.nomatch) - { - if (tret.toBasetype().ty == Tvoid) - m = MATCH.convert; - } - } - } - else - { - uint wm = 0; - m = deduceType(arg, paramscope, ta.next, *parameters, *dedtypes, &wm, inferStart); - inoutMatch |= wm; - } - if (m == MATCH.nomatch) - return nomatch(); - if (m < match) - match = m; - } - goto Lmatch; - } - case Tclass: - case Tident: - goto Lmatch; - - default: - return nomatch(); - } - assert(0); - } - //printf(". argi = %d, nfargs = %d, nfargs2 = %d\n", argi, nfargs, nfargs2); - if (argi != nfargs2 && fparameters.varargs == VarArg.none) - return nomatch(); - } - - Lmatch: - foreach (ref dedtype; *dedtypes) - { - if (Type at = isType(dedtype)) - { - if (at.ty == Tnone) - { - TypeDeduced xt = cast(TypeDeduced)at; - at = xt.tded; // 'unbox' - } - dedtype = at.merge2(); - } - } - for (size_t i = ntargs; i < dedargs.length; i++) - { - TemplateParameter tparam = (*parameters)[i]; - //printf("tparam[%d] = %s\n", i, tparam.ident.toChars()); - - /* For T:T*, the dedargs is the T*, dedtypes is the T - * But for function templates, we really need them to match - */ - RootObject oarg = (*dedargs)[i]; - RootObject oded = (*dedtypes)[i]; - //printf("1dedargs[%d] = %p, dedtypes[%d] = %p\n", i, oarg, i, oded); - //if (oarg) printf("oarg: %s\n", oarg.toChars()); - //if (oded) printf("oded: %s\n", oded.toChars()); - if (oarg) - continue; - - if (oded) - { - if (tparam.specialization() || !tparam.isTemplateTypeParameter()) - { - /* The specialization can work as long as afterwards - * the oded == oarg - */ - (*dedargs)[i] = oded; - MATCH m2 = tparam.matchArg(instLoc, paramscope, dedargs, i, parameters, *dedtypes, null); - //printf("m2 = %d\n", m2); - if (m2 == MATCH.nomatch) - return nomatch(); - if (m2 < matchTiargs) - matchTiargs = m2; // pick worst match - if (!(*dedtypes)[i].equals(oded)) - .error(loc, "%s `%s` specialization not allowed for deduced parameter `%s`", kind, toPrettyChars, tparam.ident.toChars()); - } - else - { - // Discussion: https://issues.dlang.org/show_bug.cgi?id=16484 - if (MATCH.convert < matchTiargs) - matchTiargs = MATCH.convert; - } - } - else - { - oded = tparam.defaultArg(instLoc, paramscope); - if (!oded) - { - // if tuple parameter and - // tuple parameter was not in function parameter list and - // we're one or more arguments short (i.e. no tuple argument) - if (tparam == tp && - fptupindex == IDX_NOTFOUND && - ntargs <= dedargs.length - 1) - { - // make tuple argument an empty tuple - oded = new Tuple(); - } - else - return nomatch(); - } - if (isError(oded)) - return matcherror(); - ntargs++; - - /* At the template parameter T, the picked default template argument - * X!int should be matched to T in order to deduce dependent - * template parameter A. - * auto foo(T : X!A = X!int, A...)() { ... } - * foo(); // T <-- X!int, A <-- (int) - */ - if (tparam.specialization()) - { - (*dedargs)[i] = oded; - MATCH m2 = tparam.matchArg(instLoc, paramscope, dedargs, i, parameters, *dedtypes, null); - //printf("m2 = %d\n", m2); - if (m2 == MATCH.nomatch) - return nomatch(); - if (m2 < matchTiargs) - matchTiargs = m2; // pick worst match - if (!(*dedtypes)[i].equals(oded)) - .error(loc, "%s `%s` specialization not allowed for deduced parameter `%s`", kind, toPrettyChars, tparam.ident.toChars()); - } - } - oded = declareParameter(paramscope, tparam, oded); - (*dedargs)[i] = oded; - } - - /* https://issues.dlang.org/show_bug.cgi?id=7469 - * As same as the code for 7469 in findBestMatch, - * expand a Tuple in dedargs to normalize template arguments. - */ - if (auto d = dedargs.length) - { - if (auto va = isTuple((*dedargs)[d - 1])) - { - dedargs.setDim(d - 1); - dedargs.insert(d - 1, &va.objects); - } - } - ti.tiargs = dedargs; // update to the normalized template arguments. - - // Partially instantiate function for constraint and fd.leastAsSpecialized() - { - assert(paramscope.scopesym); - Scope* sc2 = _scope; - sc2 = sc2.push(paramscope.scopesym); - sc2 = sc2.push(ti); - sc2.parent = ti; - sc2.tinst = ti; - sc2.minst = sc.minst; - sc2.stc |= fd.storage_class & STC.deprecated_; - - fd = doHeaderInstantiation(ti, sc2, fd, tthis, argumentList.arguments); - - sc2 = sc2.pop(); - sc2 = sc2.pop(); - - if (!fd) - return nomatch(); - } - - if (constraint) - { - if (!evaluateConstraint(ti, sc, paramscope, dedargs, fd)) - return nomatch(); - } - - version (none) - { - for (size_t i = 0; i < dedargs.length; i++) - { - RootObject o = (*dedargs)[i]; - printf("\tdedargs[%d] = %d, %s\n", i, o.dyncast(), o.toChars()); - } - } - - paramscope.pop(); - //printf("\tmatch %d\n", match); - return MATCHpair(matchTiargs, match); - } - /************************************************** * Declare template parameter tp with value o, and install it in the scope sc. */ @@ -2770,8 +1302,8 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, * This is because f() is "more specialized." */ { - MATCH c1 = fd.leastAsSpecialized(m.lastf, argumentList.names); - MATCH c2 = m.lastf.leastAsSpecialized(fd, argumentList.names); + MATCH c1 = FuncDeclaration.leastAsSpecialized(fd, m.lastf, argumentList.names); + MATCH c2 = FuncDeclaration.leastAsSpecialized(m.lastf, fd, argumentList.names); //printf("c1 = %d, c2 = %d\n", c1, c2); if (c1 > c2) return firstIsBetter(); if (c1 < c2) return 0; @@ -2876,7 +1408,7 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, auto ti = new TemplateInstance(loc, td, tiargs); Objects dedtypes = Objects(td.parameters.length); assert(td.semanticRun != PASS.initial); - MATCH mta = td.matchWithInstance(sc, ti, dedtypes, argumentList, 0); + MATCH mta = matchWithInstance(sc, td, ti, dedtypes, argumentList, 0); //printf("matchWithInstance = %d\n", mta); if (mta == MATCH.nomatch || mta < ta_last) // no match or less match return 0; @@ -3036,8 +1568,8 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, if (td_best) { // Disambiguate by picking the most specialized TemplateDeclaration - MATCH c1 = td.leastAsSpecialized(sc, td_best, argumentList); - MATCH c2 = td_best.leastAsSpecialized(sc, td, argumentList); + MATCH c1 = leastAsSpecialized(sc, td, td_best, argumentList); + MATCH c2 = leastAsSpecialized(sc, td_best, td, argumentList); //printf("1: c1 = %d, c2 = %d\n", c1, c2); if (c1 > c2) goto Ltd; if (c1 < c2) goto Ltd_best; @@ -3055,8 +1587,8 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, } { // Disambiguate by picking the most specialized FunctionDeclaration - MATCH c1 = fd.leastAsSpecialized(m.lastf, argumentList.names); - MATCH c2 = m.lastf.leastAsSpecialized(fd, argumentList.names); + MATCH c1 = FuncDeclaration.leastAsSpecialized(fd, m.lastf, argumentList.names); + MATCH c2 = FuncDeclaration.leastAsSpecialized(m.lastf, fd, argumentList.names); //printf("3: c1 = %d, c2 = %d\n", c1, c2); if (c1 > c2) goto Ltd; if (c1 < c2) goto Ltd_best; @@ -3199,7 +1731,7 @@ private size_t templateIdentifierLookup(Identifier id, TemplateParameters* param return IDX_NOTFOUND; } -private size_t templateParameterLookup(Type tparam, TemplateParameters* parameters) +size_t templateParameterLookup(Type tparam, TemplateParameters* parameters) { if (TypeIdentifier tident = tparam.isTypeIdentifier()) { @@ -3209,7 +1741,7 @@ private size_t templateParameterLookup(Type tparam, TemplateParameters* paramete return IDX_NOTFOUND; } -private ubyte deduceWildHelper(Type t, Type* at, Type tparam) +ubyte deduceWildHelper(Type t, Type* at, Type tparam) { if ((tparam.mod & MODFlags.wild) == 0) return 0; @@ -3292,7 +1824,7 @@ private Type rawTypeMerge(Type t1, Type t2) return null; } -private MATCH deduceTypeHelper(Type t, out Type at, Type tparam) +MATCH deduceTypeHelper(Type t, out Type at, Type tparam) { // 9*9 == 81 cases @@ -5000,7 +3532,7 @@ bool reliesOnTident(Type t, TemplateParameters* tparams, size_t iStart = 0) * t = Tested type, if null, returns false. * tparams = Template parameters. */ -private bool reliesOnTemplateParameters(Type t, TemplateParameter[] tparams) +bool reliesOnTemplateParameters(Type t, TemplateParameter[] tparams) { bool visitVector(TypeVector t) { @@ -6930,7 +5462,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol } FuncDeclaration fd = sa.isFuncDeclaration(); if (fd) - fd.functionSemantic(); + functionSemantic(fd); } else if (isParameter(o)) { @@ -7001,7 +5533,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol assert(tempdecl._scope); // Deduce tdtypes tdtypes.setDim(tempdecl.parameters.length); - if (!tempdecl.matchWithInstance(sc, this, tdtypes, argumentList, 2)) + if (!matchWithInstance(sc, tempdecl, this, tdtypes, argumentList, 2)) { .error(loc, "%s `%s` incompatible arguments for template instantiation", kind, toPrettyChars); return false; @@ -7051,7 +5583,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol dedtypes.zero(); assert(td.semanticRun != PASS.initial); - MATCH m = td.matchWithInstance(sc, this, dedtypes, argumentList, 0); + MATCH m = matchWithInstance(sc, td, this, dedtypes, argumentList, 0); //printf("matchWithInstance = %d\n", m); if (m == MATCH.nomatch) // no match at all return 0; @@ -7060,8 +5592,8 @@ extern (C++) class TemplateInstance : ScopeDsymbol // Disambiguate by picking the most specialized TemplateDeclaration { - MATCH c1 = td.leastAsSpecialized(sc, td_best, argumentList); - MATCH c2 = td_best.leastAsSpecialized(sc, td, argumentList); + MATCH c1 = leastAsSpecialized(sc, td, td_best, argumentList); + MATCH c2 = leastAsSpecialized(sc, td_best, td, argumentList); //printf("c1 = %d, c2 = %d\n", c1, c2); if (c1 > c2) goto Ltd; if (c1 < c2) goto Ltd_best; @@ -7159,7 +5691,11 @@ extern (C++) class TemplateInstance : ScopeDsymbol // Only one template, so we can give better error message const(char)* msg = "does not match template declaration"; const(char)* tip; - const tmsg = tdecl.toCharsNoConstraints(); + OutBuffer buf; + HdrGenState hgs; + hgs.skipConstraints = true; + toCharsMaybeConstraints(tdecl, buf, hgs); + const tmsg = buf.peekChars(); const cmsg = tdecl.getConstraintEvalError(tip); if (cmsg) { @@ -7327,7 +5863,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol return 1; } } - MATCH m = td.matchWithInstance(sc, this, dedtypes, ArgumentList(), 0); + MATCH m = matchWithInstance(sc, td, this, dedtypes, ArgumentList(), 0); if (m == MATCH.nomatch) return 0; } @@ -8562,15 +7098,21 @@ extern (C++) void printTemplateStats(bool listInstances, ErrorSink eSink) sortedStats.sort!(TemplateDeclarationStats.compare); + OutBuffer buf; foreach (const ref ss; sortedStats[]) { + buf.reset(); + HdrGenState hgs; + hgs.skipConstraints = true; + toCharsMaybeConstraints(ss.td, buf, hgs); + const tchars = buf.peekChars(); if (listInstances && ss.ts.allInstances) { eSink.message(ss.td.loc, "vtemplate: %u (%u distinct) instantiation(s) of template `%s` found, they are:", ss.ts.numInstantiations, ss.ts.uniqueInstantiations, - ss.td.toCharsNoConstraints()); + tchars); foreach (const ti; (*ss.ts.allInstances)[]) { if (ti.tinst) // if has enclosing instance @@ -8585,13 +7127,13 @@ extern (C++) void printTemplateStats(bool listInstances, ErrorSink eSink) "vtemplate: %u (%u distinct) instantiation(s) of template `%s` found", ss.ts.numInstantiations, ss.ts.uniqueInstantiations, - ss.td.toCharsNoConstraints()); + tchars); } } } /// Pair of MATCHes -private struct MATCHpair +struct MATCHpair { MATCH mta; /// match template parameters by initial template arguments MATCH mfa; /// match template parameters by inferred template arguments @@ -8605,7 +7147,7 @@ private struct MATCHpair } } -private void write(ref OutBuffer buf, RootObject obj) +void write(ref OutBuffer buf, RootObject obj) { if (obj) { diff --git a/gcc/d/dmd/enum.h b/gcc/d/dmd/enum.h index a4bb588..650bf3e 100644 --- a/gcc/d/dmd/enum.h +++ b/gcc/d/dmd/enum.h @@ -52,8 +52,6 @@ public: bool isDeprecated() const override; // is Dsymbol deprecated? Visibility visible() override; bool isSpecial() const; - Expression *getDefaultValue(const Loc &loc); - Type *getMemtype(const Loc &loc); EnumDeclaration *isEnumDeclaration() override { return this; } diff --git a/gcc/d/dmd/enumsem.d b/gcc/d/dmd/enumsem.d new file mode 100644 index 0000000..0603960 --- /dev/null +++ b/gcc/d/dmd/enumsem.d @@ -0,0 +1,714 @@ +/** + * Does the semantic passes on enums. + * + * 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/enumsem.d, _enumsem.d) + * Documentation: https://dlang.org/phobos/dmd_enumsem.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/enumsem.d + */ + +module dmd.enumsem; + +import core.stdc.stdio; +import core.stdc.string; + +import dmd.aggregate; +import dmd.aliasthis; +import dmd.arraytypes; +import dmd.astcodegen; +import dmd.astenums; +import dmd.attrib; +import dmd.blockexit; +import dmd.clone; +import dmd.cond; +import dmd.compiler; +import dmd.dcast; +import dmd.dclass; +import dmd.declaration; +import dmd.denum; +import dmd.dimport; +import dmd.dinterpret; +import dmd.dmangle; +import dmd.dmodule; +import dmd.dscope; +import dmd.dstruct; +import dmd.dsymbol; +import dmd.dsymbolsem; +import dmd.dtemplate; +import dmd.dversion; +import dmd.errors; +import dmd.escape; +import dmd.expression; +import dmd.expressionsem; +import dmd.func; +import dmd.funcsem; +import dmd.globals; +import dmd.id; +import dmd.identifier; +import dmd.importc; +import dmd.init; +import dmd.initsem; +import dmd.intrange; +import dmd.hdrgen; +import dmd.location; +import dmd.mtype; +import dmd.mustuse; +import dmd.nogc; +import dmd.nspace; +import dmd.objc; +import dmd.opover; +import dmd.optimize; +import dmd.parse; +import dmd.root.array; +import dmd.root.filename; +import dmd.common.outbuffer; +import dmd.root.rmem; +import dmd.rootobject; +import dmd.root.utf; +import dmd.semantic2; +import dmd.semantic3; +import dmd.sideeffect; +import dmd.statementsem; +import dmd.staticassert; +import dmd.tokens; +import dmd.utils; +import dmd.statement; +import dmd.target; +import dmd.templateparamsem; +import dmd.typesem; +import dmd.visitor; + + +/********************************* + * Perform semantic analysis on enum declaration `em` + */ +void enumSemantic(Scope* sc, EnumDeclaration ed) +{ + //printf("EnumDeclaration::semantic(sd = %p, '%s') %s\n", sc.scopesym, sc.scopesym.toChars(), ed.toChars()); + //printf("EnumDeclaration::semantic() %p %s\n", ed, ed.toChars()); + if (ed.semanticRun >= PASS.semanticdone) + return; // semantic() already completed + if (ed.semanticRun == PASS.semantic) + { + assert(ed.memtype); + error(ed.loc, "circular reference to enum base type `%s`", ed.memtype.toChars()); + ed.errors = true; + ed.semanticRun = PASS.semanticdone; + return; + } + Scope* scx = null; + if (ed._scope) + { + sc = ed._scope; + scx = ed._scope; // save so we don't make redundant copies + ed._scope = null; + } + + if (!sc) + return; + + ed.parent = sc.parent; + ed.type = ed.type.typeSemantic(ed.loc, sc); + + ed.visibility = sc.visibility; + if (sc.stc & STC.deprecated_) + ed.isdeprecated = true; + ed.userAttribDecl = sc.userAttribDecl; + ed.cppnamespace = sc.namespace; + + ed.semanticRun = PASS.semantic; + UserAttributeDeclaration.checkGNUABITag(ed, sc.linkage); + checkMustUseReserved(ed); + + if (!ed.members && !ed.memtype) // enum ident; + { + ed.semanticRun = PASS.semanticdone; + return; + } + + if (!ed.symtab) + ed.symtab = new DsymbolTable(); + + /* The separate, and distinct, cases are: + * 1. enum { ... } + * 2. enum : memtype { ... } + * 3. enum ident { ... } + * 4. enum ident : memtype { ... } + * 5. enum ident : memtype; + * 6. enum ident; + */ + + if (ed.memtype) + { + ed.memtype = ed.memtype.typeSemantic(ed.loc, sc); + + /* Check to see if memtype is forward referenced + */ + if (auto te = ed.memtype.isTypeEnum()) + { + auto sym = te.toDsymbol(sc).isEnumDeclaration(); + // Special enums like __c_[u]long[long] are fine to forward reference + // see https://issues.dlang.org/show_bug.cgi?id=20599 + if (!sym.isSpecial() && (!sym.memtype || !sym.members || !sym.symtab || sym._scope)) + { + // memtype is forward referenced, so try again later + deferDsymbolSemantic(sc, ed, scx); + //printf("\tdeferring %s\n", toChars()); + ed.semanticRun = PASS.initial; + return; + } + else + // Ensure that semantic is run to detect. e.g. invalid forward references + sym.dsymbolSemantic(sc); + } + if (ed.memtype.ty == Tvoid) + { + .error(ed.loc, "%s `%s` base type must not be `void`", ed.kind, ed.toPrettyChars); + ed.memtype = Type.terror; + } + if (ed.memtype.ty == Terror) + { + ed.errors = true; + // poison all the members + ed.members.foreachDsymbol( (s) { s.errors = true; } ); + ed.semanticRun = PASS.semanticdone; + return; + } + } + + if (!ed.members) // enum ident : memtype; + { + ed.semanticRun = PASS.semanticdone; + return; + } + + if (ed.members.length == 0) + { + .error(ed.loc, "%s `%s enum `%s` must have at least one member", ed.kind, ed.toPrettyChars, ed.toChars()); + ed.errors = true; + ed.semanticRun = PASS.semanticdone; + return; + } + + if (!(sc.flags & SCOPE.Cfile)) // C enum remains incomplete until members are done + ed.semanticRun = PASS.semanticdone; + + version (none) + { + // @@@DEPRECATED_2.110@@@ https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint + // Deprecated in 2.100 + // Make an error in 2.110 + if (sc.stc & STC.scope_) + deprecation(ed.loc, "`scope` as a type constraint is deprecated. Use `scope` at the usage site."); + } + + Scope* sce; + if (ed.isAnonymous()) + sce = sc; + else + { + sce = sc.push(ed); + sce.parent = ed; + } + sce = sce.startCTFE(); + sce.setNoFree(); // needed for getMaxMinValue() + + /* Each enum member gets the sce scope + */ + ed.members.foreachDsymbol( (s) + { + EnumMember em = s.isEnumMember(); + if (em) + em._scope = sce; + }); + + /* addMember() is not called when the EnumDeclaration appears as a function statement, + * so we have to do what addMember() does and install the enum members in the right symbol + * table + */ + addEnumMembersToSymtab(ed, sc, sc.getScopesym()); + + if (sc.flags & SCOPE.Cfile) + { + /* C11 6.7.2.2 + */ + Type commonType = ed.memtype; + if (!commonType) + commonType = Type.tint32; + ulong nextValue = 0; // C11 6.7.2.2-3 first member value defaults to 0 + + // C11 6.7.2.2-2 value must be representable as an int. + // The sizemask represents all values that int will fit into, + // from 0..uint.max. We want to cover int.min..uint.max. + IntRange ir = IntRange.fromType(commonType); + + void emSemantic(EnumMember em, ref ulong nextValue) + { + static void errorReturn(EnumMember em) + { + em.value = ErrorExp.get(); + em.errors = true; + em.semanticRun = PASS.semanticdone; + } + + em.semanticRun = PASS.semantic; + em.type = commonType; + em._linkage = LINK.c; + em.storage_class |= STC.manifest; + if (em.value) + { + Expression e = em.value; + assert(e.dyncast() == DYNCAST.expression); + + /* To merge the type of e with commonType, add 0 of type commonType + */ + if (!ed.memtype) + e = new AddExp(em.loc, e, new IntegerExp(em.loc, 0, commonType)); + + e = e.expressionSemantic(sc); + e = resolveProperties(sc, e); + e = e.integralPromotions(sc); + e = e.ctfeInterpret(); + if (e.op == EXP.error) + return errorReturn(em); + auto ie = e.isIntegerExp(); + if (!ie) + { + // C11 6.7.2.2-2 + .error(em.loc, "%s `%s` enum member must be an integral constant expression, not `%s` of type `%s`", em.kind, em.toPrettyChars, e.toChars(), e.type.toChars()); + return errorReturn(em); + } + if (ed.memtype && !ir.contains(getIntRange(ie))) + { + // C11 6.7.2.2-2 + .error(em.loc, "%s `%s` enum member value `%s` does not fit in `%s`", em.kind, em.toPrettyChars, e.toChars(), commonType.toChars()); + return errorReturn(em); + } + nextValue = ie.toInteger(); + if (!ed.memtype) + commonType = e.type; + em.value = new IntegerExp(em.loc, nextValue, commonType); + } + else + { + // C11 6.7.2.2-3 add 1 to value of previous enumeration constant + bool first = (em == (*em.ed.members)[0]); + if (!first) + { + Expression max = getProperty(commonType, null, em.loc, Id.max, 0); + if (nextValue == max.toInteger()) + { + .error(em.loc, "%s `%s` initialization with `%s+1` causes overflow for type `%s`", em.kind, em.toPrettyChars, max.toChars(), commonType.toChars()); + return errorReturn(em); + } + nextValue += 1; + } + em.value = new IntegerExp(em.loc, nextValue, commonType); + } + em.type = commonType; + em.semanticRun = PASS.semanticdone; + } + + ed.members.foreachDsymbol( (s) + { + if (EnumMember em = s.isEnumMember()) + emSemantic(em, nextValue); + }); + + if (!ed.memtype) + { + // cast all members to commonType + ed.members.foreachDsymbol( (s) + { + if (EnumMember em = s.isEnumMember()) + { + em.type = commonType; + em.value = em.value.castTo(sc, commonType); + } + }); + } + + ed.memtype = commonType; + ed.semanticRun = PASS.semanticdone; + return; + } + + ed.members.foreachDsymbol( (s) + { + if (EnumMember em = s.isEnumMember()) + em.dsymbolSemantic(em._scope); + }); + //printf("ed.defaultval = %lld\n", ed.defaultval); + + //if (ed.defaultval) printf("ed.defaultval: %s %s\n", ed.defaultval.toChars(), ed.defaultval.type.toChars()); + //printf("members = %s\n", members.toChars()); +} + +Expression getDefaultValue(EnumDeclaration ed, const ref Loc loc) +{ + Expression handleErrors(){ + ed.defaultval = ErrorExp.get(); + return ed.defaultval; + } + //printf("EnumDeclaration::getDefaultValue() %p %s\n", this, toChars()); + // https://issues.dlang.org/show_bug.cgi?id=23904 + // Return ed.defaultval only if it is not ErrorExp. + // A speculative context may set ed.defaultval to ErrorExp; + // subsequent non-speculative contexts need to be able + // to print the error. + if (ed.defaultval && !ed.defaultval.isErrorExp()) + return ed.defaultval; + + if (ed.isCsymbol()) + return ed.memtype.defaultInit(loc, true); + + if (ed._scope) + dsymbolSemantic(ed, ed._scope); + if (ed.errors) + return handleErrors(); + if (!ed.members) + { + if (ed.isSpecial()) + { + /* Allow these special enums to not need a member list + */ + return ed.defaultval = ed.memtype.defaultInit(loc); + } + + error(loc, "%s `%s` is opaque and has no default initializer", ed.kind, ed.toPrettyChars); + return handleErrors(); + } + + foreach (const i; 0 .. ed.members.length) + { + EnumMember em = (*ed.members)[i].isEnumMember(); + if (em) + { + if (em.semanticRun < PASS.semanticdone) + { + error(loc, "%s `%s` forward reference of `%s.init`", ed.kind, ed.toPrettyChars, ed.toChars()); + return handleErrors(); + } + + ed.defaultval = em.value; + return ed.defaultval; + } + } + return handleErrors(); +} + +Type getMemtype(EnumDeclaration ed, const ref Loc loc) +{ + if (ed._scope) + { + /* Enum is forward referenced. We don't need to resolve the whole thing, + * just the base type + */ + if (ed.memtype) + { + Loc locx = loc.isValid() ? loc : ed.loc; + ed.memtype = ed.memtype.typeSemantic(locx, ed._scope); + } + else + { + // Run semantic to get the type from a possible first member value + dsymbolSemantic(ed, ed._scope); + } + } + if (!ed.memtype) + { + if (!ed.isAnonymous() && (ed.members || ed.semanticRun >= PASS.semanticdone)) + ed.memtype = Type.tint32; + else + { + Loc locx = loc.isValid() ? loc : ed.loc; + error(locx, "is forward referenced looking for base type"); + return Type.terror; + } + } + return ed.memtype; +} + +/********************************* + * Perform semantic analysis on enum member `em` + */ +void enumMemberSemantic(Scope* sc, EnumMember em) +{ + //printf("EnumMember::semantic() %s\n", em.toChars()); + + void errorReturn() + { + em.errors = true; + em.semanticRun = PASS.semanticdone; + } + + if (em.errors || em.semanticRun >= PASS.semanticdone) + return; + if (em.semanticRun == PASS.semantic) + { + .error(em.loc, "%s `%s` circular reference to `enum` member", em.kind, em.toPrettyChars); + return errorReturn(); + } + assert(em.ed); + + em.ed.dsymbolSemantic(sc); + if (em.ed.errors) + return errorReturn(); + if (em.errors || em.semanticRun >= PASS.semanticdone) + return; + + if (em._scope) + sc = em._scope; + if (!sc) + return; + + em.semanticRun = PASS.semantic; + + em.visibility = em.ed.isAnonymous() ? em.ed.visibility : Visibility(Visibility.Kind.public_); + em._linkage = LINK.d; + em.storage_class |= STC.manifest; + + // https://issues.dlang.org/show_bug.cgi?id=9701 + if (em.ed.isAnonymous()) + { + if (em.userAttribDecl) + em.userAttribDecl.userAttribDecl = em.ed.userAttribDecl; + else + em.userAttribDecl = em.ed.userAttribDecl; + } + + // Eval UDA in this same scope. Issues 19344, 20835, 21122 + if (em.userAttribDecl) + { + // Set scope but avoid extra sc.uda attachment inside setScope() + auto inneruda = em.userAttribDecl.userAttribDecl; + em.userAttribDecl.setScope(sc); + em.userAttribDecl.userAttribDecl = inneruda; + em.userAttribDecl.dsymbolSemantic(sc); + } + + // The first enum member is special + bool first = (em == (*em.ed.members)[0]); + + if (em.origType) + { + em.origType = em.origType.typeSemantic(em.loc, sc); + em.type = em.origType; + assert(em.value); // "type id;" is not a valid enum member declaration + } + + if (em.value) + { + Expression e = em.value; + assert(e.dyncast() == DYNCAST.expression); + e = e.expressionSemantic(sc); + e = resolveProperties(sc, e); + e = e.ctfeInterpret(); + if (e.op == EXP.error) + return errorReturn(); + if (first && !em.ed.memtype && !em.ed.isAnonymous()) + { + em.ed.memtype = e.type; + if (em.ed.memtype.ty == Terror) + { + em.ed.errors = true; + return errorReturn(); + } + if (em.ed.memtype.ty != Terror) + { + /* https://issues.dlang.org/show_bug.cgi?id=11746 + * All of named enum members should have same type + * with the first member. If the following members were referenced + * during the first member semantic, their types should be unified. + */ + em.ed.members.foreachDsymbol( (s) + { + EnumMember enm = s.isEnumMember(); + if (!enm || enm == em || enm.semanticRun < PASS.semanticdone || enm.origType) + return; + + //printf("[%d] em = %s, em.semanticRun = %d\n", i, toChars(), em.semanticRun); + Expression ev = enm.value; + ev = ev.implicitCastTo(sc, em.ed.memtype); + ev = ev.ctfeInterpret(); + ev = ev.castTo(sc, em.ed.type); + if (ev.op == EXP.error) + em.ed.errors = true; + enm.value = ev; + }); + + if (em.ed.errors) + { + em.ed.memtype = Type.terror; + return errorReturn(); + } + } + } + + if (em.ed.memtype && !em.origType) + { + e = e.implicitCastTo(sc, em.ed.memtype); + e = e.ctfeInterpret(); + + // save origValue for better json output + em.origValue = e; + + if (!em.ed.isAnonymous()) + { + e = e.castTo(sc, em.ed.type.addMod(e.type.mod)); // https://issues.dlang.org/show_bug.cgi?id=12385 + e = e.ctfeInterpret(); + } + } + else if (em.origType) + { + e = e.implicitCastTo(sc, em.origType); + e = e.ctfeInterpret(); + assert(em.ed.isAnonymous()); + + // save origValue for better json output + em.origValue = e; + } + em.value = e; + } + else if (first) + { + Type t; + if (em.ed.memtype) + t = em.ed.memtype; + else + { + t = Type.tint32; + if (!em.ed.isAnonymous()) + em.ed.memtype = t; + } + const errors = global.startGagging(); + Expression e = new IntegerExp(em.loc, 0, t); + e = e.ctfeInterpret(); + if (global.endGagging(errors)) + { + error(em.loc, "cannot generate 0 value of type `%s` for `%s`", + t.toChars(), em.toChars()); + } + // save origValue for better json output + em.origValue = e; + + if (!em.ed.isAnonymous()) + { + e = e.castTo(sc, em.ed.type); + e = e.ctfeInterpret(); + } + em.value = e; + } + else + { + /* Find the previous enum member, + * and set this to be the previous value + 1 + */ + EnumMember emprev = null; + em.ed.members.foreachDsymbol( (s) + { + if (auto enm = s.isEnumMember()) + { + if (enm == em) + return 1; // found + emprev = enm; + } + return 0; // continue + }); + + assert(emprev); + if (emprev.semanticRun < PASS.semanticdone) // if forward reference + emprev.dsymbolSemantic(emprev._scope); // resolve it + if (emprev.errors) + return errorReturn(); + + auto errors = global.startGagging(); + Expression eprev = emprev.value; + assert(eprev); + // .toHeadMutable() due to https://issues.dlang.org/show_bug.cgi?id=18645 + Type tprev = eprev.type.toHeadMutable().equals(em.ed.type.toHeadMutable()) + ? em.ed.memtype + : eprev.type; + /* + https://issues.dlang.org/show_bug.cgi?id=20777 + Previously this used getProperty, which doesn't consider anything user defined, + this construct does do that and thus fixes the bug. + */ + Expression emax = DotIdExp.create(em.ed.loc, new TypeExp(em.ed.loc, tprev), Id.max); + emax = emax.expressionSemantic(sc); + emax = emax.ctfeInterpret(); + + // check that (eprev != emax) + Expression e = new EqualExp(EXP.equal, em.loc, eprev, emax); + e = e.expressionSemantic(sc); + e = e.ctfeInterpret(); + if (global.endGagging(errors)) + { + // display an introductory error before showing what actually failed + error(em.loc, "cannot check `%s` value for overflow", em.toPrettyChars()); + // rerun to show errors + Expression e2 = DotIdExp.create(em.ed.loc, new TypeExp(em.ed.loc, tprev), Id.max); + e2 = e2.expressionSemantic(sc); + e2 = e2.ctfeInterpret(); + e2 = new EqualExp(EXP.equal, em.loc, eprev, e2); + e2 = e2.expressionSemantic(sc); + e2 = e2.ctfeInterpret(); + } + // now any errors are for generating a value + if (e.toInteger()) + { + auto mt = em.ed.memtype; + if (!mt) + mt = eprev.type; + .error(em.loc, "%s `%s` initialization with `%s.%s+1` causes overflow for type `%s`", em.kind, em.toPrettyChars, + emprev.ed.toChars(), emprev.toChars(), mt.toChars()); + return errorReturn(); + } + errors = global.startGagging(); + // Now set e to (eprev + 1) + e = new AddExp(em.loc, eprev, IntegerExp.literal!1); + e = e.expressionSemantic(sc); + e = e.castTo(sc, eprev.type); + e = e.ctfeInterpret(); + if (global.endGagging(errors)) + { + error(em.loc, "cannot generate value for `%s`", em.toPrettyChars()); + // rerun to show errors + Expression e2 = new AddExp(em.loc, eprev, IntegerExp.literal!1); + e2 = e2.expressionSemantic(sc); + e2 = e2.castTo(sc, eprev.type); + e2 = e2.ctfeInterpret(); + } + // save origValue (without cast) for better json output + if (e.op != EXP.error) // avoid duplicate diagnostics + { + assert(emprev.origValue); + em.origValue = new AddExp(em.loc, emprev.origValue, IntegerExp.literal!1); + em.origValue = em.origValue.expressionSemantic(sc); + em.origValue = em.origValue.ctfeInterpret(); + } + + if (e.op == EXP.error) + return errorReturn(); + if (e.type.isfloating()) + { + // Check that e != eprev (not always true for floats) + Expression etest = new EqualExp(EXP.equal, em.loc, e, eprev); + etest = etest.expressionSemantic(sc); + etest = etest.ctfeInterpret(); + if (etest.toInteger()) + { + .error(em.loc, "%s `%s` has inexact value due to loss of precision", em.kind, em.toPrettyChars); + return errorReturn(); + } + } + em.value = e; + } + if (!em.origType) + em.type = em.value.type; + + assert(em.origValue); + em.semanticRun = PASS.semanticdone; +} diff --git a/gcc/d/dmd/expression.d b/gcc/d/dmd/expression.d index cbf0118..1603f2b 100644 --- a/gcc/d/dmd/expression.d +++ b/gcc/d/dmd/expression.d @@ -721,6 +721,7 @@ extern (C++) abstract class Expression : ASTNode inout(SuperExp) isSuperExp() { return op == EXP.super_ ? cast(typeof(return))this : null; } inout(NullExp) isNullExp() { return op == EXP.null_ ? cast(typeof(return))this : null; } inout(StringExp) isStringExp() { return op == EXP.string_ ? cast(typeof(return))this : null; } + inout(InterpExp) isInterpExp() { return op == EXP.interpolated ? cast(typeof(return))this : null; } inout(TupleExp) isTupleExp() { return op == EXP.tuple ? cast(typeof(return))this : null; } inout(ArrayLiteralExp) isArrayLiteralExp() { return op == EXP.arrayLiteral ? cast(typeof(return))this : null; } inout(AssocArrayLiteralExp) isAssocArrayLiteralExp() { return op == EXP.assocArrayLiteral ? cast(typeof(return))this : null; } @@ -1499,6 +1500,7 @@ extern (C++) final class StringExp : Expression char* string; // if sz == 1 wchar* wstring; // if sz == 2 dchar* dstring; // if sz == 4 + ulong* lstring; // if sz == 8 } // (const if ownedByCtfe == OwnedBy.code) size_t len; // number of code units ubyte sz = 1; // 1: char, 2: wchar, 4: dchar @@ -1662,6 +1664,13 @@ extern (C++) final class StringExp : Expression */ dchar getCodeUnit(size_t i) const pure { + assert(this.sz <= dchar.sizeof); + return cast(dchar) getIndex(i); + } + + /// Returns: integer at index `i` + dinteger_t getIndex(size_t i) const pure + { assert(i < len); final switch (sz) { @@ -1671,6 +1680,8 @@ extern (C++) final class StringExp : Expression return wstring[i]; case 4: return dstring[i]; + case 8: + return lstring[i]; } } @@ -1682,6 +1693,11 @@ extern (C++) final class StringExp : Expression */ extern (D) void setCodeUnit(size_t i, dchar c) { + return setIndex(i, c); + } + + extern (D) void setIndex(size_t i, long c) + { assert(i < len); final switch (sz) { @@ -1692,7 +1708,10 @@ extern (C++) final class StringExp : Expression wstring[i] = cast(wchar)c; break; case 4: - dstring[i] = c; + dstring[i] = cast(dchar) c; + break; + case 8: + lstring[i] = c; break; } } @@ -1847,6 +1866,28 @@ extern (C++) final class StringExp : Expression } } +extern (C++) final class InterpExp : Expression +{ + char postfix = NoPostfix; // 'c', 'w', 'd' + OwnedBy ownedByCtfe = OwnedBy.code; + InterpolatedSet* interpolatedSet; + + enum char NoPostfix = 0; + + extern (D) this(const ref Loc loc, InterpolatedSet* set, char postfix = NoPostfix) scope + { + super(loc, EXP.interpolated); + this.interpolatedSet = set; + this.postfix = postfix; + } + + override void accept(Visitor v) + { + v.visit(this); + } +} + + /*********************************************************** * A sequence of expressions * @@ -5494,6 +5535,7 @@ private immutable ubyte[EXP.max+1] expSize = [ EXP.preMinusMinus: __traits(classInstanceSize, PreExp), EXP.identifier: __traits(classInstanceSize, IdentifierExp), EXP.string_: __traits(classInstanceSize, StringExp), + EXP.interpolated: __traits(classInstanceSize, InterpExp), EXP.this_: __traits(classInstanceSize, ThisExp), EXP.super_: __traits(classInstanceSize, SuperExp), EXP.halt: __traits(classInstanceSize, HaltExp), diff --git a/gcc/d/dmd/expression.h b/gcc/d/dmd/expression.h index f57f6a4..d53cc3e 100644 --- a/gcc/d/dmd/expression.h +++ b/gcc/d/dmd/expression.h @@ -38,6 +38,7 @@ class TemplateDeclaration; class ClassDeclaration; class OverloadSet; class StringExp; +class InterpExp; class LoweredAssignExp; #ifdef IN_GCC typedef union tree_node Symbol; @@ -129,6 +130,7 @@ public: SuperExp* isSuperExp(); NullExp* isNullExp(); StringExp* isStringExp(); + InterpExp* isInterpExp(); TupleExp* isTupleExp(); ArrayLiteralExp* isArrayLiteralExp(); AssocArrayLiteralExp* isAssocArrayLiteralExp(); @@ -352,7 +354,7 @@ class StringExp final : public Expression public: utf8_t postfix; // 'c', 'w', 'd' OwnedBy ownedByCtfe; - void *string; // char, wchar, or dchar data + void *string; // char, wchar, dchar, or long data size_t len; // number of chars, wchars, or dchars unsigned char sz; // 1: char, 2: wchar, 4: dchar d_bool committed; // if type is committed @@ -362,6 +364,7 @@ public: static StringExp *create(const Loc &loc, const void *s, d_size_t len); bool equals(const RootObject * const o) const override; char32_t getCodeUnit(d_size_t i) const; + dinteger_t getIndex(d_size_t i) const; StringExp *toStringExp() override; Optional<bool> toBool() override; bool isLvalue() override; @@ -370,6 +373,16 @@ public: void writeTo(void* dest, bool zero, int tyto = 0) const; }; +class InterpExp final : public Expression +{ +public: + utf8_t postfix; // 'c', 'w', 'd' + OwnedBy ownedByCtfe; + void* interpolatedSet; + + void accept(Visitor* v) override { v->visit(this); } +}; + // Tuple class TupleExp final : public Expression diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d index d464574..f213303 100644 --- a/gcc/d/dmd/expressionsem.d +++ b/gcc/d/dmd/expressionsem.d @@ -42,10 +42,12 @@ import dmd.dsymbolsem; import dmd.dtemplate; import dmd.errors; import dmd.errorsink; +import dmd.enumsem; import dmd.escape; import dmd.expression; import dmd.file_manager; import dmd.func; +import dmd.funcsem; import dmd.globals; import dmd.hdrgen; import dmd.id; @@ -1579,7 +1581,7 @@ Lagain: if (auto f = s.isFuncDeclaration()) { f = f.toAliasFunc(); - if (!f.functionSemantic()) + if (!functionSemantic(f)) return ErrorExp.get(); if (!hasOverloads && f.checkForwardRef(loc)) @@ -2866,7 +2868,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, // If inferring return type, and semantic3() needs to be run if not already run if (!tf.next && fd.inferRetType) { - fd.functionSemantic(); + functionSemantic(fd); } else if (fd && fd.parent) { @@ -4145,6 +4147,84 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = e; } + override void visit(InterpExp e) + { + // the lexer breaks up into an odd/even array of literals and expression code + // we need to turn that into: + /+ + tuple( + .object.imported!"core.interpolation".InterpolationHeader(), + ... + .object.imported!"core.interpolation".InterpolationFooter() + ) + + There the ... loops through them all, making the even ones + .object.imported!"core.interpolation".InterpolatedLiteral!str() + and making the odd ones + .object.imported!"core.interpolation".InterpolatedExpression!str(), + the code represented by str + + Empty string literals are skipped as they provide no additional information. + +/ + + if (e.postfix) + error(e.loc, "String postfixes on interpolated expression sequences are not allowed."); + + Expression makeNonTemplateItem(Identifier which) { + Expression id = new IdentifierExp(e.loc, Id.empty); + id = new DotIdExp(e.loc, id, Id.object); + auto moduleNameArgs = new Objects(); + moduleNameArgs.push(new StringExp(e.loc, "core.interpolation")); + id = new DotTemplateInstanceExp(e.loc, id, Id.imported, moduleNameArgs); + id = new DotIdExp(e.loc, id, which); + id = new CallExp(e.loc, id, new Expressions()); + return id; + } + + Expression makeTemplateItem(Identifier which, string arg) { + Expression id = new IdentifierExp(e.loc, Id.empty); + id = new DotIdExp(e.loc, id, Id.object); + auto moduleNameArgs = new Objects(); + moduleNameArgs.push(new StringExp(e.loc, "core.interpolation")); + id = new DotTemplateInstanceExp(e.loc, id, Id.imported, moduleNameArgs); + auto tiargs = new Objects(); + auto templateStringArg = new StringExp(e.loc, arg); + // banning those instead of forwarding them + // templateStringArg.postfix = e.postfix; // forward the postfix to these literals + tiargs.push(templateStringArg); + id = new DotTemplateInstanceExp(e.loc, id, which, tiargs); + id = new CallExp(e.loc, id, new Expressions()); + return id; + } + + auto arguments = new Expressions(); + arguments.push(makeNonTemplateItem(Id.InterpolationHeader)); + + foreach (idx, str; e.interpolatedSet.parts) + { + if (idx % 2 == 0) + { + if (str.length > 0) + arguments.push(makeTemplateItem(Id.InterpolatedLiteral, str)); + } + else + { + arguments.push(makeTemplateItem(Id.InterpolatedExpression, str)); + Expressions* mix = new Expressions(); + mix.push(new StringExp(e.loc, str)); + // FIXME: i'd rather not use MixinExp but idk how to do it lol + arguments.push(new MixinExp(e.loc, mix)); + } + } + + arguments.push(makeNonTemplateItem(Id.InterpolationFooter)); + + auto loweredTo = new TupleExp(e.loc, arguments); + visit(loweredTo); + + result = loweredTo; + } + override void visit(StringExp e) { static if (LOGSEMANTIC) @@ -5366,7 +5446,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (fd) { //printf("L%d fd = %s\n", __LINE__, f.toChars()); - if (!fd.functionSemantic()) + if (!functionSemantic(fd)) return setError(); } @@ -6564,10 +6644,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor .errorSupplemental(exp.loc, "the following error occured while looking for a UFCS match"); } - .error(exp.loc, "%s `%s%s` is not callable using argument types `%s`", - exp.f.kind(), exp.f.toPrettyChars(), parametersTypeToChars(tf.parameterList), buf.peekChars()); + .error(exp.loc, "%s `%s` is not callable using argument types `%s`", + exp.f.kind(), exp.f.toChars(), buf.peekChars()); if (failMessage) errorSupplemental(exp.loc, "%s", failMessage); + .errorSupplemental(exp.f.loc, "`%s%s` declared here", exp.f.toPrettyChars(), parametersTypeToChars(tf.parameterList)); exp.f = null; } @@ -7549,7 +7630,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor se = se.toUTF8(sc); auto namez = se.toStringz(); - if (!global.filePath) + if (!global.filePath.length) { error(e.loc, "need `-J` switch to import text file `%s`", namez.ptr); return setError(); @@ -7580,12 +7661,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setError(); } - auto resolvedNamez = FileName.searchPath(global.filePath, namez, false); + auto resolvedNamez = FileName.searchPath(global.filePath[], namez, false); if (!resolvedNamez) { error(e.loc, "file `%s` cannot be found or not in a path specified with `-J`", se.toChars()); errorSupplemental(e.loc, "Path(s) searched (as provided by `-J`):"); - foreach (idx, path; *global.filePath) + foreach (idx, path; global.filePath[]) { const attr = FileName.exists(path); const(char)* err = attr == 2 ? "" : @@ -8170,7 +8251,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (FuncDeclaration fd = exp.var.isFuncDeclaration()) { // for functions, do checks after overload resolution - if (!fd.functionSemantic()) + if (!functionSemantic(fd)) return setError(); /* https://issues.dlang.org/show_bug.cgi?id=13843 @@ -8937,7 +9018,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor bool err = false; if (cd.dtor) { - err |= !cd.dtor.functionSemantic(); + err |= !functionSemantic(cd.dtor); err |= cd.dtor.checkPurity(exp.loc, sc); err |= cd.dtor.checkSafety(exp.loc, sc); err |= cd.dtor.checkNogc(exp.loc, sc); @@ -14356,7 +14437,7 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) if (auto f = s.isFuncDeclaration()) { //printf("it's a function\n"); - if (!f.functionSemantic()) + if (!functionSemantic(f)) return ErrorExp.get(); Expression e; if (f.needThis()) diff --git a/gcc/d/dmd/func.d b/gcc/d/dmd/func.d index 370da6f..adfecc8 100644 --- a/gcc/d/dmd/func.d +++ b/gcc/d/dmd/func.d @@ -39,6 +39,7 @@ import dmd.dtemplate; import dmd.errors; import dmd.escape; import dmd.expression; +import dmd.funcsem; import dmd.globals; import dmd.hdrgen; import dmd.id; @@ -467,102 +468,12 @@ extern (C++) class FuncDeclaration : Declaration } /**************************************************** - * Resolve forward reference of function signature - - * parameter types, return type, and attributes. - * Returns: - * false if any errors exist in the signature. - */ - final bool functionSemantic() - { - //printf("functionSemantic() %p %s\n", this, toChars()); - if (!_scope) - return !errors; - - this.cppnamespace = _scope.namespace; - - if (!originalType) // semantic not yet run - { - TemplateInstance spec = isSpeculative(); - uint olderrs = global.errors; - uint oldgag = global.gag; - if (global.gag && !spec) - global.gag = 0; - dsymbolSemantic(this, _scope); - global.gag = oldgag; - if (spec && global.errors != olderrs) - spec.errors = (global.errors - olderrs != 0); - if (olderrs != global.errors) // if errors compiling this function - return false; - } - - // if inferring return type, sematic3 needs to be run - // - When the function body contains any errors, we cannot assume - // the inferred return type is valid. - // So, the body errors should become the function signature error. - if (inferRetType && type && !type.nextOf()) - return functionSemantic3(); - - TemplateInstance ti; - if (isInstantiated() && !isVirtualMethod() && - ((ti = parent.isTemplateInstance()) is null || ti.isTemplateMixin() || ti.tempdecl.ident == ident)) - { - AggregateDeclaration ad = isMemberLocal(); - if (ad && ad.sizeok != Sizeok.done) - { - /* Currently dmd cannot resolve forward references per methods, - * then setting SIZOKfwd is too conservative and would break existing code. - * So, just stop method attributes inference until ad.dsymbolSemantic() done. - */ - //ad.sizeok = Sizeok.fwd; - } - else - return functionSemantic3() || !errors; - } - - if (storage_class & STC.inference) - return functionSemantic3() || !errors; - - return !errors; - } - - /**************************************************** - * Resolve forward reference of function body. - * Returns false if any errors exist in the body. - */ - final bool functionSemantic3() - { - if (semanticRun < PASS.semantic3 && _scope) - { - /* Forward reference - we need to run semantic3 on this function. - * If errors are gagged, and it's not part of a template instance, - * we need to temporarily ungag errors. - */ - TemplateInstance spec = isSpeculative(); - uint olderrs = global.errors; - uint oldgag = global.gag; - if (global.gag && !spec) - global.gag = 0; - semantic3(this, _scope); - global.gag = oldgag; - - // If it is a speculatively-instantiated template, and errors occur, - // we need to mark the template as having errors. - if (spec && global.errors != olderrs) - spec.errors = (global.errors - olderrs != 0); - if (olderrs != global.errors) // if errors compiling this function - return false; - } - - return !errors && !this.hasSemantic3Errors(); - } - - /**************************************************** * Check that this function type is properly resolved. * If not, report "forward reference error" and return true. */ extern (D) final bool checkForwardRef(const ref Loc loc) { - if (!functionSemantic()) + if (!functionSemantic(this)) return true; /* No deco means the functionSemantic() call could not resolve @@ -579,72 +490,6 @@ extern (C++) class FuncDeclaration : Declaration return false; } - // called from semantic3 - /** - * Creates and returns the hidden parameters for this function declaration. - * - * Hidden parameters include the `this` parameter of a class, struct or - * nested function and the selector parameter for Objective-C methods. - */ - extern (D) final void declareThis(Scope* sc) - { - const bool dualCtx = (toParent2() != toParentLocal()); - if (dualCtx) - this.hasDualContext = true; - auto ad = isThis(); - if (!dualCtx && !ad && !isNested()) - { - vthis = null; - objc.selectorParameter = null; - return; - } - - Type addModStc(Type t) - { - return t.addMod(type.mod).addStorageClass(storage_class); - } - - if (dualCtx || isNested()) - { - /* The 'this' for a nested function is the link to the - * enclosing function's stack frame. - * Note that nested functions and member functions are disjoint. - */ - Type tthis = addModStc(dualCtx ? - Type.tvoidptr.sarrayOf(2).pointerTo() : - Type.tvoid.pointerTo()); - vthis = new VarDeclaration(loc, tthis, dualCtx ? Id.this2 : Id.capture, null); - vthis.storage_class |= STC.parameter | STC.nodtor; - } - else if (ad) - { - Type thandle = addModStc(ad.handleType()); - vthis = new ThisDeclaration(loc, thandle); - vthis.storage_class |= STC.parameter; - if (thandle.ty == Tstruct) - { - vthis.storage_class |= STC.ref_; - } - } - - if (auto tf = type.isTypeFunction()) - { - if (tf.isreturn) - vthis.storage_class |= STC.return_; - if (tf.isScopeQual) - vthis.storage_class |= STC.scope_; - if (tf.isreturnscope) - vthis.storage_class |= STC.returnScope; - } - - vthis.dsymbolSemantic(sc); - if (!sc.insert(vthis)) - assert(0); - vthis.parent = this; - if (ad) - objc.selectorParameter = .objc.createSelectorParameter(this, sc); - } - override final bool equals(const RootObject o) const { if (this == o) @@ -1104,20 +949,24 @@ extern (C++) class FuncDeclaration : Declaration } /************************************* - * Determine partial specialization order of 'this' vs g. + * Determine partial specialization order of functions `f` vs `g`. * This is very similar to TemplateDeclaration::leastAsSpecialized(). + * Params: + * f = first function + * g = second function + * names = names of parameters * Returns: * match 'this' is at least as specialized as g * 0 g is more specialized than 'this' */ - final MATCH leastAsSpecialized(FuncDeclaration g, Identifiers* names) + static MATCH leastAsSpecialized(FuncDeclaration f, FuncDeclaration g, Identifiers* names) { enum LOG_LEASTAS = 0; static if (LOG_LEASTAS) { import core.stdc.stdio : printf; - printf("%s.leastAsSpecialized(%s, %s)\n", toChars(), g.toChars(), names ? names.toChars() : "null"); - printf("%s, %s\n", type.toChars(), g.type.toChars()); + printf("leastAsSpecialized(%s, %s, %s)\n", f.toChars(), g.toChars(), names ? names.toChars() : "null"); + printf("%s, %s\n", f.type.toChars(), g.type.toChars()); } /* This works by calling g() with f()'s parameters, and @@ -1125,15 +974,15 @@ extern (C++) class FuncDeclaration : Declaration * as g() is. */ - TypeFunction tf = type.toTypeFunction(); + TypeFunction tf = f.type.toTypeFunction(); TypeFunction tg = g.type.toTypeFunction(); /* If both functions have a 'this' pointer, and the mods are not * the same and g's is not const, then this is less specialized. */ - if (needThis() && g.needThis() && tf.mod != tg.mod) + if (f.needThis() && g.needThis() && tf.mod != tg.mod) { - if (isCtorDeclaration()) + if (f.isCtorDeclaration()) { if (!MODimplicitConv(tg.mod, tf.mod)) return MATCH.nomatch; @@ -3037,7 +2886,7 @@ Expression addInvariant(AggregateDeclaration ad, VarDeclaration vthis) // Workaround for https://issues.dlang.org/show_bug.cgi?id=13394 // For the correct mangling, // run attribute inference on inv if needed. - inv.functionSemantic(); + functionSemantic(inv); } //e = new DsymbolExp(Loc.initial, inv); @@ -3316,7 +3165,7 @@ FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, if (m.count == 1) // exactly one match { if (!(flags & FuncResolveFlag.quiet)) - m.lastf.functionSemantic(); + functionSemantic(m.lastf); return m.lastf; } if ((flags & FuncResolveFlag.overloadOnly) && !tthis && m.lastf.needThis()) @@ -3386,12 +3235,18 @@ FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, // all of overloads are templates if (td) { - const(char)* msg = "none of the overloads of %s `%s.%s` are callable using argument types `!(%s)%s`"; if (!od && !td.overnext) - msg = "%s `%s.%s` is not callable using argument types `!(%s)%s`"; - .error(loc, msg, + { + .error(loc, "%s `%s` is not callable using argument types `!(%s)%s`", + td.kind(), td.ident.toChars(), tiargsBuf.peekChars(), fargsBuf.peekChars()); + } + else + { + .error(loc, "none of the overloads of %s `%s.%s` are callable using argument types `!(%s)%s`", td.kind(), td.parent.toPrettyChars(), td.ident.toChars(), tiargsBuf.peekChars(), fargsBuf.peekChars()); + } + if (!global.gag || global.params.v.showGaggedErrors) printCandidates(loc, td, sc.isDeprecated()); @@ -3574,7 +3429,11 @@ if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration)) if (!print) return true; - const tmsg = td.toCharsNoConstraints(); + OutBuffer buf; + HdrGenState hgs; + hgs.skipConstraints = true; + toCharsMaybeConstraints(td, buf, hgs); + const tmsg = buf.peekChars(); const cmsg = td.getConstraintEvalError(constraintsTip); // add blank space if there are multiple candidates diff --git a/gcc/d/dmd/funcsem.d b/gcc/d/dmd/funcsem.d new file mode 100644 index 0000000..8420179 --- /dev/null +++ b/gcc/d/dmd/funcsem.d @@ -0,0 +1,219 @@ +/** + * Does semantic analysis for functions. + * + * Specification: $(LINK2 https://dlang.org/spec/function.html, Functions) + * + * 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/funcsem.d, _funcsem.d) + * Documentation: https://dlang.org/phobos/dmd_funcsem.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/funcsem.d + */ + +module dmd.funcsem; + +import core.stdc.stdio; + +import dmd.aggregate; +import dmd.arraytypes; +import dmd.astenums; +import dmd.blockexit; +import dmd.gluelayer; +import dmd.dcast; +import dmd.dclass; +import dmd.declaration; +import dmd.delegatize; +import dmd.dinterpret; +import dmd.dmodule; +import dmd.dscope; +import dmd.dstruct; +import dmd.dsymbol; +import dmd.dsymbolsem; +import dmd.dtemplate; +import dmd.errors; +import dmd.escape; +import dmd.expression; +import dmd.func; +import dmd.globals; +import dmd.hdrgen; +import dmd.id; +import dmd.identifier; +import dmd.init; +import dmd.location; +import dmd.mtype; +import dmd.objc; +import dmd.root.aav; +import dmd.common.outbuffer; +import dmd.rootobject; +import dmd.root.string; +import dmd.root.stringtable; +import dmd.semantic2; +import dmd.semantic3; +import dmd.statement_rewrite_walker; +import dmd.statement; +import dmd.statementsem; +import dmd.tokens; +import dmd.visitor; + +/**************************************************** + * Resolve forward reference of function signature - + * parameter types, return type, and attributes. + * Params: + * fd = function declaration + * Returns: + * false if any errors exist in the signature. + */ +public +extern (C++) +bool functionSemantic(FuncDeclaration fd) +{ + //printf("functionSemantic() %p %s\n", this, toChars()); + if (!fd._scope) + return !fd.errors; + + fd.cppnamespace = fd._scope.namespace; + + if (!fd.originalType) // semantic not yet run + { + TemplateInstance spec = fd.isSpeculative(); + uint olderrs = global.errors; + uint oldgag = global.gag; + if (global.gag && !spec) + global.gag = 0; + dsymbolSemantic(fd, fd._scope); + global.gag = oldgag; + if (spec && global.errors != olderrs) + spec.errors = (global.errors - olderrs != 0); + if (olderrs != global.errors) // if errors compiling this function + return false; + } + + // if inferring return type, sematic3 needs to be run + // - When the function body contains any errors, we cannot assume + // the inferred return type is valid. + // So, the body errors should become the function signature error. + if (fd.inferRetType && fd.type && !fd.type.nextOf()) + return fd.functionSemantic3(); + + TemplateInstance ti; + if (fd.isInstantiated() && !fd.isVirtualMethod() && + ((ti = fd.parent.isTemplateInstance()) is null || ti.isTemplateMixin() || ti.tempdecl.ident == fd.ident)) + { + AggregateDeclaration ad = fd.isMemberLocal(); + if (ad && ad.sizeok != Sizeok.done) + { + /* Currently dmd cannot resolve forward references per methods, + * then setting SIZOKfwd is too conservative and would break existing code. + * So, just stop method attributes inference until ad.dsymbolSemantic() done. + */ + //ad.sizeok = Sizeok.fwd; + } + else + return fd.functionSemantic3() || !fd.errors; + } + + if (fd.storage_class & STC.inference) + return fd.functionSemantic3() || !fd.errors; + + return !fd.errors; +} + +/**************************************************** + * Resolve forward reference of function body. + * Returns false if any errors exist in the body. + */ +public +extern (C++) +bool functionSemantic3(FuncDeclaration fd) +{ + if (fd.semanticRun < PASS.semantic3 && fd._scope) + { + /* Forward reference - we need to run semantic3 on this function. + * If errors are gagged, and it's not part of a template instance, + * we need to temporarily ungag errors. + */ + TemplateInstance spec = fd.isSpeculative(); + uint olderrs = global.errors; + uint oldgag = global.gag; + if (global.gag && !spec) + global.gag = 0; + semantic3(fd, fd._scope); + global.gag = oldgag; + + // If it is a speculatively-instantiated template, and errors occur, + // we need to mark the template as having errors. + if (spec && global.errors != olderrs) + spec.errors = (global.errors - olderrs != 0); + if (olderrs != global.errors) // if errors compiling this function + return false; + } + + return !fd.errors && !fd.hasSemantic3Errors(); +} + +// called from semantic3 +/** + * Creates and returns the hidden parameters for this function declaration. + * + * Hidden parameters include the `this` parameter of a class, struct or + * nested function and the selector parameter for Objective-C methods. + */ +extern (D) void declareThis(FuncDeclaration fd, Scope* sc) +{ + const bool dualCtx = (fd.toParent2() != fd.toParentLocal()); + if (dualCtx) + fd.hasDualContext = true; + auto ad = fd.isThis(); + if (!dualCtx && !ad && !fd.isNested()) + { + fd.vthis = null; + fd.objc.selectorParameter = null; + return; + } + + Type addModStc(Type t) + { + return t.addMod(fd.type.mod).addStorageClass(fd.storage_class); + } + + if (dualCtx || fd.isNested()) + { + /* The 'this' for a nested function is the link to the + * enclosing function's stack frame. + * Note that nested functions and member functions are disjoint. + */ + Type tthis = addModStc(dualCtx ? + Type.tvoidptr.sarrayOf(2).pointerTo() : + Type.tvoid.pointerTo()); + fd.vthis = new VarDeclaration(fd.loc, tthis, dualCtx ? Id.this2 : Id.capture, null); + fd.vthis.storage_class |= STC.parameter | STC.nodtor; + } + else if (ad) + { + Type thandle = addModStc(ad.handleType()); + fd.vthis = new ThisDeclaration(fd.loc, thandle); + fd.vthis.storage_class |= STC.parameter; + if (thandle.ty == Tstruct) + { + fd.vthis.storage_class |= STC.ref_; + } + } + + if (auto tf = fd.type.isTypeFunction()) + { + if (tf.isreturn) + fd.vthis.storage_class |= STC.return_; + if (tf.isScopeQual) + fd.vthis.storage_class |= STC.scope_; + if (tf.isreturnscope) + fd.vthis.storage_class |= STC.returnScope; + } + + fd.vthis.dsymbolSemantic(sc); + if (!sc.insert(fd.vthis)) + assert(0); + fd.vthis.parent = fd; + if (ad) + fd.objc.selectorParameter = .objc.createSelectorParameter(fd, sc); +} diff --git a/gcc/d/dmd/globals.d b/gcc/d/dmd/globals.d index e9e73e8..af7b1fa 100644 --- a/gcc/d/dmd/globals.d +++ b/gcc/d/dmd/globals.d @@ -219,8 +219,8 @@ extern (C++) struct Param 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 - Array!(const(char)*)* fileImppath; // array of char*'s of where to look for file import modules + Array!(const(char)*) imppath; // array of char*'s of where to look for import modules + Array!(const(char)*) fileImppath; // array of char*'s of where to look for file import modules const(char)[] objdir; // .obj/.lib file output directory const(char)[] objname; // .obj file output name const(char)[] libname; // .lib file output name @@ -280,8 +280,8 @@ extern (C++) struct Global string copyright = "Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved"; string written = "written by Walter Bright"; - Array!(const(char)*)* path; /// Array of char*'s which form the import lookup path - Array!(const(char)*)* filePath; /// Array of char*'s which form the file import lookup path + Array!(const(char)*) path; /// Array of char*'s which form the import lookup path + Array!(const(char)*) filePath; /// Array of char*'s which form the file import lookup path private enum string _version = import("VERSION"); char[26] datetime; /// string returned by ctime() @@ -296,8 +296,8 @@ extern (C++) struct Global void* console; /// opaque pointer to console for controlling text attributes - Array!Identifier* versionids; /// command line versions and predefined versions - Array!Identifier* debugids; /// command line debug versions and predefined versions + Array!Identifier versionids; /// command line versions and predefined versions + Array!Identifier debugids; /// command line debug versions and predefined versions bool hasMainFunction; /// Whether a main function has already been compiled in (for -main switch) uint varSequenceNumber = 1; /// Relative lifetime of `VarDeclaration` within a function, used for `scope` checks diff --git a/gcc/d/dmd/globals.h b/gcc/d/dmd/globals.h index 93e7c64..f553ae6 100644 --- a/gcc/d/dmd/globals.h +++ b/gcc/d/dmd/globals.h @@ -202,8 +202,8 @@ struct Param DString 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 - Array<const char *> *fileImppath; // array of char*'s of where to look for file import modules + Array<const char *> imppath; // array of char*'s of where to look for import modules + Array<const char *> fileImppath; // array of char*'s of where to look for file import modules DString objdir; // .obj/.lib file output directory DString objname; // .obj file output name DString libname; // .lib file output name @@ -282,8 +282,8 @@ struct Global const DString copyright; const DString written; - Array<const char *> *path; // Array of char*'s which form the import lookup path - Array<const char *> *filePath; // Array of char*'s which form the file import lookup path + Array<const char *> path; // Array of char*'s which form the import lookup path + Array<const char *> filePath; // Array of char*'s which form the file import lookup path char datetime[26]; /// string returned by ctime() CompileEnv compileEnv; @@ -297,8 +297,8 @@ struct Global void* console; // opaque pointer to console for controlling text attributes - Array<class Identifier*>* versionids; // command line versions and predefined versions - Array<class Identifier*>* debugids; // command line debug versions and predefined versions + Array<class Identifier*> versionids; // command line versions and predefined versions + Array<class Identifier*> debugids; // command line debug versions and predefined versions d_bool hasMainFunction; unsigned varSequenceNumber; diff --git a/gcc/d/dmd/hdrgen.d b/gcc/d/dmd/hdrgen.d index 570c662..030153c 100644 --- a/gcc/d/dmd/hdrgen.d +++ b/gcc/d/dmd/hdrgen.d @@ -60,6 +60,7 @@ struct HdrGenState bool importcHdr; /// true if generating a .di file from an ImportC file bool doFuncBodies; /// include function bodies in output bool vcg_ast; /// write out codegen-ast + bool skipConstraints; // skip constraints when doing templates bool fullQual; /// fully qualify types when printing int tpltMember; @@ -1957,6 +1958,47 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) s.accept(v); } +// Note: this function is not actually `const`, because iterating the +// function parameter list may run dsymbolsemantic on enum types +public +void toCharsMaybeConstraints(const TemplateDeclaration td, ref OutBuffer buf, ref HdrGenState hgs) +{ + buf.writestring(td.ident == Id.ctor ? "this" : td.ident.toString()); + buf.writeByte('('); + foreach (i, const tp; *td.parameters) + { + if (i) + buf.writestring(", "); + toCBuffer(tp, buf, hgs); + } + buf.writeByte(')'); + + if (td.onemember) + { + if (const fd = td.onemember.isFuncDeclaration()) + { + if (TypeFunction tf = cast(TypeFunction)fd.type.isTypeFunction()) + { + // !! Casted away const + buf.writestring(parametersTypeToChars(tf.parameterList)); + if (tf.mod) + { + buf.writeByte(' '); + buf.MODtoBuffer(tf.mod); + } + } + } + } + + if (!hgs.skipConstraints && + td.constraint) + { + buf.writestring(" if ("); + toCBuffer(td.constraint, buf, hgs); + buf.writeByte(')'); + } +} + /***************************************** * Pretty-print a template parameter list to a buffer. @@ -2234,6 +2276,16 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt void visitString(StringExp e) { + if (e.hexString || e.sz == 8) + { + buf.writeByte('x'); + buf.writeByte('"'); + buf.writeHexString(e.peekData, true); + buf.writeByte('"'); + if (e.postfix) + buf.writeByte(e.postfix); + return; + } buf.writeByte('"'); const o = buf.length; foreach (i; 0 .. e.len) @@ -2247,6 +2299,37 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt buf.writeByte(e.postfix); } + void visitInterpolation(InterpExp e) + { + buf.writeByte('i'); + buf.writeByte('"'); + const o = buf.length; + + foreach (idx, str; e.interpolatedSet.parts) + { + if (idx % 2 == 0) + { + foreach(ch; str) + writeCharLiteral(buf, ch); + } + else + { + buf.writeByte('$'); + buf.writeByte('('); + foreach(ch; str) + buf.writeByte(ch); + buf.writeByte(')'); + } + } + + if (hgs.ddoc) + escapeDdocString(buf, o); + buf.writeByte('"'); + if (e.postfix) + buf.writeByte(e.postfix); + + } + void visitArrayLiteral(ArrayLiteralExp e) { buf.writeByte('['); @@ -2827,6 +2910,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt case EXP.super_: return visitSuper(e.isSuperExp()); case EXP.null_: return visitNull(e.isNullExp()); case EXP.string_: return visitString(e.isStringExp()); + case EXP.interpolated: return visitInterpolation(e.isInterpExp()); case EXP.arrayLiteral: return visitArrayLiteral(e.isArrayLiteralExp()); case EXP.assocArrayLiteral: return visitAssocArrayLiteral(e.isAssocArrayLiteralExp()); case EXP.structLiteral: return visitStructLiteral(e.isStructLiteralExp()); diff --git a/gcc/d/dmd/id.d b/gcc/d/dmd/id.d index a66f2af4..5ad324d 100644 --- a/gcc/d/dmd/id.d +++ b/gcc/d/dmd/id.d @@ -335,6 +335,12 @@ immutable Msgtable[] msgtable = { "_d_arrayassign_l" }, { "_d_arrayassign_r" }, + { "imported" }, + { "InterpolationHeader" }, + { "InterpolationFooter" }, + { "InterpolatedLiteral" }, + { "InterpolatedExpression" }, + // For pragma's { "Pinline", "inline" }, { "lib" }, diff --git a/gcc/d/dmd/json.d b/gcc/d/dmd/json.d index 9819c3a..7c65067 100644 --- a/gcc/d/dmd/json.d +++ b/gcc/d/dmd/json.d @@ -873,12 +873,9 @@ public: propertyStart("predefinedVersions"); arrayStart(); - if (global.versionids) + foreach (const versionid; global.versionids) { - foreach (const versionid; *global.versionids) - { - item(versionid.toString()); - } + item(versionid.toString()); } arrayEnd(); @@ -905,12 +902,9 @@ public: propertyStart("importPaths"); arrayStart(); - if (global.params.imppath) + foreach (importPath; global.params.imppath[]) { - foreach (importPath; *global.params.imppath) - { - item(importPath.toDString); - } + item(importPath.toDString); } arrayEnd(); diff --git a/gcc/d/dmd/lexer.d b/gcc/d/dmd/lexer.d index 5eadd72..937597c 100644 --- a/gcc/d/dmd/lexer.d +++ b/gcc/d/dmd/lexer.d @@ -506,6 +506,29 @@ class Lexer } else goto case_ident; + case 'i': + if (Ccompile) + goto case_ident; + if (p[1] == '"') + { + p++; // skip the i + escapeStringConstant(t, true); + return; + } + else if (p[1] == '`') + { + p++; // skip the i + wysiwygStringConstant(t, true); + return; + } + else if (p[1] == 'q' && p[2] == '{') + { + p += 2; // skip the i and q + tokenStringConstant(t, true); + return; + } + else + goto case_ident; case '"': escapeStringConstant(t); return; @@ -517,7 +540,7 @@ class Lexer case 'f': case 'g': case 'h': - case 'i': + /*case 'i':*/ case 'j': case 'k': case 'l': @@ -1429,9 +1452,18 @@ class Lexer Params: result = pointer to the token that accepts the result */ - private void wysiwygStringConstant(Token* result) + private void wysiwygStringConstant(Token* result, bool supportInterpolation = false) { - result.value = TOK.string_; + if (supportInterpolation) + { + result.value = TOK.interpolated; + result.interpolatedSet = null; + } + else + { + result.value = TOK.string_; + } + Loc start = loc(); auto terminator = p[0]; p++; @@ -1451,6 +1483,14 @@ class Lexer c = '\n'; // treat EndOfLine as \n character endOfLine(); break; + case '$': + if (!supportInterpolation) + goto default; + + if (!handleInterpolatedSegment(result, start)) + goto default; + + continue; case 0: case 0x1A: error("unterminated string constant starting at %s", start.toChars()); @@ -1461,7 +1501,11 @@ class Lexer default: if (c == terminator) { - result.setString(stringbuffer); + if (supportInterpolation) + result.appendInterpolatedPart(stringbuffer); + else + result.setString(stringbuffer); + stringPostfix(result); return; } @@ -1736,13 +1780,21 @@ class Lexer Params: result = pointer to the token that accepts the result */ - private void tokenStringConstant(Token* result) + private void tokenStringConstant(Token* result, bool supportInterpolation = false) { - result.value = TOK.string_; + if (supportInterpolation) + { + result.value = TOK.interpolated; + result.interpolatedSet = null; + } + else + { + result.value = TOK.string_; + } uint nest = 1; const start = loc(); - const pstart = ++p; + auto pstart = ++p; inTokenStringConstant++; scope(exit) inTokenStringConstant--; while (1) @@ -1757,11 +1809,29 @@ class Lexer case TOK.rightCurly: if (--nest == 0) { - result.setString(pstart, p - 1 - pstart); + if (supportInterpolation) + result.appendInterpolatedPart(pstart, p - 1 - pstart); + else + result.setString(pstart, p - 1 - pstart); + stringPostfix(result); return; } continue; + case TOK.dollar: + if (!supportInterpolation) + goto default; + + stringbuffer.setsize(0); + stringbuffer.write(pstart, p - 1 - pstart); + if (!handleInterpolatedSegment(result, start)) + goto default; + + stringbuffer.setsize(0); + + pstart = p; + + continue; case TOK.endOfFile: error("unterminated token string constant starting at %s", start.toChars()); result.setString(); @@ -1772,6 +1842,52 @@ class Lexer } } + // returns true if it got special treatment as an interpolated segment + // otherwise returns false, indicating to treat it as just part of a normal string + private bool handleInterpolatedSegment(Token* token, Loc start) + { + switch(*p) + { + case '(': + // expression, at this level we need to scan until the closing ')' + + // always put the string part in first + token.appendInterpolatedPart(stringbuffer); + stringbuffer.setsize(0); + + int openParenCount = 1; + p++; // skip the first open paren + auto pstart = p; + while (openParenCount > 0) + { + // need to scan with the lexer to support embedded strings and other complex cases + Token tok; + scan(&tok); + if (tok.value == TOK.leftParenthesis) + openParenCount++; + if (tok.value == TOK.rightParenthesis) + openParenCount--; + if (tok.value == TOK.endOfFile) + { + // FIXME: make this error better, it spams a lot + error("unterminated interpolated string constant starting at %s", start.toChars()); + return false; + } + } + + // then put the interpolated string segment + token.appendInterpolatedPart(pstart[0 .. p - 1 - pstart]); + + stringbuffer.setsize(0); // make sure this is reset from the last token scan + // otherwise something like i"$(func("thing")) stuff" can still include it + + return true; + default: + // nothing special + return false; + } + } + /** Scan a quoted string while building the processed string value by handling escape sequences. The result is returned in the given `t` token. @@ -1783,9 +1899,17 @@ class Lexer * D https://dlang.org/spec/lex.html#double_quoted_strings * ImportC C11 6.4.5 */ - private void escapeStringConstant(Token* t) + private void escapeStringConstant(Token* t, bool supportInterpolation = false) { - t.value = TOK.string_; + if (supportInterpolation) + { + t.value = TOK.interpolated; + t.interpolatedSet = null; + } + else + { + t.value = TOK.string_; + } const start = loc(); const tc = *p++; // opening quote @@ -1813,11 +1937,28 @@ class Lexer c = escapeSequence(c2); stringbuffer.writeUTF8(c); continue; + case '$': + if (supportInterpolation) + { + p++; // skip escaped $ + stringbuffer.writeByte('$'); + continue; + } + else + goto default; default: c = escapeSequence(c2); break; } break; + case '$': + if (!supportInterpolation) + goto default; + + if (!handleInterpolatedSegment(t, start)) + goto default; + + continue; case '\n': endOfLine(); if (Ccompile) @@ -1835,7 +1976,10 @@ class Lexer case '"': if (c != tc) goto default; - t.setString(stringbuffer); + if (supportInterpolation) + t.appendInterpolatedPart(stringbuffer); + else + t.setString(stringbuffer); if (!Ccompile) stringPostfix(t); return; diff --git a/gcc/d/dmd/mtype.d b/gcc/d/dmd/mtype.d index 3d88a1d..276f209 100644 --- a/gcc/d/dmd/mtype.d +++ b/gcc/d/dmd/mtype.d @@ -27,9 +27,11 @@ import dmd.denum; import dmd.dstruct; import dmd.dsymbol; import dmd.dtemplate; +import dmd.enumsem; import dmd.errors; import dmd.expression; import dmd.func; +import dmd.funcsem; import dmd.globals; import dmd.hdrgen; import dmd.id; @@ -516,16 +518,39 @@ extern (C++) abstract class Type : ASTNode Terror ]; + static Type merge(Type t) + { + import dmd.basicmangle : tyToDecoBuffer; + + OutBuffer buf; + buf.reserve(3); + + if (t.ty == Tnoreturn) + buf.writestring("Nn"); + else + tyToDecoBuffer(buf, t.ty); + + auto sv = t.stringtable.update(buf[]); + if (sv.value) + return sv.value; + else + { + t.deco = cast(char*)sv.toDchars(); + sv.value = t; + return t; + } + } + for (size_t i = 0; basetab[i] != Terror; i++) { Type t = new TypeBasic(basetab[i]); - t = t.merge(); + t = merge(t); basic[basetab[i]] = t; } basic[Terror] = new TypeError(); tnoreturn = new TypeNoreturn(); - tnoreturn.deco = tnoreturn.merge().deco; + tnoreturn.deco = merge(tnoreturn).deco; basic[Tnoreturn] = tnoreturn; tvoid = basic[Tvoid]; @@ -560,7 +585,7 @@ extern (C++) abstract class Type : ASTNode terror = basic[Terror]; tnoreturn = basic[Tnoreturn]; tnull = new TypeNull(); - tnull.deco = tnull.merge().deco; + tnull.deco = merge(tnull).deco; tvoidptr = tvoid.pointerTo(); tstring = tchar.immutableOf().arrayOf(); @@ -601,29 +626,6 @@ extern (C++) abstract class Type : ASTNode return cast(uint)size(Loc.initial); } - /************************************* - * This version does a merge even if the deco is already computed. - * Necessary for types that have a deco, but are not merged. - */ - final Type merge2() - { - //printf("merge2(%s)\n", toChars()); - Type t = this; - assert(t); - if (!t.deco) - return t.merge(); - - auto sv = stringtable.lookup(t.deco, strlen(t.deco)); - if (sv && sv.value) - { - t = sv.value; - assert(t.deco); - } - else - assert(0); - return t; - } - /********************************* * Store this type's modifier name into buf. */ @@ -1693,7 +1695,7 @@ extern (C++) abstract class Type : ASTNode if (callable) { auto fd = resolveFuncCall(Loc.initial, null, callable, null, this, ArgumentList(), FuncResolveFlag.quiet); - if (!fd || fd.errors || !fd.functionSemantic()) + if (!fd || fd.errors || !functionSemantic(fd)) return Type.terror; auto t = fd.type.nextOf(); diff --git a/gcc/d/dmd/mtype.h b/gcc/d/dmd/mtype.h index c777f35..8b0a1b2 100644 --- a/gcc/d/dmd/mtype.h +++ b/gcc/d/dmd/mtype.h @@ -233,7 +233,6 @@ public: uinteger_t size(); virtual uinteger_t size(const Loc &loc); virtual unsigned alignsize(); - Type *merge2(); void modToBuffer(OutBuffer& buf) const; char *modToChars() const; @@ -909,3 +908,4 @@ Covariant covariant(Type *, Type *, StorageClass * = NULL, bool = false); bool isBaseOf(Type *tthis, Type *t, int *poffset); Type *trySemantic(Type *type, const Loc &loc, Scope *sc); void purityLevel(TypeFunction *type); +Type *merge2(Type *type); diff --git a/gcc/d/dmd/parse.d b/gcc/d/dmd/parse.d index 268622a..0dc54ff 100644 --- a/gcc/d/dmd/parse.d +++ b/gcc/d/dmd/parse.d @@ -2015,6 +2015,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.wcharLiteral: case TOK.dcharLiteral: case TOK.string_: + case TOK.interpolated: case TOK.hexadecimalString: case TOK.file: case TOK.fileFullPath: @@ -5820,6 +5821,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.true_: case TOK.false_: case TOK.string_: + case TOK.interpolated: case TOK.hexadecimalString: case TOK.leftParenthesis: case TOK.cast_: @@ -7313,6 +7315,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.wcharLiteral: case TOK.dcharLiteral: case TOK.string_: + case TOK.interpolated: case TOK.hexadecimalString: case TOK.file: case TOK.fileFullPath: @@ -8177,6 +8180,11 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer nextToken(); break; + case TOK.interpolated: + e = new AST.InterpExp(loc, token.interpolatedSet, token.postfix); + nextToken(); + break; + case TOK.string_: case TOK.hexadecimalString: const bool hexString = token.value == TOK.hexadecimalString; @@ -8810,6 +8818,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.wcharLiteral: case TOK.dcharLiteral: case TOK.string_: + case TOK.interpolated: case TOK.function_: case TOK.delegate_: case TOK.typeof_: diff --git a/gcc/d/dmd/parsetimevisitor.d b/gcc/d/dmd/parsetimevisitor.d index 3d0a585..422c1c8 100644 --- a/gcc/d/dmd/parsetimevisitor.d +++ b/gcc/d/dmd/parsetimevisitor.d @@ -183,6 +183,7 @@ public: void visit(AST.TypeidExp e) { visit(cast(AST.Expression)e); } void visit(AST.TraitsExp e) { visit(cast(AST.Expression)e); } void visit(AST.StringExp e) { visit(cast(AST.Expression)e); } + void visit(AST.InterpExp e) { visit(cast(AST.Expression)e); } void visit(AST.NewExp e) { visit(cast(AST.Expression)e); } void visit(AST.AssocArrayLiteralExp e) { visit(cast(AST.Expression)e); } void visit(AST.ArrayLiteralExp e) { visit(cast(AST.Expression)e); } diff --git a/gcc/d/dmd/res/default_ddoc_theme.ddoc b/gcc/d/dmd/res/default_ddoc_theme.ddoc index 7ae0db8..20269e1 100644 --- a/gcc/d/dmd/res/default_ddoc_theme.ddoc +++ b/gcc/d/dmd/res/default_ddoc_theme.ddoc @@ -70,7 +70,7 @@ D_CODE = <div class="code_sample"> <div class="dlang"> <ol class="code_lines"> - <li><code class="code">$0</code></li> + <li><pre><code class="code">$0</code></pre></li> </ol> </div> </div> @@ -81,7 +81,7 @@ OTHER_CODE = <div class="code_sample"> <div class="dlang"> <ol class="code_lines"> - <li><code class="code language-$1">$+</code></li> + <li><pre><code class="code language-$1">$+</code></pre></li> </ol> </div> </div> @@ -517,6 +517,10 @@ DDOC = white-space: pre-wrap; } + .ddoc .code_lines pre { + display: contents; + } + .ddoc .code_lines li:only-of-type::before { color: rgba(255, 255, 255, 1); content: " "; diff --git a/gcc/d/dmd/root/filename.d b/gcc/d/dmd/root/filename.d index 5b0bba4..41c2050 100644 --- a/gcc/d/dmd/root/filename.d +++ b/gcc/d/dmd/root/filename.d @@ -78,7 +78,7 @@ nothrow: private const(char)[] str; /// - extern (D) this(const(char)[] str) pure + extern (D) this(const char[] str) pure { this.str = str.xarraydup; } @@ -96,7 +96,7 @@ nothrow: } /// Ditto - extern (D) static bool equals(const(char)[] name1, const(char)[] name2) pure @nogc + extern (D) static bool equals(const char[] name1, const char[] name2) pure @nogc { if (name1.length != name2.length) return false; @@ -125,7 +125,7 @@ nothrow: } /// Ditto - extern (D) static bool absolute(const(char)[] name) pure @nogc @safe + extern (D) static bool absolute(const char[] name) pure @nogc @safe { if (!name.length) return false; @@ -189,7 +189,7 @@ nothrow: } /// Ditto - extern (D) static const(char)[] ext(const(char)[] str) nothrow pure @safe @nogc + extern (D) static const(char)[] ext(const char[] str) nothrow pure @safe @nogc { foreach_reverse (idx, char e; str) { @@ -249,7 +249,7 @@ nothrow: } /// Ditto - extern (D) static const(char)[] removeExt(const(char)[] str) + extern (D) static const(char)[] removeExt(const char[] str) { auto e = ext(str); if (e.length) @@ -278,7 +278,7 @@ nothrow: } /// Ditto - extern (D) static const(char)[] name(const(char)[] str) pure @nogc @safe + extern (D) static const(char)[] name(const char[] str) pure @nogc @safe { foreach_reverse (idx, char e; str) { @@ -333,7 +333,7 @@ nothrow: } /// Ditto - extern (D) static const(char)[] path(const(char)[] str) + extern (D) static const(char)[] path(const char[] str) { const n = name(str); bool hasTrailingSlash; @@ -358,7 +358,7 @@ nothrow: /************************************** * Replace filename portion of path. */ - extern (D) static const(char)[] replaceName(const(char)[] path, const(char)[] name) + extern (D) static const(char)[] replaceName(const char[] path, const char[] name) { if (absolute(name)) return name; @@ -387,7 +387,7 @@ nothrow: } /// Ditto - extern(D) static const(char)[] combine(const(char)[] path, const(char)[] name) + extern(D) static const(char)[] combine(const char[] path, const char[] name) { return !path.length ? name : buildPath(path, name); } @@ -401,7 +401,7 @@ nothrow: assert(combine("foo/"[], "bar"[]) == "foo/bar"); } - static const(char)[] buildPath(const(char)[][] fragments...) + static const(char)[] buildPath(const char[][] fragments...) { size_t size; foreach (f; fragments) @@ -563,7 +563,7 @@ nothrow: * Returns: * A newly allocated string (free with `FileName.free`) */ - extern(D) static char[] addExt(const(char)[] name, const(char)[] ext) pure + extern(D) static char[] addExt(const char[] name, const char[] ext) pure { const len = name.length + ext.length + 2; auto s = cast(char*)mem.xmalloc(len); @@ -584,7 +584,7 @@ nothrow: } /// Ditto - extern (D) static const(char)[] defaultExt(const(char)[] name, const(char)[] ext) + extern (D) static const(char)[] defaultExt(const char[] name, const char[] ext) { auto e = FileName.ext(name); if (e.length) // it already has an extension @@ -608,7 +608,7 @@ nothrow: } /// Ditto - extern (D) static const(char)[] forceExt(const(char)[] name, const(char)[] ext) + extern (D) static const(char)[] forceExt(const char[] name, const char[] ext) { if (auto e = FileName.ext(name)) return addExt(name[0 .. $ - e.length - 1], ext); @@ -630,7 +630,7 @@ nothrow: } /// Ditto - extern (D) static bool equalsExt(const(char)[] name, const(char)[] ext) pure @nogc + extern (D) static bool equalsExt(const char[] name, const char[] ext) pure @nogc { auto e = FileName.ext(name); if (!e.length && !ext.length) @@ -665,12 +665,12 @@ nothrow: * Returns: * if found, filename combined with path, otherwise null */ - extern (C++) static const(char)* searchPath(Strings* path, const(char)* name, bool cwd) + extern (C++) static const(char)* searchPath(const ref Strings path, const char* name, bool cwd) { - return searchPath(path, name.toDString, cwd).ptr; + return searchPath(path[], name.toDString, cwd).ptr; } - extern (D) static const(char)[] searchPath(Strings* path, const(char)[] name, bool cwd) + extern (D) static const(char)[] searchPath(const char*[] path, const char[] name, bool cwd) { if (absolute(name)) { @@ -681,24 +681,21 @@ nothrow: if (exists(name)) return name; } - if (path) + foreach (p; path) { - foreach (p; *path) + auto n = combine(p.toDString, name); + if (exists(n)) + return n; + //combine might return name + if (n.ptr != name.ptr) { - auto n = combine(p.toDString, name); - if (exists(n)) - return n; - //combine might return name - if (n.ptr != name.ptr) - { - mem.xfree(cast(void*)n.ptr); - } + mem.xfree(cast(void*)n.ptr); } } return null; } - extern (D) static const(char)[] searchPath(const(char)* path, const(char)[] name, bool cwd) + extern (D) static const(char)[] searchPath(const char* path, const char[] name, bool cwd) { if (absolute(name)) { @@ -738,7 +735,7 @@ nothrow: * Returns: * index of the first reserved character in path if found, size_t.max otherwise */ - extern (D) static size_t findReservedChar(const(char)[] name) pure @nogc @safe + extern (D) static size_t findReservedChar(const char[] name) pure @nogc @safe { version (Windows) { @@ -787,7 +784,7 @@ nothrow: * Returns: * true if path contains '..' reference to parent directory */ - extern (D) static bool refersToParentDir(const(char)[] name) pure @nogc @safe + extern (D) static bool refersToParentDir(const char[] name) pure @nogc @safe { size_t s = 0; foreach (i; 0 .. name.length) @@ -845,7 +842,7 @@ nothrow: } /// Ditto - extern (D) static int exists(const(char)[] name) + extern (D) static int exists(const char[] name) { if (!name.length) return 0; @@ -892,7 +889,7 @@ nothrow: Returns: `true` if the directory exists or was successfully created */ - extern (D) static bool ensurePathExists(const(char)[] path) + extern (D) static bool ensurePathExists(const char[] path) { //printf("FileName::ensurePathExists(%s)\n", path ? path : ""); if (!path.length) @@ -967,7 +964,7 @@ nothrow: } /// Ditto - extern (D) static const(char)[] canonicalName(const(char)[] name) + extern (D) static const(char)[] canonicalName(const char[] name) { version (Posix) { @@ -1127,7 +1124,7 @@ version(Windows) * References: * https://msdn.microsoft.com/en-us/library/windows/desktop/aa363855(v=vs.85).aspx */ - private int _mkdir(const(char)[] path) nothrow + private int _mkdir(const char[] path) nothrow { const createRet = path.extendedPathThen!( p => CreateDirectoryW(&p[0], null /*securityAttributes*/)); @@ -1175,7 +1172,7 @@ version(Windows) * Returns: * The result of calling F on the UTF16 version of str. */ - private auto toWStringzThen(alias F)(const(char)[] str) nothrow + private auto toWStringzThen(alias F)(const char[] str) nothrow { import dmd.common.smallbuffer : SmallBuffer, toWStringz; diff --git a/gcc/d/dmd/root/filename.h b/gcc/d/dmd/root/filename.h index d8834a1..0e52b98 100644 --- a/gcc/d/dmd/root/filename.h +++ b/gcc/d/dmd/root/filename.h @@ -38,7 +38,7 @@ public: bool equalsExt(const char *ext); - static const char *searchPath(Strings *path, const char *name, bool cwd); + static const char *searchPath(Strings& path, const char *name, bool cwd); static int exists(const char *name); static bool ensurePathExists(const char *path); static const char *canonicalName(const char *name); diff --git a/gcc/d/dmd/semantic3.d b/gcc/d/dmd/semantic3.d index 174d9b4..125a39d 100644 --- a/gcc/d/dmd/semantic3.d +++ b/gcc/d/dmd/semantic3.d @@ -41,6 +41,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; @@ -384,7 +385,7 @@ private extern(C++) final class Semantic3Visitor : Visitor } } - funcdecl.declareThis(sc2); + declareThis(funcdecl, sc2); // Reverts: https://issues.dlang.org/show_bug.cgi?id=5710 // No compiler supports this, and there was never any spec for it. diff --git a/gcc/d/dmd/statementsem.d b/gcc/d/dmd/statementsem.d index d4827ae..840035c 100644 --- a/gcc/d/dmd/statementsem.d +++ b/gcc/d/dmd/statementsem.d @@ -43,6 +43,7 @@ import dmd.escape; import dmd.expression; import dmd.expressionsem; import dmd.func; +import dmd.funcsem; import dmd.globals; import dmd.gluelayer; import dmd.hdrgen; @@ -1284,7 +1285,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) Type tfront; if (auto fd = sfront.isFuncDeclaration()) { - if (!fd.functionSemantic()) + if (!functionSemantic(fd)) return rangeError(); tfront = fd.type; } diff --git a/gcc/d/dmd/templatesem.d b/gcc/d/dmd/templatesem.d new file mode 100644 index 0000000..1942afe --- /dev/null +++ b/gcc/d/dmd/templatesem.d @@ -0,0 +1,1497 @@ +/** + * Template semantics. + * + * 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/templatesem.d, _templatesem.d) + * Documentation: https://dlang.org/phobos/dmd_templatesem.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/templatesem.d + */ + +module dmd.templatesem; + +import core.stdc.stdio; +import core.stdc.string; +import dmd.aggregate; +import dmd.aliasthis; +import dmd.arraytypes; +import dmd.astenums; +import dmd.ast_node; +import dmd.attrib; +import dmd.dcast; +import dmd.dclass; +import dmd.declaration; +import dmd.dinterpret; +import dmd.dmangle; +import dmd.dmodule; +import dmd.dscope; +import dmd.dsymbol; +import dmd.dsymbolsem; +import dmd.dtemplate; +import dmd.errors; +import dmd.errorsink; +import dmd.expression; +import dmd.expressionsem; +import dmd.func; +import dmd.funcsem; +import dmd.globals; +import dmd.hdrgen; +import dmd.id; +import dmd.identifier; +import dmd.impcnvtab; +import dmd.init; +import dmd.initsem; +import dmd.location; +import dmd.mtype; +import dmd.opover; +import dmd.optimize; +import dmd.root.array; +import dmd.common.outbuffer; +import dmd.rootobject; +import dmd.semantic2; +import dmd.semantic3; +import dmd.tokens; +import dmd.typesem; +import dmd.visitor; + +/*************************************** + * Given that ti is an instance of this TemplateDeclaration, + * deduce the types of the parameters to this, and store + * those deduced types in dedtypes[]. + * Params: + * sc = context + * td = template + * ti = instance of td + * dedtypes = fill in with deduced types + * argumentList = arguments to template instance + * flag = 1 - don't do semantic() because of dummy types + * 2 - don't change types in matchArg() + * Returns: match level. + */ +public +MATCH matchWithInstance(Scope* sc, TemplateDeclaration td, TemplateInstance ti, ref Objects dedtypes, ArgumentList argumentList, int flag) +{ + enum LOGM = 0; + static if (LOGM) + { + printf("\n+TemplateDeclaration.matchWithInstance(td = %s, ti = %s, flag = %d)\n", td.toChars(), ti.toChars(), flag); + } + version (none) + { + printf("dedtypes.length = %d, parameters.length = %d\n", dedtypes.length, parameters.length); + if (ti.tiargs.length) + printf("ti.tiargs.length = %d, [0] = %p\n", ti.tiargs.length, (*ti.tiargs)[0]); + } + MATCH nomatch() + { + static if (LOGM) + { + printf(" no match\n"); + } + return MATCH.nomatch; + } + MATCH m; + size_t dedtypes_dim = dedtypes.length; + + dedtypes.zero(); + + if (td.errors) + return MATCH.nomatch; + + size_t parameters_dim = td.parameters.length; + int variadic = td.isVariadic() !is null; + + // If more arguments than parameters, no match + if (ti.tiargs.length > parameters_dim && !variadic) + { + static if (LOGM) + { + printf(" no match: more arguments than parameters\n"); + } + return MATCH.nomatch; + } + + assert(dedtypes_dim == parameters_dim); + assert(dedtypes_dim >= ti.tiargs.length || variadic); + + assert(td._scope); + + // Set up scope for template parameters + Scope* paramscope = createScopeForTemplateParameters(td, ti, sc); + + // Attempt type deduction + m = MATCH.exact; + for (size_t i = 0; i < dedtypes_dim; i++) + { + MATCH m2; + TemplateParameter tp = (*td.parameters)[i]; + Declaration sparam; + + //printf("\targument [%d]\n", i); + static if (LOGM) + { + //printf("\targument [%d] is %s\n", i, oarg ? oarg.toChars() : "null"); + TemplateTypeParameter ttp = tp.isTemplateTypeParameter(); + if (ttp) + printf("\tparameter[%d] is %s : %s\n", i, tp.ident.toChars(), ttp.specType ? ttp.specType.toChars() : ""); + } + + m2 = tp.matchArg(ti.loc, paramscope, ti.tiargs, i, td.parameters, dedtypes, &sparam); + //printf("\tm2 = %d\n", m2); + if (m2 == MATCH.nomatch) + { + version (none) + { + printf("\tmatchArg() for parameter %i failed\n", i); + } + return nomatch(); + } + + if (m2 < m) + m = m2; + + if (!flag) + sparam.dsymbolSemantic(paramscope); + if (!paramscope.insert(sparam)) // TODO: This check can make more early + { + // in TemplateDeclaration.semantic, and + // then we don't need to make sparam if flags == 0 + return nomatch(); + } + } + + if (!flag) + { + /* Any parameter left without a type gets the type of + * its corresponding arg + */ + foreach (i, ref dedtype; dedtypes) + { + if (!dedtype) + { + assert(i < ti.tiargs.length); + dedtype = cast(Type)(*ti.tiargs)[i]; + } + } + } + + if (m > MATCH.nomatch && td.constraint && !flag) + { + if (ti.hasNestedArgs(ti.tiargs, td.isstatic)) // TODO: should gag error + ti.parent = ti.enclosing; + else + ti.parent = td.parent; + + // Similar to doHeaderInstantiation + FuncDeclaration fd = td.onemember ? td.onemember.isFuncDeclaration() : null; + if (fd) + { + TypeFunction tf = fd.type.isTypeFunction().syntaxCopy(); + if (argumentList.hasNames) + return nomatch(); + Expressions* fargs = argumentList.arguments; + // TODO: Expressions* fargs = tf.resolveNamedArgs(argumentList, null); + // if (!fargs) + // return nomatch(); + + fd = new FuncDeclaration(fd.loc, fd.endloc, fd.ident, fd.storage_class, tf); + fd.parent = ti; + fd.inferRetType = true; + + // Shouldn't run semantic on default arguments and return type. + foreach (ref param; *tf.parameterList.parameters) + param.defaultArg = null; + + tf.next = null; + tf.incomplete = true; + + // Resolve parameter types and 'auto ref's. + tf.fargs = fargs; + uint olderrors = global.startGagging(); + fd.type = tf.typeSemantic(td.loc, paramscope); + global.endGagging(olderrors); + if (fd.type.ty != Tfunction) + return nomatch(); + fd.originalType = fd.type; // for mangling + } + + // TODO: dedtypes => ti.tiargs ? + if (!evaluateConstraint(td, ti, sc, paramscope, &dedtypes, fd)) + return nomatch(); + } + + static if (LOGM) + { + // Print out the results + printf("--------------------------\n"); + printf("template %s\n", toChars()); + printf("instance %s\n", ti.toChars()); + if (m > MATCH.nomatch) + { + for (size_t i = 0; i < dedtypes_dim; i++) + { + TemplateParameter tp = (*parameters)[i]; + RootObject oarg; + printf(" [%d]", i); + if (i < ti.tiargs.length) + oarg = (*ti.tiargs)[i]; + else + oarg = null; + tp.print(oarg, (*dedtypes)[i]); + } + } + else + return nomatch(); + } + static if (LOGM) + { + printf(" match = %d\n", m); + } + + paramscope.pop(); + static if (LOGM) + { + printf("-TemplateDeclaration.matchWithInstance(td = %s, ti = %s) = %d\n", td.toChars(), ti.toChars(), m); + } + return m; +} + +/**************************** + * Check to see if constraint is satisfied. + */ +bool evaluateConstraint(TemplateDeclaration td, TemplateInstance ti, Scope* sc, Scope* paramscope, Objects* dedargs, FuncDeclaration fd) +{ + /* Detect recursive attempts to instantiate this template declaration, + * https://issues.dlang.org/show_bug.cgi?id=4072 + * void foo(T)(T x) if (is(typeof(foo(x)))) { } + * static assert(!is(typeof(foo(7)))); + * Recursive attempts are regarded as a constraint failure. + */ + /* There's a chicken-and-egg problem here. We don't know yet if this template + * instantiation will be a local one (enclosing is set), and we won't know until + * after selecting the correct template. Thus, function we're nesting inside + * is not on the sc scope chain, and this can cause errors in FuncDeclaration.getLevel(). + * Workaround the problem by setting a flag to relax the checking on frame errors. + */ + + for (TemplatePrevious* p = td.previous; p; p = p.prev) + { + if (!arrayObjectMatch(*p.dedargs, *dedargs)) + continue; + //printf("recursive, no match p.sc=%p %p %s\n", p.sc, this, this.toChars()); + /* It must be a subscope of p.sc, other scope chains are not recursive + * instantiations. + * the chain of enclosing scopes is broken by paramscope (its enclosing + * scope is _scope, but paramscope.callsc is the instantiating scope). So + * it's good enough to check the chain of callsc + */ + for (Scope* scx = paramscope.callsc; scx; scx = scx.callsc) + { + // The first scx might be identical for nested eponymeous templates, e.g. + // template foo() { void foo()() {...} } + if (scx == p.sc && scx !is paramscope.callsc) + return false; + } + /* BUG: should also check for ref param differences + */ + } + + TemplatePrevious pr; + pr.prev = td.previous; + pr.sc = paramscope.callsc; + pr.dedargs = dedargs; + td.previous = ≺ // add this to threaded list + + Scope* scx = paramscope.push(ti); + scx.parent = ti; + scx.tinst = null; + scx.minst = null; + // Set SCOPE.constraint before declaring function parameters for the static condition + // (previously, this was immediately before calling evalStaticCondition), so the + // semantic pass knows not to issue deprecation warnings for these throw-away decls. + // https://issues.dlang.org/show_bug.cgi?id=21831 + scx.flags |= SCOPE.constraint; + + assert(!ti.symtab); + if (fd) + { + /* Declare all the function parameters as variables and add them to the scope + * Making parameters is similar to FuncDeclaration.semantic3 + */ + auto tf = fd.type.isTypeFunction(); + + scx.parent = fd; + + Parameters* fparameters = tf.parameterList.parameters; + const nfparams = tf.parameterList.length; + foreach (i, fparam; tf.parameterList) + { + fparam.storageClass &= (STC.IOR | STC.lazy_ | STC.final_ | STC.TYPECTOR | STC.nodtor); + fparam.storageClass |= STC.parameter; + if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nfparams) + { + fparam.storageClass |= STC.variadic; + /* Don't need to set STC.scope_ because this will only + * be evaluated at compile time + */ + } + } + foreach (fparam; *fparameters) + { + if (!fparam.ident) + continue; + // don't add it, if it has no name + auto v = new VarDeclaration(fparam.loc, fparam.type, fparam.ident, null); + fparam.storageClass |= STC.parameter; + v.storage_class = fparam.storageClass; + v.dsymbolSemantic(scx); + if (!ti.symtab) + ti.symtab = new DsymbolTable(); + if (!scx.insert(v)) + .error(td.loc, "%s `%s` parameter `%s.%s` is already defined", td.kind, td.toPrettyChars, td.toChars(), v.toChars()); + else + v.parent = fd; + } + if (td.isstatic) + fd.storage_class |= STC.static_; + declareThis(fd, scx); + } + + td.lastConstraint = td.constraint.syntaxCopy(); + td.lastConstraintTiargs = ti.tiargs; + td.lastConstraintNegs.setDim(0); + + import dmd.staticcond; + + assert(ti.inst is null); + ti.inst = ti; // temporary instantiation to enable genIdent() + bool errors; + const bool result = evalStaticCondition(scx, td.constraint, td.lastConstraint, errors, &td.lastConstraintNegs); + if (result || errors) + { + td.lastConstraint = null; + td.lastConstraintTiargs = null; + td.lastConstraintNegs.setDim(0); + } + ti.inst = null; + ti.symtab = null; + scx = scx.pop(); + td.previous = pr.prev; // unlink from threaded list + if (errors) + return false; + return result; +} + +/******************************************* + * Append to buf a textual representation of template parameters with their arguments. + * Params: + * parameters = the template parameters + * tiargs = the correspondeing template arguments + * variadic = if it's a variadic argument list + * buf = where the text output goes + */ +void formatParamsWithTiargs(ref TemplateParameters parameters, ref Objects tiargs, bool variadic, ref OutBuffer buf) +{ + buf.writestring(" with `"); + + // write usual arguments line-by-line + // skips trailing default ones - they are not present in `tiargs` + const end = parameters.length - (variadic ? 1 : 0); + size_t i; + for (; i < tiargs.length && i < end; i++) + { + if (i) + { + buf.writeByte(','); + buf.writenl(); + buf.writestring(" "); + } + write(buf, parameters[i]); + buf.writestring(" = "); + write(buf, tiargs[i]); + } + // write remaining variadic arguments on the last line + if (variadic) + { + if (i) + { + buf.writeByte(','); + buf.writenl(); + buf.writestring(" "); + } + write(buf, parameters[end]); + buf.writestring(" = "); + buf.writeByte('('); + if (end < tiargs.length) + { + write(buf, tiargs[end]); + foreach (j; parameters.length .. tiargs.length) + { + buf.writestring(", "); + write(buf, tiargs[j]); + } + } + buf.writeByte(')'); + } + buf.writeByte('`'); +} + +/****************************** + * Create a scope for the parameters of the TemplateInstance + * `ti` in the parent scope sc from the ScopeDsymbol paramsym. + * + * If paramsym is null a new ScopeDsymbol is used in place of + * paramsym. + * Params: + * td = template that ti is an instance of + * ti = the TemplateInstance whose parameters to generate the scope for. + * sc = the parent scope of ti + * Returns: + * new scope for the parameters of ti + */ +Scope* createScopeForTemplateParameters(TemplateDeclaration td, TemplateInstance ti, Scope* sc) +{ + ScopeDsymbol paramsym = new ScopeDsymbol(); + paramsym.parent = td._scope.parent; + Scope* paramscope = td._scope.push(paramsym); + paramscope.tinst = ti; + paramscope.minst = sc.minst; + paramscope.callsc = sc; + paramscope.stc = 0; + return paramscope; +} + +/******************************************** + * Determine partial specialization order of `td` vs `td2`. + * Params: + * sc = context + * td = first template + * td2 = second template + * argumentList = arguments to template + * Returns: + * MATCH - td is at least as specialized as td2 + * MATCH.nomatch - td2 is more specialized than td + */ +MATCH leastAsSpecialized(Scope* sc, TemplateDeclaration td, TemplateDeclaration td2, ArgumentList argumentList) +{ + enum LOG_LEASTAS = 0; + static if (LOG_LEASTAS) + { + printf("%s.leastAsSpecialized(%s)\n", toChars(), td2.toChars()); + } + + /* This works by taking the template parameters to this template + * declaration and feeding them to td2 as if it were a template + * instance. + * If it works, then this template is at least as specialized + * as td2. + */ + + // Set type arguments to dummy template instance to be types + // generated from the parameters to this template declaration + auto tiargs = new Objects(); + tiargs.reserve(td.parameters.length); + foreach (tp; *td.parameters) + { + if (tp.dependent) + break; + RootObject p = tp.dummyArg(); + if (!p) //TemplateTupleParameter + break; + + tiargs.push(p); + } + scope TemplateInstance ti = new TemplateInstance(Loc.initial, td.ident, tiargs); // create dummy template instance + + // Temporary Array to hold deduced types + Objects dedtypes = Objects(td2.parameters.length); + + // Attempt a type deduction + MATCH m = matchWithInstance(sc, td2, ti, dedtypes, argumentList, 1); + if (m > MATCH.nomatch) + { + /* A non-variadic template is more specialized than a + * variadic one. + */ + TemplateTupleParameter tp = td.isVariadic(); + if (tp && !tp.dependent && !td2.isVariadic()) + goto L1; + + static if (LOG_LEASTAS) + { + printf(" matches %d, so is least as specialized\n", m); + } + return m; + } +L1: + static if (LOG_LEASTAS) + { + printf(" doesn't match, so is not as specialized\n"); + } + return MATCH.nomatch; +} + +/************************************************* + * Match function arguments against a specific template function. + * + * Params: + * td = template declaration for template instance + * ti = template instance. `ti.tdtypes` will be set to Expression/Type deduced template arguments + * sc = instantiation scope + * fd = Partially instantiated function declaration, which is set to an instantiated function declaration + * tthis = 'this' argument if !NULL + * argumentList = arguments to function + * + * Returns: + * match pair of initial and inferred template arguments + */ +extern (D) MATCHpair deduceFunctionTemplateMatch(TemplateDeclaration td, TemplateInstance ti, Scope* sc, ref FuncDeclaration fd, Type tthis, ArgumentList argumentList) +{ + version (none) + { + printf("\nTemplateDeclaration.deduceFunctionTemplateMatch() %s\n", td.toChars()); + for (size_t i = 0; i < (fargs ? fargs.length : 0); i++) + { + Expression e = (*fargs)[i]; + printf("\tfarg[%d] is %s, type is %s\n", cast(int) i, e.toChars(), e.type.toChars()); + } + printf("fd = %s\n", fd.toChars()); + printf("fd.type = %s\n", fd.type.toChars()); + if (tthis) + printf("tthis = %s\n", tthis.toChars()); + } + + assert(td._scope); + + auto dedargs = new Objects(td.parameters.length); + dedargs.zero(); + + Objects* dedtypes = &ti.tdtypes; // for T:T*, the dedargs is the T*, dedtypes is the T + dedtypes.setDim(td.parameters.length); + dedtypes.zero(); + + if (td.errors || fd.errors) + return MATCHpair(MATCH.nomatch, MATCH.nomatch); + + // Set up scope for parameters + Scope* paramscope = createScopeForTemplateParameters(td, ti,sc); + + MATCHpair nomatch() + { + paramscope.pop(); + //printf("\tnomatch\n"); + return MATCHpair(MATCH.nomatch, MATCH.nomatch); + } + + MATCHpair matcherror() + { + // todo: for the future improvement + paramscope.pop(); + //printf("\terror\n"); + return MATCHpair(MATCH.nomatch, MATCH.nomatch); + } + // Mark the parameter scope as deprecated if the templated + // function is deprecated (since paramscope.enclosing is the + // calling scope already) + paramscope.stc |= fd.storage_class & STC.deprecated_; + + TemplateTupleParameter tp = td.isVariadic(); + Tuple declaredTuple = null; + + version (none) + { + for (size_t i = 0; i < dedargs.length; i++) + { + printf("\tdedarg[%d] = ", i); + RootObject oarg = (*dedargs)[i]; + if (oarg) + printf("%s", oarg.toChars()); + printf("\n"); + } + } + + size_t ntargs = 0; // array size of tiargs + size_t inferStart = 0; // index of first parameter to infer + const Loc instLoc = ti.loc; + MATCH matchTiargs = MATCH.exact; + + if (auto tiargs = ti.tiargs) + { + // Set initial template arguments + ntargs = tiargs.length; + size_t n = td.parameters.length; + if (tp) + n--; + if (ntargs > n) + { + if (!tp) + return nomatch(); + + /* The extra initial template arguments + * now form the tuple argument. + */ + auto t = new Tuple(ntargs - n); + assert(td.parameters.length); + (*dedargs)[td.parameters.length - 1] = t; + + for (size_t i = 0; i < t.objects.length; i++) + { + t.objects[i] = (*tiargs)[n + i]; + } + td.declareParameter(paramscope, tp, t); + declaredTuple = t; + } + else + n = ntargs; + + memcpy(dedargs.tdata(), tiargs.tdata(), n * (*dedargs.tdata()).sizeof); + + for (size_t i = 0; i < n; i++) + { + assert(i < td.parameters.length); + Declaration sparam = null; + MATCH m = (*td.parameters)[i].matchArg(instLoc, paramscope, dedargs, i, td.parameters, *dedtypes, &sparam); + //printf("\tdeduceType m = %d\n", m); + if (m == MATCH.nomatch) + return nomatch(); + if (m < matchTiargs) + matchTiargs = m; + + sparam.dsymbolSemantic(paramscope); + if (!paramscope.insert(sparam)) + return nomatch(); + } + if (n < td.parameters.length && !declaredTuple) + { + inferStart = n; + } + else + inferStart = td.parameters.length; + //printf("tiargs matchTiargs = %d\n", matchTiargs); + } + version (none) + { + for (size_t i = 0; i < dedargs.length; i++) + { + printf("\tdedarg[%d] = ", i); + RootObject oarg = (*dedargs)[i]; + if (oarg) + printf("%s", oarg.toChars()); + printf("\n"); + } + } + + ParameterList fparameters = fd.getParameterList(); // function parameter list + const nfparams = fparameters.length; // number of function parameters + if (argumentList.hasNames) + return matcherror(); // TODO: resolve named args + Expression[] fargs = argumentList.arguments ? (*argumentList.arguments)[] : null; + + /* Check for match of function arguments with variadic template + * parameter, such as: + * + * void foo(T, A...)(T t, A a); + * void main() { foo(1,2,3); } + */ + size_t fptupindex = IDX_NOTFOUND; + if (tp) // if variadic + { + // TemplateTupleParameter always makes most lesser matching. + matchTiargs = MATCH.convert; + + if (nfparams == 0 && argumentList.length != 0) // if no function parameters + { + if (!declaredTuple) + { + auto t = new Tuple(); + //printf("t = %p\n", t); + (*dedargs)[td.parameters.length - 1] = t; + td.declareParameter(paramscope, tp, t); + declaredTuple = t; + } + } + else + { + /* Figure out which of the function parameters matches + * the tuple template parameter. Do this by matching + * type identifiers. + * Set the index of this function parameter to fptupindex. + */ + for (fptupindex = 0; fptupindex < nfparams; fptupindex++) + { + auto fparam = (*fparameters.parameters)[fptupindex]; // fparameters[fptupindex] ? + if (fparam.type.ty != Tident) + continue; + TypeIdentifier tid = fparam.type.isTypeIdentifier(); + if (!tp.ident.equals(tid.ident) || tid.idents.length) + continue; + + if (fparameters.varargs != VarArg.none) // variadic function doesn't + return nomatch(); // go with variadic template + + goto L1; + } + fptupindex = IDX_NOTFOUND; + L1: + } + } + + MATCH match = MATCH.exact; + if (td.toParent().isModule()) + tthis = null; + if (tthis) + { + bool hasttp = false; + + // Match 'tthis' to any TemplateThisParameter's + foreach (param; *td.parameters) + { + if (auto ttp = param.isTemplateThisParameter()) + { + hasttp = true; + + Type t = new TypeIdentifier(Loc.initial, ttp.ident); + MATCH m = deduceType(tthis, paramscope, t, *td.parameters, *dedtypes); + if (m == MATCH.nomatch) + return nomatch(); + if (m < match) + match = m; // pick worst match + } + } + + // Match attributes of tthis against attributes of fd + if (fd.type && !fd.isCtorDeclaration() && !(td._scope.stc & STC.static_)) + { + StorageClass stc = td._scope.stc | fd.storage_class2; + // Propagate parent storage class, https://issues.dlang.org/show_bug.cgi?id=5504 + Dsymbol p = td.parent; + while (p.isTemplateDeclaration() || p.isTemplateInstance()) + p = p.parent; + AggregateDeclaration ad = p.isAggregateDeclaration(); + if (ad) + stc |= ad.storage_class; + + ubyte mod = fd.type.mod; + if (stc & STC.immutable_) + mod = MODFlags.immutable_; + else + { + if (stc & (STC.shared_ | STC.synchronized_)) + mod |= MODFlags.shared_; + if (stc & STC.const_) + mod |= MODFlags.const_; + if (stc & STC.wild) + mod |= MODFlags.wild; + } + + ubyte thismod = tthis.mod; + if (hasttp) + mod = MODmerge(thismod, mod); + MATCH m = MODmethodConv(thismod, mod); + if (m == MATCH.nomatch) + return nomatch(); + if (m < match) + match = m; + } + } + + // Loop through the function parameters + { + //printf("%s\n\tnfargs = %d, nfparams = %d, tuple_dim = %d\n", toChars(), nfargs, nfparams, declaredTuple ? declaredTuple.objects.length : 0); + //printf("\ttp = %p, fptupindex = %d, found = %d, declaredTuple = %s\n", tp, fptupindex, fptupindex != IDX_NOTFOUND, declaredTuple ? declaredTuple.toChars() : NULL); + size_t argi = 0; + size_t nfargs2 = fargs.length; // nfargs + supplied defaultArgs + uint inoutMatch = 0; // for debugging only + for (size_t parami = 0; parami < nfparams; parami++) + { + Parameter fparam = fparameters[parami]; + + // Apply function parameter storage classes to parameter types + Type prmtype = fparam.type.addStorageClass(fparam.storageClass); + + Expression farg; + + /* See function parameters which wound up + * as part of a template tuple parameter. + */ + if (fptupindex != IDX_NOTFOUND && parami == fptupindex) + { + TypeIdentifier tid = prmtype.isTypeIdentifier(); + assert(tid); + if (!declaredTuple) + { + /* The types of the function arguments + * now form the tuple argument. + */ + declaredTuple = new Tuple(); + (*dedargs)[td.parameters.length - 1] = declaredTuple; + + /* Count function parameters with no defaults following a tuple parameter. + * void foo(U, T...)(int y, T, U, double, int bar = 0) {} // rem == 2 (U, double) + */ + size_t rem = 0; + foreach (j; parami + 1 .. nfparams) + { + Parameter p = fparameters[j]; + if (p.defaultArg) + { + break; + } + if (!reliesOnTemplateParameters(p.type, (*td.parameters)[inferStart .. td.parameters.length])) + { + Type pt = p.type.syntaxCopy().typeSemantic(fd.loc, paramscope); + if (auto ptt = pt.isTypeTuple()) + rem += ptt.arguments.length; + else + rem += 1; + } + else + { + ++rem; + } + } + + if (nfargs2 - argi < rem) + return nomatch(); + declaredTuple.objects.setDim(nfargs2 - argi - rem); + foreach (i; 0 .. declaredTuple.objects.length) + { + farg = fargs[argi + i]; + + // Check invalid arguments to detect errors early. + if (farg.op == EXP.error || farg.type.ty == Terror) + return nomatch(); + + if (!fparam.isLazy() && farg.type.ty == Tvoid) + return nomatch(); + + Type tt; + MATCH m; + if (ubyte wm = deduceWildHelper(farg.type, &tt, tid)) + { + inoutMatch |= wm; + m = MATCH.constant; + } + else + { + m = deduceTypeHelper(farg.type, tt, tid); + } + if (m == MATCH.nomatch) + return nomatch(); + if (m < match) + match = m; + + /* Remove top const for dynamic array types and pointer types + */ + if ((tt.ty == Tarray || tt.ty == Tpointer) && !tt.isMutable() && (!(fparam.storageClass & STC.ref_) || (fparam.storageClass & STC.auto_) && !farg.isLvalue())) + { + tt = tt.mutableOf(); + } + declaredTuple.objects[i] = tt; + } + td.declareParameter(paramscope, tp, declaredTuple); + } + else + { + // https://issues.dlang.org/show_bug.cgi?id=6810 + // If declared tuple is not a type tuple, + // it cannot be function parameter types. + for (size_t i = 0; i < declaredTuple.objects.length; i++) + { + if (!isType(declaredTuple.objects[i])) + return nomatch(); + } + } + assert(declaredTuple); + argi += declaredTuple.objects.length; + continue; + } + + // If parameter type doesn't depend on inferred template parameters, + // semantic it to get actual type. + if (!reliesOnTemplateParameters(prmtype, (*td.parameters)[inferStart .. td.parameters.length])) + { + // should copy prmtype to avoid affecting semantic result + prmtype = prmtype.syntaxCopy().typeSemantic(fd.loc, paramscope); + + if (TypeTuple tt = prmtype.isTypeTuple()) + { + const tt_dim = tt.arguments.length; + for (size_t j = 0; j < tt_dim; j++, ++argi) + { + Parameter p = (*tt.arguments)[j]; + if (j == tt_dim - 1 && fparameters.varargs == VarArg.typesafe && + parami + 1 == nfparams && argi < fargs.length) + { + prmtype = p.type; + goto Lvarargs; + } + if (argi >= fargs.length) + { + if (p.defaultArg) + continue; + + // https://issues.dlang.org/show_bug.cgi?id=19888 + if (fparam.defaultArg) + break; + + return nomatch(); + } + farg = fargs[argi]; + if (!farg.implicitConvTo(p.type)) + return nomatch(); + } + continue; + } + } + + if (argi >= fargs.length) // if not enough arguments + { + if (!fparam.defaultArg) + goto Lvarargs; + + /* https://issues.dlang.org/show_bug.cgi?id=2803 + * Before the starting of type deduction from the function + * default arguments, set the already deduced parameters into paramscope. + * It's necessary to avoid breaking existing acceptable code. Cases: + * + * 1. Already deduced template parameters can appear in fparam.defaultArg: + * auto foo(A, B)(A a, B b = A.stringof); + * foo(1); + * // at fparam == 'B b = A.string', A is equivalent with the deduced type 'int' + * + * 2. If prmtype depends on default-specified template parameter, the + * default type should be preferred. + * auto foo(N = size_t, R)(R r, N start = 0) + * foo([1,2,3]); + * // at fparam `N start = 0`, N should be 'size_t' before + * // the deduction result from fparam.defaultArg. + */ + if (argi == fargs.length) + { + foreach (ref dedtype; *dedtypes) + { + Type at = isType(dedtype); + if (at && at.ty == Tnone) + { + TypeDeduced xt = cast(TypeDeduced)at; + dedtype = xt.tded; // 'unbox' + } + } + for (size_t i = ntargs; i < dedargs.length; i++) + { + TemplateParameter tparam = (*td.parameters)[i]; + + RootObject oarg = (*dedargs)[i]; + RootObject oded = (*dedtypes)[i]; + if (oarg) + continue; + + if (oded) + { + if (tparam.specialization() || !tparam.isTemplateTypeParameter()) + { + /* The specialization can work as long as afterwards + * the oded == oarg + */ + (*dedargs)[i] = oded; + MATCH m2 = tparam.matchArg(instLoc, paramscope, dedargs, i, td.parameters, *dedtypes, null); + //printf("m2 = %d\n", m2); + if (m2 == MATCH.nomatch) + return nomatch(); + if (m2 < matchTiargs) + matchTiargs = m2; // pick worst match + if (!(*dedtypes)[i].equals(oded)) + .error(td.loc, "%s `%s` specialization not allowed for deduced parameter `%s`", + td.kind, td.toPrettyChars, td.kind, td.toPrettyChars, tparam.ident.toChars()); + } + else + { + if (MATCH.convert < matchTiargs) + matchTiargs = MATCH.convert; + } + (*dedargs)[i] = td.declareParameter(paramscope, tparam, oded); + } + else + { + oded = tparam.defaultArg(instLoc, paramscope); + if (oded) + (*dedargs)[i] = td.declareParameter(paramscope, tparam, oded); + } + } + } + nfargs2 = argi + 1; + + /* If prmtype does not depend on any template parameters: + * + * auto foo(T)(T v, double x = 0); + * foo("str"); + * // at fparam == 'double x = 0' + * + * or, if all template parameters in the prmtype are already deduced: + * + * auto foo(R)(R range, ElementType!R sum = 0); + * foo([1,2,3]); + * // at fparam == 'ElementType!R sum = 0' + * + * Deducing prmtype from fparam.defaultArg is not necessary. + */ + if (prmtype.deco || prmtype.syntaxCopy().trySemantic(td.loc, paramscope)) + { + ++argi; + continue; + } + + // Deduce prmtype from the defaultArg. + farg = fparam.defaultArg.syntaxCopy(); + farg = farg.expressionSemantic(paramscope); + farg = resolveProperties(paramscope, farg); + } + else + { + farg = fargs[argi]; + } + { + // Check invalid arguments to detect errors early. + if (farg.op == EXP.error || farg.type.ty == Terror) + return nomatch(); + + Type att = null; + Lretry: + version (none) + { + printf("\tfarg.type = %s\n", farg.type.toChars()); + printf("\tfparam.type = %s\n", prmtype.toChars()); + } + Type argtype = farg.type; + + if (!fparam.isLazy() && argtype.ty == Tvoid && farg.op != EXP.function_) + return nomatch(); + + // https://issues.dlang.org/show_bug.cgi?id=12876 + // Optimize argument to allow CT-known length matching + farg = farg.optimize(WANTvalue, fparam.isReference()); + //printf("farg = %s %s\n", farg.type.toChars(), farg.toChars()); + + RootObject oarg = farg; + if ((fparam.storageClass & STC.ref_) && (!(fparam.storageClass & STC.auto_) || farg.isLvalue())) + { + /* Allow expressions that have CT-known boundaries and type [] to match with [dim] + */ + bool inferIndexType = (argtype.ty == Tarray) && (prmtype.ty == Tsarray || prmtype.ty == Taarray); + if (auto aaType = prmtype.isTypeAArray()) + { + if (auto indexType = aaType.index.isTypeIdentifier()) + { + inferIndexType = indexType.idents.length == 0; + } + } + if (inferIndexType) + { + if (StringExp se = farg.isStringExp()) + { + argtype = se.type.nextOf().sarrayOf(se.len); + } + else if (ArrayLiteralExp ae = farg.isArrayLiteralExp()) + { + argtype = ae.type.nextOf().sarrayOf(ae.elements.length); + } + else if (SliceExp se = farg.isSliceExp()) + { + if (Type tsa = toStaticArrayType(se)) + argtype = tsa; + } + } + + oarg = argtype; + } + else if ((fparam.storageClass & STC.out_) == 0 && + (argtype.ty == Tarray || argtype.ty == Tpointer) && + templateParameterLookup(prmtype, td.parameters) != IDX_NOTFOUND && + prmtype.isTypeIdentifier().idents.length == 0) + { + /* The farg passing to the prmtype always make a copy. Therefore, + * we can shrink the set of the deduced type arguments for prmtype + * by adjusting top-qualifier of the argtype. + * + * prmtype argtype ta + * T <- const(E)[] const(E)[] + * T <- const(E[]) const(E)[] + * qualifier(T) <- const(E)[] const(E[]) + * qualifier(T) <- const(E[]) const(E[]) + */ + Type ta = argtype.castMod(prmtype.mod ? argtype.nextOf().mod : 0); + if (ta != argtype) + { + Expression ea = farg.copy(); + ea.type = ta; + oarg = ea; + } + } + + if (fparameters.varargs == VarArg.typesafe && parami + 1 == nfparams && argi + 1 < fargs.length) + goto Lvarargs; + + uint im = 0; + MATCH m = deduceType(oarg, paramscope, prmtype, *td.parameters, *dedtypes, &im, inferStart); + //printf("\tL%d deduceType m = %d, im = x%x, inoutMatch = x%x\n", __LINE__, m, im, inoutMatch); + inoutMatch |= im; + + /* If no match, see if the argument can be matched by using + * implicit conversions. + */ + if (m == MATCH.nomatch && prmtype.deco) + m = farg.implicitConvTo(prmtype); + + if (m == MATCH.nomatch) + { + AggregateDeclaration ad = isAggregate(farg.type); + if (ad && ad.aliasthis && !isRecursiveAliasThis(att, argtype)) + { + // https://issues.dlang.org/show_bug.cgi?id=12537 + // The isRecursiveAliasThis() call above + + /* If a semantic error occurs while doing alias this, + * eg purity(https://issues.dlang.org/show_bug.cgi?id=7295), + * just regard it as not a match. + * + * We also save/restore sc.func.flags to avoid messing up + * attribute inference in the evaluation. + */ + const oldflags = sc.func ? sc.func.flags : 0; + auto e = resolveAliasThis(sc, farg, true); + if (sc.func) + sc.func.flags = oldflags; + if (e) + { + farg = e; + goto Lretry; + } + } + } + + if (m > MATCH.nomatch && (fparam.storageClass & (STC.ref_ | STC.auto_)) == STC.ref_) + { + if (!farg.isLvalue()) + { + if ((farg.op == EXP.string_ || farg.op == EXP.slice) && (prmtype.ty == Tsarray || prmtype.ty == Taarray)) + { + // Allow conversion from T[lwr .. upr] to ref T[upr-lwr] + } + else if (global.params.rvalueRefParam == FeatureState.enabled) + { + // Allow implicit conversion to ref + } + else + return nomatch(); + } + } + if (m > MATCH.nomatch && (fparam.storageClass & STC.out_)) + { + if (!farg.isLvalue()) + return nomatch(); + if (!farg.type.isMutable()) // https://issues.dlang.org/show_bug.cgi?id=11916 + return nomatch(); + } + if (m == MATCH.nomatch && fparam.isLazy() && prmtype.ty == Tvoid && farg.type.ty != Tvoid) + m = MATCH.convert; + if (m != MATCH.nomatch) + { + if (m < match) + match = m; // pick worst match + argi++; + continue; + } + } + + Lvarargs: + /* The following code for variadic arguments closely + * matches TypeFunction.callMatch() + */ + if (!(fparameters.varargs == VarArg.typesafe && parami + 1 == nfparams)) + return nomatch(); + + /* Check for match with function parameter T... + */ + Type tb = prmtype.toBasetype(); + switch (tb.ty) + { + // 6764 fix - TypeAArray may be TypeSArray have not yet run semantic(). + case Tsarray: + case Taarray: + { + // Perhaps we can do better with this, see TypeFunction.callMatch() + if (TypeSArray tsa = tb.isTypeSArray()) + { + dinteger_t sz = tsa.dim.toInteger(); + if (sz != fargs.length - argi) + return nomatch(); + } + else if (TypeAArray taa = tb.isTypeAArray()) + { + Expression dim = new IntegerExp(instLoc, fargs.length - argi, Type.tsize_t); + + size_t i = templateParameterLookup(taa.index, td.parameters); + if (i == IDX_NOTFOUND) + { + Expression e; + Type t; + Dsymbol s; + Scope *sco; + + uint errors = global.startGagging(); + /* ref: https://issues.dlang.org/show_bug.cgi?id=11118 + * The parameter isn't part of the template + * ones, let's try to find it in the + * instantiation scope 'sc' and the one + * belonging to the template itself. */ + sco = sc; + taa.index.resolve(instLoc, sco, e, t, s); + if (!e) + { + sco = paramscope; + taa.index.resolve(instLoc, sco, e, t, s); + } + global.endGagging(errors); + + if (!e) + return nomatch(); + + e = e.ctfeInterpret(); + e = e.implicitCastTo(sco, Type.tsize_t); + e = e.optimize(WANTvalue); + if (!dim.equals(e)) + return nomatch(); + } + else + { + // This code matches code in TypeInstance.deduceType() + TemplateParameter tprm = (*td.parameters)[i]; + TemplateValueParameter tvp = tprm.isTemplateValueParameter(); + if (!tvp) + return nomatch(); + Expression e = cast(Expression)(*dedtypes)[i]; + if (e) + { + if (!dim.equals(e)) + return nomatch(); + } + else + { + Type vt = tvp.valType.typeSemantic(Loc.initial, sc); + MATCH m = dim.implicitConvTo(vt); + if (m == MATCH.nomatch) + return nomatch(); + (*dedtypes)[i] = dim; + } + } + } + goto case Tarray; + } + case Tarray: + { + TypeArray ta = cast(TypeArray)tb; + Type tret = fparam.isLazyArray(); + for (; argi < fargs.length; argi++) + { + Expression arg = fargs[argi]; + assert(arg); + + MATCH m; + /* If lazy array of delegates, + * convert arg(s) to delegate(s) + */ + if (tret) + { + if (ta.next.equals(arg.type)) + { + m = MATCH.exact; + } + else + { + m = arg.implicitConvTo(tret); + if (m == MATCH.nomatch) + { + if (tret.toBasetype().ty == Tvoid) + m = MATCH.convert; + } + } + } + else + { + uint wm = 0; + m = deduceType(arg, paramscope, ta.next, *td.parameters, *dedtypes, &wm, inferStart); + inoutMatch |= wm; + } + if (m == MATCH.nomatch) + return nomatch(); + if (m < match) + match = m; + } + goto Lmatch; + } + case Tclass: + case Tident: + goto Lmatch; + + default: + return nomatch(); + } + assert(0); + } + //printf(". argi = %d, nfargs = %d, nfargs2 = %d\n", argi, nfargs, nfargs2); + if (argi != nfargs2 && fparameters.varargs == VarArg.none) + return nomatch(); + } + +Lmatch: + foreach (ref dedtype; *dedtypes) + { + if (Type at = isType(dedtype)) + { + if (at.ty == Tnone) + { + TypeDeduced xt = cast(TypeDeduced)at; + at = xt.tded; // 'unbox' + } + dedtype = at.merge2(); + } + } + for (size_t i = ntargs; i < dedargs.length; i++) + { + TemplateParameter tparam = (*td.parameters)[i]; + //printf("tparam[%d] = %s\n", i, tparam.ident.toChars()); + + /* For T:T*, the dedargs is the T*, dedtypes is the T + * But for function templates, we really need them to match + */ + RootObject oarg = (*dedargs)[i]; + RootObject oded = (*dedtypes)[i]; + //printf("1dedargs[%d] = %p, dedtypes[%d] = %p\n", i, oarg, i, oded); + //if (oarg) printf("oarg: %s\n", oarg.toChars()); + //if (oded) printf("oded: %s\n", oded.toChars()); + if (oarg) + continue; + + if (oded) + { + if (tparam.specialization() || !tparam.isTemplateTypeParameter()) + { + /* The specialization can work as long as afterwards + * the oded == oarg + */ + (*dedargs)[i] = oded; + MATCH m2 = tparam.matchArg(instLoc, paramscope, dedargs, i, td.parameters, *dedtypes, null); + //printf("m2 = %d\n", m2); + if (m2 == MATCH.nomatch) + return nomatch(); + if (m2 < matchTiargs) + matchTiargs = m2; // pick worst match + if (!(*dedtypes)[i].equals(oded)) + .error(td.loc, "%s `%s` specialization not allowed for deduced parameter `%s`", td.kind, td.toPrettyChars, tparam.ident.toChars()); + } + else + { + // Discussion: https://issues.dlang.org/show_bug.cgi?id=16484 + if (MATCH.convert < matchTiargs) + matchTiargs = MATCH.convert; + } + } + else + { + oded = tparam.defaultArg(instLoc, paramscope); + if (!oded) + { + // if tuple parameter and + // tuple parameter was not in function parameter list and + // we're one or more arguments short (i.e. no tuple argument) + if (tparam == tp && + fptupindex == IDX_NOTFOUND && + ntargs <= dedargs.length - 1) + { + // make tuple argument an empty tuple + oded = new Tuple(); + } + else + return nomatch(); + } + if (isError(oded)) + return matcherror(); + ntargs++; + + /* At the template parameter T, the picked default template argument + * X!int should be matched to T in order to deduce dependent + * template parameter A. + * auto foo(T : X!A = X!int, A...)() { ... } + * foo(); // T <-- X!int, A <-- (int) + */ + if (tparam.specialization()) + { + (*dedargs)[i] = oded; + MATCH m2 = tparam.matchArg(instLoc, paramscope, dedargs, i, td.parameters, *dedtypes, null); + //printf("m2 = %d\n", m2); + if (m2 == MATCH.nomatch) + return nomatch(); + if (m2 < matchTiargs) + matchTiargs = m2; // pick worst match + if (!(*dedtypes)[i].equals(oded)) + .error(td.loc, "%s `%s` specialization not allowed for deduced parameter `%s`", td.kind, td.toPrettyChars, tparam.ident.toChars()); + } + } + oded = td.declareParameter(paramscope, tparam, oded); + (*dedargs)[i] = oded; + } + + /* https://issues.dlang.org/show_bug.cgi?id=7469 + * As same as the code for 7469 in findBestMatch, + * expand a Tuple in dedargs to normalize template arguments. + */ + if (auto d = dedargs.length) + { + if (auto va = isTuple((*dedargs)[d - 1])) + { + dedargs.setDim(d - 1); + dedargs.insert(d - 1, &va.objects); + } + } + ti.tiargs = dedargs; // update to the normalized template arguments. + + // Partially instantiate function for constraint and fd.leastAsSpecialized() + { + assert(paramscope.scopesym); + Scope* sc2 = td._scope; + sc2 = sc2.push(paramscope.scopesym); + sc2 = sc2.push(ti); + sc2.parent = ti; + sc2.tinst = ti; + sc2.minst = sc.minst; + sc2.stc |= fd.storage_class & STC.deprecated_; + + fd = td.doHeaderInstantiation(ti, sc2, fd, tthis, argumentList.arguments); + sc2 = sc2.pop(); + sc2 = sc2.pop(); + + if (!fd) + return nomatch(); + } + + if (td.constraint) + { + if (!evaluateConstraint(td, ti, sc, paramscope, dedargs, fd)) + return nomatch(); + } + + version (none) + { + for (size_t i = 0; i < dedargs.length; i++) + { + RootObject o = (*dedargs)[i]; + printf("\tdedargs[%d] = %d, %s\n", i, o.dyncast(), o.toChars()); + } + } + + paramscope.pop(); + //printf("\tmatch %d\n", match); + return MATCHpair(matchTiargs, match); +} diff --git a/gcc/d/dmd/tokens.d b/gcc/d/dmd/tokens.d index 589bc2b..da4a3ee 100644 --- a/gcc/d/dmd/tokens.d +++ b/gcc/d/dmd/tokens.d @@ -124,6 +124,7 @@ enum TOK : ubyte // Leaf operators identifier, string_, + interpolated, hexadecimalString, this_, super_, @@ -380,6 +381,7 @@ enum EXP : ubyte // Leaf operators identifier, string_, + interpolated, this_, super_, halt, @@ -623,6 +625,10 @@ static immutable TOK[TOK.max + 1] Ckeywords = } } (); +struct InterpolatedSet { + // all strings in the parts are zero terminated at length+1 + string[] parts; +} /*********************************************************** */ @@ -645,7 +651,11 @@ extern (C++) struct Token struct { - const(char)* ustring; // UTF8 string + union + { + const(char)* ustring; // UTF8 string + InterpolatedSet* interpolatedSet; + } uint len; ubyte postfix; // 'c', 'w', 'd' } @@ -833,6 +843,7 @@ extern (C++) struct Token // For debugging TOK.error: "error", TOK.string_: "string", + TOK.interpolated: "interpolated string", TOK.onScopeExit: "scope(exit)", TOK.onScopeSuccess: "scope(success)", TOK.onScopeFailure: "scope(failure)", @@ -910,6 +921,24 @@ nothrow: return 0; } + extern(D) void appendInterpolatedPart(const ref OutBuffer buf) { + appendInterpolatedPart(cast(const(char)*)buf[].ptr, buf.length); + } + extern(D) void appendInterpolatedPart(const(char)[] str) { + appendInterpolatedPart(str.ptr, str.length); + } + extern(D) void appendInterpolatedPart(const(char)* ptr, size_t length) { + assert(value == TOK.interpolated); + if (interpolatedSet is null) + interpolatedSet = new InterpolatedSet; + + auto s = cast(char*)mem.xmalloc_noscan(length + 1); + memcpy(s, ptr, length); + s[length] = 0; + + interpolatedSet.parts ~= cast(string) s[0 .. length]; + } + /**** * Set to contents of ptr[0..length] * Params: @@ -918,6 +947,7 @@ nothrow: */ void setString(const(char)* ptr, size_t length) { + value = TOK.string_; auto s = cast(char*)mem.xmalloc_noscan(length + 1); memcpy(s, ptr, length); s[length] = 0; @@ -941,6 +971,7 @@ nothrow: */ void setString() { + value = TOK.string_; ustring = ""; len = 0; postfix = 0; diff --git a/gcc/d/dmd/tokens.h b/gcc/d/dmd/tokens.h index f944663..ef91001 100644 --- a/gcc/d/dmd/tokens.h +++ b/gcc/d/dmd/tokens.h @@ -133,6 +133,7 @@ enum class TOK : unsigned char // Leaf operators identifier, string_, + interpolated, hexadecimalString, this_, super_, @@ -390,6 +391,7 @@ enum class EXP : unsigned char // Leaf operators identifier, string_, + interpolated, this_, super_, halt, @@ -461,7 +463,12 @@ struct Token real_t floatvalue; struct - { utf8_t *ustring; // UTF8 string + { + union + { + utf8_t *ustring; // UTF8 string + void *interpolatedSet; + }; unsigned len; unsigned char postfix; // 'c', 'w', 'd' }; diff --git a/gcc/d/dmd/traits.d b/gcc/d/dmd/traits.d index c67ee81..be7aa99 100644 --- a/gcc/d/dmd/traits.d +++ b/gcc/d/dmd/traits.d @@ -36,6 +36,7 @@ import dmd.errorsink; import dmd.expression; import dmd.expressionsem; import dmd.func; +import dmd.funcsem; import dmd.globals; import dmd.hdrgen; import dmd.id; @@ -1306,7 +1307,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) // attribute inference. if (fd && fd.parent && fd.parent.isTemplateInstance) { - fd.functionSemantic3(); + functionSemantic3(fd); tf = fd.type.isTypeFunction(); } diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d index 714af8a..6721fa6 100644 --- a/gcc/d/dmd/typesem.d +++ b/gcc/d/dmd/typesem.d @@ -35,6 +35,7 @@ import dmd.dstruct; import dmd.dsymbol; import dmd.dsymbolsem; import dmd.dtemplate; +import dmd.enumsem; import dmd.errors; import dmd.errorsink; import dmd.expression; @@ -2919,6 +2920,29 @@ extern (C++) Type merge(Type type) return type; } +/************************************* + * This version does a merge even if the deco is already computed. + * Necessary for types that have a deco, but are not merged. + */ +extern(C++) Type merge2(Type type) +{ + //printf("merge2(%s)\n", toChars()); + Type t = type; + assert(t); + if (!t.deco) + return t.merge(); + + auto sv = Type.stringtable.lookup(t.deco, strlen(t.deco)); + if (sv && sv.value) + { + t = sv.value; + assert(t.deco); + } + else + assert(0); + return t; +} + /*************************************** * Calculate built-in properties which just the type is necessary. * @@ -5697,7 +5721,7 @@ Type getComplexLibraryType(const ref Loc loc, Scope* sc, TY ty) * Returns: * An enum value of either `Covariant.yes` or a reason it's not covariant. */ -extern (C++) Covariant covariant(Type src, Type t, StorageClass* pstc = null, bool cppCovariant = false) +extern(C++) Covariant covariant(Type src, Type t, StorageClass* pstc = null, bool cppCovariant = false) { version (none) { diff --git a/gcc/d/dmd/typinf.d b/gcc/d/dmd/typinf.d index 9e062bd..6ae6df0 100644 --- a/gcc/d/dmd/typinf.d +++ b/gcc/d/dmd/typinf.d @@ -65,6 +65,7 @@ extern (C++) bool genTypeInfo(Expression e, const ref Loc loc, Type torig, Scope fatal(); } + import dmd.typesem : merge2; Type t = torig.merge2(); // do this since not all Type's are merge'd bool needsCodegen = false; if (!t.vtinfo) diff --git a/gcc/d/dmd/visitor.h b/gcc/d/dmd/visitor.h index 7fa08cb0..6e3d315 100644 --- a/gcc/d/dmd/visitor.h +++ b/gcc/d/dmd/visitor.h @@ -195,6 +195,7 @@ class ThisExp; class SuperExp; class NullExp; class StringExp; +class InterpExp; class TupleExp; class ArrayLiteralExp; class AssocArrayLiteralExp; @@ -480,6 +481,7 @@ public: virtual void visit(TypeidExp *e) { visit((Expression *)e); } virtual void visit(TraitsExp *e) { visit((Expression *)e); } virtual void visit(StringExp *e) { visit((Expression *)e); } + virtual void visit(InterpExp *e) { visit((Expression *)e); } virtual void visit(NewExp *e) { visit((Expression *)e); } virtual void visit(AssocArrayLiteralExp *e) { visit((Expression *)e); } virtual void visit(ArrayLiteralExp *e) { visit((Expression *)e); } |