diff options
author | Iain Buclaw <ibuclaw@gdcproject.org> | 2025-01-06 23:11:02 +0100 |
---|---|---|
committer | Iain Buclaw <ibuclaw@gdcproject.org> | 2025-01-11 00:51:15 +0100 |
commit | c82395e03592e5858c34f0b824f494eb94b37404 (patch) | |
tree | 8fddc3a6b179866de8e4950888cef1c6b4e834a5 /gcc | |
parent | a7ae0c31245a7db7abf2e80d0016510afe9c8ad0 (diff) | |
download | gcc-c82395e03592e5858c34f0b824f494eb94b37404.zip gcc-c82395e03592e5858c34f0b824f494eb94b37404.tar.gz gcc-c82395e03592e5858c34f0b824f494eb94b37404.tar.bz2 |
d: Merge dmd, druntime 4ccb01fde5, phobos eab6595ad
D front-end changes:
- Added pragma for ImportC to allow setting `nothrow', `@nogc'
or `pure'.
- Mixin templates can now use assignment syntax.
D runtime changes:
- Removed `ThreadBase.criticalRegionLock' from `core.thread'.
- Added `expect', `[un]likely', `trap' to `core.builtins'.
Phobos changes:
- Import latest fixes from phobos v2.110.0-beta.1.
gcc/d/ChangeLog:
* dmd/MERGE: Merge upstream dmd 4ccb01fde5.
* Make-lang.in (D_FRONTEND_OBJS): Rename d/foreachvar.o to
d/visitor-foreachvar.o, d/visitor.o to d/visitor-package.o, and
d/statement_rewrite_walker.o to d/visitor-statement_rewrite_walker.o.
(D_FRONTEND_OBJS): Rename
d/{parsetime,permissive,postorder,transitive}visitor.o to
d/visitor-{parsetime,permissive,postorder,transitive}.o.
(D_FRONTEND_OBJS): Remove d/sapply.o.
(d.tags): Add dmd/common/*.h.
(d/visitor-%.o:): New rule.
* d-codegen.cc (get_frameinfo): Update for new front-end interface.
libphobos/ChangeLog:
* libdruntime/MERGE: Merge upstream druntime 4ccb01fde5.
* src/MERGE: Merge upstream phobos eab6595ad.
Diffstat (limited to 'gcc')
61 files changed, 3136 insertions, 2869 deletions
diff --git a/gcc/d/Make-lang.in b/gcc/d/Make-lang.in index 679ef57..ae575cf 100644 --- a/gcc/d/Make-lang.in +++ b/gcc/d/Make-lang.in @@ -135,7 +135,6 @@ D_FRONTEND_OBJS = \ d/expression.o \ d/expressionsem.o \ d/file_manager.o \ - d/foreachvar.o \ d/func.o \ d/funcsem.o \ d/globals.o \ @@ -165,9 +164,6 @@ D_FRONTEND_OBJS = \ d/opover.o \ d/optimize.o \ d/parse.o \ - d/parsetimevisitor.o \ - d/permissivevisitor.o \ - d/postordervisitor.o \ d/pragmasem.o \ d/printast.o \ d/root-aav.o \ @@ -189,12 +185,10 @@ D_FRONTEND_OBJS = \ d/root-utf.o \ d/rootobject.o \ d/safe.o \ - d/sapply.o \ d/semantic2.o \ d/semantic3.o \ d/sideeffect.o \ d/statement.o \ - d/statement_rewrite_walker.o \ d/statementsem.o \ d/staticassert.o \ d/staticcond.o \ @@ -205,11 +199,16 @@ D_FRONTEND_OBJS = \ d/timetrace.o \ d/tokens.o \ d/traits.o \ - d/transitivevisitor.o \ d/typesem.o \ d/typinf.o \ d/utils.o \ - d/visitor.o + d/visitor-foreachvar.o \ + d/visitor-package.o \ + d/visitor-parsetime.o \ + d/visitor-permissive.o \ + d/visitor-postorder.o \ + d/visitor-statement_rewrite_walker.o \ + d/visitor-transitive.o # Language-specific object files for D. D_OBJS = \ @@ -296,7 +295,7 @@ d.srcextra: d.tags: force cd $(srcdir)/d; \ - $(ETAGS) -o TAGS.sub *.cc *.h dmd/*.h dmd/root/*.h; \ + $(ETAGS) -o TAGS.sub *.cc *.h dmd/*.h dmd/common/*.h dmd/root/*.h; \ $(ETAGS) --include TAGS.sub --include ../TAGS.sub d.man: doc/gdc.1 @@ -424,3 +423,7 @@ d/common-%.o: d/dmd/common/%.d d/root-%.o: d/dmd/root/%.d $(DCOMPILE) $(D_INCLUDES) $< $(DPOSTCOMPILE) + +d/visitor-%.o: d/dmd/visitor/%.d + $(DCOMPILE) $(D_INCLUDES) $< + $(DPOSTCOMPILE) diff --git a/gcc/d/d-codegen.cc b/gcc/d/d-codegen.cc index 8ecd813..e9962e6 100644 --- a/gcc/d/d-codegen.cc +++ b/gcc/d/d-codegen.cc @@ -2915,7 +2915,7 @@ get_frameinfo (FuncDeclaration *fd) symbols, give it a decent error for now. */ if (requiresClosure != fd->requiresClosure && (fd->nrvo_var || !global.params.useGC)) - fd->checkClosure (); + dmd::checkClosure (fd); /* Set-up a closure frame, this will be allocated on the heap. */ FRAMEINFO_CREATES_FRAME (ffi) = 1; diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE index 654fbed..acb7d98 100644 --- a/gcc/d/dmd/MERGE +++ b/gcc/d/dmd/MERGE @@ -1,4 +1,4 @@ -6884b433d21d9b6356e5c83ffc6eb06a62a5cad1 +4ccb01fde535c7ad6ad4bdae2516c99420751814 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 00bbf84..a2c940f 100644 --- a/gcc/d/dmd/README.md +++ b/gcc/d/dmd/README.md @@ -98,14 +98,14 @@ Note that these groups have no strict meaning, the category assignments are a bi | File | Purpose | |-----------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------| -| [parsetimevisitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/parsetimevisitor.d) | General [visitor](https://en.wikipedia.org/wiki/Visitor_pattern) for AST nodes | -| [permissivevisitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/permissivevisitor.d) | Subclass of ParseTimeVisitor that does not `assert(0)` on unimplemented nodes | -| [strictvisitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/strictvisitor.d) | Visitor that forces derived classes to implement `visit` for every possible node | -| [visitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor.d) | A visitor implementing `visit` for all nodes present in the compiler | -| [transitivevisitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/transitivevisitor.d) | Provide a mixin template with visit methods for the parse time AST | -| [postordervisitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/postordervisitor.d) | Depth-first expression visitor | -| [sapply.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/sapply.d) | Depth-first statement visitor | -| [statement_rewrite_walker.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/statement_rewrite_walker.d) | Statement visitor that allows replacing the currently visited node | +| [visitor/parsetime.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/parsetime.d) | General [visitor](https://en.wikipedia.org/wiki/Visitor_pattern) for AST nodes | +| [visitor/permissive.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/permissive.d) | Subclass of ParseTimeVisitor that does not `assert(0)` on unimplemented nodes | +| [visitor/strict.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/strict.d) | Visitor that forces derived classes to implement `visit` for every possible node | +| [visitor/package.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/package.d) | A visitor implementing `visit` for all nodes present in the compiler | +| [visitor/transitive.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/transitive.d) | Provide a mixin template with visit methods for the parse time AST | +| [visitor/postorder.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/postorder.d) | Depth-first expression & statement visitor | +| [visitor/statement_rewrite_walker.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/statement_rewrite_walker.d) | Statement visitor that allows replacing the currently visited node | +| [visitor/foreachvar.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/foreachvar.d) | Used in `ob.d` to iterate over all variables in an expression | **Semantic passes** @@ -270,4 +270,3 @@ Note: many other utilities are in [dmd/root](https://github.com/dlang/dmd/tree/m |---------------------------------------------------------------------------------|---------------------------------------------------------------| | [asttypename.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/asttypename.d) | Print the internal name of an AST node (for debugging only) | | [printast.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/printast.d) | Print the AST data structure | -| [foreachvar.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/foreachvar.d) | Used in `ob.d` to iterate over all variables in an expression | diff --git a/gcc/d/dmd/canthrow.d b/gcc/d/dmd/canthrow.d index e597550..dfc91e1 100644 --- a/gcc/d/dmd/canthrow.d +++ b/gcc/d/dmd/canthrow.d @@ -26,9 +26,9 @@ import dmd.func; import dmd.globals; import dmd.init; import dmd.mtype; -import dmd.postordervisitor; import dmd.tokens; import dmd.visitor; +import dmd.visitor.postorder; /** * Status indicating what kind of throwable might be caused by an expression. diff --git a/gcc/d/dmd/clone.d b/gcc/d/dmd/clone.d index 4989ab4..8746baa 100644 --- a/gcc/d/dmd/clone.d +++ b/gcc/d/dmd/clone.d @@ -57,7 +57,7 @@ StorageClass mergeFuncAttrs(StorageClass s1, const FuncDeclaration f) pure return s1; StorageClass s2 = (f.storage_class & STC.disable); - auto tf = cast(TypeFunction)f.type; + auto tf = f.type.isTypeFunction(); if (tf.trust == TRUST.safe) s2 |= STC.safe; else if (tf.trust == TRUST.system) @@ -96,6 +96,8 @@ StorageClass mergeFuncAttrs(StorageClass s1, const FuncDeclaration f) pure * sc = current scope * Returns: * if found, returns FuncDeclaration of opAssign, otherwise null + * References: + * https://dlang.org/spec/operatoroverloading.html#assignment */ FuncDeclaration hasIdentityOpAssign(AggregateDeclaration ad, Scope* sc) { @@ -106,12 +108,12 @@ FuncDeclaration hasIdentityOpAssign(AggregateDeclaration ad, Scope* sc) scope er = new NullExp(ad.loc, ad.type); // dummy rvalue scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue el.type = ad.type; - auto a = new Expressions(1); const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it. sc = sc.push(); sc.tinst = null; sc.minst = null; + auto a = new Expressions(1); (*a)[0] = er; auto f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, ArgumentList(a), FuncResolveFlag.quiet); if (!f) @@ -144,7 +146,11 @@ FuncDeclaration hasIdentityOpAssign(AggregateDeclaration ad, Scope* sc) /******************************************* * We need an opAssign for the struct if * it has a destructor or a postblit. - * We need to generate one if a user-specified one does not exist. + * (We will later generate one if a user-specified one does not exist) + * Params: + * sd = struct to check + * Returns: + * true if an opAssign is needed */ private bool needOpAssign(StructDeclaration sd) { @@ -174,9 +180,8 @@ private bool needOpAssign(StructDeclaration sd) if (v.overlapped) // if field of a union continue; // user must handle it themselves Type tv = v.type.baseElemOf(); - if (tv.ty == Tstruct) + if (auto ts = tv.isTypeStruct()) { - auto ts = cast(TypeStruct)tv; if (ts.sym.isUnionDeclaration()) continue; if (needOpAssign(ts.sym)) @@ -250,7 +255,7 @@ private bool needOpAssign(StructDeclaration sd) * sd = struct to generate opAssign for * sc = context * Returns: - * generated `opAssign` function + * generated `opAssign` function, or null if it is not needed */ FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) { @@ -280,10 +285,10 @@ FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) if (v.overlapped) continue; Type tv = v.type.baseElemOf(); - if (tv.ty != Tstruct) - continue; - StructDeclaration sdv = (cast(TypeStruct)tv).sym; - stc = mergeFuncAttrs(stc, hasIdentityOpAssign(sdv, sc)); + if (auto tvs = tv.isTypeStruct()) + { + stc = mergeFuncAttrs(stc, hasIdentityOpAssign(tvs.sym, sc)); + } } if (sd.dtor || sd.postblit) @@ -313,8 +318,7 @@ FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) else if (sd.dtor) { //printf("\tswap copy\n"); - auto tdtor = cast(TypeFunction)sd.dtor.type; - assert(tdtor.ty == Tfunction); + auto tdtor = sd.dtor.type.isTypeFunction(); auto idswap = Identifier.generateId("__swap"); auto swap = new VarDeclaration(loc, sd.type, idswap, new VoidInitializer(loc)); @@ -406,8 +410,11 @@ FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) /******************************************* * We need an opEquals for the struct if - * any fields has an opEquals. - * Generate one if a user-specified one does not exist. + * any field has an opEquals and a user-specified one does not exist. + * Params: + * sd = struct to check + * Returns: + * true if need to generate one */ bool needOpEquals(StructDeclaration sd) { @@ -432,9 +439,8 @@ bool needOpEquals(StructDeclaration sd) continue; Type tv = v.type.toBasetype(); auto tvbase = tv.baseElemOf(); - if (tvbase.ty == Tstruct) + if (auto ts = tvbase.isTypeStruct()) { - auto ts = cast(TypeStruct)tvbase; if (ts.sym.isUnionDeclaration() && ts.sym.fields.length != 1) continue; if (needOpEquals(ts.sym)) @@ -464,6 +470,10 @@ Lneed: /******************************************* * Check given aggregate actually has an identity opEquals or not. + * ad = aggregate to check + * sc = context + * Returns: + * identity opEquals if it is there, null if not */ private FuncDeclaration hasIdentityOpEquals(AggregateDeclaration ad, Scope* sc) { @@ -562,7 +572,7 @@ FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc) parameters.push(new Parameter(Loc.initial, STC.ref_ | STC.const_, sd.type, null, null, null)); tfeqptr = new TypeFunction(ParameterList(parameters), Type.tbool, LINK.d); tfeqptr.mod = MODFlags.const_; - tfeqptr = cast(TypeFunction)tfeqptr.typeSemantic(Loc.initial, &scx); + tfeqptr = tfeqptr.typeSemantic(Loc.initial, &scx).isTypeFunction(); } fd = fd.overloadExactMatch(tfeqptr); if (fd) @@ -598,7 +608,7 @@ FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc) Expression e2 = new IdentifierExp(loc, Id.p); Expression e = new EqualExp(EXP.equal, loc, e1, e2); fop.fbody = new ReturnStatement(loc, e); - uint errors = global.startGagging(); // Do not report errors + const errors = global.startGagging(); // Do not report errors Scope* sc2 = sc.push(); sc2.stc = 0; sc2.linkage = LINK.d; @@ -637,11 +647,10 @@ FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) parameters.push(new Parameter(Loc.initial, STC.ref_ | STC.const_, sd.type, null, null, null)); tfcmpptr = new TypeFunction(ParameterList(parameters), Type.tint32, LINK.d); tfcmpptr.mod = MODFlags.const_; - tfcmpptr = cast(TypeFunction)tfcmpptr.typeSemantic(Loc.initial, &scx); + tfcmpptr = tfcmpptr.typeSemantic(Loc.initial, &scx).isTypeFunction(); } - fd = fd.overloadExactMatch(tfcmpptr); - if (fd) - return fd; + if (auto fdo = fd.overloadExactMatch(tfcmpptr)) + return fdo; } } else @@ -738,7 +747,13 @@ FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) /******************************************* * We need a toHash for the struct if * any fields has a toHash. - * Generate one if a user-specified one does not exist. + * (will generate one if a user-specified one does not exist) + * Params: + * sd = struct to check + * Returns: + * need to generate toHash() + * References: + * https://dlang.org/spec/hash-map.html#using_struct_as_key */ private bool needToHash(StructDeclaration sd) { @@ -759,9 +774,8 @@ private bool needToHash(StructDeclaration sd) continue; Type tv = v.type.toBasetype(); auto tvbase = tv.baseElemOf(); - if (tvbase.ty == Tstruct) + if (auto ts = tvbase.isTypeStruct()) { - auto ts = cast(TypeStruct)tvbase; if (ts.sym.isUnionDeclaration()) continue; if (needToHash(ts.sym)) @@ -802,13 +816,12 @@ FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc) { tftohash = new TypeFunction(ParameterList(), Type.thash_t, LINK.d); tftohash.mod = MODFlags.const_; - tftohash = cast(TypeFunction)tftohash.merge(); + tftohash = tftohash.merge().isTypeFunction(); } if (FuncDeclaration fd = s.isFuncDeclaration()) { - fd = fd.overloadExactMatch(tftohash); - if (fd) - return fd; + if (auto fdo = fd.overloadExactMatch(tftohash)) + return fdo; } } if (!needToHash(sd)) @@ -899,10 +912,10 @@ void buildDtors(AggregateDeclaration ad, Scope* sc) continue; if (v.overlapped) continue; - auto tv = v.type.baseElemOf(); - if (tv.ty != Tstruct) + auto tvs = v.type.baseElemOf().isTypeStruct(); + if (!tvs) continue; - auto sdv = (cast(TypeStruct)tv).sym; + auto sdv = tvs.sym; if (!sdv.dtor) continue; @@ -924,8 +937,8 @@ void buildDtors(AggregateDeclaration ad, Scope* sc) } Expression ex; - tv = v.type.toBasetype(); - if (tv.ty == Tstruct) + Type tv = v.type.toBasetype(); + if (tv.isTypeStruct()) { // this.v.__xdtor() @@ -1178,6 +1191,14 @@ private DtorDeclaration buildExternDDtor(AggregateDeclaration ad, Scope* sc) * invs[0](), invs[1](), ...; * } * --- + * Params: + * ad = aggregate for creating invariant + * sc = context + * Returns: + * generated invariant, null if not needed + * References: + * https://dlang.org/spec/class.html#invariants + * https://dlang.org/spec/struct.html#Invariant */ FuncDeclaration buildInv(AggregateDeclaration ad, Scope* sc) { @@ -1231,6 +1252,11 @@ FuncDeclaration buildInv(AggregateDeclaration ad, Scope* sc) * all the members. * Note the close similarity with AggregateDeclaration::buildDtor(), * and the ordering changes (runs forward instead of backwards). + * Params: + * sd = struct to create postblit for + * sc = context + * Returns: + * generated postblit, or null if not */ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) { @@ -1261,10 +1287,10 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) if (structField.overlapped) continue; // if it's a struct declaration or an array of structs - Type tv = structField.type.baseElemOf(); - if (tv.ty != Tstruct) + TypeStruct tvs = structField.type.baseElemOf().isTypeStruct(); + if (!tvs) continue; - auto sdv = (cast(TypeStruct)tv).sym; + auto sdv = tvs.sym; // which has a postblit declaration if (!sdv.postblit) continue; @@ -1274,15 +1300,15 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) // block to destroy any prior successfully postblitted fields should // this field's postblit fail. // Don't generate it for betterC code since it cannot throw exceptions. - if (fieldsToDestroy.length > 0 && !(cast(TypeFunction)sdv.postblit.type).isNothrow && global.params.useExceptions) + if (fieldsToDestroy.length > 0 && !sdv.postblit.type.isTypeFunction().isNothrow && global.params.useExceptions) { // create a list of destructors that need to be called Expression[] dtorCalls; foreach(sf; fieldsToDestroy) { Expression ex; - tv = sf.type.toBasetype(); - if (tv.ty == Tstruct) + Type tv = sf.type.toBasetype(); + if (auto tvs2 = tv.isTypeStruct()) { // this.v.__xdtor() @@ -1296,9 +1322,7 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) if (stc & STC.safe) stc = (stc & ~STC.safe) | STC.trusted; - auto sfv = (cast(TypeStruct)sf.type.baseElemOf()).sym; - - ex = new DotVarExp(loc, ex, sfv.dtor, false); + ex = new DotVarExp(loc, ex, tvs2.sym.dtor, false); ex = new CallExp(loc, ex); dtorCalls ~= ex; @@ -1360,8 +1384,8 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) } Expression ex; - tv = structField.type.toBasetype(); - if (tv.ty == Tstruct) + Type tv = structField.type.toBasetype(); + if (tv.isTypeStruct()) { // this.v.__xpostblit() @@ -1613,16 +1637,8 @@ bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor) return 0; } - auto tf = ctorDecl.type.toTypeFunction(); - const dim = tf.parameterList.length; - if (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg)) - { - auto param = tf.parameterList[0]; - if (param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf()) - { - rvalueCtor = ctorDecl; - } - } + if (isRvalueConstructor(sd, ctorDecl)) + rvalueCtor = ctorDecl; return 0; }); @@ -1687,6 +1703,8 @@ LcheckFields: * Returns: * `true` if `struct` sd defines a copy constructor (explicitly or generated), * `false` otherwise. + * References: + * https://dlang.org/spec/struct.html#struct-copy-constructor */ bool buildCopyCtor(StructDeclaration sd, Scope* sc) { diff --git a/gcc/d/dmd/cparse.d b/gcc/d/dmd/cparse.d index e02ff43..354d83b 100644 --- a/gcc/d/dmd/cparse.d +++ b/gcc/d/dmd/cparse.d @@ -45,6 +45,9 @@ final class CParser(AST) : Parser!AST // #pragma pack stack Array!Identifier* records; // identifers (or null) Array!structalign_t* packs; // parallel alignment values + + STC defaultStorageClasses; + Array!STC* defaultStorageClassesStack; } /* C cannot be parsed without determining if an identifier is a type or a variable. @@ -3019,6 +3022,7 @@ final class CParser(AST) : Parser!AST StorageClass stc = specifier._nothrow ? STC.nothrow_ : 0; if (specifier._pure) stc |= STC.pure_; + stc |= defaultStorageClasses; AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, stc); //tf = tf.addSTC(storageClass); // TODO insertTx(ts, tf, t); // ts -> ... -> tf -> t @@ -5670,6 +5674,8 @@ final class CParser(AST) : Parser!AST scan(&n); if (n.value == TOK.identifier && n.ident == Id.pack) return pragmaPack(loc, true); + if (n.value == TOK.identifier && n.ident == Id.attribute) + return pragmaAttribute(loc); if (n.value != TOK.endOfLine) skipToNextLine(); } @@ -5877,6 +5883,125 @@ final class CParser(AST) : Parser!AST skipToNextLine(); } + /********* + * # pragma attribute(...) + * Sets default storage classes + * Params: + * startloc = location to use for error messages + */ + private void pragmaAttribute(const ref Loc startloc) + { + const loc = startloc; + + if (!defaultStorageClassesStack) + { + defaultStorageClassesStack = new Array!STC; + } + + Token n; + Lexer.scan(&n); + if (n.value != TOK.leftParenthesis) + { + error(loc, "left parenthesis expected to follow `#pragma attribute`"); + if (n.value != TOK.endOfLine) + skipToNextLine(); + return; + } + + void closingParen() + { + if (n.value != TOK.rightParenthesis) + { + error(loc, "right parenthesis expected to close `#pragma attribute(`"); + } + if (n.value != TOK.endOfLine) + skipToNextLine(); + } + + Lexer.scan(&n); + + /* # pragma attribute (push, ...) + */ + if (n.value == TOK.identifier && n.ident == Id.push) + { + Lexer.scan(&n); + if (n.value != TOK.comma) + { + error(loc, "comma expected to follow `#pragma attribute(push`"); + if (n.value != TOK.endOfLine) + skipToNextLine(); + return; + } + + while (1) + { + Lexer.scan(&n); + if (n.value == TOK.endOfLine) + { + error(loc, "right parenthesis expected to close `#pragma attribute(push, `"); + break; + } + + if (n.value == TOK.rightParenthesis) + break; + + if (n.value == TOK.identifier) + { + if (n.ident == Id._nothrow) + defaultStorageClasses |= STC.nothrow_; + else if (n.ident == Id.nogc) + defaultStorageClasses |= STC.nogc; + else if (n.ident == Id._pure) + defaultStorageClasses |= STC.pure_; + // Ignore unknown identifiers + } + else + { + error(loc, "unrecognized `#pragma attribute(push, %s)`", n.toChars()); + break; + } + + Lexer.scan(&n); + + if (n.value == TOK.rightParenthesis) + break; + + if (n.value != TOK.comma) + { + error(loc, "unrecognized `#pragma attribute(push, %s)`", n.toChars()); + break; + } + } + + this.defaultStorageClassesStack.push(defaultStorageClasses); + + return closingParen(); + } + + /* # pragma attribute(pop) + */ + if (n.value == TOK.identifier && n.ident == Id.pop) + { + scan(&n); + size_t len = this.defaultStorageClassesStack.length; + + if (len) + { + this.defaultStorageClassesStack.setDim(len - 1); + if (len == 1) // stack is now empty + defaultStorageClasses = STC.init; + else + defaultStorageClasses = (*this.defaultStorageClassesStack)[len - 2]; + } + + return closingParen(); + } + + error(loc, "unrecognized `#pragma attribute(%s)`", n.toChars()); + if (n.value != TOK.endOfLine) + skipToNextLine(); + } + //} /******************************************************************************/ diff --git a/gcc/d/dmd/cxxfrontend.d b/gcc/d/dmd/cxxfrontend.d index 09e364d..7cb4011 100644 --- a/gcc/d/dmd/cxxfrontend.d +++ b/gcc/d/dmd/cxxfrontend.d @@ -425,6 +425,11 @@ void semanticTypeInfoMembers(StructDeclaration sd) return dmd.semantic3.semanticTypeInfoMembers(sd); } +bool checkClosure(FuncDeclaration fd) +{ + import dmd.semantic3; + return dmd.semantic3.checkClosure(fd); +} /*********************************************************** * statementsem.d */ diff --git a/gcc/d/dmd/declaration.h b/gcc/d/dmd/declaration.h index 4ac22b9..c19aa06 100644 --- a/gcc/d/dmd/declaration.h +++ b/gcc/d/dmd/declaration.h @@ -34,6 +34,7 @@ namespace dmd { bool functionSemantic(FuncDeclaration* fd); bool functionSemantic3(FuncDeclaration* fd); + bool checkClosure(FuncDeclaration* fd); MATCH leastAsSpecialized(FuncDeclaration *f, FuncDeclaration *g, Identifiers *names); PURE isPure(FuncDeclaration *f); } @@ -719,7 +720,6 @@ public: bool isAbstract() override final; bool isSafe(); bool isTrusted(); - bool isNogc(); virtual bool isNested() const; AggregateDeclaration *isThis() override; @@ -732,7 +732,6 @@ public: const char *kind() const override; bool isUnique(); bool needsClosure(); - bool checkClosure(); bool hasNestedFrameRefs(); ParameterList getParameterList(); diff --git a/gcc/d/dmd/delegatize.d b/gcc/d/dmd/delegatize.d index 95064b0..f302d55 100644 --- a/gcc/d/dmd/delegatize.d +++ b/gcc/d/dmd/delegatize.d @@ -25,10 +25,10 @@ import dmd.init; import dmd.initsem; import dmd.location; import dmd.mtype; -import dmd.postordervisitor; import dmd.statement; import dmd.tokens; import dmd.visitor; +import dmd.visitor.postorder; /********************************* diff --git a/gcc/d/dmd/dinterpret.d b/gcc/d/dmd/dinterpret.d index 7a715a3..4e04a27 100644 --- a/gcc/d/dmd/dinterpret.d +++ b/gcc/d/dmd/dinterpret.d @@ -6880,10 +6880,10 @@ private Expression scrubReturnValue(const ref Loc loc, Expression e) Expression scrubSE(StructLiteralExp sle) { sle.ownedByCtfe = OwnedBy.code; - if (!(sle.stageflags & stageScrub)) + if (!(sle.stageflags & StructLiteralExp.StageFlags.scrub)) { const old = sle.stageflags; - sle.stageflags |= stageScrub; // prevent infinite recursion + sle.stageflags |= StructLiteralExp.StageFlags.scrub; // prevent infinite recursion if (auto ex = scrubArray(sle.elements, true)) return ex; sle.stageflags = old; @@ -6960,10 +6960,10 @@ private Expression scrubCacheValue(Expression e) Expression scrubSE(StructLiteralExp sle) { sle.ownedByCtfe = OwnedBy.cache; - if (!(sle.stageflags & stageScrub)) + if (!(sle.stageflags & StructLiteralExp.StageFlags.scrub)) { const old = sle.stageflags; - sle.stageflags |= stageScrub; // prevent infinite recursion + sle.stageflags |= StructLiteralExp.StageFlags.scrub; // prevent infinite recursion if (auto ex = scrubArrayCache(sle.elements)) return ex; sle.stageflags = old; @@ -7037,10 +7037,10 @@ private Expression copyRegionExp(Expression e) static void copySE(StructLiteralExp sle) { - if (1 || !(sle.stageflags & stageScrub)) + if (1 || !(sle.stageflags & StructLiteralExp.StageFlags.scrub)) { const old = sle.stageflags; - sle.stageflags |= stageScrub; // prevent infinite recursion + sle.stageflags |= StructLiteralExp.StageFlags.scrub; // prevent infinite recursion copyArray(sle.elements); sle.stageflags = old; } diff --git a/gcc/d/dmd/dmodule.d b/gcc/d/dmd/dmodule.d index 583b467..eda20a2 100644 --- a/gcc/d/dmd/dmodule.d +++ b/gcc/d/dmd/dmodule.d @@ -349,8 +349,8 @@ extern (C++) final class Module : Package const(char)[] arg; // original argument name ModuleDeclaration* md; // if !=null, the contents of the ModuleDeclaration declaration const FileName srcfile; // input source file - const FileName objfile; // output .obj file - const FileName hdrfile; // 'header' file + FileName objfile; // output .obj file + FileName hdrfile; // 'header' file FileName docfile; // output documentation file const(ubyte)[] src; /// Raw content of the file uint errors; // if any errors in file @@ -579,6 +579,22 @@ extern (C++) final class Module : Package argdoc = arg; else argdoc = FileName.name(arg); + + if (global.params.fullyQualifiedObjectFiles) + { + const fqn = md ? md.toString() : toString(); + argdoc = FileName.replaceName(argdoc, fqn); + + // add ext, otherwise forceExt will make nested.module into nested.<ext> + const bufferLength = argdoc.length + 1 + ext.length + /* null terminator */ 1; + char[] s = new char[bufferLength]; + s[0 .. argdoc.length] = argdoc[]; + s[argdoc.length] = '.'; + s[$-1-ext.length .. $-1] = ext[]; + s[$-1] = 0; + argdoc = s; + } + // If argdoc doesn't have an absolute path, make it relative to dir if (!FileName.absolute(argdoc)) { diff --git a/gcc/d/dmd/dstruct.d b/gcc/d/dmd/dstruct.d index 64f19d9..8b4ac7d 100644 --- a/gcc/d/dmd/dstruct.d +++ b/gcc/d/dmd/dstruct.d @@ -431,13 +431,15 @@ extern (C++) class StructDeclaration : AggregateDeclaration if (ispod != ThreeState.none) return (ispod == ThreeState.yes); - ispod = ThreeState.yes; - import dmd.clone; + bool hasCpCtorLocal; needCopyCtor(this, hasCpCtorLocal); - if (enclosing || search(this, loc, Id.postblit) || search(this, loc, Id.dtor) || hasCpCtorLocal) + if (enclosing || // is nested + search(this, loc, Id.postblit) || // has postblit + search(this, loc, Id.dtor) || // has destructor + hasCpCtorLocal) // has copy constructor { ispod = ThreeState.no; return false; @@ -453,12 +455,9 @@ extern (C++) class StructDeclaration : AggregateDeclaration return false; } - Type tv = v.type.baseElemOf(); - if (tv.ty == Tstruct) + if (auto ts = v.type.baseElemOf().isTypeStruct()) { - auto ts = cast(TypeStruct)tv; - StructDeclaration sd = ts.sym; - if (!sd.isPOD()) + if (!ts.sym.isPOD()) { ispod = ThreeState.no; return false; @@ -466,7 +465,8 @@ extern (C++) class StructDeclaration : AggregateDeclaration } } - return (ispod == ThreeState.yes); + ispod = ThreeState.yes; + return true; } /*************************************** diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d index e0eebb4..2958d95 100644 --- a/gcc/d/dmd/dsymbolsem.d +++ b/gcc/d/dmd/dsymbolsem.d @@ -242,38 +242,53 @@ package bool allowsContractWithoutBody(FuncDeclaration funcdecl) } /* -Tests whether the `ctor` that is part of `ti` is an rvalue constructor -(i.e. a constructor that receives a single parameter of the same type as -`Unqual!typeof(this)`). If that is the case and `sd` contains a copy -constructor, than an error is issued. +If sd has a copy constructor and ctor is an rvalue constructor, +issue an error. Params: - sd = struct declaration that may contin both an rvalue and copy constructor - ctor = constructor that will be checked if it is an evalue constructor + sd = struct declaration that may contain both an rvalue and copy constructor + ctor = constructor that will be checked if it is an rvalue constructor ti = template instance the ctor is part of Return: - `false` if ctor is not an rvalue constructor or if `sd` does not contain a - copy constructor. `true` otherwise + `true` if sd has a copy constructor and ctor is an rvalue constructor */ bool checkHasBothRvalueAndCpCtor(StructDeclaration sd, CtorDeclaration ctor, TemplateInstance ti) { - auto loc = ctor.loc; - auto tf = cast(TypeFunction)ctor.type; - auto dim = tf.parameterList.length; - if (sd && sd.hasCopyCtor && (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg))) + if (sd && sd.hasCopyCtor && isRvalueConstructor(sd, ctor)) + { + .error(ctor.loc, "cannot define both an rvalue constructor and a copy constructor for `struct %s`", sd.toChars()); + .errorSupplemental(ti.loc, "Template instance `%s` creates an rvalue constructor for `struct %s`", + ti.toPrettyChars(), sd.toChars()); + + return true; + } + + return false; +} + +/************************************************ + * Check if ctor is an rvalue constructor. + * A constructor that receives a single parameter of the same type as + * `Unqual!typeof(this)` is an rvalue constructor. + * Params: + * sd = struct that ctor is a member of + * ctor = constructor to test + * Returns: + * true if it is an rvalue constructor + */ +bool isRvalueConstructor(StructDeclaration sd, CtorDeclaration ctor) +{ + auto tf = ctor.type.isTypeFunction(); + const dim = tf.parameterList.length; + if (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg)) { auto param = tf.parameterList[0]; if (!(param.storageClass & STC.ref_) && param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf()) { - .error(loc, "cannot define both an rvalue constructor and a copy constructor for `struct %s`", sd.toChars()); - .errorSupplemental(ti.loc, "Template instance `%s` creates an rvalue constructor for `struct %s`", - ti.toPrettyChars(), sd.toChars()); - return true; } } - return false; } @@ -1643,85 +1658,88 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor imp.mod.checkImportDeprecation(imp.loc, sc); } } - if (imp.mod) + if (!imp.mod) { - // Modules need a list of each imported module - - // if inside a template instantiation, the instantianting - // module gets the import. - // https://issues.dlang.org/show_bug.cgi?id=17181 - Module importer = sc._module; - if (sc.minst && sc.tinst) - { - importer = sc.minst; - if (!sc.tinst.importedModules.contains(imp.mod)) - sc.tinst.importedModules.push(imp.mod); - } - //printf("%s imports %s\n", importer.toChars(), imp.mod.toChars()); - if (!importer.aimports.contains(imp.mod)) - importer.aimports.push(imp.mod); + imp.semanticRun = PASS.semanticdone; + addImportDep(global.params.moduleDeps, imp, sc._module); + } - if (sc.explicitVisibility) - imp.visibility = sc.visibility; + // Modules need a list of each imported module - if (!imp.aliasId && !imp.names.length) // neither a selective nor a renamed import - { - ScopeDsymbol scopesym = sc.getScopesym(); + // if inside a template instantiation, the instantianting + // module gets the import. + // https://issues.dlang.org/show_bug.cgi?id=17181 + Module importer = sc._module; + if (sc.minst && sc.tinst) + { + importer = sc.minst; + if (!sc.tinst.importedModules.contains(imp.mod)) + sc.tinst.importedModules.push(imp.mod); + } + //printf("%s imports %s\n", importer.toChars(), imp.mod.toChars()); + if (!importer.aimports.contains(imp.mod)) + importer.aimports.push(imp.mod); - if (!imp.isstatic) - { - scopesym.importScope(imp.mod, imp.visibility); - } + if (sc.explicitVisibility) + imp.visibility = sc.visibility; + if (!imp.aliasId && !imp.names.length) // neither a selective nor a renamed import + { + ScopeDsymbol scopesym = sc.getScopesym(); - imp.addPackageAccess(scopesym); + if (!imp.isstatic) + { + scopesym.importScope(imp.mod, imp.visibility); } - // if a module has errors it means that parsing has failed. - if (!imp.mod.errors) - imp.mod.dsymbolSemantic(null); - if (imp.mod.needmoduleinfo) - { - //printf("module4 %s because of %s\n", importer.toChars(), imp.mod.toChars()); - importer.needmoduleinfo = 1; - } + imp.addPackageAccess(scopesym); + } + + // if a module has errors it means that parsing has failed. + if (!imp.mod.errors) + imp.mod.dsymbolSemantic(null); + + if (imp.mod.needmoduleinfo) + { + //printf("module4 %s because of %s\n", importer.toChars(), imp.mod.toChars()); + importer.needmoduleinfo = 1; + } - sc = sc.push(imp.mod); - sc.visibility = imp.visibility; - for (size_t i = 0; i < imp.aliasdecls.length; i++) + sc = sc.push(imp.mod); + sc.visibility = imp.visibility; + for (size_t i = 0; i < imp.aliasdecls.length; i++) + { + AliasDeclaration ad = imp.aliasdecls[i]; + //printf("\tImport %s alias %s = %s, scope = %p\n", toPrettyChars(), aliases[i].toChars(), names[i].toChars(), ad._scope); + Dsymbol sym = imp.mod.search(imp.loc, imp.names[i], SearchOpt.ignorePrivateImports); + if (sym) { - AliasDeclaration ad = imp.aliasdecls[i]; - //printf("\tImport %s alias %s = %s, scope = %p\n", toPrettyChars(), aliases[i].toChars(), names[i].toChars(), ad._scope); - Dsymbol sym = imp.mod.search(imp.loc, imp.names[i], SearchOpt.ignorePrivateImports); - if (sym) + import dmd.access : symbolIsVisible; + if (!symbolIsVisible(sc, sym) && !sym.errors) { - import dmd.access : symbolIsVisible; - if (!symbolIsVisible(sc, sym) && !sym.errors) - { - .error(imp.loc, "%s `%s` member `%s` is not visible from module `%s`", imp.mod.kind, imp.mod.toPrettyChars, - imp.names[i].toChars(), sc._module.toChars()); - sym.errors = true; - } - ad.dsymbolSemantic(sc); - // If the import declaration is in non-root module, - // analysis of the aliased symbol is deferred. - // Therefore, don't see the ad.aliassym or ad.type here. + .error(imp.loc, "%s `%s` member `%s` is not visible from module `%s`", imp.mod.kind, imp.mod.toPrettyChars, + imp.names[i].toChars(), sc._module.toChars()); + sym.errors = true; } + ad.dsymbolSemantic(sc); + // If the import declaration is in non-root module, + // analysis of the aliased symbol is deferred. + // Therefore, don't see the ad.aliassym or ad.type here. + } + else + { + Dsymbol s = imp.mod.search_correct(imp.names[i]); + // https://issues.dlang.org/show_bug.cgi?id=23908 + // Don't suggest symbols from the importer's module + if (s && s.parent != importer) + .error(imp.loc, "%s `%s` import `%s` not found, did you mean %s `%s`?", imp.mod.kind, imp.mod.toPrettyChars, imp.names[i].toChars(), s.kind(), s.toPrettyChars()); else - { - Dsymbol s = imp.mod.search_correct(imp.names[i]); - // https://issues.dlang.org/show_bug.cgi?id=23908 - // Don't suggest symbols from the importer's module - if (s && s.parent != importer) - .error(imp.loc, "%s `%s` import `%s` not found, did you mean %s `%s`?", imp.mod.kind, imp.mod.toPrettyChars, imp.names[i].toChars(), s.kind(), s.toPrettyChars()); - else - .error(imp.loc, "%s `%s` import `%s` not found", imp.mod.kind, imp.mod.toPrettyChars, imp.names[i].toChars()); - ad.type = Type.terror; - } + .error(imp.loc, "%s `%s` import `%s` not found", imp.mod.kind, imp.mod.toPrettyChars, imp.names[i].toChars()); + ad.type = Type.terror; } - sc = sc.pop(); } + sc = sc.pop(); imp.semanticRun = PASS.semanticdone; addImportDep(global.params.moduleDeps, imp, sc._module); @@ -1734,20 +1752,24 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor ad.semanticRun = PASS.semantic; Dsymbols* d = ad.include(sc); //printf("\tAttribDeclaration::semantic '%s', d = %p\n",toChars(), d); - if (d) + if (!d) { - Scope* sc2 = ad.newScope(sc); - bool errors; - for (size_t i = 0; i < d.length; i++) - { - Dsymbol s = (*d)[i]; - s.dsymbolSemantic(sc2); - errors |= s.errors; - } - ad.errors |= errors; - if (sc2 != sc) - sc2.pop(); + ad.semanticRun = PASS.semanticdone; + return; } + + Scope* sc2 = ad.newScope(sc); + bool errors; + for (size_t i = 0; i < d.length; i++) + { + Dsymbol s = (*d)[i]; + s.dsymbolSemantic(sc2); + errors |= s.errors; + } + ad.errors |= errors; + if (sc2 != sc) + sc2.pop(); + ad.semanticRun = PASS.semanticdone; } @@ -2579,48 +2601,51 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor sc.pop(); } - override void visit(StaticCtorDeclaration scd) + void visitStaticCDtorDeclaration(FuncDeclaration sd, bool isDestructor) { - //printf("StaticCtorDeclaration::semantic()\n"); - if (scd.semanticRun >= PASS.semanticdone) + if (sd.semanticRun >= PASS.semanticdone) return; - if (scd._scope) + if (sd._scope) { - sc = scd._scope; - scd._scope = null; + sc = sd._scope; + sd._scope = null; } - - scd.parent = sc.parent; - Dsymbol p = scd.parent.pastMixin(); + sd.parent = sc.parent; + Dsymbol p = sd.parent.pastMixin(); + const bool isShared = !!(sd.isSharedStaticDtorDeclaration() || sd.isSharedStaticCtorDeclaration()); + const(char)* what = isDestructor ? "destructor" : "constructor"; if (!p.isScopeDsymbol()) { - const(char)* s = (scd.isSharedStaticCtorDeclaration() ? "shared " : ""); - error(scd.loc, "`%sstatic` constructor can only be member of module/aggregate/template, not %s `%s`", s, p.kind(), p.toChars()); - scd.type = Type.terror; - scd.errors = true; + const(char)* s = isShared ? "shared " : ""; + error(sd.loc, "`%sstatic` %s can only be member of module/aggregate/template, not %s `%s`", s, what, p.kind(), p.toChars()); + sd.type = Type.terror; + sd.errors = true; return; } - if (!scd.type) - scd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, scd.storage_class); - /* If the static ctor appears within a template instantiation, + if (!sd.type) + sd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, sd.storage_class); + + /* If the static [dc]tor appears within a template instantiation, * it could get called multiple times by the module constructors * for different modules. Thus, protect it with a gate. */ - if (scd.isInstantiated() && scd.semanticRun < PASS.semantic) + if (sd.isInstantiated() && sd.semanticRun < PASS.semantic) { /* Add this prefix to the constructor: * ``` * static int gate; - * if (++gate != 1) return; + * if ([--|++]gate != [0|1]) return; // dependant on ctor/dtor * ``` * or, for shared constructor: * ``` * shared int gate; - * if (core.atomic.atomicOp!"+="(gate, 1) != 1) return; + * enum op = isDestructor ? "-=" : "+="; + * enum cmp = isDestructor ? 0 : 1; + * if (core.atomic.atomicOp!op(gate, 1) != cmp) return; * ``` */ - const bool isShared = !!scd.isSharedStaticCtorDeclaration(); + auto v = new VarDeclaration(Loc.initial, Type.tint32, Id.gate, null); v.storage_class = STC.temp | STC.static_ | (isShared ? STC.shared_ : 0); @@ -2631,49 +2656,56 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor Expression e; if (isShared) { - e = doAtomicOp("+=", v.ident, IntegerExp.literal!(1)); + e = doAtomicOp(isDestructor ? "-=" : "+=", v.ident, IntegerExp.literal!(1)); if (e is null) { - .error(scd.loc, "%s `%s` shared static constructor within a template require `core.atomic : atomicOp` to be present", scd.kind, scd.toPrettyChars); + .error(sd.loc, "%s `%s` shared static %s within a template require `core.atomic : atomicOp` to be present", sd.kind, sd.toPrettyChars, what); return; } } else { + IntegerExp one = isDestructor ? IntegerExp.literal!(-1) : IntegerExp.literal!(1); e = new AddAssignExp( - Loc.initial, new IdentifierExp(Loc.initial, v.ident), IntegerExp.literal!1); + Loc.initial, new IdentifierExp(Loc.initial, v.ident), one); } - - e = new EqualExp(EXP.notEqual, Loc.initial, e, IntegerExp.literal!1); + IntegerExp cmp = isDestructor ? IntegerExp.literal!0 : IntegerExp.literal!1; + e = new EqualExp(EXP.notEqual, Loc.initial, e, cmp); s = new IfStatement(Loc.initial, null, e, new ReturnStatement(Loc.initial, null), null, Loc.initial); sa.push(s); - if (scd.fbody) - sa.push(scd.fbody); + if (sd.fbody) + sa.push(sd.fbody); - scd.fbody = new CompoundStatement(Loc.initial, sa); + sd.fbody = new CompoundStatement(Loc.initial, sa); + if (isDestructor) + (cast(StaticDtorDeclaration)sd).vgate = v; } - const LINK save = sc.linkage; if (save != LINK.d) { - const(char)* s = (scd.isSharedStaticCtorDeclaration() ? "shared " : ""); - deprecation(scd.loc, "`%sstatic` constructor can only be of D linkage", s); + const(char)* s = isShared ? "shared " : ""; + deprecation(sd.loc, "`%sstatic` %s can only be of D linkage", s, what); // Just correct it sc.linkage = LINK.d; } - funcDeclarationSemantic(sc, scd); + funcDeclarationSemantic(sc, sd); sc.linkage = save; // We're going to need ModuleInfo - Module m = scd.getModule(); + Module m = sd.getModule(); if (!m) m = sc._module; if (m) { m.needmoduleinfo = 1; - //printf("module1 %s needs moduleinfo\n", m.toChars()); + //printf("module2 %s needs moduleinfo\n", m.toChars()); } + } + override void visit(StaticCtorDeclaration scd) + { + //printf("StaticCtorDeclaration::semantic()\n"); + visitStaticCDtorDeclaration(scd, false); foreachUda(scd, sc, (Expression e) { import dmd.attrib : isEnumAttribute; @@ -2696,100 +2728,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor override void visit(StaticDtorDeclaration sdd) { - if (sdd.semanticRun >= PASS.semanticdone) - return; - if (sdd._scope) - { - sc = sdd._scope; - sdd._scope = null; - } - - sdd.parent = sc.parent; - Dsymbol p = sdd.parent.pastMixin(); - if (!p.isScopeDsymbol()) - { - const(char)* s = (sdd.isSharedStaticDtorDeclaration() ? "shared " : ""); - error(sdd.loc, "`%sstatic` destructor can only be member of module/aggregate/template, not %s `%s`", s, p.kind(), p.toChars()); - sdd.type = Type.terror; - sdd.errors = true; - return; - } - if (!sdd.type) - sdd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, sdd.storage_class); - - /* If the static ctor appears within a template instantiation, - * it could get called multiple times by the module constructors - * for different modules. Thus, protect it with a gate. - */ - if (sdd.isInstantiated() && sdd.semanticRun < PASS.semantic) - { - /* Add this prefix to the constructor: - * ``` - * static int gate; - * if (--gate != 0) return; - * ``` - * or, for shared constructor: - * ``` - * shared int gate; - * if (core.atomic.atomicOp!"-="(gate, 1) != 0) return; - * ``` - */ - const bool isShared = !!sdd.isSharedStaticDtorDeclaration(); - auto v = new VarDeclaration(Loc.initial, Type.tint32, Id.gate, null); - v.storage_class = STC.temp | STC.static_ | (isShared ? STC.shared_ : 0); - - auto sa = new Statements(); - Statement s = new ExpStatement(Loc.initial, v); - sa.push(s); - - Expression e; - if (isShared) - { - e = doAtomicOp("-=", v.ident, IntegerExp.literal!(1)); - if (e is null) - { - .error(sdd.loc, "%s `%s` shared static destructo within a template require `core.atomic : atomicOp` to be present", sdd.kind, sdd.toPrettyChars); - return; - } - } - else - { - e = new AddAssignExp( - Loc.initial, new IdentifierExp(Loc.initial, v.ident), IntegerExp.literal!(-1)); - } - - e = new EqualExp(EXP.notEqual, Loc.initial, e, IntegerExp.literal!0); - s = new IfStatement(Loc.initial, null, e, new ReturnStatement(Loc.initial, null), null, Loc.initial); - - sa.push(s); - if (sdd.fbody) - sa.push(sdd.fbody); - - sdd.fbody = new CompoundStatement(Loc.initial, sa); - - sdd.vgate = v; - } - - const LINK save = sc.linkage; - if (save != LINK.d) - { - const(char)* s = (sdd.isSharedStaticDtorDeclaration() ? "shared " : ""); - deprecation(sdd.loc, "`%sstatic` destructor can only be of D linkage", s); - // Just correct it - sc.linkage = LINK.d; - } - funcDeclarationSemantic(sc, sdd); - sc.linkage = save; - - // We're going to need ModuleInfo - Module m = sdd.getModule(); - if (!m) - m = sc._module; - if (m) - { - m.needmoduleinfo = 1; - //printf("module2 %s needs moduleinfo\n", m.toChars()); - } + visitStaticCDtorDeclaration(sdd, true); } override void visit(InvariantDeclaration invd) diff --git a/gcc/d/dmd/dtemplate.d b/gcc/d/dmd/dtemplate.d index f812231..fe60120 100644 --- a/gcc/d/dmd/dtemplate.d +++ b/gcc/d/dmd/dtemplate.d @@ -1643,60 +1643,64 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, ref TemplateParameters pa override void visit(TypeSArray t) { // Extra check that array dimensions must match - if (tparam) + if (!tparam) { - if (tparam.ty == Tarray) - { - MATCH m = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm); - result = (m >= MATCH.constant) ? MATCH.convert : MATCH.nomatch; - return; - } + visit(cast(Type)t); + return; + } - TemplateParameter tp = null; - Expression edim = null; - size_t i; - if (auto tsa = tparam.isTypeSArray()) + if (tparam.ty == Tarray) + { + MATCH m = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm); + result = (m >= MATCH.constant) ? MATCH.convert : MATCH.nomatch; + return; + } + + TemplateParameter tp = null; + Expression edim = null; + size_t i; + if (auto tsa = tparam.isTypeSArray()) + { + if (tsa.dim.isVarExp() && tsa.dim.isVarExp().var.storage_class & STC.templateparameter) { - if (tsa.dim.isVarExp() && tsa.dim.isVarExp().var.storage_class & STC.templateparameter) - { - Identifier id = tsa.dim.isVarExp().var.ident; - i = templateIdentifierLookup(id, ¶meters); - assert(i != IDX_NOTFOUND); - tp = parameters[i]; - } - else - edim = tsa.dim; + Identifier id = tsa.dim.isVarExp().var.ident; + i = templateIdentifierLookup(id, ¶meters); + assert(i != IDX_NOTFOUND); + tp = parameters[i]; } - else if (auto taa = tparam.isTypeAArray()) + else + edim = tsa.dim; + } + else if (auto taa = tparam.isTypeAArray()) + { + i = templateParameterLookup(taa.index, ¶meters); + if (i != IDX_NOTFOUND) + tp = parameters[i]; + else { - i = templateParameterLookup(taa.index, ¶meters); - if (i != IDX_NOTFOUND) - tp = parameters[i]; - else + Loc loc; + // The "type" (it hasn't been resolved yet) of the function parameter + // does not have a location but the parameter it is related to does, + // so we use that for the resolution (better error message). + if (inferStart < parameters.length) { - Loc loc; - // The "type" (it hasn't been resolved yet) of the function parameter - // does not have a location but the parameter it is related to does, - // so we use that for the resolution (better error message). - if (inferStart < parameters.length) - { - TemplateParameter loctp = parameters[inferStart]; - loc = loctp.loc; - } - - Expression e; - Type tx; - Dsymbol s; - taa.index.resolve(loc, sc, e, tx, s); - edim = s ? getValue(s) : getValue(e); + TemplateParameter loctp = parameters[inferStart]; + loc = loctp.loc; } + + Expression e; + Type tx; + Dsymbol s; + taa.index.resolve(loc, sc, e, tx, s); + edim = s ? getValue(s) : getValue(e); } - if (tp && tp.matchArg(sc, t.dim, i, ¶meters, dedtypes, null) || edim && edim.toInteger() == t.dim.toInteger()) - { - result = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm); - return; - } } + if (tp && tp.matchArg(sc, t.dim, i, ¶meters, dedtypes, null) || edim && edim.toInteger() == t.dim.toInteger()) + { + result = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm); + return; + } + visit(cast(Type)t); } @@ -1721,132 +1725,137 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, ref TemplateParameters pa if (!tparam) return visit(cast(Type)t); - if (auto tp = tparam.isTypeFunction()) + auto tp = tparam.isTypeFunction(); + if (!tp) + { + visit(cast(Type)t); + return; + } + + if (t.parameterList.varargs != tp.parameterList.varargs || t.linkage != tp.linkage) { - if (t.parameterList.varargs != tp.parameterList.varargs || t.linkage != tp.linkage) - { - result = MATCH.nomatch; - return; - } + result = MATCH.nomatch; + return; + } + + foreach (fparam; *tp.parameterList.parameters) + { + // https://issues.dlang.org/show_bug.cgi?id=2579 + // Apply function parameter storage classes to parameter types + fparam.type = fparam.type.addStorageClass(fparam.storageClass); + fparam.storageClass &= ~STC.TYPECTOR; - foreach (fparam; *tp.parameterList.parameters) + // https://issues.dlang.org/show_bug.cgi?id=15243 + // Resolve parameter type if it's not related with template parameters + if (!reliesOnTemplateParameters(fparam.type, parameters[inferStart .. parameters.length])) { - // https://issues.dlang.org/show_bug.cgi?id=2579 - // Apply function parameter storage classes to parameter types - fparam.type = fparam.type.addStorageClass(fparam.storageClass); - fparam.storageClass &= ~STC.TYPECTOR; - - // https://issues.dlang.org/show_bug.cgi?id=15243 - // Resolve parameter type if it's not related with template parameters - if (!reliesOnTemplateParameters(fparam.type, parameters[inferStart .. parameters.length])) + auto tx = fparam.type.typeSemantic(Loc.initial, sc); + if (tx.ty == Terror) { - auto tx = fparam.type.typeSemantic(Loc.initial, sc); - if (tx.ty == Terror) - { - result = MATCH.nomatch; - return; - } - fparam.type = tx; + result = MATCH.nomatch; + return; } + fparam.type = tx; } + } + + const size_t nfargs = t.parameterList.length; + size_t nfparams = tp.parameterList.length; - const size_t nfargs = t.parameterList.length; - size_t nfparams = tp.parameterList.length; + /* See if tuple match + */ + if (nfparams > 0 && nfargs >= nfparams - 1) + { + /* See if 'A' of the template parameter matches 'A' + * of the type of the last function parameter. + */ + Parameter fparam = tp.parameterList[nfparams - 1]; + assert(fparam); + assert(fparam.type); + if (fparam.type.ty != Tident) + goto L1; + TypeIdentifier tid = fparam.type.isTypeIdentifier(); + if (tid.idents.length) + goto L1; - /* See if tuple match + /* Look through parameters to find tuple matching tid.ident */ - if (nfparams > 0 && nfargs >= nfparams - 1) + size_t tupi = 0; + for (; 1; tupi++) { - /* See if 'A' of the template parameter matches 'A' - * of the type of the last function parameter. - */ - Parameter fparam = tp.parameterList[nfparams - 1]; - assert(fparam); - assert(fparam.type); - if (fparam.type.ty != Tident) - goto L1; - TypeIdentifier tid = fparam.type.isTypeIdentifier(); - if (tid.idents.length) + if (tupi == parameters.length) goto L1; + TemplateParameter tx = parameters[tupi]; + TemplateTupleParameter tup = tx.isTemplateTupleParameter(); + if (tup && tup.ident.equals(tid.ident)) + break; + } - /* Look through parameters to find tuple matching tid.ident - */ - size_t tupi = 0; - for (; 1; tupi++) + /* The types of the function arguments [nfparams - 1 .. nfargs] + * now form the tuple argument. + */ + size_t tuple_dim = nfargs - (nfparams - 1); + + /* See if existing tuple, and whether it matches or not + */ + RootObject o = dedtypes[tupi]; + if (o) + { + // Existing deduced argument must be a tuple, and must match + Tuple tup = isTuple(o); + if (!tup || tup.objects.length != tuple_dim) { - if (tupi == parameters.length) - goto L1; - TemplateParameter tx = parameters[tupi]; - TemplateTupleParameter tup = tx.isTemplateTupleParameter(); - if (tup && tup.ident.equals(tid.ident)) - break; + result = MATCH.nomatch; + return; } - - /* The types of the function arguments [nfparams - 1 .. nfargs] - * now form the tuple argument. - */ - size_t tuple_dim = nfargs - (nfparams - 1); - - /* See if existing tuple, and whether it matches or not - */ - RootObject o = dedtypes[tupi]; - if (o) + for (size_t i = 0; i < tuple_dim; i++) { - // Existing deduced argument must be a tuple, and must match - Tuple tup = isTuple(o); - if (!tup || tup.objects.length != tuple_dim) + Parameter arg = t.parameterList[nfparams - 1 + i]; + if (!arg.type.equals(tup.objects[i])) { result = MATCH.nomatch; return; } - for (size_t i = 0; i < tuple_dim; i++) - { - Parameter arg = t.parameterList[nfparams - 1 + i]; - if (!arg.type.equals(tup.objects[i])) - { - result = MATCH.nomatch; - return; - } - } } - else + } + else + { + // Create new tuple + auto tup = new Tuple(tuple_dim); + for (size_t i = 0; i < tuple_dim; i++) { - // Create new tuple - auto tup = new Tuple(tuple_dim); - for (size_t i = 0; i < tuple_dim; i++) - { - Parameter arg = t.parameterList[nfparams - 1 + i]; - tup.objects[i] = arg.type; - } - dedtypes[tupi] = tup; + Parameter arg = t.parameterList[nfparams - 1 + i]; + tup.objects[i] = arg.type; } - nfparams--; // don't consider the last parameter for type deduction - goto L2; + dedtypes[tupi] = tup; } + nfparams--; // don't consider the last parameter for type deduction + goto L2; + } + + L1: + if (nfargs != nfparams) + { + result = MATCH.nomatch; + return; + } + L2: + assert(nfparams <= tp.parameterList.length); + foreach (i, ap; tp.parameterList) + { + if (i == nfparams) + break; - L1: - if (nfargs != nfparams) + Parameter a = t.parameterList[i]; + + if (!a.isCovariant(t.isRef, ap) || + !deduceType(a.type, sc, ap.type, parameters, dedtypes)) { result = MATCH.nomatch; return; } - L2: - assert(nfparams <= tp.parameterList.length); - foreach (i, ap; tp.parameterList) - { - if (i == nfparams) - break; - - Parameter a = t.parameterList[i]; - - if (!a.isCovariant(t.isRef, ap) || - !deduceType(a.type, sc, ap.type, parameters, dedtypes)) - { - result = MATCH.nomatch; - return; - } - } } + visit(cast(Type)t); } @@ -1873,78 +1882,82 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, ref TemplateParameters pa override void visit(TypeInstance t) { // Extra check - if (tparam && tparam.ty == Tinstance && t.tempinst.tempdecl) + if (!tparam || tparam.ty != Tinstance || !t.tempinst.tempdecl) { - TemplateDeclaration tempdecl = t.tempinst.tempdecl.isTemplateDeclaration(); - assert(tempdecl); + visit(cast(Type)t); + return; + } - TypeInstance tp = tparam.isTypeInstance(); + TemplateDeclaration tempdecl = t.tempinst.tempdecl.isTemplateDeclaration(); + assert(tempdecl); - //printf("tempinst.tempdecl = %p\n", tempdecl); - //printf("tp.tempinst.tempdecl = %p\n", tp.tempinst.tempdecl); - if (!tp.tempinst.tempdecl) - { - //printf("tp.tempinst.name = '%s'\n", tp.tempinst.name.toChars()); + TypeInstance tp = tparam.isTypeInstance(); + + //printf("tempinst.tempdecl = %p\n", tempdecl); + //printf("tp.tempinst.tempdecl = %p\n", tp.tempinst.tempdecl); + if (!tp.tempinst.tempdecl) + { + //printf("tp.tempinst.name = '%s'\n", tp.tempinst.name.toChars()); - /* Handle case of: - * template Foo(T : sa!(T), alias sa) + /* Handle case of: + * template Foo(T : sa!(T), alias sa) + */ + size_t i = templateIdentifierLookup(tp.tempinst.name, ¶meters); + if (i == IDX_NOTFOUND) + { + /* Didn't find it as a parameter identifier. Try looking + * it up and seeing if is an alias. + * https://issues.dlang.org/show_bug.cgi?id=1454 */ - size_t i = templateIdentifierLookup(tp.tempinst.name, ¶meters); - if (i == IDX_NOTFOUND) + auto tid = new TypeIdentifier(tp.loc, tp.tempinst.name); + Type tx; + Expression e; + Dsymbol s; + tid.resolve(tp.loc, sc, e, tx, s); + if (tx) { - /* Didn't find it as a parameter identifier. Try looking - * it up and seeing if is an alias. - * https://issues.dlang.org/show_bug.cgi?id=1454 - */ - auto tid = new TypeIdentifier(tp.loc, tp.tempinst.name); - Type tx; - Expression e; - Dsymbol s; - tid.resolve(tp.loc, sc, e, tx, s); - if (tx) + s = tx.toDsymbol(sc); + if (TemplateInstance ti = s ? s.parent.isTemplateInstance() : null) { - s = tx.toDsymbol(sc); - if (TemplateInstance ti = s ? s.parent.isTemplateInstance() : null) - { - // https://issues.dlang.org/show_bug.cgi?id=14290 - // Try to match with ti.tempecl, - // only when ti is an enclosing instance. - Dsymbol p = sc.parent; - while (p && p != ti) - p = p.parent; - if (p) - s = ti.tempdecl; - } + // https://issues.dlang.org/show_bug.cgi?id=14290 + // Try to match with ti.tempecl, + // only when ti is an enclosing instance. + Dsymbol p = sc.parent; + while (p && p != ti) + p = p.parent; + if (p) + s = ti.tempdecl; } - if (s) + } + if (s) + { + s = s.toAlias(); + TemplateDeclaration td = s.isTemplateDeclaration(); + if (td) { - s = s.toAlias(); - TemplateDeclaration td = s.isTemplateDeclaration(); - if (td) + if (td.overroot) + td = td.overroot; + for (; td; td = td.overnext) { - if (td.overroot) - td = td.overroot; - for (; td; td = td.overnext) - { - if (td == tempdecl) - goto L2; - } + if (td == tempdecl) + goto L2; } } - goto Lnomatch; } - - TemplateParameter tpx = parameters[i]; - if (!tpx.matchArg(sc, tempdecl, i, ¶meters, dedtypes, null)) - goto Lnomatch; - } - else if (tempdecl != tp.tempinst.tempdecl) goto Lnomatch; + } - L2: - if (!resolveTemplateInstantiation(sc, ¶meters, t.tempinst.tiargs, &t.tempinst.tdtypes, tempdecl, tp, &dedtypes)) + TemplateParameter tpx = parameters[i]; + if (!tpx.matchArg(sc, tempdecl, i, ¶meters, dedtypes, null)) goto Lnomatch; } + else if (tempdecl != tp.tempinst.tempdecl) + goto Lnomatch; + + L2: + if (!resolveTemplateInstantiation(sc, ¶meters, t.tempinst.tiargs, &t.tempinst.tdtypes, tempdecl, tp, &dedtypes)) + goto Lnomatch; + visit(cast(Type)t); return; diff --git a/gcc/d/dmd/escape.d b/gcc/d/dmd/escape.d index 7feeb27..fb3fb9b 100644 --- a/gcc/d/dmd/escape.d +++ b/gcc/d/dmd/escape.d @@ -1305,10 +1305,9 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g /* Check for returning a ref variable by 'ref', but should be 'return ref' * Infer the addition of 'return', or set result to be the offending expression. */ - if ((vsr == ScopeRef.Ref || + if (vsr == ScopeRef.Ref || vsr == ScopeRef.RefScope || - vsr == ScopeRef.Ref_ReturnScope) && - !(v.storage_class & STC.foreach_)) + vsr == ScopeRef.Ref_ReturnScope) { if (p == sc.func && (vsr == ScopeRef.Ref || vsr == ScopeRef.RefScope) && inferReturn(sc.func, v, /*returnScope:*/ false)) diff --git a/gcc/d/dmd/expression.d b/gcc/d/dmd/expression.d index 7b90c0e..a72df72 100644 --- a/gcc/d/dmd/expression.d +++ b/gcc/d/dmd/expression.d @@ -2230,13 +2230,6 @@ extern (C++) final class AssocArrayLiteralExp : Expression } } -enum stageScrub = 0x1; /// scrubReturnValue is running -enum stageSearchPointers = 0x2; /// hasNonConstPointers is running -enum stageOptimize = 0x4; /// optimize is running -enum stageApply = 0x8; /// apply is running -enum stageInlineScan = 0x10; /// inlineScan is running -enum stageToCBuffer = 0x20; /// toCBuffer is running - /*********************************************************** * sd( e1, e2, e3, ... ) */ @@ -2269,7 +2262,17 @@ extern (C++) final class StructLiteralExp : Expression * 'inlinecopy' uses similar 'stageflags' and from multiple evaluation 'doInline' * (with infinite recursion) of this expression. */ - ubyte stageflags; + enum StageFlags : ubyte + { + none = 0x0, + scrub = 0x1, /// scrubReturnValue is running + searchPointers = 0x2, /// hasNonConstPointers is running + optimize = 0x4, /// optimize is running + apply = 0x8, /// apply is running + inlineScan = 0x10, /// inlineScan is running + toCBuffer = 0x20 /// toCBuffer is running + } + StageFlags stageflags; bool useStaticInit; /// if this is true, use the StructDeclaration's init symbol bool isOriginal = false; /// used when moving instances to indicate `this is this.origin` @@ -3028,28 +3031,6 @@ extern (C++) abstract class UnaExp : Expression return e; } - /******************************** - * The type for a unary expression is incompatible. - * Print error message. - * Returns: - * ErrorExp - */ - extern (D) final Expression incompatibleTypes() - { - if (e1.type.toBasetype() == Type.terror) - return e1; - - if (e1.op == EXP.type) - { - error(loc, "incompatible type for `%s(%s)`: cannot use `%s` with types", EXPtoString(op).ptr, e1.toChars(), EXPtoString(op).ptr); - } - else - { - error(loc, "incompatible type for `%s(%s)`: `%s`", EXPtoString(op).ptr, e1.toChars(), e1.type.toChars()); - } - return ErrorExp.get(); - } - /********************* * Mark the operand as will never be dereferenced, * which is useful info for @safe checks. @@ -3094,40 +3075,6 @@ extern (C++) abstract class BinExp : Expression return e; } - /******************************** - * The types for a binary expression are incompatible. - * Print error message. - * Returns: - * ErrorExp - */ - extern (D) final Expression incompatibleTypes() - { - if (e1.type.toBasetype() == Type.terror) - return e1; - if (e2.type.toBasetype() == Type.terror) - return e2; - - // CondExp uses 'a ? b : c' but we're comparing 'b : c' - const(char)* thisOp = (op == EXP.question) ? ":" : EXPtoString(op).ptr; - if (e1.op == EXP.type || e2.op == EXP.type) - { - error(loc, "incompatible types for `(%s) %s (%s)`: cannot use `%s` with types", - e1.toChars(), thisOp, e2.toChars(), EXPtoString(op).ptr); - } - else if (e1.type.equals(e2.type)) - { - error(loc, "incompatible types for `(%s) %s (%s)`: both operands are of type `%s`", - e1.toChars(), thisOp, e2.toChars(), e1.type.toChars()); - } - else - { - auto ts = toAutoQualChars(e1.type, e2.type); - error(loc, "incompatible types for `(%s) %s (%s)`: `%s` and `%s`", - e1.toChars(), thisOp, e2.toChars(), ts[0], ts[1]); - } - return ErrorExp.get(); - } - extern (D) final bool checkIntegralBin() { bool r1 = e1.checkIntegral(); diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d index 6e4b5a5..5733698 100644 --- a/gcc/d/dmd/expressionsem.d +++ b/gcc/d/dmd/expressionsem.d @@ -63,12 +63,12 @@ import dmd.location; import dmd.mtype; import dmd.mustuse; import dmd.nspace; +import dmd.nogc; import dmd.objc; import dmd.opover; import dmd.optimize; import dmd.parse; import dmd.printast; -import dmd.postordervisitor; import dmd.root.array; import dmd.root.ctfloat; import dmd.root.filename; @@ -88,6 +88,7 @@ import dmd.typinf; import dmd.utils; import dmd.utils : arrayCastBigEndian; import dmd.visitor; +import dmd.visitor.postorder; enum LOGSEMANTIC = false; @@ -339,6 +340,61 @@ StringExp toUTF8(StringExp se, Scope* sc) } return se; } +/******************************** + * The type for a unary expression is incompatible. + * Print error message. + * Returns: + * ErrorExp + */ +extern (D) Expression incompatibleTypes(UnaExp e) +{ + if (e.e1.type.toBasetype() == Type.terror) + return e.e1; + + if (e.e1.op == EXP.type) + { + error(e.loc, "incompatible type for `%s(%s)`: cannot use `%s` with types", EXPtoString(e.op).ptr, e.e1.toChars(), EXPtoString(e.op).ptr); + } + else + { + error(e.loc, "incompatible type for `%s(%s)`: `%s`", EXPtoString(e.op).ptr, e.e1.toChars(), e.e1.type.toChars()); + } + return ErrorExp.get(); +} + +/******************************** + * The types for a binary expression are incompatible. + * Print error message. + * Returns: + * ErrorExp + */ +extern (D) Expression incompatibleTypes(BinExp e) +{ + if (e.e1.type.toBasetype() == Type.terror) + return e.e1; + if (e.e2.type.toBasetype() == Type.terror) + return e.e2; + + // CondExp uses 'a ? b : c' but we're comparing 'b : c' + const(char)* thisOp = (e.op == EXP.question) ? ":" : EXPtoString(e.op).ptr; + if (e.e1.op == EXP.type || e.e2.op == EXP.type) + { + error(e.loc, "incompatible types for `(%s) %s (%s)`: cannot use `%s` with types", + e.e1.toChars(), thisOp, e.e2.toChars(), EXPtoString(e.op).ptr); + } + else if (e.e1.type.equals(e.e2.type)) + { + error(e.loc, "incompatible types for `(%s) %s (%s)`: both operands are of type `%s`", + e.e1.toChars(), thisOp, e.e2.toChars(), e.e1.type.toChars()); + } + else + { + auto ts = toAutoQualChars(e.e1.type, e.e2.type); + error(e.loc, "incompatible types for `(%s) %s (%s)`: `%s` and `%s`", + e.e1.toChars(), thisOp, e.e2.toChars(), ts[0], ts[1]); + } + return ErrorExp.get(); +} private Expression reorderSettingAAElem(BinExp exp, Scope* sc) { @@ -577,25 +633,21 @@ TupleDeclaration isAliasThisTuple(Expression e) Type t = e.type.toBasetype(); while (true) { - if (Dsymbol s = t.toDsymbol(null)) + Dsymbol s = t.toDsymbol(null); + if (!s) + return null; + auto ad = s.isAggregateDeclaration(); + if (!ad) + return null; + s = ad.aliasthis ? ad.aliasthis.sym : null; + if (s && s.isVarDeclaration()) { - if (auto ad = s.isAggregateDeclaration()) - { - s = ad.aliasthis ? ad.aliasthis.sym : null; - if (s && s.isVarDeclaration()) - { - TupleDeclaration td = s.isVarDeclaration().toAlias().isTupleDeclaration(); - if (td && td.isexp) - return td; - } - if (Type att = t.aliasthisOf()) - { - t = att; - continue; - } - } + TupleDeclaration td = s.isVarDeclaration().toAlias().isTupleDeclaration(); + if (td && td.isexp) + return td; } - return null; + if (Type att = t.aliasthisOf()) + t = att; } } @@ -610,6 +662,13 @@ Expression resolveOpDollar(Scope* sc, ArrayExp ae, Expression* pe0) AggregateDeclaration ad = isAggregate(ae.e1.type); Dsymbol slice = search_function(ad, Id.slice); //printf("slice = %s %s\n", slice.kind(), slice.toChars()); + Expression fallback() + { + if (ae.arguments.length == 1) + return null; + error(ae.loc, "multi-dimensional slicing requires template `opSlice`"); + return ErrorExp.get(); + } foreach (i, e; *ae.arguments) { if (i == 0) @@ -617,11 +676,7 @@ Expression resolveOpDollar(Scope* sc, ArrayExp ae, Expression* pe0) if (e.op == EXP.interval && !(slice && slice.isTemplateDeclaration())) { - Lfallback: - if (ae.arguments.length == 1) - return null; - error(ae.loc, "multi-dimensional slicing requires template `opSlice`"); - return ErrorExp.get(); + return fallback(); } //printf("[%d] e = %s\n", i, e.toChars()); @@ -661,7 +716,7 @@ Expression resolveOpDollar(Scope* sc, ArrayExp ae, Expression* pe0) sc = sc.pop(); global.endGagging(xerrors); if (!fslice) - goto Lfallback; + return fallback(); e = new DotTemplateInstanceExp(ae.loc, ae.e1, slice.ident, tiargs); e = new CallExp(ae.loc, e, fargs); @@ -787,40 +842,40 @@ extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t = null) */ private Expression callCpCtor(Scope* sc, Expression e, Type destinationType) { - if (auto ts = e.type.baseElemOf().isTypeStruct()) - { - StructDeclaration sd = ts.sym; - if (sd.postblit || sd.hasCopyCtor) - { - /* Create a variable tmp, and replace the argument e with: - * (tmp = e),tmp - * and let AssignExp() handle the construction. - * This is not the most efficient, ideally tmp would be constructed - * directly onto the stack. - */ - auto tmp = copyToTemp(STC.rvalue, "__copytmp", e); - if (sd.hasCopyCtor && destinationType) - { - // https://issues.dlang.org/show_bug.cgi?id=22619 - // If the destination type is inout we can preserve it - // only if inside an inout function; if we are not inside - // an inout function, then we will preserve the type of - // the source - if (destinationType.hasWild && !(sc.func.storage_class & STC.wild)) - tmp.type = e.type; - else - tmp.type = destinationType; - } - tmp.storage_class |= STC.nodtor; - tmp.dsymbolSemantic(sc); - Expression de = new DeclarationExp(e.loc, tmp); - Expression ve = new VarExp(e.loc, tmp); - de.type = Type.tvoid; - ve.type = e.type; - return Expression.combine(de, ve); - } - } - return e; + auto ts = e.type.baseElemOf().isTypeStruct(); + + if (!ts) + return e; + StructDeclaration sd = ts.sym; + if (!sd.postblit && !sd.hasCopyCtor) + return e; + + /* Create a variable tmp, and replace the argument e with: + * (tmp = e),tmp + * and let AssignExp() handle the construction. + * This is not the most efficient, ideally tmp would be constructed + * directly onto the stack. + */ + auto tmp = copyToTemp(STC.rvalue, "__copytmp", e); + if (sd.hasCopyCtor && destinationType) + { + // https://issues.dlang.org/show_bug.cgi?id=22619 + // If the destination type is inout we can preserve it + // only if inside an inout function; if we are not inside + // an inout function, then we will preserve the type of + // the source + if (destinationType.hasWild && !(sc.func.storage_class & STC.wild)) + tmp.type = e.type; + else + tmp.type = destinationType; + } + tmp.storage_class |= STC.nodtor; + tmp.dsymbolSemantic(sc); + Expression de = new DeclarationExp(e.loc, tmp); + Expression ve = new VarExp(e.loc, tmp); + de.type = Type.tvoid; + ve.type = e.type; + return Expression.combine(de, ve); } /************************************************ @@ -1033,41 +1088,41 @@ private void hookDtors(CondExp ce, Scope* sc) override void visit(DeclarationExp e) { auto v = e.declaration.isVarDeclaration(); - if (v && !v.isDataseg()) + if (!v || v.isDataseg()) + return; + + if (v._init) { - if (v._init) - { - if (auto ei = v._init.isExpInitializer()) - walkPostorder(ei.exp, this); - } + if (auto ei = v._init.isExpInitializer()) + walkPostorder(ei.exp, this); + } - if (v.edtor) - walkPostorder(v.edtor, this); + if (v.edtor) + walkPostorder(v.edtor, this); - if (v.needsScopeDtor()) - { - if (!vcond) - { - vcond = copyToTemp(STC.volatile_ | STC.const_, "__cond", ce.econd); - vcond.dsymbolSemantic(sc); + if (!v.needsScopeDtor()) + return; - Expression de = new DeclarationExp(ce.econd.loc, vcond); - de = de.expressionSemantic(sc); + if (!vcond) + { + vcond = copyToTemp(STC.volatile_ | STC.const_, "__cond", ce.econd); + vcond.dsymbolSemantic(sc); - Expression ve = new VarExp(ce.econd.loc, vcond); - ce.econd = Expression.combine(de, ve); - } + Expression de = new DeclarationExp(ce.econd.loc, vcond); + de = de.expressionSemantic(sc); - //printf("\t++v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars()); - Expression ve = new VarExp(vcond.loc, vcond); - if (isThen) - v.edtor = new LogicalExp(v.edtor.loc, EXP.andAnd, ve, v.edtor); - else - v.edtor = new LogicalExp(v.edtor.loc, EXP.orOr, ve, v.edtor); - v.edtor = v.edtor.expressionSemantic(sc); - //printf("\t--v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars()); - } + Expression ve = new VarExp(ce.econd.loc, vcond); + ce.econd = Expression.combine(de, ve); } + + //printf("\t++v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars()); + Expression ve = new VarExp(vcond.loc, vcond); + if (isThen) + v.edtor = new LogicalExp(v.edtor.loc, EXP.andAnd, ve, v.edtor); + else + v.edtor = new LogicalExp(v.edtor.loc, EXP.orOr, ve, v.edtor); + v.edtor = v.edtor.expressionSemantic(sc); + //printf("\t--v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars()); } } @@ -2188,31 +2243,32 @@ private bool checkNogc(FuncDeclaration f, ref Loc loc, Scope* sc) if (f.ident == Id._d_newitemT || f.ident == Id._d_newarrayT || f.ident == Id._d_newarraymTX) return false; - if (!f.isNogc()) + if (f.isNogc()) + return false; + + if (isRootTraitsCompilesScope(sc) ? sc.func.isNogcBypassingInference() : sc.func.setGCCall(f)) { - if (isRootTraitsCompilesScope(sc) ? sc.func.isNogcBypassingInference() : sc.func.setGCCall(f)) - { - if (loc.linnum == 0) // e.g. implicitly generated dtor - loc = sc.func.loc; + if (loc.linnum == 0) // e.g. implicitly generated dtor + loc = sc.func.loc; - // Lowered non-@nogc'd hooks will print their own error message inside of nogc.d (NOGCVisitor.visit(CallExp e)), - // so don't print anything to avoid double error messages. - if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT - || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX - || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT)) - { - error(loc, "`@nogc` %s `%s` cannot call non-@nogc %s `%s`", - sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars()); + // Lowered non-@nogc'd hooks will print their own error message inside of nogc.d (NOGCVisitor.visit(CallExp e)), + // so don't print anything to avoid double error messages. + if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT + || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX + || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT)) + { + error(loc, "`@nogc` %s `%s` cannot call non-@nogc %s `%s`", + sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars()); - if (!f.isDtorDeclaration) - f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc); - } + if (!f.isDtorDeclaration) + f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc); + } - f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().isNogc, "non-@nogc"); + f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().isNogc, "non-@nogc"); - return true; - } + return true; } + return false; } @@ -2222,30 +2278,31 @@ private bool checkNogc(FuncDeclaration f, ref Loc loc, Scope* sc) */ private bool checkPostblit(Type t, ref Loc loc, Scope* sc) { - if (auto ts = t.baseElemOf().isTypeStruct()) + auto ts = t.baseElemOf().isTypeStruct(); + if (!ts) + return false; + + if (global.params.useTypeInfo && Type.dtypeinfo) { - if (global.params.useTypeInfo && Type.dtypeinfo) - { - // https://issues.dlang.org/show_bug.cgi?id=11395 - // Require TypeInfo generation for array concatenation - semanticTypeInfo(sc, t); - } + // https://issues.dlang.org/show_bug.cgi?id=11395 + // Require TypeInfo generation for array concatenation + semanticTypeInfo(sc, t); + } - StructDeclaration sd = ts.sym; - if (sd.postblit) - { - if (sd.postblit.checkDisabled(loc, sc)) - return true; + StructDeclaration sd = ts.sym; + if (!sd.postblit) + return false; - //checkDeprecated(sc, sd.postblit); // necessary? - sd.postblit.checkPurity(loc, sc); - sd.postblit.checkSafety(loc, sc); - sd.postblit.checkNogc(loc, sc); - //checkAccess(sd, loc, sc, sd.postblit); // necessary? - return false; - } - } + if (sd.postblit.checkDisabled(loc, sc)) + return true; + + //checkDeprecated(sc, sd.postblit); // necessary? + sd.postblit.checkPurity(loc, sc); + sd.postblit.checkSafety(loc, sc); + sd.postblit.checkNogc(loc, sc); + //checkAccess(sd, loc, sc, sd.postblit); // necessary? return false; + } /*************************************** @@ -2741,48 +2798,49 @@ private Expression rewriteOpAssign(BinExp exp) private bool preFunctionParameters(Scope* sc, ArgumentList argumentList, const bool reportErrors = true) { Expressions* exps = argumentList.arguments; + if (!exps) + return false; + + expandTuples(exps, argumentList.names); + bool err = false; - if (exps) + for (size_t i = 0; i < exps.length; i++) { - expandTuples(exps, argumentList.names); - - for (size_t i = 0; i < exps.length; i++) + Expression arg = (*exps)[i]; + arg = resolveProperties(sc, arg); + arg = arg.arrayFuncConv(sc); + if (arg.op == EXP.type) { - Expression arg = (*exps)[i]; - arg = resolveProperties(sc, arg); - arg = arg.arrayFuncConv(sc); - if (arg.op == EXP.type) - { - // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684 - arg = resolveAliasThis(sc, arg); + // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684 + arg = resolveAliasThis(sc, arg); - if (arg.op == EXP.type) - { - if (reportErrors) - { - error(arg.loc, "cannot pass type `%s` as a function argument", arg.toChars()); - arg = ErrorExp.get(); - } - err = true; - } - } - else if (arg.type.toBasetype().ty == Tfunction) + if (arg.op == EXP.type) { if (reportErrors) { - error(arg.loc, "cannot pass function `%s` as a function argument", arg.toChars()); + error(arg.loc, "cannot pass type `%s` as a function argument", arg.toChars()); arg = ErrorExp.get(); } err = true; } - else if (checkNonAssignmentArrayOp(arg)) + } + else if (arg.type.toBasetype().ty == Tfunction) + { + if (reportErrors) { + error(arg.loc, "cannot pass function `%s` as a function argument", arg.toChars()); arg = ErrorExp.get(); - err = true; } - (*exps)[i] = arg; + err = true; } + else if (checkNonAssignmentArrayOp(arg)) + { + arg = ErrorExp.get(); + err = true; + } + (*exps)[i] = arg; } + return err; } @@ -2907,143 +2965,144 @@ private bool functionParameters(const ref Loc loc, Scope* sc, { Expression arg = (i < nargs) ? (*arguments)[i] : null; - if (i < nparams) + if (i >= nparams) + break; + + bool errorArgs() { - bool errorArgs() - { - error(loc, "expected %llu function arguments, not %llu", cast(ulong)nparams, cast(ulong)nargs); - return true; - } + error(loc, "expected %llu function arguments, not %llu", cast(ulong)nparams, cast(ulong)nargs); + return true; + } - Parameter p = tf.parameterList[i]; + Parameter p = tf.parameterList[i]; - if (!arg) + if (!arg) + { + if (!p.defaultArg) { - if (!p.defaultArg) - { - if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nparams) - goto L2; - return errorArgs(); - } - arg = p.defaultArg; - if (!arg.type) - arg = arg.expressionSemantic(sc); - arg = inlineCopy(arg, sc); - // __FILE__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__ - arg = arg.resolveLoc(loc, sc); - if (i >= nargs) - { - arguments.push(arg); - nargs++; - } - else - (*arguments)[i] = arg; + if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nparams) + goto L2; + return errorArgs(); + } + arg = p.defaultArg; + if (!arg.type) + arg = arg.expressionSemantic(sc); + arg = inlineCopy(arg, sc); + // __FILE__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__ + arg = arg.resolveLoc(loc, sc); + if (i >= nargs) + { + arguments.push(arg); + nargs++; } else + (*arguments)[i] = arg; + } + else + { + if (arg.isDefaultInitExp()) { - if (arg.isDefaultInitExp()) - { - arg = arg.resolveLoc(loc, sc); - (*arguments)[i] = arg; - } + arg = arg.resolveLoc(loc, sc); + (*arguments)[i] = arg; } + } - if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nparams) // https://dlang.org/spec/function.html#variadic + if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nparams) // https://dlang.org/spec/function.html#variadic + { + //printf("\t\tvarargs == 2, p.type = '%s'\n", p.type.toChars()); { - //printf("\t\tvarargs == 2, p.type = '%s'\n", p.type.toChars()); + MATCH m; + if ((m = arg.implicitConvTo(p.type)) > MATCH.nomatch) { - MATCH m; - if ((m = arg.implicitConvTo(p.type)) > MATCH.nomatch) - { - if (p.type.nextOf() && arg.implicitConvTo(p.type.nextOf()) >= m) - goto L2; - else if (nargs != nparams) - return errorArgs(); - goto L1; - } + if (p.type.nextOf() && arg.implicitConvTo(p.type.nextOf()) >= m) + goto L2; + else if (nargs != nparams) + return errorArgs(); + goto L1; } - L2: - Type tb = p.type.toBasetype(); - switch (tb.ty) + } + L2: + Type tb = p.type.toBasetype(); + switch (tb.ty) + { + case Tsarray: + case Tarray: { - case Tsarray: - case Tarray: - { - /* Create a static array variable v of type arg.type: - * T[dim] __arrayArg = [ arguments[i], ..., arguments[nargs-1] ]; - * - * The array literal in the initializer of the hidden variable - * is now optimized. - * https://issues.dlang.org/show_bug.cgi?id=2356 - */ - Type tbn = (cast(TypeArray)tb).next; // array element type - Type tret = p.isLazyArray(); + /* Create a static array variable v of type arg.type: + * T[dim] __arrayArg = [ arguments[i], ..., arguments[nargs-1] ]; + * + * The array literal in the initializer of the hidden variable + * is now optimized. + * https://issues.dlang.org/show_bug.cgi?id=2356 + */ + Type tbn = (cast(TypeArray)tb).next; // array element type + Type tret = p.isLazyArray(); - auto elements = new Expressions(nargs - i); - foreach (u; 0 .. elements.length) - { - Expression a = (*arguments)[i + u]; - assert(a); - if (tret && a.implicitConvTo(tret)) - { - // p is a lazy array of delegates, tret is return type of the delegates - a = a.implicitCastTo(sc, tret) - .optimize(WANTvalue) - .toDelegate(tret, sc); - } - else - a = a.implicitCastTo(sc, tbn); - a = a.addDtorHook(sc); - (*elements)[u] = a; - } - // https://issues.dlang.org/show_bug.cgi?id=14395 - // Convert to a static array literal, or its slice. - arg = new ArrayLiteralExp(loc, tbn.sarrayOf(nargs - i), elements); - if (tb.ty == Tarray) + auto elements = new Expressions(nargs - i); + foreach (u; 0 .. elements.length) + { + Expression a = (*arguments)[i + u]; + assert(a); + if (tret && a.implicitConvTo(tret)) { - arg = new SliceExp(loc, arg, null, null); - arg.type = p.type; + // p is a lazy array of delegates, tret is return type of the delegates + a = a.implicitCastTo(sc, tret) + .optimize(WANTvalue) + .toDelegate(tret, sc); } - break; + else + a = a.implicitCastTo(sc, tbn); + a = a.addDtorHook(sc); + (*elements)[u] = a; } - case Tclass: + // https://issues.dlang.org/show_bug.cgi?id=14395 + // Convert to a static array literal, or its slice. + arg = new ArrayLiteralExp(loc, tbn.sarrayOf(nargs - i), elements); + if (tb.ty == Tarray) { - /* Set arg to be: - * new Tclass(arg0, arg1, ..., argn) - */ - auto args = new Expressions(nargs - i); - foreach (u; i .. nargs) - (*args)[u - i] = (*arguments)[u]; - arg = new NewExp(loc, null, p.type, args); - break; - } - default: - if (!arg) - { - error(loc, "not enough arguments"); - return true; + arg = new SliceExp(loc, arg, null, null); + arg.type = p.type; } break; } - arg = arg.expressionSemantic(sc); - //printf("\targ = '%s'\n", arg.toChars()); - arguments.setDim(i + 1); - (*arguments)[i] = arg; - nargs = i + 1; - done = true; + case Tclass: + { + /* Set arg to be: + * new Tclass(arg0, arg1, ..., argn) + */ + auto args = new Expressions(nargs - i); + foreach (u; i .. nargs) + (*args)[u - i] = (*arguments)[u]; + arg = new NewExp(loc, null, p.type, args); + break; + } + default: + if (!arg) + { + error(loc, "not enough arguments"); + return true; + } + break; } + arg = arg.expressionSemantic(sc); + //printf("\targ = '%s'\n", arg.toChars()); + arguments.setDim(i + 1); + (*arguments)[i] = arg; + nargs = i + 1; + done = true; + } - L1: - if (!(p.isLazy() && p.type.ty == Tvoid)) + L1: + if (!(p.isLazy() && p.type.ty == Tvoid)) + { + if (ubyte wm = arg.type.deduceWild(p.type, p.isReference())) { - if (ubyte wm = arg.type.deduceWild(p.type, p.isReference())) - { - wildmatch = wildmatch ? MODmerge(wildmatch, wm) : wm; - //printf("[%d] p = %s, a = %s, wm = %d, wildmatch = %d\n", i, p.type.toChars(), arg.type.toChars(), wm, wildmatch); - } + wildmatch = wildmatch ? MODmerge(wildmatch, wm) : wm; + //printf("[%d] p = %s, a = %s, wm = %d, wildmatch = %d\n", i, p.type.toChars(), arg.type.toChars(), wm, wildmatch); } } + if (done) break; } @@ -3933,35 +3992,37 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!sc2.scopesym) continue; - if (auto ss = sc2.scopesym.isWithScopeSymbol()) + auto ss = sc2.scopesym.isWithScopeSymbol(); + if (!ss) + continue; + + if (ss.withstate.wthis) { - if (ss.withstate.wthis) + Expression e; + e = new VarExp(exp.loc, ss.withstate.wthis); + e = new DotIdExp(exp.loc, e, exp.ident); + e = e.trySemantic(sc); + if (e) { - Expression e; - e = new VarExp(exp.loc, ss.withstate.wthis); - e = new DotIdExp(exp.loc, e, exp.ident); - e = e.trySemantic(sc); - if (e) - { - result = e; - return; - } + result = e; + return; } - // Try Type.opDispatch (so the static version) - else if (ss.withstate.exp && ss.withstate.exp.op == EXP.type) + } + // Try Type.opDispatch (so the static version) + else if (ss.withstate.exp && ss.withstate.exp.op == EXP.type) + { + Type t = ss.withstate.exp.isTypeExp().type; + if (!t) + continue; + + Expression e; + e = new TypeExp(exp.loc, t); + e = new DotIdExp(exp.loc, e, exp.ident); + e = e.trySemantic(sc); + if (e) { - if (Type t = ss.withstate.exp.isTypeExp().type) - { - Expression e; - e = new TypeExp(exp.loc, t); - e = new DotIdExp(exp.loc, e, exp.ident); - e = e.trySemantic(sc); - if (e) - { - result = e; - return; - } - } + result = e; + return; } } } @@ -5531,48 +5592,48 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor private void genIdent(FuncExp exp, Scope* sc) { - if (exp.fd.ident == Id.empty) - { - const(char)[] s; - if (exp.fd.fes) - s = "__foreachbody"; - else if (exp.fd.tok == TOK.reserved) - s = "__lambda"; - else if (exp.fd.tok == TOK.delegate_) - s = "__dgliteral"; - else - s = "__funcliteral"; + if (exp.fd.ident != Id.empty) + return; - DsymbolTable symtab; - if (FuncDeclaration func = sc.parent.isFuncDeclaration()) + const(char)[] s; + if (exp.fd.fes) + s = "__foreachbody"; + else if (exp.fd.tok == TOK.reserved) + s = "__lambda"; + else if (exp.fd.tok == TOK.delegate_) + s = "__dgliteral"; + else + s = "__funcliteral"; + + DsymbolTable symtab; + if (FuncDeclaration func = sc.parent.isFuncDeclaration()) + { + if (func.localsymtab is null) { - if (func.localsymtab is null) - { - // Inside template constraint, symtab is not set yet. - // Initialize it lazily. - func.localsymtab = new DsymbolTable(); - } - symtab = func.localsymtab; + // Inside template constraint, symtab is not set yet. + // Initialize it lazily. + func.localsymtab = new DsymbolTable(); } - else + symtab = func.localsymtab; + } + else + { + ScopeDsymbol sds = sc.parent.isScopeDsymbol(); + if (!sds.symtab) { - ScopeDsymbol sds = sc.parent.isScopeDsymbol(); - if (!sds.symtab) - { - // Inside template constraint, symtab may not be set yet. - // Initialize it lazily. - assert(sds.isTemplateInstance()); - sds.symtab = new DsymbolTable(); - } - symtab = sds.symtab; + // Inside template constraint, symtab may not be set yet. + // Initialize it lazily. + assert(sds.isTemplateInstance()); + sds.symtab = new DsymbolTable(); } - assert(symtab); - Identifier id = Identifier.generateId(s, symtab.length() + 1); - exp.fd.ident = id; - if (exp.td) - exp.td.ident = id; - symtab.insert(exp.td ? cast(Dsymbol)exp.td : cast(Dsymbol)exp.fd); + symtab = sds.symtab; } + assert(symtab); + Identifier id = Identifier.generateId(s, symtab.length() + 1); + exp.fd.ident = id; + if (exp.td) + exp.td.ident = id; + symtab.insert(exp.td ? cast(Dsymbol)exp.td : cast(Dsymbol)exp.fd); } override void visit(FuncExp exp) @@ -5709,70 +5770,69 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ Expression callExpSemantic(FuncExp exp, Scope* sc, Expressions* arguments) { - if ((!exp.type || exp.type == Type.tvoid) && exp.td && arguments && arguments.length) + if ((exp.type && exp.type != Type.tvoid) || !exp.td ||! arguments || !arguments.length) + return exp.expressionSemantic(sc); + + for (size_t k = 0; k < arguments.length; k++) { - for (size_t k = 0; k < arguments.length; k++) - { - Expression checkarg = (*arguments)[k]; - if (checkarg.op == EXP.error) - return checkarg; - } + Expression checkarg = (*arguments)[k]; + if (checkarg.op == EXP.error) + return checkarg; + } - genIdent(exp, sc); + genIdent(exp, sc); - assert(exp.td.parameters && exp.td.parameters.length); - exp.td.dsymbolSemantic(sc); + assert(exp.td.parameters && exp.td.parameters.length); + exp.td.dsymbolSemantic(sc); - TypeFunction tfl = cast(TypeFunction)exp.fd.type; - size_t dim = tfl.parameterList.length; - if (arguments.length < dim) - { - // Default arguments are always typed, so they don't need inference. - Parameter p = tfl.parameterList[arguments.length]; - if (p.defaultArg) - dim = arguments.length; - } + TypeFunction tfl = cast(TypeFunction)exp.fd.type; + size_t dim = tfl.parameterList.length; + if (arguments.length < dim) + { + // Default arguments are always typed, so they don't need inference. + Parameter p = tfl.parameterList[arguments.length]; + if (p.defaultArg) + dim = arguments.length; + } - if ((tfl.parameterList.varargs == VarArg.none && arguments.length > dim) || - arguments.length < dim) - { - OutBuffer buf; - foreach (idx, ref arg; *arguments) - buf.printf("%s%s", (idx ? ", ".ptr : "".ptr), arg.type.toChars()); - error(exp.loc, "function literal `%s%s` is not callable using argument types `(%s)`", - exp.fd.toChars(), parametersTypeToChars(tfl.parameterList), - buf.peekChars()); - errorSupplemental(exp.loc, "too %s arguments, expected %d, got %d", - arguments.length < dim ? "few".ptr : "many".ptr, - cast(int)dim, cast(int)arguments.length); - return ErrorExp.get(); - } + if ((tfl.parameterList.varargs == VarArg.none && arguments.length > dim) || + arguments.length < dim) + { + OutBuffer buf; + foreach (idx, ref arg; *arguments) + buf.printf("%s%s", (idx ? ", ".ptr : "".ptr), arg.type.toChars()); + error(exp.loc, "function literal `%s%s` is not callable using argument types `(%s)`", + exp.fd.toChars(), parametersTypeToChars(tfl.parameterList), + buf.peekChars()); + errorSupplemental(exp.loc, "too %s arguments, expected %d, got %d", + arguments.length < dim ? "few".ptr : "many".ptr, + cast(int)dim, cast(int)arguments.length); + return ErrorExp.get(); + } - auto tiargs = new Objects(); - tiargs.reserve(exp.td.parameters.length); + auto tiargs = new Objects(); + tiargs.reserve(exp.td.parameters.length); - for (size_t i = 0; i < exp.td.parameters.length; i++) + for (size_t i = 0; i < exp.td.parameters.length; i++) + { + TemplateParameter tp = (*exp.td.parameters)[i]; + assert(dim <= tfl.parameterList.length); + foreach (u, p; tfl.parameterList) { - TemplateParameter tp = (*exp.td.parameters)[i]; - assert(dim <= tfl.parameterList.length); - foreach (u, p; tfl.parameterList) - { - if (u == dim) - break; + if (u == dim) + break; - if (p.type.ty == Tident && (cast(TypeIdentifier)p.type).ident == tp.ident) - { - Expression e = (*arguments)[u]; - tiargs.push(e.type); - break; - } + if (p.type.ty == Tident && (cast(TypeIdentifier)p.type).ident == tp.ident) + { + Expression e = (*arguments)[u]; + tiargs.push(e.type); + break; } } - - auto ti = new TemplateInstance(exp.loc, exp.td, tiargs); - return (new ScopeExp(exp.loc, ti)).expressionSemantic(sc); } - return exp.expressionSemantic(sc); + + auto ti = new TemplateInstance(exp.loc, exp.td, tiargs); + return (new ScopeExp(exp.loc, ti)).expressionSemantic(sc); } override void visit(CallExp exp) @@ -6866,15 +6926,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor while (1) { AttribDeclaration ad = s.isAttribDeclaration(); - if (ad) - { - if (ad.decl && ad.decl.length == 1) - { - s = (*ad.decl)[0]; - continue; - } - } - break; + if (!ad) + break; + if (ad.decl && ad.decl.length == 1) + s = (*ad.decl)[0]; } //printf("inserting '%s' %p into sc = %p\n", s.toChars(), s, sc); @@ -6970,27 +7025,26 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // Disallow shadowing for (Scope* scx = sc.enclosing; scx && (scx.func == sc.func || (fes_enclosing_func && scx.func == fes_enclosing_func)); scx = scx.enclosing) { - Dsymbol s2; - if (scx.scopesym && scx.scopesym.symtab && (s2 = scx.scopesym.symtab.lookup(s.ident)) !is null && s != s2) + if (!scx.scopesym || !scx.scopesym.symtab) + continue; + Dsymbol s2 = scx.scopesym.symtab.lookup(s.ident); + if (s2 is null || s == s2) + continue; + // allow STC.local symbols to be shadowed + // TODO: not really an optimal design + auto decl = s2.isDeclaration(); + if (decl && (decl.storage_class & STC.local)) + continue; + if (sc.func.fes) { - // allow STC.local symbols to be shadowed - // TODO: not really an optimal design - auto decl = s2.isDeclaration(); - if (!decl || !(decl.storage_class & STC.local)) - { - if (sc.func.fes) - { - deprecation(e.loc, "%s `%s` is shadowing %s `%s`", s.kind(), s.ident.toChars(), s2.kind(), s2.toPrettyChars()); - deprecationSupplemental(s2.loc, "declared here"); - - } - else - { - error(e.loc, "%s `%s` is shadowing %s `%s`", s.kind(), s.ident.toChars(), s2.kind(), s2.toPrettyChars()); - errorSupplemental(s2.loc, "declared here"); - return setError(); - } - } + deprecation(e.loc, "%s `%s` is shadowing %s `%s`", s.kind(), s.ident.toChars(), s2.kind(), s2.toPrettyChars()); + deprecationSupplemental(s2.loc, "declared here"); + } + else + { + error(e.loc, "%s `%s` is shadowing %s `%s`", s.kind(), s.ident.toChars(), s2.kind(), s2.toPrettyChars()); + errorSupplemental(s2.loc, "declared here"); + return setError(); } } } @@ -7058,56 +7112,55 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor ta.checkComplexTransition(exp.loc, sc); - Expression e; auto tb = ta.toBasetype(); if (ea && tb.ty == Tclass) { if (tb.toDsymbol(sc).isClassDeclaration().classKind == ClassKind.cpp) { error(exp.loc, "runtime type information is not supported for `extern(C++)` classes"); - e = ErrorExp.get(); + return setError(); } else if (!Type.typeinfoclass) { error(exp.loc, "`object.TypeInfo_Class` could not be found, but is implicitly used"); - e = ErrorExp.get(); + return setError(); } else { /* Get the dynamic type, which is .classinfo */ ea = ea.expressionSemantic(sc); - e = new TypeidExp(ea.loc, ea); + Expression e = new TypeidExp(ea.loc, ea); e.type = Type.typeinfoclass.type; + result = e; + return; } } else if (ta.ty == Terror) { - e = ErrorExp.get(); + return setError(); } - else - { - // Handle this in the glue layer - e = new TypeidExp(exp.loc, ta); - bool genObjCode = true; + // Handle this in the glue layer + Expression e = new TypeidExp(exp.loc, ta); - // https://issues.dlang.org/show_bug.cgi?id=23650 - // We generate object code for typeinfo, required - // by typeid, only if in non-speculative context - if (sc.traitsCompiles) - { - genObjCode = false; - } + bool genObjCode = true; + + // https://issues.dlang.org/show_bug.cgi?id=23650 + // We generate object code for typeinfo, required + // by typeid, only if in non-speculative context + if (sc.traitsCompiles) + { + genObjCode = false; + } - e.type = getTypeInfoType(exp.loc, ta, sc, genObjCode); - semanticTypeInfo(sc, ta); + e.type = getTypeInfoType(exp.loc, ta, sc, genObjCode); + semanticTypeInfo(sc, ta); - if (ea) - { - e = new CommaExp(exp.loc, ea, e); // execute ea - e = e.expressionSemantic(sc); - } + if (ea) + { + e = new CommaExp(exp.loc, ea, e); // execute ea + e = e.expressionSemantic(sc); } result = e; } @@ -7441,41 +7494,37 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor MATCH m = deduceType(e.targ, sc, e.tspec, *e.parameters, dedtypes, null, 0, e.tok == TOK.equal); if (m == MATCH.nomatch || (m != MATCH.exact && e.tok == TOK.equal)) - { return no(); - } - else - { - tded = cast(Type)dedtypes[0]; - if (!tded) - tded = e.targ; - Objects tiargs = Objects(1); - tiargs[0] = e.targ; - /* Declare trailing parameters - */ - for (size_t i = 1; i < e.parameters.length; i++) - { - TemplateParameter tp = (*e.parameters)[i]; - Declaration s = null; + tded = cast(Type)dedtypes[0]; + if (!tded) + tded = e.targ; + Objects tiargs = Objects(1); + tiargs[0] = e.targ; - m = tp.matchArg(e.loc, sc, &tiargs, i, e.parameters, dedtypes, &s); - if (m == MATCH.nomatch) - return no(); - s.dsymbolSemantic(sc); - if (!sc.insert(s)) - { - Dsymbol pscopesym; - auto conflict = sc.search(Loc.initial, s.ident, pscopesym); - error(e.loc, "declaration `%s` is already defined", s.toPrettyChars()); - errorSupplemental(conflict.loc, "`%s` `%s` is defined here", - conflict.kind(), conflict.toChars()); - } + /* Declare trailing parameters + */ + for (size_t i = 1; i < e.parameters.length; i++) + { + TemplateParameter tp = (*e.parameters)[i]; + Declaration s = null; - unSpeculative(sc, s); + m = tp.matchArg(e.loc, sc, &tiargs, i, e.parameters, dedtypes, &s); + if (m == MATCH.nomatch) + return no(); + s.dsymbolSemantic(sc); + if (!sc.insert(s)) + { + Dsymbol pscopesym; + auto conflict = sc.search(Loc.initial, s.ident, pscopesym); + error(e.loc, "declaration `%s` is already defined", s.toPrettyChars()); + errorSupplemental(conflict.loc, "`%s` `%s` is defined here", + conflict.kind(), conflict.toChars()); } - return yes(); + + unSpeculative(sc, s); } + return yes(); } else if (e.id) { @@ -9753,8 +9802,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } - if (isAggregate(exp.e1.type)) + if (auto ad = isAggregate(exp.e1.type)) + { error(exp.loc, "no `[]` operator overload for type `%s`", exp.e1.type.toChars()); + errorSupplemental(ad.loc, "`%s` declared here", ad.toPrettyChars()); + } else if (exp.e1.op == EXP.type && exp.e1.type.ty != Ttuple) error(exp.loc, "static array of `%s` with multiple lengths not allowed", exp.e1.type.toChars()); else if (isIndexableNonAggregate(exp.e1.type)) @@ -12153,7 +12205,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } - if (tb1.ty == Tpointer && tb2.ty == Tpointer) + if (tb1.ty == Tpointer && tb2.ty == Tpointer || + tb1.ty == Tnull && tb2.ty == Tnull) { result = exp.incompatibleTypes(); return; @@ -12250,6 +12303,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { err |= exp.e2.checkArithmetic(exp.op) || exp.e2.checkSharedAccess(sc); } + if (t1.ty == Tnull && t2.ty == Tnull) + { + exp.incompatibleTypes(); + return setError(); + } if (err) return setError(); diff --git a/gcc/d/dmd/func.d b/gcc/d/dmd/func.d index 9bd4522..9d59fc9 100644 --- a/gcc/d/dmd/func.d +++ b/gcc/d/dmd/func.d @@ -46,17 +46,14 @@ 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.tokens; -import dmd.typesem; import dmd.visitor; version (IN_GCC) {} @@ -452,26 +449,6 @@ extern (C++) class FuncDeclaration : Declaration if (!fd) return false; - version (none) - { - /* Disable this check because: - * const void foo(); - * semantic() isn't run yet on foo(), so the const hasn't been - * applied yet. - */ - if (type) - { - printf("type = %s\n", type.toChars()); - printf("fd.type = %s\n", fd.type.toChars()); - } - // fd.type can be NULL for overloaded constructors - if (type && fd.type && fd.type.covariant(type) && fd.type.mod == type.mod && !isFuncAliasDeclaration()) - { - //printf("\tfalse: conflict %s\n", kind()); - return false; - } - } - if (overnext) { td = overnext.isTemplateDeclaration(); @@ -697,29 +674,6 @@ extern (C++) class FuncDeclaration : Declaration return false; } - /***************************************** - * Initialize for inferring the attributes of this function. - */ - final void initInferAttributes() - { - //printf("initInferAttributes() for %s (%s)\n", toPrettyChars(), ident.toChars()); - TypeFunction tf = type.toTypeFunction(); - if (tf.purity == PURE.impure) // purity not specified - purityInprocess = true; - - if (tf.trust == TRUST.default_) - safetyInprocess = true; - - if (!tf.isNothrow) - nothrowInprocess = true; - - if (!tf.isNogc) - nogcInprocess = true; - - // Initialize for inferring STC.scope_ - scopeInprocess = true; - } - extern (D) final uint saveFlags() { return bitFields; @@ -798,69 +752,6 @@ extern (C++) class FuncDeclaration : Declaration return setUnsafe(false, f.loc, null, f, null); } - final bool isNogc() - { - //printf("isNogc() %s, inprocess: %d\n", toChars(), !!(flags & FUNCFLAG.nogcInprocess)); - if (nogcInprocess) - setGC(loc, null); - return type.toTypeFunction().isNogc; - } - - extern (D) final bool isNogcBypassingInference() - { - return !nogcInprocess && isNogc(); - } - - /************************************** - * The function is doing something that may allocate with the GC, - * so mark it as not nogc (not no-how). - * - * Params: - * loc = location of impure action - * fmt = format string for error message. Must include "%s `%s`" for the function kind and name. - * arg0 = (optional) argument to format string - * - * Returns: - * true if function is marked as @nogc, meaning a user error occurred - */ - extern (D) final bool setGC(Loc loc, const(char)* fmt, RootObject arg0 = null) - { - //printf("setGC() %s\n", toChars()); - if (nogcInprocess && semanticRun < PASS.semantic3 && _scope) - { - this.semantic2(_scope); - this.semantic3(_scope); - } - - if (nogcInprocess) - { - nogcInprocess = false; - if (fmt) - nogcViolation = new AttributeViolation(loc, fmt, this, arg0); // action that requires GC - else if (arg0) - nogcViolation = new AttributeViolation(loc, fmt, arg0); // call to non-@nogc function - - type.toTypeFunction().isNogc = false; - if (fes) - fes.func.setGC(Loc.init, null, null); - } - else if (isNogc()) - return true; - return false; - } - - /************************************** - * The function calls non-`@nogc` function f, mark it as not nogc. - * Params: - * f = function being called - * Returns: - * true if function is marked as @nogc, meaning a user error occurred - */ - extern (D) final bool setGCCall(FuncDeclaration f) - { - return setGC(loc, null, f); - } - /************************************** * The function is doing something that may throw an exception, register that in case nothrow is being inferred * @@ -888,18 +779,6 @@ extern (C++) class FuncDeclaration : Declaration return setThrow(loc, null, f); } - extern (D) final void printGCUsage(const ref Loc loc, const(char)* warn) - { - if (!global.params.v.gc) - return; - - Module m = getModule(); - if (m && m.isRoot() && !inUnittest()) - { - message(loc, "vgc: %s", warn); - } - } - /**************************************** * Determine if function needs a static frame pointer. * Returns: @@ -1143,75 +1022,6 @@ extern (C++) class FuncDeclaration : Declaration } /*********************************************** - * Check that the function contains any closure. - * If it's @nogc, report suitable errors. - * This is mostly consistent with FuncDeclaration::needsClosure(). - * - * Returns: - * true if any errors occur. - */ - extern (C++) final bool checkClosure() - { - //printf("checkClosure() %s\n", toPrettyChars()); - if (!needsClosure()) - return false; - - if (setGC(loc, "%s `%s` is `@nogc` yet allocates closure for `%s()` with the GC", this)) - { - .error(loc, "%s `%s` is `@nogc` yet allocates closure for `%s()` with the GC", kind, toPrettyChars, toChars()); - if (global.gag) // need not report supplemental errors - return true; - } - else if (!global.params.useGC) - { - .error(loc, "%s `%s` is `-betterC` yet allocates closure for `%s()` with the GC", kind, toPrettyChars, toChars()); - if (global.gag) // need not report supplemental errors - return true; - } - else - { - printGCUsage(loc, "using closure causes GC allocation"); - return false; - } - - FuncDeclarations a; - foreach (v; closureVars) - { - foreach (f; v.nestedrefs) - { - assert(f !is this); - - LcheckAncestorsOfANestedRef: - for (Dsymbol s = f; s && s !is this; s = s.toParentP(this)) - { - auto fx = s.isFuncDeclaration(); - if (!fx) - continue; - if (fx.isThis() || - fx.tookAddressOf || - checkEscapingSiblings(fx, this)) - { - foreach (f2; a) - { - if (f2 == f) - break LcheckAncestorsOfANestedRef; - } - a.push(f); - .errorSupplemental(f.loc, "%s `%s` closes over variable `%s`", - f.kind, f.toPrettyChars(), v.toChars()); - if (v.ident != Id.This) - .errorSupplemental(v.loc, "`%s` declared here", v.toChars()); - - break LcheckAncestorsOfANestedRef; - } - } - } - } - - return true; - } - - /*********************************************** * Determine if function's variables are referenced by a function * nested within it. */ @@ -1539,7 +1349,7 @@ private void markAsNeedingClosure(Dsymbol f, FuncDeclaration outerFunc) * Returns: * true if any closures were needed */ -private bool checkEscapingSiblings(FuncDeclaration f, FuncDeclaration outerFunc, void* p = null) +bool checkEscapingSiblings(FuncDeclaration f, FuncDeclaration outerFunc, void* p = null) { static struct PrevSibling { diff --git a/gcc/d/dmd/funcsem.d b/gcc/d/dmd/funcsem.d index 2cd89f7..2ffd3ad 100644 --- a/gcc/d/dmd/funcsem.d +++ b/gcc/d/dmd/funcsem.d @@ -56,7 +56,6 @@ 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.target; @@ -64,6 +63,7 @@ import dmd.templatesem; import dmd.tokens; import dmd.typesem; import dmd.visitor; +import dmd.visitor.statement_rewrite_walker; version (IN_GCC) {} else version (IN_LLVM) {} @@ -1123,6 +1123,28 @@ Ldone: } } +/***************************************** + * Initialize for inferring the attributes of this function. + */ +private void initInferAttributes(FuncDeclaration fd) +{ + //printf("initInferAttributes() for %s (%s)\n", toPrettyChars(), ident.toChars()); + TypeFunction tf = fd.type.toTypeFunction(); + if (tf.purity == PURE.impure) // purity not specified + fd.purityInprocess = true; + + if (tf.trust == TRUST.default_) + fd.safetyInprocess = true; + + if (!tf.isNothrow) + fd.nothrowInprocess = true; + + if (!tf.isNogc) + fd.nogcInprocess = true; + + // Initialize for inferring STC.scope_ + fd.scopeInprocess = true; +} /**************************************************** * Resolve forward reference of function signature - @@ -1621,6 +1643,8 @@ FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, { .error(loc, "none of the overloads of `%s` are callable using argument types `!(%s)%s`", od.ident.toChars(), tiargsBuf.peekChars(), fargsBuf.peekChars()); + if (!global.gag || global.params.v.showGaggedErrors) + printCandidates(loc, od, sc.isDeprecated()); return null; } @@ -1745,7 +1769,6 @@ FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, * showDeprecated = If `false`, `deprecated` function won't be shown */ private void printCandidates(Decl)(const ref Loc loc, Decl declaration, bool showDeprecated) -if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration)) { // max num of overloads to print (-v or -verror-supplements overrides this). const uint DisplayLimit = global.params.v.errorSupplementCount(); @@ -1799,13 +1822,16 @@ if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration)) if (!print) return true; + // if td.onemember is a function, toCharsMaybeConstraints can print it + // without us recursing, otherwise we have to handle it. // td.onemember may not have overloads set // (see fail_compilation/onemember_overloads.d) // assume if more than one member it is overloaded internally - bool recurse = td.onemember && td.members.length > 1; + bool recurse = td.onemember && (!td.onemember.isFuncDeclaration || + td.members.length > 1); OutBuffer buf; HdrGenState hgs; - hgs.skipConstraints = true; + hgs.skipConstraints = true; // failing constraint should get printed below hgs.showOneMember = !recurse; toCharsMaybeConstraints(td, buf, hgs); const tmsg = buf.peekChars(); @@ -2731,7 +2757,6 @@ Statement mergeFensure(FuncDeclaration fd, Statement sf, Identifier oid, Express */ void modifyReturns(FuncLiteralDeclaration fld, Scope* sc, Type tret) { - import dmd.statement_rewrite_walker; extern (C++) final class RetWalker : StatementRewriteWalker { alias visit = typeof(super).visit; diff --git a/gcc/d/dmd/globals.d b/gcc/d/dmd/globals.d index 2bdc185..ea88445 100644 --- a/gcc/d/dmd/globals.d +++ b/gcc/d/dmd/globals.d @@ -248,6 +248,8 @@ extern (C++) struct Param const(char)[] exefile; const(char)[] mapfile; + bool fullyQualifiedObjectFiles; // prepend module names to object files to prevent name conflicts with -od + // Time tracing bool timeTrace = false; /// Whether profiling of compile time is enabled uint timeTraceGranularityUs = 500; /// In microseconds, minimum event size to report diff --git a/gcc/d/dmd/globals.h b/gcc/d/dmd/globals.h index e2aa82a..45efab0 100644 --- a/gcc/d/dmd/globals.h +++ b/gcc/d/dmd/globals.h @@ -250,6 +250,7 @@ struct Param DString resfile; DString exefile; DString mapfile; + bool fullyQualifiedObjectFiles; bool timeTrace; uint32_t timeTraceGranularityUs; const char* timeTraceFile; diff --git a/gcc/d/dmd/hdrgen.d b/gcc/d/dmd/hdrgen.d index 198b49e..e3f1dc2 100644 --- a/gcc/d/dmd/hdrgen.d +++ b/gcc/d/dmd/hdrgen.d @@ -2363,12 +2363,12 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt // to themselves, need to avoid infinite recursion: // struct S { this(int){ this.s = &this; } S* s; } // const foo = new S(0); - if (e.stageflags & stageToCBuffer) + if (e.stageflags & StructLiteralExp.StageFlags.toCBuffer) buf.writestring("<recursion>"); else { const old = e.stageflags; - e.stageflags |= stageToCBuffer; + e.stageflags |= StructLiteralExp.StageFlags.toCBuffer; argsToBuffer(e.elements, buf, hgs); e.stageflags = old; } diff --git a/gcc/d/dmd/imphint.d b/gcc/d/dmd/imphint.d index ea2f13d..9f9993f 100644 --- a/gcc/d/dmd/imphint.d +++ b/gcc/d/dmd/imphint.d @@ -80,6 +80,8 @@ shared static this() "writeln": "std.stdio", "__va_argsave_t": "core.stdc.stdarg", "__va_list_tag": "core.stdc.stdarg", + "InterpolationHeader": "core.interpolation", + "InterpolationFooter": "core.interpolation", ]; } diff --git a/gcc/d/dmd/init.d b/gcc/d/dmd/init.d index 7d9e3e6..934ccc6 100644 --- a/gcc/d/dmd/init.d +++ b/gcc/d/dmd/init.d @@ -38,6 +38,7 @@ extern (C++) class Initializer : ASTNode { Loc loc; InitKind kind; + bool semanticDone = false; /// initializerSemantic has been run on this override DYNCAST dyncast() const { @@ -176,7 +177,6 @@ extern (C++) final class ArrayInitializer : Initializer Initializers value; // of Initializer *'s uint dim; // length of array being initialized Type type; // type that array will be used to initialize - bool sem; // true if semantic() is run bool isCarray; // C array semantics extern (D) this(const ref Loc loc) @@ -256,7 +256,6 @@ extern (C++) final class CInitializer : Initializer { DesigInits initializerList; /// initializer-list Type type; /// type that array will be used to initialize - bool sem; /// true if semantic() is run extern (D) this(const ref Loc loc) { diff --git a/gcc/d/dmd/init.h b/gcc/d/dmd/init.h index 2ee69f6..951168b 100644 --- a/gcc/d/dmd/init.h +++ b/gcc/d/dmd/init.h @@ -33,6 +33,7 @@ class Initializer : public ASTNode public: Loc loc; unsigned char kind; + d_bool semanticDone; DYNCAST dyncast() const override { return DYNCAST_INITIALIZER; } @@ -85,7 +86,6 @@ public: Initializers value; // of Initializer *'s unsigned dim; // length of array being initialized Type *type; // type that array will be used to initialize - d_bool sem; // true if semantic() is run d_bool isCarray; // C array semantics bool isAssociativeArray() const; @@ -119,7 +119,6 @@ class CInitializer final : public Initializer public: DesigInits initializerList; Type *type; // type that array will be used to initialize - d_bool sem; // true if semantic() is run void accept(Visitor *v) override { v->visit(this); } }; diff --git a/gcc/d/dmd/initsem.d b/gcc/d/dmd/initsem.d index ab25689..a72fd90 100644 --- a/gcc/d/dmd/initsem.d +++ b/gcc/d/dmd/initsem.d @@ -106,6 +106,9 @@ Expression toAssocArrayLiteral(ArrayInitializer ai) Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedInterpret needInterpret) { //printf("initializerSemantic() tx: %p %s\n", tx, tx.toChars()); + if (init.semanticDone) + return init; + Type t = tx; static Initializer err() @@ -203,11 +206,6 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn const(uint) amax = 0x80000000; bool errors = false; //printf("ArrayInitializer::semantic(%s), ai: %s\n", t.toChars(), toChars(i)); - if (i.sem) // if semantic() already run - { - return i; - } - i.sem = true; t = t.toBasetype(); switch (t.ty) { @@ -692,51 +690,49 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn auto di = ci.initializerList[index]; if (di.designatorList && fieldi != 0) break; // back to top level - else + + VarDeclaration field; + while (1) // skip field if it overlaps with previously seen fields { - VarDeclaration field; - while (1) // skip field if it overlaps with previously seen fields - { - field = sd.fields[fieldi]; - ++fieldi; - if (!overlaps(field, sd.fields[], si)) - break; - if (fieldi == nfields) - break; - } - auto tn = field.type.toBasetype(); - auto tnsa = tn.isTypeSArray(); - auto tns = tn.isTypeStruct(); - auto ix = di.initializer; - if (tnsa && ix.isExpInitializer()) - { - ExpInitializer ei = ix.isExpInitializer(); - if (ei.exp.isStringExp() && tnsa.nextOf().isIntegral()) - { - si.addInit(field.ident, ei); - ++index; - } - else - si.addInit(field.ident, subArray(tnsa, index)); // fwd ref of subArray is why subStruct is a template - } - else if (tns && ix.isExpInitializer()) + field = sd.fields[fieldi]; + ++fieldi; + if (!overlaps(field, sd.fields[], si)) + break; + if (fieldi == nfields) + break; + } + auto tn = field.type.toBasetype(); + auto tnsa = tn.isTypeSArray(); + auto tns = tn.isTypeStruct(); + auto ix = di.initializer; + if (tnsa && ix.isExpInitializer()) + { + ExpInitializer ei = ix.isExpInitializer(); + if (ei.exp.isStringExp() && tnsa.nextOf().isIntegral()) { - /* Disambiguate between an exp representing the entire - * struct, and an exp representing the first field of the struct - */ - if (representsStruct(ix.isExpInitializer(), tns)) // initializer represents the entire struct - { - si.addInit(field.ident, initializerSemantic(ix, sc, tn, needInterpret)); - ++index; - } - else // field initializers for struct - si.addInit(field.ident, subStruct(tns, index)); // the first field + si.addInit(field.ident, ei); + ++index; } else + si.addInit(field.ident, subArray(tnsa, index)); // fwd ref of subArray is why subStruct is a template + } + else if (tns && ix.isExpInitializer()) + { + /* Disambiguate between an exp representing the entire + * struct, and an exp representing the first field of the struct + */ + if (representsStruct(ix.isExpInitializer(), tns)) // initializer represents the entire struct { - si.addInit(field.ident, ix); + si.addInit(field.ident, initializerSemantic(ix, sc, tn, needInterpret)); ++index; } + else // field initializers for struct + si.addInit(field.ident, subStruct(tns, index)); // the first field + } + else + { + si.addInit(field.ident, ix); + ++index; } } //printf("subStruct() returns ai: %s, index: %d\n", si.toChars(), cast(int)index); @@ -837,112 +833,110 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn error(ci.loc, "`.%s` is not a field of `%s`\n", id.toChars(), sd.toChars()); return err(); } - else - { - if (fieldi == nfields) - break; - auto ix = di.initializer; + if (fieldi == nfields) + break; - /* If a C initializer is wrapped in a C initializer, with no designators, - * peel off the outer one - */ - if (ix.isCInitializer()) + auto ix = di.initializer; + + /* If a C initializer is wrapped in a C initializer, with no designators, + * peel off the outer one + */ + if (ix.isCInitializer()) + { + CInitializer cix = ix.isCInitializer(); + if (cix.initializerList.length == 1) { - CInitializer cix = ix.isCInitializer(); - if (cix.initializerList.length == 1) + DesigInit dix = cix.initializerList[0]; + if (!dix.designatorList) { - DesigInit dix = cix.initializerList[0]; - if (!dix.designatorList) - { - Initializer inix = dix.initializer; - if (inix.isCInitializer()) - ix = inix; - } + Initializer inix = dix.initializer; + if (inix.isCInitializer()) + ix = inix; } } + } - if (auto cix = ix.isCInitializer()) + if (auto cix = ix.isCInitializer()) + { + /* ImportC loses the structure from anonymous structs, but this is retained + * by the initializer syntax. if a CInitializer has a Designator, it is probably + * a nested anonymous struct + */ + int found; + foreach (dix; cix.initializerList) { - /* ImportC loses the structure from anonymous structs, but this is retained - * by the initializer syntax. if a CInitializer has a Designator, it is probably - * a nested anonymous struct - */ - int found; - foreach (dix; cix.initializerList) + Designators* dlistx = dix.designatorList; + if (!dlistx) + continue; + if ((*dlistx).length == 1 && (*dlistx)[0].ident) { - Designators* dlistx = dix.designatorList; - if (!dlistx) - continue; - if ((*dlistx).length == 1 && (*dlistx)[0].ident) + auto id = (*dlistx)[0].ident; + foreach (k, f; sd.fields[]) // linear search for now { - auto id = (*dlistx)[0].ident; - foreach (k, f; sd.fields[]) // linear search for now + if (f.ident == id) { - if (f.ident == id) - { - fieldi = k; - si.addInit(id, dix.initializer); - ++fieldi; - ++index; - ++found; - break; - } + fieldi = k; + si.addInit(id, dix.initializer); + ++fieldi; + ++index; + ++found; + break; } } - else { - error(ci.loc, "only 1 designator currently allowed for C struct field initializer `%s`", toChars(ci)); - } } - - if (found == cix.initializerList.length) - continue Loop1; + else { + error(ci.loc, "only 1 designator currently allowed for C struct field initializer `%s`", toChars(ci)); + } } - VarDeclaration field; - while (1) // skip field if it overlaps with previously seen fields - { - field = sd.fields[fieldi]; - ++fieldi; - if (!overlaps(field, sd.fields[], si)) - break; - if (fieldi == nfields) - break; - } + if (found == cix.initializerList.length) + continue Loop1; + } - auto tn = field.type.toBasetype(); - auto tnsa = tn.isTypeSArray(); - auto tns = tn.isTypeStruct(); + VarDeclaration field; + while (1) // skip field if it overlaps with previously seen fields + { + field = sd.fields[fieldi]; + ++fieldi; + if (!overlaps(field, sd.fields[], si)) + break; + if (fieldi == nfields) + break; + } - if (tnsa && ix.isExpInitializer()) - { - ExpInitializer ei = ix.isExpInitializer(); - if (ei.exp.isStringExp() && tnsa.nextOf().isIntegral()) - { - si.addInit(field.ident, ei); - ++index; - } - else - si.addInit(field.ident, subArray(tnsa, index)); - } - else if (tns && ix.isExpInitializer()) + auto tn = field.type.toBasetype(); + auto tnsa = tn.isTypeSArray(); + auto tns = tn.isTypeStruct(); + + if (tnsa && ix.isExpInitializer()) + { + ExpInitializer ei = ix.isExpInitializer(); + if (ei.exp.isStringExp() && tnsa.nextOf().isIntegral()) { - /* Disambiguate between an exp representing the entire - * struct, and an exp representing the first field of the struct - */ - if (representsStruct(ix.isExpInitializer(), tns)) // initializer represents the entire struct - { - si.addInit(field.ident, initializerSemantic(ix, sc, tn, needInterpret)); - ++index; - } - else // field initializers for struct - si.addInit(field.ident, subStruct(tns, index)); // the first field + si.addInit(field.ident, ei); + ++index; } else + si.addInit(field.ident, subArray(tnsa, index)); + } + else if (tns && ix.isExpInitializer()) + { + /* Disambiguate between an exp representing the entire + * struct, and an exp representing the first field of the struct + */ + if (representsStruct(ix.isExpInitializer(), tns)) // initializer represents the entire struct { - si.addInit(field.ident, di.initializer); + si.addInit(field.ident, initializerSemantic(ix, sc, tn, needInterpret)); ++index; } + else // field initializers for struct + si.addInit(field.ident, subStruct(tns, index)); // the first field + } + else + { + si.addInit(field.ident, di.initializer); + ++index; } } return initializerSemantic(si, sc, t, needInterpret); @@ -1059,6 +1053,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn mixin VisitInitializer!Initializer visit; auto result = visit.VisitInitializer(init); + result.semanticDone = true; return (result !is null) ? result : new ErrorInitializer(); } @@ -1099,64 +1094,50 @@ Initializer inferType(Initializer init, Scope* sc) { //printf("ArrayInitializer::inferType() %s\n", toChars()); Expressions* keys = null; - Expressions* values; - if (init.isAssociativeArray()) + Expressions* values = new Expressions(init.value.length); + Initializer no() { + if (keys) + error(init.loc, "not an associative array initializer"); + else + error(init.loc, "cannot infer type from array initializer"); + return new ErrorInitializer(); + } + const bool isAssoc = init.isAssociativeArray(); + if (isAssoc) keys = new Expressions(init.value.length); - values = new Expressions(init.value.length); - for (size_t i = 0; i < init.value.length; i++) + else + values.zero(); + + for (size_t i = 0; i < init.value.length; i++) + { + if (isAssoc) { Expression e = init.index[i]; if (!e) - goto Lno; + return no(); (*keys)[i] = e; - Initializer iz = init.value[i]; - if (!iz) - goto Lno; - iz = iz.inferType(sc); - if (iz.isErrorInitializer()) - { - return iz; - } - (*values)[i] = iz.isExpInitializer().exp; - assert(!(*values)[i].isErrorExp()); } - Expression e = new AssocArrayLiteralExp(init.loc, keys, values); - auto ei = new ExpInitializer(init.loc, e); - return ei.inferType(sc); - } - else - { - auto elements = new Expressions(init.value.length); - elements.zero(); - for (size_t i = 0; i < init.value.length; i++) - { + else assert(!init.index[i]); // already asserted by isAssociativeArray() - Initializer iz = init.value[i]; - if (!iz) - goto Lno; - iz = iz.inferType(sc); - if (iz.isErrorInitializer()) - { - return iz; - } - (*elements)[i] = iz.isExpInitializer().exp; - assert(!(*elements)[i].isErrorExp()); + Initializer iz = init.value[i]; + if (!iz) + return no(); + iz = iz.inferType(sc); + if (iz.isErrorInitializer()) + { + return iz; } - Expression e = new ArrayLiteralExp(init.loc, null, elements); - auto ei = new ExpInitializer(init.loc, e); - return ei.inferType(sc); + (*values)[i] = iz.isExpInitializer().exp; + assert(!(*values)[i].isErrorExp()); } - Lno: - if (keys) - { - error(init.loc, "not an associative array initializer"); - } - else - { - error(init.loc, "cannot infer type from array initializer"); - } - return new ErrorInitializer(); + + Expression e; + e = isAssoc + ? new AssocArrayLiteralExp(init.loc, keys, values) + : new ArrayLiteralExp(init.loc, null, values); + auto ei = new ExpInitializer(init.loc, e); + return ei.inferType(sc); } Initializer visitExp(ExpInitializer init) @@ -1486,10 +1467,10 @@ private bool hasNonConstPointers(Expression e) } if (auto se = ae.e1.isStructLiteralExp()) { - if (!(se.stageflags & stageSearchPointers)) + if (!(se.stageflags & StructLiteralExp.StageFlags.searchPointers)) { const old = se.stageflags; - se.stageflags |= stageSearchPointers; + se.stageflags |= StructLiteralExp.StageFlags.searchPointers; bool ret = checkArray(se.elements); se.stageflags = old; return ret; diff --git a/gcc/d/dmd/lambdacomp.d b/gcc/d/dmd/lambdacomp.d index a1db8d5..0ef2dbd 100644 --- a/gcc/d/dmd/lambdacomp.d +++ b/gcc/d/dmd/lambdacomp.d @@ -240,39 +240,39 @@ public: auto id = exp.ident.toChars(); // If it's not an argument - if (!checkArgument(id)) + if (checkArgument(id)) + return; + + // we must check what the identifier expression is. + Dsymbol scopesym; + Dsymbol s = sc.search(exp.loc, exp.ident, scopesym); + + // If it's an unknown symbol, consider the function incomparable + if (!s) { - // we must check what the identifier expression is. - Dsymbol scopesym; - Dsymbol s = sc.search(exp.loc, exp.ident, scopesym); - if (s) - { - auto v = s.isVarDeclaration(); - // If it's a VarDeclaration, it must be a manifest constant - if (v && (v.storage_class & STC.manifest)) - { - v.getConstInitializer.accept(this); - } - else if (auto em = s.isEnumDeclaration()) - { - d = em; - et = ExpType.EnumDecl; - } - else if (auto fd = s.isFuncDeclaration()) - { - writeMangledName(fd); - } - // For anything else, the function is deemed uncomparable - else - { - buf.setsize(0); - } - } - // If it's an unknown symbol, consider the function incomparable - else - { - buf.setsize(0); - } + buf.setsize(0); + return; + } + + auto v = s.isVarDeclaration(); + // If it's a VarDeclaration, it must be a manifest constant + if (v && (v.storage_class & STC.manifest)) + { + v.getConstInitializer.accept(this); + } + else if (auto em = s.isEnumDeclaration()) + { + d = em; + et = ExpType.EnumDecl; + } + else if (auto fd = s.isFuncDeclaration()) + { + writeMangledName(fd); + } + // For anything else, the function is deemed uncomparable + else + { + buf.setsize(0); } } @@ -450,21 +450,22 @@ public: printf("StructLiteralExp: %s\n", e.toChars); auto ty = cast(TypeStruct)e.stype; - if (ty) + if (!ty) { - writeMangledName(ty.sym); - auto dim = e.elements.length; - foreach (i; 0..dim) - { - auto elem = (*e.elements)[i]; - if (elem) - elem.accept(this); - else - buf.writestring("null_"); - } - } - else buf.setsize(0); + return; + } + + writeMangledName(ty.sym); + auto dim = e.elements.length; + foreach (i; 0..dim) + { + auto elem = (*e.elements)[i]; + if (elem) + elem.accept(this); + else + buf.writestring("null_"); + } } override void visit(ArrayLiteralExp) { buf.setsize(0); } diff --git a/gcc/d/dmd/nogc.d b/gcc/d/dmd/nogc.d index 0676d56..cedc4a4 100644 --- a/gcc/d/dmd/nogc.d +++ b/gcc/d/dmd/nogc.d @@ -18,17 +18,23 @@ import core.stdc.stdio; import dmd.aggregate; import dmd.astenums; import dmd.declaration; +import dmd.dmodule; import dmd.dscope; import dmd.dtemplate : isDsymbol; +import dmd.dsymbol : PASS; import dmd.errors; import dmd.expression; import dmd.func; import dmd.globals; import dmd.init; +import dmd.location; import dmd.mtype; -import dmd.postordervisitor; +import dmd.rootobject : RootObject, DYNCAST; +import dmd.semantic2; +import dmd.semantic3; import dmd.tokens; import dmd.visitor; +import dmd.visitor.postorder; /************************************** * Look for GC-allocations @@ -234,6 +240,18 @@ Expression checkGC(Scope* sc, Expression e) return e; } +extern (D) void printGCUsage(FuncDeclaration fd, const ref Loc loc, const(char)* warn) +{ + if (!global.params.v.gc) + return; + + Module m = fd.getModule(); + if (m && m.isRoot() && !fd.inUnittest()) + { + message(loc, "vgc: %s", warn); + } +} + /** * Removes `_d_HookTraceImpl` if found from `fd`. * This is needed to be able to find hooks that are called though the hook's `*Trace` wrapper. @@ -244,7 +262,6 @@ private FuncDeclaration stripHookTraceImpl(FuncDeclaration fd) { import dmd.id : Id; import dmd.dsymbol : Dsymbol; - import dmd.rootobject : RootObject, DYNCAST; if (fd.ident != Id._d_HookTraceImpl) return fd; @@ -256,3 +273,68 @@ private FuncDeclaration stripHookTraceImpl(FuncDeclaration fd) assert(s, "Expected _d_HookTraceImpl's second template parameter to be an alias to the hook!"); return s.isFuncDeclaration; } + +/************************************** + * The function is doing something that may allocate with the GC, + * so mark it as not nogc (not no-how). + * + * Params: + * fd = function + * loc = location of GC action + * fmt = format string for error message. Must include "%s `%s`" for the function kind and name. + * arg0 = (optional) argument to format string + * + * Returns: + * true if function is marked as @nogc, meaning a user error occurred + */ +extern (D) bool setGC(FuncDeclaration fd, Loc loc, const(char)* fmt, RootObject arg0 = null) +{ + //printf("setGC() %s\n", toChars()); + if (fd.nogcInprocess && fd.semanticRun < PASS.semantic3 && fd._scope) + { + fd.semantic2(fd._scope); + fd.semantic3(fd._scope); + } + + if (fd.nogcInprocess) + { + fd.nogcInprocess = false; + if (fmt) + fd.nogcViolation = new AttributeViolation(loc, fmt, fd, arg0); // action that requires GC + else if (arg0) + fd.nogcViolation = new AttributeViolation(loc, fmt, arg0); // call to non-@nogc function + + fd.type.toTypeFunction().isNogc = false; + if (fd.fes) + fd.fes.func.setGC(Loc.init, null, null); + } + else if (fd.isNogc()) + return true; + return false; +} + +/************************************** + * The function calls non-`@nogc` function f, mark it as not nogc. + * Params: + * fd = function doin the call + * f = function being called + * Returns: + * true if function is marked as @nogc, meaning a user error occurred + */ +extern (D) bool setGCCall(FuncDeclaration fd, FuncDeclaration f) +{ + return fd.setGC(fd.loc, null, f); +} + + bool isNogc(FuncDeclaration fd) +{ + //printf("isNogc() %s, inprocess: %d\n", toChars(), !!(flags & FUNCFLAG.nogcInprocess)); + if (fd.nogcInprocess) + fd.setGC(fd.loc, null); + return fd.type.toTypeFunction().isNogc; +} + +extern (D) bool isNogcBypassingInference(FuncDeclaration fd) +{ + return !fd.nogcInprocess && fd.isNogc(); +} diff --git a/gcc/d/dmd/ob.d b/gcc/d/dmd/ob.d index ed73233..ee4b652 100644 --- a/gcc/d/dmd/ob.d +++ b/gcc/d/dmd/ob.d @@ -32,7 +32,7 @@ import dmd.dtemplate; import dmd.errors; import dmd.escape; import dmd.expression; -import dmd.foreachvar; + import dmd.func; import dmd.globals; import dmd.hdrgen; @@ -46,6 +46,7 @@ import dmd.stmtstate; import dmd.tokens; import dmd.typesem; import dmd.visitor; +import dmd.visitor.foreachvar; import dmd.root.bitarray; import dmd.common.outbuffer; diff --git a/gcc/d/dmd/opover.d b/gcc/d/dmd/opover.d index 22fbbc4..c126b1d 100644 --- a/gcc/d/dmd/opover.d +++ b/gcc/d/dmd/opover.d @@ -277,164 +277,17 @@ private Expression checkAliasThisForRhs(AggregateDeclaration ad, Scope* sc, BinE */ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) { - Expression visit(Expression e) - { - assert(0); - } - - Expression visitUna(UnaExp e) - { - //printf("UnaExp::op_overload() (%s)\n", e.toChars()); - Expression result; - if (auto ae = e.e1.isArrayExp()) - { - ae.e1 = ae.e1.expressionSemantic(sc); - ae.e1 = resolveProperties(sc, ae.e1); - Expression ae1old = ae.e1; - const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval); - IntervalExp ie = null; - if (maybeSlice && ae.arguments.length) - { - ie = (*ae.arguments)[0].isIntervalExp(); - } - Type att = null; // first cyclic `alias this` type - while (true) - { - if (ae.e1.op == EXP.error) - { - return ae.e1; - } - Expression e0 = null; - Expression ae1save = ae.e1; - ae.lengthVar = null; - Type t1b = ae.e1.type.toBasetype(); - AggregateDeclaration ad = isAggregate(t1b); - if (!ad) - break; - if (search_function(ad, Id.opIndexUnary)) - { - // Deal with $ - result = resolveOpDollar(sc, ae, &e0); - if (!result) // op(a[i..j]) might be: a.opSliceUnary!(op)(i, j) - goto Lfallback; - if (result.op == EXP.error) - return result; - /* Rewrite op(a[arguments]) as: - * a.opIndexUnary!(op)(arguments) - */ - Expressions* a = ae.arguments.copy(); - Objects* tiargs = opToArg(sc, e.op); - result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexUnary, tiargs); - result = new CallExp(e.loc, result, a); - if (maybeSlice) // op(a[]) might be: a.opSliceUnary!(op)() - result = result.trySemantic(sc); - else - result = result.expressionSemantic(sc); - if (result) - { - return Expression.combine(e0, result); - } - } - Lfallback: - if (maybeSlice && search_function(ad, Id.opSliceUnary)) - { - // Deal with $ - result = resolveOpDollar(sc, ae, ie, &e0); - if (result.op == EXP.error) - return result; - /* Rewrite op(a[i..j]) as: - * a.opSliceUnary!(op)(i, j) - */ - auto a = new Expressions(); - if (ie) - { - a.push(ie.lwr); - a.push(ie.upr); - } - Objects* tiargs = opToArg(sc, e.op); - result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceUnary, tiargs); - result = new CallExp(e.loc, result, a); - result = result.expressionSemantic(sc); - result = Expression.combine(e0, result); - return result; - } - // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) - { - /* Rewrite op(a[arguments]) as: - * op(a.aliasthis[arguments]) - */ - ae.e1 = resolveAliasThis(sc, ae1save, true); - if (ae.e1) - continue; - } - break; - } - ae.e1 = ae1old; // recovery - ae.lengthVar = null; - } - e.e1 = e.e1.expressionSemantic(sc); - e.e1 = resolveProperties(sc, e.e1); - Type att = null; // first cyclic `alias this` type - while (1) - { - if (e.e1.op == EXP.error) - { - return e.e1; - } - - AggregateDeclaration ad = isAggregate(e.e1.type); - if (!ad) - break; - - Dsymbol fd = null; - /* Rewrite as: - * e1.opUnary!(op)() - */ - fd = search_function(ad, Id.opUnary); - if (fd) - { - Objects* tiargs = opToArg(sc, e.op); - result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs); - result = new CallExp(e.loc, result); - result = result.expressionSemantic(sc); - return result; - } - // D1-style operator overloads, deprecated - if (e.op != EXP.prePlusPlus && e.op != EXP.preMinusMinus) - { - auto id = opId(e); - fd = search_function(ad, id); - if (fd) - { - // @@@DEPRECATED_2.110@@@. - // Deprecated in 2.088, made an error in 2.100 - error(e.loc, "`%s` is obsolete. Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr); - return ErrorExp.get(); - } - } - // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type)) - { - /* Rewrite op(e1) as: - * op(e1.aliasthis) - */ - //printf("att una %s e1 = %s\n", EXPtoString(op).ptr, this.e1.type.toChars()); - if (auto e1 = resolveAliasThis(sc, e.e1, true)) - { - e.e1 = e1; - continue; - } - break; - } - break; - } - return result; - } + Expression visit(Expression e) + { + assert(0); + } - Expression visitArray(ArrayExp ae) + Expression visitUna(UnaExp e) + { + //printf("UnaExp::op_overload() (%s)\n", e.toChars()); + Expression result; + if (auto ae = e.e1.isArrayExp()) { - //printf("ArrayExp::op_overload() (%s)\n", ae.toChars()); ae.e1 = ae.e1.expressionSemantic(sc); ae.e1 = resolveProperties(sc, ae.e1); Expression ae1old = ae.e1; @@ -444,7 +297,6 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) { ie = (*ae.arguments)[0].isIntervalExp(); } - Expression result; Type att = null; // first cyclic `alias this` type while (true) { @@ -458,43 +310,23 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) Type t1b = ae.e1.type.toBasetype(); AggregateDeclaration ad = isAggregate(t1b); if (!ad) - { - // If the non-aggregate expression ae.e1 is indexable or sliceable, - // convert it to the corresponding concrete expression. - if (isIndexableNonAggregate(t1b) || ae.e1.op == EXP.type) - { - // Convert to SliceExp - if (maybeSlice) - { - result = new SliceExp(ae.loc, ae.e1, ie); - result = result.expressionSemantic(sc); - return result; - } - // Convert to IndexExp - if (ae.arguments.length == 1) - { - result = new IndexExp(ae.loc, ae.e1, (*ae.arguments)[0]); - result = result.expressionSemantic(sc); - return result; - } - } break; - } - if (search_function(ad, Id.index)) + if (search_function(ad, Id.opIndexUnary)) { // Deal with $ result = resolveOpDollar(sc, ae, &e0); - if (!result) // a[i..j] might be: a.opSlice(i, j) + if (!result) // op(a[i..j]) might be: a.opSliceUnary!(op)(i, j) goto Lfallback; if (result.op == EXP.error) return result; - /* Rewrite e1[arguments] as: - * e1.opIndex(arguments) + /* Rewrite op(a[arguments]) as: + * a.opIndexUnary!(op)(arguments) */ Expressions* a = ae.arguments.copy(); - result = new DotIdExp(ae.loc, ae.e1, Id.index); - result = new CallExp(ae.loc, result, a); - if (maybeSlice) // a[] might be: a.opSlice() + Objects* tiargs = opToArg(sc, e.op); + result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexUnary, tiargs); + result = new CallExp(e.loc, result, a); + if (maybeSlice) // op(a[]) might be: a.opSliceUnary!(op)() result = result.trySemantic(sc); else result = result.expressionSemantic(sc); @@ -504,27 +336,14 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) } } Lfallback: - if (maybeSlice && ae.e1.op == EXP.type) - { - result = new SliceExp(ae.loc, ae.e1, ie); - result = result.expressionSemantic(sc); - result = Expression.combine(e0, result); - return result; - } - if (maybeSlice && search_function(ad, Id.slice)) + if (maybeSlice && search_function(ad, Id.opSliceUnary)) { // Deal with $ result = resolveOpDollar(sc, ae, ie, &e0); - if (result.op == EXP.error) - { - if (!e0 && !search_function(ad, Id.dollar)) { - ae.loc.errorSupplemental("Aggregate declaration '%s' does not define 'opDollar'", ae.e1.toChars()); - } return result; - } - /* Rewrite a[i..j] as: - * a.opSlice(i, j) + /* Rewrite op(a[i..j]) as: + * a.opSliceUnary!(op)(i, j) */ auto a = new Expressions(); if (ie) @@ -532,8 +351,9 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) a.push(ie.lwr); a.push(ie.upr); } - result = new DotIdExp(ae.loc, ae.e1, Id.slice); - result = new CallExp(ae.loc, result, a); + Objects* tiargs = opToArg(sc, e.op); + result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceUnary, tiargs); + result = new CallExp(e.loc, result, a); result = result.expressionSemantic(sc); result = Expression.combine(e0, result); return result; @@ -541,7 +361,6 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) // Didn't find it. Forward to aliasthis if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) { - //printf("att arr e1 = %s\n", this.e1.type.toChars()); /* Rewrite op(a[arguments]) as: * op(a.aliasthis[arguments]) */ @@ -553,727 +372,908 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) } ae.e1 = ae1old; // recovery ae.lengthVar = null; - return result; } - - /*********************************************** - * This is mostly the same as UnaryExp::op_overload(), but has - * a different rewrite. - */ - Expression visitCast(CastExp e, Type att = null) + e.e1 = e.e1.expressionSemantic(sc); + e.e1 = resolveProperties(sc, e.e1); + Type att = null; // first cyclic `alias this` type + while (1) { - //printf("CastExp::op_overload() (%s)\n", e.toChars()); - Expression result; - if (AggregateDeclaration ad = isAggregate(e.e1.type)) + if (e.e1.op == EXP.error) { - Dsymbol fd = null; - /* Rewrite as: - * e1.opCast!(T)() - */ - fd = search_function(ad, Id._cast); + return e.e1; + } + + AggregateDeclaration ad = isAggregate(e.e1.type); + if (!ad) + break; + + Dsymbol fd = null; + /* Rewrite as: + * e1.opUnary!(op)() + */ + fd = search_function(ad, Id.opUnary); + if (fd) + { + Objects* tiargs = opToArg(sc, e.op); + result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs); + result = new CallExp(e.loc, result); + result = result.expressionSemantic(sc); + return result; + } + // D1-style operator overloads, deprecated + if (e.op != EXP.prePlusPlus && e.op != EXP.preMinusMinus) + { + auto id = opId(e); + fd = search_function(ad, id); if (fd) { - version (all) - { - // Backwards compatibility with D1 if opCast is a function, not a template - if (fd.isFuncDeclaration()) - { - // Rewrite as: e1.opCast() - return build_overload(e.loc, sc, e.e1, null, fd); - } - } - auto tiargs = new Objects(); - tiargs.push(e.to); - result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs); - result = new CallExp(e.loc, result); - result = result.expressionSemantic(sc); - return result; + // @@@DEPRECATED_2.110@@@. + // Deprecated in 2.088, made an error in 2.100 + error(e.loc, "`%s` is obsolete. Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr); + return ErrorExp.get(); } - // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type)) + } + // Didn't find it. Forward to aliasthis + if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type)) + { + /* Rewrite op(e1) as: + * op(e1.aliasthis) + */ + //printf("att una %s e1 = %s\n", EXPtoString(op).ptr, this.e1.type.toChars()); + if (auto e1 = resolveAliasThis(sc, e.e1, true)) { - /* Rewrite op(e1) as: - * op(e1.aliasthis) - */ - if (auto e1 = resolveAliasThis(sc, e.e1, true)) - { - result = e.copy(); - (cast(UnaExp)result).e1 = e1; - result = visitCast(result.isCastExp(), att); - return result; - } + e.e1 = e1; + continue; } + break; } - return result; + break; } + return result; + } - Expression visitBin(BinExp e) + Expression visitArray(ArrayExp ae) + { + //printf("ArrayExp::op_overload() (%s)\n", ae.toChars()); + ae.e1 = ae.e1.expressionSemantic(sc); + ae.e1 = resolveProperties(sc, ae.e1); + Expression ae1old = ae.e1; + const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval); + IntervalExp ie = null; + if (maybeSlice && ae.arguments.length) { - //printf("BinExp::op_overload() (%s)\n", e.toChars()); - Identifier id = opId(e); - Identifier id_r = opId_r(e); - int argsset = 0; - AggregateDeclaration ad1 = isAggregate(e.e1.type); - AggregateDeclaration ad2 = isAggregate(e.e2.type); - if (e.op == EXP.assign && ad1 == ad2) - { - StructDeclaration sd = ad1.isStructDeclaration(); - if (sd && - (!sd.hasIdentityAssign || - /* Do a blit if we can and the rvalue is something like .init, - * where a postblit is not necessary. - */ - (sd.hasBlitAssign && !e.e2.isLvalue()))) - { - /* This is bitwise struct assignment. */ - return null; - } - } - Dsymbol s = null; - Dsymbol s_r = null; - Objects* tiargs = null; - if (e.op == EXP.plusPlus || e.op == EXP.minusMinus) + ie = (*ae.arguments)[0].isIntervalExp(); + } + Expression result; + Type att = null; // first cyclic `alias this` type + while (true) + { + if (ae.e1.op == EXP.error) { - // Bug4099 fix - if (ad1 && search_function(ad1, Id.opUnary)) - return null; + return ae.e1; } - if (e.op != EXP.equal && e.op != EXP.notEqual && e.op != EXP.assign && e.op != EXP.plusPlus && e.op != EXP.minusMinus) + Expression e0 = null; + Expression ae1save = ae.e1; + ae.lengthVar = null; + Type t1b = ae.e1.type.toBasetype(); + AggregateDeclaration ad = isAggregate(t1b); + if (!ad) { - /* Try opBinary and opBinaryRight - */ - if (ad1) + // If the non-aggregate expression ae.e1 is indexable or sliceable, + // convert it to the corresponding concrete expression. + if (isIndexableNonAggregate(t1b) || ae.e1.op == EXP.type) { - s = search_function(ad1, Id.opBinary); - if (s && !s.isTemplateDeclaration()) + // Convert to SliceExp + if (maybeSlice) { - error(e.e1.loc, "`%s.opBinary` isn't a template", e.e1.toChars()); - return ErrorExp.get(); + result = new SliceExp(ae.loc, ae.e1, ie); + result = result.expressionSemantic(sc); + return result; } - } - if (ad2) - { - s_r = search_function(ad2, Id.opBinaryRight); - if (s_r && !s_r.isTemplateDeclaration()) + // Convert to IndexExp + if (ae.arguments.length == 1) { - error(e.e2.loc, "`%s.opBinaryRight` isn't a template", e.e2.toChars()); - return ErrorExp.get(); + result = new IndexExp(ae.loc, ae.e1, (*ae.arguments)[0]); + result = result.expressionSemantic(sc); + return result; } - if (s_r && s_r == s) // https://issues.dlang.org/show_bug.cgi?id=12778 - s_r = null; } - // Set tiargs, the template argument list, which will be the operator string - if (s || s_r) + break; + } + if (search_function(ad, Id.index)) + { + // Deal with $ + result = resolveOpDollar(sc, ae, &e0); + if (!result) // a[i..j] might be: a.opSlice(i, j) + goto Lfallback; + if (result.op == EXP.error) + return result; + /* Rewrite e1[arguments] as: + * e1.opIndex(arguments) + */ + Expressions* a = ae.arguments.copy(); + result = new DotIdExp(ae.loc, ae.e1, Id.index); + result = new CallExp(ae.loc, result, a); + if (maybeSlice) // a[] might be: a.opSlice() + result = result.trySemantic(sc); + else + result = result.expressionSemantic(sc); + if (result) { - id = Id.opBinary; - id_r = Id.opBinaryRight; - tiargs = opToArg(sc, e.op); + return Expression.combine(e0, result); } } - if (!s && !s_r) + Lfallback: + if (maybeSlice && ae.e1.op == EXP.type) + { + result = new SliceExp(ae.loc, ae.e1, ie); + result = result.expressionSemantic(sc); + result = Expression.combine(e0, result); + return result; + } + if (maybeSlice && search_function(ad, Id.slice)) { - // Try the D1-style operators, deprecated - if (ad1 && id) + // Deal with $ + result = resolveOpDollar(sc, ae, ie, &e0); + + if (result.op == EXP.error) { - s = search_function(ad1, id); - if (s && id != Id.assign) - { - // @@@DEPRECATED_2.110@@@. - // Deprecated in 2.088, made an error in 2.100 - if (id == Id.postinc || id == Id.postdec) - error(e.loc, "`%s` is obsolete. Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr); - else - error(e.loc, "`%s` is obsolete. Use `opBinary(string op)(...) if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr); - return ErrorExp.get(); + if (!e0 && !search_function(ad, Id.dollar)) { + ae.loc.errorSupplemental("Aggregate declaration '%s' does not define 'opDollar'", ae.e1.toChars()); } + return result; } - if (ad2 && id_r) + /* Rewrite a[i..j] as: + * a.opSlice(i, j) + */ + auto a = new Expressions(); + if (ie) { - s_r = search_function(ad2, id_r); - // https://issues.dlang.org/show_bug.cgi?id=12778 - // If both x.opBinary(y) and y.opBinaryRight(x) found, - // and they are exactly same symbol, x.opBinary(y) should be preferred. - if (s_r && s_r == s) - s_r = null; - if (s_r) - { - // @@@DEPRECATED_2.110@@@. - // Deprecated in 2.088, made an error in 2.100 - error(e.loc, "`%s` is obsolete. Use `opBinaryRight(string op)(...) if (op == \"%s\")` instead.", id_r.toChars(), EXPtoString(e.op).ptr); - return ErrorExp.get(); - } + a.push(ie.lwr); + a.push(ie.upr); } + result = new DotIdExp(ae.loc, ae.e1, Id.slice); + result = new CallExp(ae.loc, result, a); + result = result.expressionSemantic(sc); + result = Expression.combine(e0, result); + return result; } - Expressions* args1 = new Expressions(); - Expressions* args2 = new Expressions(); - if (s || s_r) + // Didn't find it. Forward to aliasthis + if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) { - /* Try: - * a.opfunc(b) - * b.opfunc_r(a) - * and see which is better. + //printf("att arr e1 = %s\n", this.e1.type.toChars()); + /* Rewrite op(a[arguments]) as: + * op(a.aliasthis[arguments]) */ - args1.setDim(1); - (*args1)[0] = e.e1; - expandTuples(args1); - args2.setDim(1); - (*args2)[0] = e.e2; - expandTuples(args2); - argsset = 1; - MatchAccumulator m; - if (s) + ae.e1 = resolveAliasThis(sc, ae1save, true); + if (ae.e1) + continue; + } + break; + } + ae.e1 = ae1old; // recovery + ae.lengthVar = null; + return result; + } + + /*********************************************** + * This is mostly the same as UnaryExp::op_overload(), but has + * a different rewrite. + */ + Expression visitCast(CastExp e, Type att = null) + { + //printf("CastExp::op_overload() (%s)\n", e.toChars()); + Expression result; + if (AggregateDeclaration ad = isAggregate(e.e1.type)) + { + Dsymbol fd = null; + /* Rewrite as: + * e1.opCast!(T)() + */ + fd = search_function(ad, Id._cast); + if (fd) + { + version (all) { - functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) + // Backwards compatibility with D1 if opCast is a function, not a template + if (fd.isFuncDeclaration()) { - return ErrorExp.get(); + // Rewrite as: e1.opCast() + return build_overload(e.loc, sc, e.e1, null, fd); } } - FuncDeclaration lastf = m.lastf; - if (s_r) + auto tiargs = new Objects(); + tiargs.push(e.to); + result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs); + result = new CallExp(e.loc, result); + result = result.expressionSemantic(sc); + return result; + } + // Didn't find it. Forward to aliasthis + if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type)) + { + /* Rewrite op(e1) as: + * op(e1.aliasthis) + */ + if (auto e1 = resolveAliasThis(sc, e.e1, true)) { - functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) - { - return ErrorExp.get(); - } + result = e.copy(); + (cast(UnaExp)result).e1 = e1; + result = visitCast(result.isCastExp(), att); + return result; + } + } + } + return result; + } + + Expression visitBin(BinExp e) + { + //printf("BinExp::op_overload() (%s)\n", e.toChars()); + Identifier id = opId(e); + Identifier id_r = opId_r(e); + int argsset = 0; + AggregateDeclaration ad1 = isAggregate(e.e1.type); + AggregateDeclaration ad2 = isAggregate(e.e2.type); + if (e.op == EXP.assign && ad1 == ad2) + { + StructDeclaration sd = ad1.isStructDeclaration(); + if (sd && + (!sd.hasIdentityAssign || + /* Do a blit if we can and the rvalue is something like .init, + * where a postblit is not necessary. + */ + (sd.hasBlitAssign && !e.e2.isLvalue()))) + { + /* This is bitwise struct assignment. */ + return null; + } + } + Dsymbol s = null; + Dsymbol s_r = null; + Objects* tiargs = null; + if (e.op == EXP.plusPlus || e.op == EXP.minusMinus) + { + // Bug4099 fix + if (ad1 && search_function(ad1, Id.opUnary)) + return null; + } + if (e.op != EXP.equal && e.op != EXP.notEqual && e.op != EXP.assign && e.op != EXP.plusPlus && e.op != EXP.minusMinus) + { + /* Try opBinary and opBinaryRight + */ + if (ad1) + { + s = search_function(ad1, Id.opBinary); + if (s && !s.isTemplateDeclaration()) + { + error(e.e1.loc, "`%s.opBinary` isn't a template", e.e1.toChars()); + return ErrorExp.get(); } - if (m.count > 1) + } + if (ad2) + { + s_r = search_function(ad2, Id.opBinaryRight); + if (s_r && !s_r.isTemplateDeclaration()) { - // Error, ambiguous - error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); + error(e.e2.loc, "`%s.opBinaryRight` isn't a template", e.e2.toChars()); + return ErrorExp.get(); } - else if (m.last == MATCH.nomatch) + if (s_r && s_r == s) // https://issues.dlang.org/show_bug.cgi?id=12778 + s_r = null; + } + // Set tiargs, the template argument list, which will be the operator string + if (s || s_r) + { + id = Id.opBinary; + id_r = Id.opBinaryRight; + tiargs = opToArg(sc, e.op); + } + } + if (!s && !s_r) + { + // Try the D1-style operators, deprecated + if (ad1 && id) + { + s = search_function(ad1, id); + if (s && id != Id.assign) { - if (tiargs) - goto L1; - m.lastf = null; + // @@@DEPRECATED_2.110@@@. + // Deprecated in 2.088, made an error in 2.100 + if (id == Id.postinc || id == Id.postdec) + error(e.loc, "`%s` is obsolete. Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr); + else + error(e.loc, "`%s` is obsolete. Use `opBinary(string op)(...) if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr); + return ErrorExp.get(); } - if (e.op == EXP.plusPlus || e.op == EXP.minusMinus) + } + if (ad2 && id_r) + { + s_r = search_function(ad2, id_r); + // https://issues.dlang.org/show_bug.cgi?id=12778 + // If both x.opBinary(y) and y.opBinaryRight(x) found, + // and they are exactly same symbol, x.opBinary(y) should be preferred. + if (s_r && s_r == s) + s_r = null; + if (s_r) { - // Kludge because operator overloading regards e++ and e-- - // as unary, but it's implemented as a binary. - // Rewrite (e1 ++ e2) as e1.postinc() - // Rewrite (e1 -- e2) as e1.postdec() - return build_overload(e.loc, sc, e.e1, null, m.lastf ? m.lastf : s); + // @@@DEPRECATED_2.110@@@. + // Deprecated in 2.088, made an error in 2.100 + error(e.loc, "`%s` is obsolete. Use `opBinaryRight(string op)(...) if (op == \"%s\")` instead.", id_r.toChars(), EXPtoString(e.op).ptr); + return ErrorExp.get(); } - else if (lastf && m.lastf == lastf || !s_r && m.last == MATCH.nomatch) + } + } + Expressions* args1 = new Expressions(); + Expressions* args2 = new Expressions(); + if (s || s_r) + { + /* Try: + * a.opfunc(b) + * b.opfunc_r(a) + * and see which is better. + */ + args1.setDim(1); + (*args1)[0] = e.e1; + expandTuples(args1); + args2.setDim(1); + (*args2)[0] = e.e2; + expandTuples(args2); + argsset = 1; + MatchAccumulator m; + if (s) + { + functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2)); + if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) { - // Rewrite (e1 op e2) as e1.opfunc(e2) - return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s); + return ErrorExp.get(); } - else + } + FuncDeclaration lastf = m.lastf; + if (s_r) + { + functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1)); + if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) { - // Rewrite (e1 op e2) as e2.opfunc_r(e1) - return build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r); + return ErrorExp.get(); } } - L1: - version (all) + if (m.count > 1) + { + // Error, ambiguous + error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); + } + else if (m.last == MATCH.nomatch) + { + if (tiargs) + goto L1; + m.lastf = null; + } + if (e.op == EXP.plusPlus || e.op == EXP.minusMinus) + { + // Kludge because operator overloading regards e++ and e-- + // as unary, but it's implemented as a binary. + // Rewrite (e1 ++ e2) as e1.postinc() + // Rewrite (e1 -- e2) as e1.postdec() + return build_overload(e.loc, sc, e.e1, null, m.lastf ? m.lastf : s); + } + else if (lastf && m.lastf == lastf || !s_r && m.last == MATCH.nomatch) + { + // Rewrite (e1 op e2) as e1.opfunc(e2) + return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s); + } + else { - // Retained for D1 compatibility - if (isCommutative(e.op) && !tiargs) + // Rewrite (e1 op e2) as e2.opfunc_r(e1) + return build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r); + } + } + L1: + version (all) + { + // Retained for D1 compatibility + if (isCommutative(e.op) && !tiargs) + { + s = null; + s_r = null; + if (ad1 && id_r) { - s = null; - s_r = null; - if (ad1 && id_r) - { - s_r = search_function(ad1, id_r); - } - if (ad2 && id) + s_r = search_function(ad1, id_r); + } + if (ad2 && id) + { + s = search_function(ad2, id); + if (s && s == s_r) // https://issues.dlang.org/show_bug.cgi?id=12778 + s = null; + } + if (s || s_r) + { + /* Try: + * a.opfunc_r(b) + * b.opfunc(a) + * and see which is better. + */ + if (!argsset) { - s = search_function(ad2, id); - if (s && s == s_r) // https://issues.dlang.org/show_bug.cgi?id=12778 - s = null; + args1.setDim(1); + (*args1)[0] = e.e1; + expandTuples(args1); + args2.setDim(1); + (*args2)[0] = e.e2; + expandTuples(args2); } - if (s || s_r) + MatchAccumulator m; + if (s_r) { - /* Try: - * a.opfunc_r(b) - * b.opfunc(a) - * and see which is better. - */ - if (!argsset) - { - args1.setDim(1); - (*args1)[0] = e.e1; - expandTuples(args1); - args2.setDim(1); - (*args2)[0] = e.e2; - expandTuples(args2); - } - MatchAccumulator m; - if (s_r) + functionResolve(m, s_r, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2)); + if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) { - functionResolve(m, s_r, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) - { - return ErrorExp.get(); - } + return ErrorExp.get(); } - FuncDeclaration lastf = m.lastf; - if (s) - { - functionResolve(m, s, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) - { - return ErrorExp.get(); - } - } - if (m.count > 1) - { - // Error, ambiguous - error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); - } - else if (m.last == MATCH.nomatch) + } + FuncDeclaration lastf = m.lastf; + if (s) + { + functionResolve(m, s, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1)); + if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) { - m.lastf = null; + return ErrorExp.get(); } + } + if (m.count > 1) + { + // Error, ambiguous + error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); + } + else if (m.last == MATCH.nomatch) + { + m.lastf = null; + } - if (lastf && m.lastf == lastf || !s && m.last == MATCH.nomatch) - { - // Rewrite (e1 op e2) as e1.opfunc_r(e2) - return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s_r); - } - else - { - // Rewrite (e1 op e2) as e2.opfunc(e1) - Expression result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s); - // When reversing operands of comparison operators, - // need to reverse the sense of the op - if (pop) - *pop = reverseRelation(e.op); - return result; - } + if (lastf && m.lastf == lastf || !s && m.last == MATCH.nomatch) + { + // Rewrite (e1 op e2) as e1.opfunc_r(e2) + return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s_r); + } + else + { + // Rewrite (e1 op e2) as e2.opfunc(e1) + Expression result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s); + // When reversing operands of comparison operators, + // need to reverse the sense of the op + if (pop) + *pop = reverseRelation(e.op); + return result; } } } + } - Expression rewrittenLhs; - if (!(e.op == EXP.assign && ad2 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943 + Expression rewrittenLhs; + if (!(e.op == EXP.assign && ad2 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943 + { + if (Expression result = checkAliasThisForLhs(ad1, sc, e)) { - if (Expression result = checkAliasThisForLhs(ad1, sc, e)) - { - /* https://issues.dlang.org/show_bug.cgi?id=19441 - * - * alias this may not be used for partial assignment. - * If a struct has a single member which is aliased this - * directly or aliased to a ref getter function that returns - * the mentioned member, then alias this may be - * used since the object will be fully initialised. - * If the struct is nested, the context pointer is considered - * one of the members, hence the `ad1.fields.length == 2 && ad1.vthis` - * condition. - */ - if (result.op != EXP.assign) - return result; // i.e: Rewrote `e1 = e2` -> `e1(e2)` + /* https://issues.dlang.org/show_bug.cgi?id=19441 + * + * alias this may not be used for partial assignment. + * If a struct has a single member which is aliased this + * directly or aliased to a ref getter function that returns + * the mentioned member, then alias this may be + * used since the object will be fully initialised. + * If the struct is nested, the context pointer is considered + * one of the members, hence the `ad1.fields.length == 2 && ad1.vthis` + * condition. + */ + if (result.op != EXP.assign) + return result; // i.e: Rewrote `e1 = e2` -> `e1(e2)` - auto ae = result.isAssignExp(); - if (ae.e1.op != EXP.dotVariable) - return result; // i.e: Rewrote `e1 = e2` -> `e1() = e2` + auto ae = result.isAssignExp(); + if (ae.e1.op != EXP.dotVariable) + return result; // i.e: Rewrote `e1 = e2` -> `e1() = e2` - auto dve = ae.e1.isDotVarExp(); - if (auto ad = dve.var.isMember2()) + auto dve = ae.e1.isDotVarExp(); + if (auto ad = dve.var.isMember2()) + { + // i.e: Rewrote `e1 = e2` -> `e1.some.var = e2` + // Ensure that `var` is the only field member in `ad` + if (ad.fields.length == 1 || (ad.fields.length == 2 && ad.vthis)) { - // i.e: Rewrote `e1 = e2` -> `e1.some.var = e2` - // Ensure that `var` is the only field member in `ad` - if (ad.fields.length == 1 || (ad.fields.length == 2 && ad.vthis)) - { - if (dve.var == ad.aliasthis.sym) - return result; - } + if (dve.var == ad.aliasthis.sym) + return result; } - rewrittenLhs = ae.e1; } + rewrittenLhs = ae.e1; } - if (!(e.op == EXP.assign && ad1 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943 - { - if (Expression result = checkAliasThisForRhs(ad2, sc, e)) - return result; - } - if (rewrittenLhs) - { - error(e.loc, "cannot use `alias this` to partially initialize variable `%s` of type `%s`. Use `%s`", - e.e1.toChars(), ad1.toChars(), rewrittenLhs.toChars()); - return ErrorExp.get(); - } - return null; } - - Expression visitEqual(EqualExp e) + if (!(e.op == EXP.assign && ad1 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943 { - //printf("EqualExp::op_overload() (%s)\n", e.toChars()); - Type t1 = e.e1.type.toBasetype(); - Type t2 = e.e2.type.toBasetype(); + if (Expression result = checkAliasThisForRhs(ad2, sc, e)) + return result; + } + if (rewrittenLhs) + { + error(e.loc, "cannot use `alias this` to partially initialize variable `%s` of type `%s`. Use `%s`", + e.e1.toChars(), ad1.toChars(), rewrittenLhs.toChars()); + return ErrorExp.get(); + } + return null; + } - /* Array equality is handled by expressionSemantic() potentially - * lowering to object.__equals(), which takes care of overloaded - * operators for the element types. - */ - if ((t1.ty == Tarray || t1.ty == Tsarray) && - (t2.ty == Tarray || t2.ty == Tsarray)) - { - return null; - } + Expression visitEqual(EqualExp e) + { + //printf("EqualExp::op_overload() (%s)\n", e.toChars()); + Type t1 = e.e1.type.toBasetype(); + Type t2 = e.e2.type.toBasetype(); - /* Check for class equality with null literal or typeof(null). - */ - if (t1.ty == Tclass && e.e2.op == EXP.null_ || - t2.ty == Tclass && e.e1.op == EXP.null_) - { - error(e.loc, "use `%s` instead of `%s` when comparing with `null`", - EXPtoString(e.op == EXP.equal ? EXP.identity : EXP.notIdentity).ptr, - EXPtoString(e.op).ptr); - return ErrorExp.get(); - } - if (t1.ty == Tclass && t2.ty == Tnull || - t1.ty == Tnull && t2.ty == Tclass) - { - // Comparing a class with typeof(null) should not call opEquals - return null; - } + /* Array equality is handled by expressionSemantic() potentially + * lowering to object.__equals(), which takes care of overloaded + * operators for the element types. + */ + if ((t1.ty == Tarray || t1.ty == Tsarray) && + (t2.ty == Tarray || t2.ty == Tsarray)) + { + return null; + } - /* Check for class equality. - */ - if (t1.ty == Tclass && t2.ty == Tclass) + /* Check for class equality with null literal or typeof(null). + */ + if (t1.ty == Tclass && e.e2.op == EXP.null_ || + t2.ty == Tclass && e.e1.op == EXP.null_) + { + error(e.loc, "use `%s` instead of `%s` when comparing with `null`", + EXPtoString(e.op == EXP.equal ? EXP.identity : EXP.notIdentity).ptr, + EXPtoString(e.op).ptr); + return ErrorExp.get(); + } + if (t1.ty == Tclass && t2.ty == Tnull || + t1.ty == Tnull && t2.ty == Tclass) + { + // Comparing a class with typeof(null) should not call opEquals + return null; + } + + /* Check for class equality. + */ + if (t1.ty == Tclass && t2.ty == Tclass) + { + ClassDeclaration cd1 = t1.isClassHandle(); + ClassDeclaration cd2 = t2.isClassHandle(); + if (!(cd1.classKind == ClassKind.cpp || cd2.classKind == ClassKind.cpp)) { - ClassDeclaration cd1 = t1.isClassHandle(); - ClassDeclaration cd2 = t2.isClassHandle(); - if (!(cd1.classKind == ClassKind.cpp || cd2.classKind == ClassKind.cpp)) + /* Rewrite as: + * .object.opEquals(e1, e2) + */ + if (!ClassDeclaration.object) { - /* Rewrite as: - * .object.opEquals(e1, e2) - */ - if (!ClassDeclaration.object) - { - error(e.loc, "cannot compare classes for equality because `object.Object` was not declared"); - return null; - } + error(e.loc, "cannot compare classes for equality because `object.Object` was not declared"); + return null; + } - Expression e1x = e.e1; - Expression e2x = e.e2; + Expression e1x = e.e1; + Expression e2x = e.e2; - /* The explicit cast is necessary for interfaces - * https://issues.dlang.org/show_bug.cgi?id=4088 - */ - Type to = ClassDeclaration.object.getType(); - if (cd1.isInterfaceDeclaration()) - e1x = new CastExp(e.loc, e.e1, t1.isMutable() ? to : to.constOf()); - if (cd2.isInterfaceDeclaration()) - e2x = new CastExp(e.loc, e.e2, t2.isMutable() ? to : to.constOf()); - - Expression result = new IdentifierExp(e.loc, Id.empty); - result = new DotIdExp(e.loc, result, Id.object); - result = new DotIdExp(e.loc, result, Id.eq); - result = new CallExp(e.loc, result, e1x, e2x); - if (e.op == EXP.notEqual) - result = new NotExp(e.loc, result); - result = result.expressionSemantic(sc); - return result; - } + /* The explicit cast is necessary for interfaces + * https://issues.dlang.org/show_bug.cgi?id=4088 + */ + Type to = ClassDeclaration.object.getType(); + if (cd1.isInterfaceDeclaration()) + e1x = new CastExp(e.loc, e.e1, t1.isMutable() ? to : to.constOf()); + if (cd2.isInterfaceDeclaration()) + e2x = new CastExp(e.loc, e.e2, t2.isMutable() ? to : to.constOf()); + + Expression result = new IdentifierExp(e.loc, Id.empty); + result = new DotIdExp(e.loc, result, Id.object); + result = new DotIdExp(e.loc, result, Id.eq); + result = new CallExp(e.loc, result, e1x, e2x); + if (e.op == EXP.notEqual) + result = new NotExp(e.loc, result); + result = result.expressionSemantic(sc); + return result; } + } - if (Expression result = compare_overload(e, sc, Id.eq, null)) + if (Expression result = compare_overload(e, sc, Id.eq, null)) + { + if (lastComma(result).op == EXP.call && e.op == EXP.notEqual) { - if (lastComma(result).op == EXP.call && e.op == EXP.notEqual) - { - result = new NotExp(result.loc, result); - result = result.expressionSemantic(sc); - } - return result; + result = new NotExp(result.loc, result); + result = result.expressionSemantic(sc); } + return result; + } - /* Check for pointer equality. + /* Check for pointer equality. + */ + if (t1.ty == Tpointer || t2.ty == Tpointer) + { + /* Rewrite: + * ptr1 == ptr2 + * as: + * ptr1 is ptr2 + * + * This is just a rewriting for deterministic AST representation + * as the backend input. */ - if (t1.ty == Tpointer || t2.ty == Tpointer) + auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity; + Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2); + return r.expressionSemantic(sc); + } + + /* Check for struct equality without opEquals. + */ + if (t1.ty == Tstruct && t2.ty == Tstruct) + { + auto sd = t1.isTypeStruct().sym; + if (sd != t2.isTypeStruct().sym) + return null; + + import dmd.clone : needOpEquals; + if (global.params.fieldwise != FeatureState.enabled && !needOpEquals(sd)) { - /* Rewrite: - * ptr1 == ptr2 - * as: - * ptr1 is ptr2 - * - * This is just a rewriting for deterministic AST representation - * as the backend input. - */ + // Use bitwise equality. auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity; Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2); return r.expressionSemantic(sc); } - /* Check for struct equality without opEquals. + /* Do memberwise equality. + * https://dlang.org/spec/expression.html#equality_expressions + * Rewrite: + * e1 == e2 + * as: + * e1.tupleof == e2.tupleof + * + * If sd is a nested struct, and if it's nested in a class, it will + * also compare the parent class's equality. Otherwise, compares + * the identity of parent context through void*. */ - if (t1.ty == Tstruct && t2.ty == Tstruct) - { - auto sd = t1.isTypeStruct().sym; - if (sd != t2.isTypeStruct().sym) - return null; - - import dmd.clone : needOpEquals; - if (global.params.fieldwise != FeatureState.enabled && !needOpEquals(sd)) - { - // Use bitwise equality. - auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity; - Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2); - return r.expressionSemantic(sc); - } + e = e.copy().isEqualExp(); + e.e1 = new DotIdExp(e.loc, e.e1, Id._tupleof); + e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof); + + auto sc2 = sc.push(); + sc2.noAccessCheck = true; + Expression r = e.expressionSemantic(sc2); + sc2.pop(); + return r; + } - /* Do memberwise equality. - * https://dlang.org/spec/expression.html#equality_expressions - * Rewrite: - * e1 == e2 - * as: - * e1.tupleof == e2.tupleof - * - * If sd is a nested struct, and if it's nested in a class, it will - * also compare the parent class's equality. Otherwise, compares - * the identity of parent context through void*. - */ - e = e.copy().isEqualExp(); - e.e1 = new DotIdExp(e.loc, e.e1, Id._tupleof); - e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof); - - auto sc2 = sc.push(); - sc2.noAccessCheck = true; - Expression r = e.expressionSemantic(sc2); - sc2.pop(); - return r; + /* Check for tuple equality. + */ + if (e.e1.op == EXP.tuple && e.e2.op == EXP.tuple) + { + auto tup1 = e.e1.isTupleExp(); + auto tup2 = e.e2.isTupleExp(); + size_t dim = tup1.exps.length; + if (dim != tup2.exps.length) + { + error(e.loc, "mismatched sequence lengths, `%d` and `%d`", + cast(int)dim, cast(int)tup2.exps.length); + return ErrorExp.get(); } - /* Check for tuple equality. - */ - if (e.e1.op == EXP.tuple && e.e2.op == EXP.tuple) + Expression result; + if (dim == 0) { - auto tup1 = e.e1.isTupleExp(); - auto tup2 = e.e2.isTupleExp(); - size_t dim = tup1.exps.length; - if (dim != tup2.exps.length) - { - error(e.loc, "mismatched sequence lengths, `%d` and `%d`", - cast(int)dim, cast(int)tup2.exps.length); - return ErrorExp.get(); - } - - Expression result; - if (dim == 0) - { - // zero-length tuple comparison should always return true or false. - result = IntegerExp.createBool(e.op == EXP.equal); - } - else + // zero-length tuple comparison should always return true or false. + result = IntegerExp.createBool(e.op == EXP.equal); + } + else + { + for (size_t i = 0; i < dim; i++) { - for (size_t i = 0; i < dim; i++) - { - auto ex1 = (*tup1.exps)[i]; - auto ex2 = (*tup2.exps)[i]; - auto eeq = new EqualExp(e.op, e.loc, ex1, ex2); - - if (!result) - result = eeq; - else if (e.op == EXP.equal) - result = new LogicalExp(e.loc, EXP.andAnd, result, eeq); - else - result = new LogicalExp(e.loc, EXP.orOr, result, eeq); - } - assert(result); + auto ex1 = (*tup1.exps)[i]; + auto ex2 = (*tup2.exps)[i]; + auto eeq = new EqualExp(e.op, e.loc, ex1, ex2); + + if (!result) + result = eeq; + else if (e.op == EXP.equal) + result = new LogicalExp(e.loc, EXP.andAnd, result, eeq); + else + result = new LogicalExp(e.loc, EXP.orOr, result, eeq); } - result = Expression.combine(tup1.e0, tup2.e0, result); - result = result.expressionSemantic(sc); - - return result; + assert(result); } - return null; - } + result = Expression.combine(tup1.e0, tup2.e0, result); + result = result.expressionSemantic(sc); - Expression visitCmp(CmpExp e) - { - //printf("CmpExp:: () (%s)\n", e.toChars()); - return compare_overload(e, sc, Id.cmp, pop); + return result; } + return null; + } - /********************************* - * Operator overloading for op= - */ - Expression visitBinAssign(BinAssignExp e) + Expression visitCmp(CmpExp e) + { + //printf("CmpExp:: () (%s)\n", e.toChars()); + return compare_overload(e, sc, Id.cmp, pop); + } + + /********************************* + * Operator overloading for op= + */ + Expression visitBinAssign(BinAssignExp e) + { + //printf("BinAssignExp::op_overload() (%s)\n", e.toChars()); + if (auto ae = e.e1.isArrayExp()) { - //printf("BinAssignExp::op_overload() (%s)\n", e.toChars()); - if (auto ae = e.e1.isArrayExp()) + ae.e1 = ae.e1.expressionSemantic(sc); + ae.e1 = resolveProperties(sc, ae.e1); + Expression ae1old = ae.e1; + const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval); + IntervalExp ie = null; + if (maybeSlice && ae.arguments.length) { - ae.e1 = ae.e1.expressionSemantic(sc); - ae.e1 = resolveProperties(sc, ae.e1); - Expression ae1old = ae.e1; - const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval); - IntervalExp ie = null; - if (maybeSlice && ae.arguments.length) + ie = (*ae.arguments)[0].isIntervalExp(); + } + Type att = null; // first cyclic `alias this` type + while (true) + { + if (ae.e1.op == EXP.error) { - ie = (*ae.arguments)[0].isIntervalExp(); + return ae.e1; } - Type att = null; // first cyclic `alias this` type - while (true) + Expression e0 = null; + Expression ae1save = ae.e1; + ae.lengthVar = null; + Type t1b = ae.e1.type.toBasetype(); + AggregateDeclaration ad = isAggregate(t1b); + if (!ad) + break; + if (search_function(ad, Id.opIndexOpAssign)) { - if (ae.e1.op == EXP.error) - { - return ae.e1; - } - Expression e0 = null; - Expression ae1save = ae.e1; - ae.lengthVar = null; - Type t1b = ae.e1.type.toBasetype(); - AggregateDeclaration ad = isAggregate(t1b); - if (!ad) - break; - if (search_function(ad, Id.opIndexOpAssign)) + // Deal with $ + Expression result = resolveOpDollar(sc, ae, &e0); + if (!result) // (a[i..j] op= e2) might be: a.opSliceOpAssign!(op)(e2, i, j) + goto Lfallback; + if (result.op == EXP.error) + return result; + result = e.e2.expressionSemantic(sc); + if (result.op == EXP.error) + return result; + e.e2 = result; + /* Rewrite a[arguments] op= e2 as: + * a.opIndexOpAssign!(op)(e2, arguments) + */ + Expressions* a = ae.arguments.copy(); + a.insert(0, e.e2); + Objects* tiargs = opToArg(sc, e.op); + result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexOpAssign, tiargs); + result = new CallExp(e.loc, result, a); + if (maybeSlice) // (a[] op= e2) might be: a.opSliceOpAssign!(op)(e2) + result = result.trySemantic(sc); + else + result = result.expressionSemantic(sc); + if (result) { - // Deal with $ - Expression result = resolveOpDollar(sc, ae, &e0); - if (!result) // (a[i..j] op= e2) might be: a.opSliceOpAssign!(op)(e2, i, j) - goto Lfallback; - if (result.op == EXP.error) - return result; - result = e.e2.expressionSemantic(sc); - if (result.op == EXP.error) - return result; - e.e2 = result; - /* Rewrite a[arguments] op= e2 as: - * a.opIndexOpAssign!(op)(e2, arguments) - */ - Expressions* a = ae.arguments.copy(); - a.insert(0, e.e2); - Objects* tiargs = opToArg(sc, e.op); - result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexOpAssign, tiargs); - result = new CallExp(e.loc, result, a); - if (maybeSlice) // (a[] op= e2) might be: a.opSliceOpAssign!(op)(e2) - result = result.trySemantic(sc); - else - result = result.expressionSemantic(sc); - if (result) - { - return Expression.combine(e0, result); - } + return Expression.combine(e0, result); } - Lfallback: - if (maybeSlice && search_function(ad, Id.opSliceOpAssign)) - { - // Deal with $ - Expression result = resolveOpDollar(sc, ae, ie, &e0); - if (result.op == EXP.error) - return result; - result = e.e2.expressionSemantic(sc); - if (result.op == EXP.error) - return result; - e.e2 = result; - /* Rewrite (a[i..j] op= e2) as: - * a.opSliceOpAssign!(op)(e2, i, j) - */ - auto a = new Expressions(); - a.push(e.e2); - if (ie) - { - a.push(ie.lwr); - a.push(ie.upr); - } - Objects* tiargs = opToArg(sc, e.op); - result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceOpAssign, tiargs); - result = new CallExp(e.loc, result, a); - result = result.expressionSemantic(sc); - result = Expression.combine(e0, result); + } + Lfallback: + if (maybeSlice && search_function(ad, Id.opSliceOpAssign)) + { + // Deal with $ + Expression result = resolveOpDollar(sc, ae, ie, &e0); + if (result.op == EXP.error) return result; - } - // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) + result = e.e2.expressionSemantic(sc); + if (result.op == EXP.error) + return result; + e.e2 = result; + /* Rewrite (a[i..j] op= e2) as: + * a.opSliceOpAssign!(op)(e2, i, j) + */ + auto a = new Expressions(); + a.push(e.e2); + if (ie) { - /* Rewrite (a[arguments] op= e2) as: - * a.aliasthis[arguments] op= e2 - */ - ae.e1 = resolveAliasThis(sc, ae1save, true); - if (ae.e1) - continue; + a.push(ie.lwr); + a.push(ie.upr); } - break; + Objects* tiargs = opToArg(sc, e.op); + result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceOpAssign, tiargs); + result = new CallExp(e.loc, result, a); + result = result.expressionSemantic(sc); + result = Expression.combine(e0, result); + return result; } - ae.e1 = ae1old; // recovery - ae.lengthVar = null; + // Didn't find it. Forward to aliasthis + if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) + { + /* Rewrite (a[arguments] op= e2) as: + * a.aliasthis[arguments] op= e2 + */ + ae.e1 = resolveAliasThis(sc, ae1save, true); + if (ae.e1) + continue; + } + break; } - Expression result = e.binSemanticProp(sc); - if (result) - return result; - // Don't attempt 'alias this' if an error occurred - if (e.e1.type.ty == Terror || e.e2.type.ty == Terror) + ae.e1 = ae1old; // recovery + ae.lengthVar = null; + } + Expression result = e.binSemanticProp(sc); + if (result) + return result; + // Don't attempt 'alias this' if an error occurred + if (e.e1.type.ty == Terror || e.e2.type.ty == Terror) + { + return ErrorExp.get(); + } + Identifier id = opId(e); + Expressions* args2 = new Expressions(); + AggregateDeclaration ad1 = isAggregate(e.e1.type); + Dsymbol s = null; + Objects* tiargs = null; + /* Try opOpAssign + */ + if (ad1) + { + s = search_function(ad1, Id.opOpAssign); + if (s && !s.isTemplateDeclaration()) { + error(e.loc, "`%s.opOpAssign` isn't a template", e.e1.toChars()); return ErrorExp.get(); } - Identifier id = opId(e); - Expressions* args2 = new Expressions(); - AggregateDeclaration ad1 = isAggregate(e.e1.type); - Dsymbol s = null; - Objects* tiargs = null; - /* Try opOpAssign - */ - if (ad1) - { - s = search_function(ad1, Id.opOpAssign); - if (s && !s.isTemplateDeclaration()) - { - error(e.loc, "`%s.opOpAssign` isn't a template", e.e1.toChars()); - return ErrorExp.get(); - } - } - // Set tiargs, the template argument list, which will be the operator string + } + // Set tiargs, the template argument list, which will be the operator string + if (s) + { + id = Id.opOpAssign; + tiargs = opToArg(sc, e.op); + } + + // Try D1-style operator overload, deprecated + if (!s && ad1 && id) + { + s = search_function(ad1, id); if (s) { - id = Id.opOpAssign; - tiargs = opToArg(sc, e.op); + // @@@DEPRECATED_2.110@@@. + // Deprecated in 2.088, made an error in 2.100 + scope char[] op = EXPtoString(e.op).dup; + op[$-1] = '\0'; // remove trailing `=` + error(e.loc, "`%s` is obsolete. Use `opOpAssign(string op)(...) if (op == \"%s\")` instead.", id.toChars(), op.ptr); + return ErrorExp.get(); } + } - // Try D1-style operator overload, deprecated - if (!s && ad1 && id) + if (s) + { + /* Try: + * a.opOpAssign(b) + */ + args2.setDim(1); + (*args2)[0] = e.e2; + expandTuples(args2); + MatchAccumulator m; + functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2)); + if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) { - s = search_function(ad1, id); - if (s) - { - // @@@DEPRECATED_2.110@@@. - // Deprecated in 2.088, made an error in 2.100 - scope char[] op = EXPtoString(e.op).dup; - op[$-1] = '\0'; // remove trailing `=` - error(e.loc, "`%s` is obsolete. Use `opOpAssign(string op)(...) if (op == \"%s\")` instead.", id.toChars(), op.ptr); - return ErrorExp.get(); - } + return ErrorExp.get(); } - - if (s) + if (m.count > 1) { - /* Try: - * a.opOpAssign(b) - */ - args2.setDim(1); - (*args2)[0] = e.e2; - expandTuples(args2); - MatchAccumulator m; - functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) - { - return ErrorExp.get(); - } - if (m.count > 1) - { - // Error, ambiguous - error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); - } - else if (m.last == MATCH.nomatch) - { - if (tiargs) - goto L1; - m.lastf = null; - } - // Rewrite (e1 op e2) as e1.opOpAssign(e2) - return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s); + // Error, ambiguous + error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); } - L1: - result = checkAliasThisForLhs(ad1, sc, e); - if (result || !s) // no point in trying Rhs alias-this if there's no overload of any kind in lhs - return result; - - return checkAliasThisForRhs(isAggregate(e.e2.type), sc, e); + else if (m.last == MATCH.nomatch) + { + if (tiargs) + goto L1; + m.lastf = null; + } + // Rewrite (e1 op e2) as e1.opOpAssign(e2) + return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s); } + L1: + result = checkAliasThisForLhs(ad1, sc, e); + if (result || !s) // no point in trying Rhs alias-this if there's no overload of any kind in lhs + return result; + + return checkAliasThisForRhs(isAggregate(e.e2.type), sc, e); + } if (pop) *pop = e.op; diff --git a/gcc/d/dmd/optimize.d b/gcc/d/dmd/optimize.d index 9db2710..e409a79 100644 --- a/gcc/d/dmd/optimize.d +++ b/gcc/d/dmd/optimize.d @@ -372,10 +372,10 @@ Expression optimize(Expression e, int result, bool keepLvalue = false) void visitStructLiteral(StructLiteralExp e) { - if (e.stageflags & stageOptimize) + if (e.stageflags & StructLiteralExp.StageFlags.optimize) return; const old = e.stageflags; - e.stageflags |= stageOptimize; + e.stageflags |= StructLiteralExp.StageFlags.optimize; if (e.elements) { foreach (ref ex; (*e.elements)[]) diff --git a/gcc/d/dmd/parse.d b/gcc/d/dmd/parse.d index 012f764..bb24118 100644 --- a/gcc/d/dmd/parse.d +++ b/gcc/d/dmd/parse.d @@ -110,43 +110,44 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer parseModuleAttributes(msg, isdeprecated); // ModuleDeclaration leads off - if (token.value == TOK.module_) + if (token.value != TOK.module_) + return true; + + const loc = token.loc; + nextToken(); + + /* parse ModuleFullyQualifiedName + * https://dlang.org/spec/module.html#ModuleFullyQualifiedName + */ + + if (token.value != TOK.identifier) { - const loc = token.loc; - nextToken(); + error("identifier expected following `module`"); + return false; + } - /* parse ModuleFullyQualifiedName - * https://dlang.org/spec/module.html#ModuleFullyQualifiedName - */ + Identifier[] a; + Identifier id = token.ident; + while (nextToken() == TOK.dot) + { + a ~= id; + nextToken(); if (token.value != TOK.identifier) { - error("identifier expected following `module`"); + error("identifier expected following `package`"); return false; } + id = token.ident; + } - Identifier[] a; - Identifier id = token.ident; - - while (nextToken() == TOK.dot) - { - a ~= id; - nextToken(); - if (token.value != TOK.identifier) - { - error("identifier expected following `package`"); - return false; - } - id = token.ident; - } + md = new AST.ModuleDeclaration(loc, a, id, msg, isdeprecated); - md = new AST.ModuleDeclaration(loc, a, id, msg, isdeprecated); + if (token.value != TOK.semicolon) + error("`;` expected following module declaration instead of `%s`", token.toChars()); + nextToken(); + addComment(mod, comment); - if (token.value != TOK.semicolon) - error("`;` expected following module declaration instead of `%s`", token.toChars()); - nextToken(); - addComment(mod, comment); - } return true; } diff --git a/gcc/d/dmd/postordervisitor.d b/gcc/d/dmd/postordervisitor.d deleted file mode 100644 index fe189d4..0000000 --- a/gcc/d/dmd/postordervisitor.d +++ /dev/null @@ -1,153 +0,0 @@ -/** - * A depth-first visitor for expressions. - * - * 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/apply.d, _apply.d) - * Documentation: https://dlang.org/phobos/dmd_apply.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/apply.d - */ - -module dmd.postordervisitor; - -import dmd.arraytypes; -import dmd.dtemplate; -import dmd.expression; -import dmd.root.array; -import dmd.visitor; - -bool walkPostorder(Expression e, StoppableVisitor v) -{ - scope PostorderExpressionVisitor pv = new PostorderExpressionVisitor(v); - e.accept(pv); - return v.stop; -} - -/************************************** - * An Expression tree walker that will visit each Expression e in the tree, - * in depth-first evaluation order, and call fp(e,param) on it. - * fp() signals whether the walking continues with its return value: - * Returns: - * 0 continue - * 1 done - * It's a bit slower than using virtual functions, but more encapsulated and less brittle. - * Creating an iterator for this would be much more complex. - */ -private extern (C++) final class PostorderExpressionVisitor : StoppableVisitor -{ - alias visit = typeof(super).visit; -public: - StoppableVisitor v; - - extern (D) this(StoppableVisitor v) scope @safe - { - this.v = v; - } - - bool doCond(Expression e) - { - if (!stop && e) - e.accept(this); - return stop; - } - - extern(D) bool doCond(Expression[] e) - { - for (size_t i = 0; i < e.length && !stop; i++) - doCond(e[i]); - return stop; - } - - bool applyTo(Expression e) - { - e.accept(v); - stop = v.stop; - return true; - } - - override void visit(Expression e) - { - applyTo(e); - } - - override void visit(NewExp e) - { - //printf("NewExp::apply(): %s\n", toChars()); - doCond(e.thisexp) || doCond(e.arguments.peekSlice()) || applyTo(e); - } - - override void visit(NewAnonClassExp e) - { - //printf("NewAnonClassExp::apply(): %s\n", toChars()); - doCond(e.thisexp) || doCond(e.arguments.peekSlice()) || applyTo(e); - } - - override void visit(TypeidExp e) - { - doCond(isExpression(e.obj)) || applyTo(e); - } - - override void visit(UnaExp e) - { - doCond(e.e1) || applyTo(e); - } - - override void visit(BinExp e) - { - doCond(e.e1) || doCond(e.e2) || applyTo(e); - } - - override void visit(AssertExp e) - { - //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); - doCond(e.e1) || doCond(e.msg) || applyTo(e); - } - - override void visit(CallExp e) - { - //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); - doCond(e.e1) || doCond(e.arguments.peekSlice()) || applyTo(e); - } - - override void visit(ArrayExp e) - { - //printf("ArrayExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); - doCond(e.e1) || doCond(e.arguments.peekSlice()) || applyTo(e); - } - - override void visit(SliceExp e) - { - doCond(e.e1) || doCond(e.lwr) || doCond(e.upr) || applyTo(e); - } - - override void visit(ArrayLiteralExp e) - { - doCond(e.basis) || doCond(e.elements.peekSlice()) || applyTo(e); - } - - override void visit(AssocArrayLiteralExp e) - { - doCond(e.keys.peekSlice()) || doCond(e.values.peekSlice()) || applyTo(e); - } - - override void visit(StructLiteralExp e) - { - if (e.stageflags & stageApply) - return; - const old = e.stageflags; - e.stageflags |= stageApply; - doCond(e.elements.peekSlice()) || applyTo(e); - e.stageflags = old; - } - - override void visit(TupleExp e) - { - doCond(e.e0) || doCond(e.exps.peekSlice()) || applyTo(e); - } - - override void visit(CondExp e) - { - doCond(e.econd) || doCond(e.e1) || doCond(e.e2) || applyTo(e); - } -} diff --git a/gcc/d/dmd/safe.d b/gcc/d/dmd/safe.d index b0eb3d1..34cab80 100644 --- a/gcc/d/dmd/safe.d +++ b/gcc/d/dmd/safe.d @@ -51,102 +51,103 @@ bool checkUnsafeAccess(Scope* sc, Expression e, bool readonly, bool printmsg) if (e.op != EXP.dotVariable) return false; auto dve = cast(DotVarExp)e; - if (VarDeclaration v = dve.var.isVarDeclaration()) - { - if (!sc.func) - return false; - auto ad = v.isMember2(); - if (!ad) - return false; + VarDeclaration v = dve.var.isVarDeclaration(); + if (!v) + return false; + if (!sc.func) + return false; + auto ad = v.isMember2(); + if (!ad) + return false; - import dmd.globals : global; - if (v.isSystem()) - { - if (sc.setUnsafePreview(global.params.systemVariables, !printmsg, e.loc, - "cannot access `@system` field `%s.%s` in `@safe` code", ad, v)) - return true; - } + import dmd.globals : global; + if (v.isSystem()) + { + if (sc.setUnsafePreview(global.params.systemVariables, !printmsg, e.loc, + "cannot access `@system` field `%s.%s` in `@safe` code", ad, v)) + return true; + } - // This branch shouldn't be here, but unfortunately calling `ad.determineSize` - // breaks code with circular reference errors. Specifically, test23589.d fails - if (ad.sizeok != Sizeok.done && !sc.func.isSafeBypassingInference()) - return false; + // This branch shouldn't be here, but unfortunately calling `ad.determineSize` + // breaks code with circular reference errors. Specifically, test23589.d fails + if (ad.sizeok != Sizeok.done && !sc.func.isSafeBypassingInference()) + return false; - // needed to set v.overlapped and v.overlapUnsafe - if (ad.sizeok != Sizeok.done) - ad.determineSize(ad.loc); + // needed to set v.overlapped and v.overlapUnsafe + if (ad.sizeok != Sizeok.done) + ad.determineSize(ad.loc); - import dmd.globals : FeatureState; - const hasPointers = v.type.hasPointers(); - if (hasPointers) + import dmd.globals : FeatureState; + const hasPointers = v.type.hasPointers(); + if (hasPointers) + { + if (v.overlapped) { - if (v.overlapped) + if (sc.func.isSafeBypassingInference() && sc.setUnsafe(!printmsg, e.loc, + "field `%s.%s` cannot access pointers in `@safe` code that overlap other fields", ad, v)) { - if (sc.func.isSafeBypassingInference() && sc.setUnsafe(!printmsg, e.loc, - "field `%s.%s` cannot access pointers in `@safe` code that overlap other fields", ad, v)) - { - return true; - } - else - { - // @@@DEPRECATED_2.116@@@ - // https://issues.dlang.org/show_bug.cgi?id=20655 - // Inferring `@system` because of union access breaks code, - // so make it a deprecation safety violation as of 2.106 - // To turn into an error, remove `isSafeBypassingInference` check in the - // above if statement and remove the else branch - sc.setUnsafePreview(FeatureState.default_, !printmsg, e.loc, - "field `%s.%s` cannot access pointers in `@safe` code that overlap other fields", ad, v); - } + return true; } - } - - if (v.type.hasInvariant()) - { - if (v.overlapped) + else { - if (sc.setUnsafe(!printmsg, e.loc, - "field `%s.%s` cannot access structs with invariants in `@safe` code that overlap other fields", - ad, v)) - return true; + // @@@DEPRECATED_2.116@@@ + // https://issues.dlang.org/show_bug.cgi?id=20655 + // Inferring `@system` because of union access breaks code, + // so make it a deprecation safety violation as of 2.106 + // To turn into an error, remove `isSafeBypassingInference` check in the + // above if statement and remove the else branch + sc.setUnsafePreview(FeatureState.default_, !printmsg, e.loc, + "field `%s.%s` cannot access pointers in `@safe` code that overlap other fields", ad, v); } } + } - // @@@DEPRECATED_2.119@@@ - // https://issues.dlang.org/show_bug.cgi?id=24477 - // Should probably be turned into an error in a new edition - if (v.type.hasUnsafeBitpatterns() && v.overlapped && sc.setUnsafePreview( - FeatureState.default_, !printmsg, e.loc, - "cannot access overlapped field `%s.%s` with unsafe bit patterns in `@safe` code", ad, v) - ) + if (v.type.hasInvariant()) + { + if (v.overlapped) { - return true; + if (sc.setUnsafe(!printmsg, e.loc, + "field `%s.%s` cannot access structs with invariants in `@safe` code that overlap other fields", + ad, v)) + return true; } + } - if (readonly || !e.type.isMutable()) - return false; + // @@@DEPRECATED_2.119@@@ + // https://issues.dlang.org/show_bug.cgi?id=24477 + // Should probably be turned into an error in a new edition + if (v.type.hasUnsafeBitpatterns() && v.overlapped && sc.setUnsafePreview( + FeatureState.default_, !printmsg, e.loc, + "cannot access overlapped field `%s.%s` with unsafe bit patterns in `@safe` code", ad, v) + ) + { + return true; + } - if (hasPointers && v.type.toBasetype().ty != Tstruct) - { - if ((!ad.type.alignment.isDefault() && ad.type.alignment.get() < target.ptrsize) || - (v.offset & (target.ptrsize - 1))) - { - if (sc.setUnsafe(!printmsg, e.loc, - "field `%s.%s` cannot modify misaligned pointers in `@safe` code", ad, v)) - return true; - } - } + if (readonly || !e.type.isMutable()) + return false; - if (v.overlapUnsafe) + if (hasPointers && v.type.toBasetype().ty != Tstruct) + { + if ((!ad.type.alignment.isDefault() && ad.type.alignment.get() < target.ptrsize) || + (v.offset & (target.ptrsize - 1))) { if (sc.setUnsafe(!printmsg, e.loc, - "field `%s.%s` cannot modify fields in `@safe` code that overlap fields with other storage classes", - ad, v)) - { + "field `%s.%s` cannot modify misaligned pointers in `@safe` code", ad, v)) return true; - } } } + + if (v.overlapUnsafe) + { + if (sc.setUnsafe(!printmsg, e.loc, + "field `%s.%s` cannot modify fields in `@safe` code that overlap fields with other storage classes", + ad, v)) + { + return true; + } + } + return false; } diff --git a/gcc/d/dmd/semantic3.d b/gcc/d/dmd/semantic3.d index afdc87f..24bdd44 100644 --- a/gcc/d/dmd/semantic3.d +++ b/gcc/d/dmd/semantic3.d @@ -1685,3 +1685,72 @@ void semanticTypeInfoMembers(StructDeclaration sd) sd.dtor.semantic3(sd.dtor._scope); } } + +/*********************************************** + * Check that the function contains any closure. + * If it's @nogc, report suitable errors. + * This is mostly consistent with FuncDeclaration::needsClosure(). + * + * Returns: + * true if any errors occur. + */ +extern (D) bool checkClosure(FuncDeclaration fd) +{ + //printf("checkClosure() %s\n", toPrettyChars()); + if (!fd.needsClosure()) + return false; + + if (fd.setGC(fd.loc, "%s `%s` is `@nogc` yet allocates closure for `%s()` with the GC", fd)) + { + .error(fd.loc, "%s `%s` is `@nogc` yet allocates closure for `%s()` with the GC", fd.kind, fd.toPrettyChars(), fd.toChars()); + if (global.gag) // need not report supplemental errors + return true; + } + else if (!global.params.useGC) + { + .error(fd.loc, "%s `%s` is `-betterC` yet allocates closure for `%s()` with the GC", fd.kind, fd.toPrettyChars(), fd.toChars()); + if (global.gag) // need not report supplemental errors + return true; + } + else + { + fd.printGCUsage(fd.loc, "using closure causes GC allocation"); + return false; + } + + FuncDeclarations a; + foreach (v; fd.closureVars) + { + foreach (f; v.nestedrefs) + { + assert(f !is fd); + + LcheckAncestorsOfANestedRef: + for (Dsymbol s = f; s && s !is fd; s = s.toParentP(fd)) + { + auto fx = s.isFuncDeclaration(); + if (!fx) + continue; + if (fx.isThis() || + fx.tookAddressOf || + checkEscapingSiblings(fx, fd)) + { + foreach (f2; a) + { + if (f2 == f) + break LcheckAncestorsOfANestedRef; + } + a.push(f); + .errorSupplemental(f.loc, "%s `%s` closes over variable `%s`", + f.kind, f.toPrettyChars(), v.toChars()); + if (v.ident != Id.This) + .errorSupplemental(v.loc, "`%s` declared here", v.toChars()); + + break LcheckAncestorsOfANestedRef; + } + } + } + } + + return true; +} diff --git a/gcc/d/dmd/sideeffect.d b/gcc/d/dmd/sideeffect.d index 3ed5940..84b9e4e 100644 --- a/gcc/d/dmd/sideeffect.d +++ b/gcc/d/dmd/sideeffect.d @@ -24,10 +24,10 @@ import dmd.id; import dmd.identifier; import dmd.init; import dmd.mtype; -import dmd.postordervisitor; import dmd.tokens; import dmd.typesem; import dmd.visitor; +import dmd.visitor.postorder; /************************************************** * Front-end expression rewriting should create temporary variables for diff --git a/gcc/d/dmd/statement.d b/gcc/d/dmd/statement.d index 81891ff..b53c3d0 100644 --- a/gcc/d/dmd/statement.d +++ b/gcc/d/dmd/statement.d @@ -30,10 +30,10 @@ import dmd.identifier; import dmd.location; import dmd.mtype; import dmd.rootobject; -import dmd.sapply; import dmd.staticassert; import dmd.tokens; import dmd.visitor; +import dmd.visitor.postorder; /*********************************************************** * Specification: https://dlang.org/spec/statement.html diff --git a/gcc/d/dmd/target.d b/gcc/d/dmd/target.d index d86be85..cc44b6c 100644 --- a/gcc/d/dmd/target.d +++ b/gcc/d/dmd/target.d @@ -114,6 +114,7 @@ extern (C++) struct Target /// Architecture name const(char)[] architectureName; CPU cpu; // CPU instruction set to target + bool isAArch64; // generate 64 bit Arm code bool isX86_64; // generate 64 bit code for x86_64; true by default for 64 bit dmd bool isX86; // generate 32 bit Intel x86 code bool isLP64; // pointers are 64 bits diff --git a/gcc/d/dmd/target.h b/gcc/d/dmd/target.h index a380bb8..2284b83 100644 --- a/gcc/d/dmd/target.h +++ b/gcc/d/dmd/target.h @@ -153,6 +153,7 @@ struct Target DString architectureName; // name of the platform architecture (e.g. X86_64) CPU cpu; // CPU instruction set to target + d_bool isAArch64; // generate 64 bit Arm code d_bool isX86_64; // generate 64 bit code for x86_64; true by default for 64 bit dmd d_bool isX86; // generate 32 bit Intel x86 code d_bool isLP64; // pointers are 64 bits diff --git a/gcc/d/dmd/traits.d b/gcc/d/dmd/traits.d index 946df2d..1e73861 100644 --- a/gcc/d/dmd/traits.d +++ b/gcc/d/dmd/traits.d @@ -338,7 +338,9 @@ Expression semanticTraits(TraitsExp e, Scope* sc) const save = sc.stc; if (e.ident == Id.isDeprecated) sc.stc |= STC.deprecated_; - if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 1)) + Scope* sc2 = sc.startCTFE(); + scope(exit) { sc2.endCTFE(); } + if (!TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1)) { sc.stc = save; return ErrorExp.get(); diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d index 6bb5177..a35a6eb 100644 --- a/gcc/d/dmd/typesem.d +++ b/gcc/d/dmd/typesem.d @@ -53,6 +53,7 @@ import dmd.initsem; import dmd.location; import dmd.visitor; import dmd.mtype; +import dmd.nogc; import dmd.objc; import dmd.opover; import dmd.optimize; @@ -891,66 +892,70 @@ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct, if (dmd.expressionsem.trySemantic(e, sc)) return true; - if (pMessage) - { - /* https://issues.dlang.org/show_bug.cgi?id=22202 - * - * If a function was deduced by semantic on the CallExp, - * it means that resolveFuncCall completed succesfully. - * Therefore, there exists a callable copy constructor, - * however, it cannot be called because scope constraints - * such as purity, safety or nogc. - */ - OutBuffer buf; - auto callExp = e.isCallExp(); - if (auto f = callExp.f) - { - char[] s; - if (!f.isPure && sc.func.setImpure()) - s ~= "pure "; - if (!f.isSafe() && !f.isTrusted() && sc.setUnsafe()) - s ~= "@safe "; - if (!f.isNogc && sc.func.setGC(arg.loc, null)) - s ~= "nogc "; - if (f.isDisabled() && !f.isGenerated()) - { - /* https://issues.dlang.org/show_bug.cgi?id=24301 - * Copy constructor is explicitly disabled - */ - buf.printf("`%s` copy constructor cannot be used because it is annotated with `@disable`", - f.type.toChars()); - } - else if (s) - { - s[$-1] = '\0'; - buf.printf("`%s` copy constructor cannot be called from a `%s` context", f.type.toChars(), s.ptr); - } - else if (f.isGenerated() && f.isDisabled()) - { - /* https://issues.dlang.org/show_bug.cgi?id=23097 - * Compiler generated copy constructor failed. - */ - buf.printf("generating a copy constructor for `struct %s` failed, therefore instances of it are uncopyable", - argStruct.toChars()); - } - else - { - /* Although a copy constructor may exist, no suitable match was found. - * i.e: `inout` constructor creates `const` object, not mutable. - * Fallback to using the original generic error before https://issues.dlang.org/show_bug.cgi?id=22202. - */ - goto Lnocpctor; - } - } - else - { - Lnocpctor: - buf.printf("`struct %s` does not define a copy constructor for `%s` to `%s` copies", - argStruct.toChars(), arg.type.toChars(), tprm.toChars()); - } + if (!pMessage) + return false; + /* https://issues.dlang.org/show_bug.cgi?id=22202 + * + * If a function was deduced by semantic on the CallExp, + * it means that resolveFuncCall completed succesfully. + * Therefore, there exists a callable copy constructor, + * however, it cannot be called because scope constraints + * such as purity, safety or nogc. + */ + OutBuffer buf; + auto callExp = e.isCallExp(); + void nocpctor() + { + buf.printf("`struct %s` does not define a copy constructor for `%s` to `%s` copies", + argStruct.toChars(), arg.type.toChars(), tprm.toChars()); + } + auto f = callExp.f; + if (!f) + { + nocpctor(); *pMessage = buf.extractChars(); + return false; + } + char[] s; + if (!f.isPure && sc.func.setImpure()) + s ~= "pure "; + if (!f.isSafe() && !f.isTrusted() && sc.setUnsafe()) + s ~= "@safe "; + if (!f.isNogc && sc.func.setGC(arg.loc, null)) + s ~= "nogc "; + if (f.isDisabled() && !f.isGenerated()) + { + /* https://issues.dlang.org/show_bug.cgi?id=24301 + * Copy constructor is explicitly disabled + */ + buf.printf("`%s` copy constructor cannot be used because it is annotated with `@disable`", + f.type.toChars()); + } + else if (s) + { + s[$-1] = '\0'; + buf.printf("`%s` copy constructor cannot be called from a `%s` context", f.type.toChars(), s.ptr); + } + else if (f.isGenerated() && f.isDisabled()) + { + /* https://issues.dlang.org/show_bug.cgi?id=23097 + * Compiler generated copy constructor failed. + */ + buf.printf("generating a copy constructor for `struct %s` failed, therefore instances of it are uncopyable", + argStruct.toChars()); } + else + { + /* Although a copy constructor may exist, no suitable match was found. + * i.e: `inout` constructor creates `const` object, not mutable. + * Fallback to using the original generic error before https://issues.dlang.org/show_bug.cgi?id=22202. + */ + nocpctor(); + } + + *pMessage = buf.extractChars(); + return false; } @@ -1017,101 +1022,102 @@ private extern(D) MATCH argumentMatchParameter (TypeFunction tf, Parameter p, } // Non-lvalues do not match ref or out parameters - if (p.isReference()) - { - // https://issues.dlang.org/show_bug.cgi?id=13783 - // Don't use toBasetype() to handle enum types. - Type ta = targ; - Type tp = tprm; - //printf("fparam[%d] ta = %s, tp = %s\n", u, ta.toChars(), tp.toChars()); + if (!p.isReference()) + return m; - if (m && !arg.isLvalue()) - { - if (p.storageClass & STC.out_) - { - if (pMessage) *pMessage = tf.getParamError(arg, p); - return MATCH.nomatch; - } + // https://issues.dlang.org/show_bug.cgi?id=13783 + // Don't use toBasetype() to handle enum types. + Type ta = targ; + Type tp = tprm; + //printf("fparam[%d] ta = %s, tp = %s\n", u, ta.toChars(), tp.toChars()); - if (arg.op == EXP.string_ && tp.ty == Tsarray) - { - if (ta.ty != Tsarray) - { - Type tn = tp.nextOf().castMod(ta.nextOf().mod); - dinteger_t dim = (cast(StringExp)arg).len; - ta = tn.sarrayOf(dim); - } - } - else if (arg.op == EXP.slice && tp.ty == Tsarray) - { - // Allow conversion from T[lwr .. upr] to ref T[upr-lwr] - if (ta.ty != Tsarray) - { - Type tn = ta.nextOf(); - dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger(); - ta = tn.sarrayOf(dim); - } - } - else if (p.storageClass & STC.constscoperef) - { - // Allow converting a literal to an `in` which is `ref` - if (arg.op == EXP.arrayLiteral && tp.ty == Tsarray) - { - Type tn = tp.nextOf(); - dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger(); - ta = tn.sarrayOf(dim); - } + if (m && !arg.isLvalue()) + { + if (p.storageClass & STC.out_) + { + if (pMessage) *pMessage = tf.getParamError(arg, p); + return MATCH.nomatch; + } - // Need to make this a rvalue through a temporary - m = MATCH.convert; - } - else if (global.params.rvalueRefParam != FeatureState.enabled || - p.storageClass & STC.out_ || - !arg.type.isCopyable()) // can't copy to temp for ref parameter + if (arg.op == EXP.string_ && tp.ty == Tsarray) + { + if (ta.ty != Tsarray) { - if (pMessage) *pMessage = tf.getParamError(arg, p); - return MATCH.nomatch; + Type tn = tp.nextOf().castMod(ta.nextOf().mod); + dinteger_t dim = (cast(StringExp)arg).len; + ta = tn.sarrayOf(dim); } - else + } + else if (arg.op == EXP.slice && tp.ty == Tsarray) + { + // Allow conversion from T[lwr .. upr] to ref T[upr-lwr] + if (ta.ty != Tsarray) { - /* in functionParameters() we'll convert this - * rvalue into a temporary - */ - m = MATCH.convert; + Type tn = ta.nextOf(); + dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger(); + ta = tn.sarrayOf(dim); } } - - /* If the match is not already perfect or if the arg - is not a lvalue then try the `alias this` chain - see https://issues.dlang.org/show_bug.cgi?id=15674 - and https://issues.dlang.org/show_bug.cgi?id=21905 - */ - if (ta != tp || !arg.isLvalue()) + else if (p.storageClass & STC.constscoperef) { - Type firsttab = ta.toBasetype(); - while (1) + // Allow converting a literal to an `in` which is `ref` + if (arg.op == EXP.arrayLiteral && tp.ty == Tsarray) { - Type tab = ta.toBasetype(); - Type tat = tab.aliasthisOf(); - if (!tat || !tat.implicitConvTo(tprm)) - break; - if (tat == tab || tat == firsttab) - break; - ta = tat; + Type tn = tp.nextOf(); + dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger(); + ta = tn.sarrayOf(dim); } - } - /* A ref variable should work like a head-const reference. - * e.g. disallows: - * ref T <- an lvalue of const(T) argument - * ref T[dim] <- an lvalue of const(T[dim]) argument - */ - if (!ta.constConv(tp)) + // Need to make this a rvalue through a temporary + m = MATCH.convert; + } + else if (global.params.rvalueRefParam != FeatureState.enabled || + p.storageClass & STC.out_ || + !arg.type.isCopyable()) // can't copy to temp for ref parameter { if (pMessage) *pMessage = tf.getParamError(arg, p); return MATCH.nomatch; } + else + { + /* in functionParameters() we'll convert this + * rvalue into a temporary + */ + m = MATCH.convert; + } + } + + /* If the match is not already perfect or if the arg + is not a lvalue then try the `alias this` chain + see https://issues.dlang.org/show_bug.cgi?id=15674 + and https://issues.dlang.org/show_bug.cgi?id=21905 + */ + if (ta != tp || !arg.isLvalue()) + { + Type firsttab = ta.toBasetype(); + while (1) + { + Type tab = ta.toBasetype(); + Type tat = tab.aliasthisOf(); + if (!tat || !tat.implicitConvTo(tprm)) + break; + if (tat == tab || tat == firsttab) + break; + ta = tat; + } } + + /* A ref variable should work like a head-const reference. + * e.g. disallows: + * ref T <- an lvalue of const(T) argument + * ref T[dim] <- an lvalue of const(T[dim]) argument + */ + if (!ta.constConv(tp)) + { + if (pMessage) *pMessage = tf.getParamError(arg, p); + return MATCH.nomatch; + } + return m; } @@ -2676,53 +2682,51 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) //printf("\tit's a type %d, %s, %s\n", t.ty, t.toChars(), t.deco); return t.addMod(mtype.mod); } - else - { - if (s) - { - auto td = s.isTemplateDeclaration; - if (td && td.onemember && td.onemember.isAggregateDeclaration) - .error(loc, "template %s `%s` is used as a type without instantiation" - ~ "; to instantiate it use `%s!(arguments)`", - s.kind, s.toPrettyChars, s.ident.toChars); - else - .error(loc, "%s `%s` is used as a type", s.kind, s.toPrettyChars); - //assert(0); - } - else if (e.op == EXP.variable) // special case: variable is used as a type - { - /* - N.B. This branch currently triggers for the following code - template test(x* x) - { - } - i.e. the compiler prints "variable x is used as a type" - which isn't a particularly good error message (x is a variable?). - */ - Dsymbol varDecl = mtype.toDsymbol(sc); - const(Loc) varDeclLoc = varDecl.getLoc(); - Module varDeclModule = varDecl.getModule(); //This can be null - - .error(loc, "variable `%s` is used as a type", mtype.toChars()); - //Check for null to avoid https://issues.dlang.org/show_bug.cgi?id=22574 - if ((varDeclModule !is null) && varDeclModule != sc._module) // variable is imported + if (s) + { + auto td = s.isTemplateDeclaration; + if (td && td.onemember && td.onemember.isAggregateDeclaration) + .error(loc, "template %s `%s` is used as a type without instantiation" + ~ "; to instantiate it use `%s!(arguments)`", + s.kind, s.toPrettyChars, s.ident.toChars); + else + .error(loc, "%s `%s` is used as a type", s.kind, s.toPrettyChars); + //assert(0); + } + else if (e.op == EXP.variable) // special case: variable is used as a type + { + /* + N.B. This branch currently triggers for the following code + template test(x* x) { - const(Loc) varDeclModuleImportLoc = varDeclModule.getLoc(); - .errorSupplemental( - varDeclModuleImportLoc, - "variable `%s` is imported here from: `%s`", - varDecl.toChars, - varDeclModule.toPrettyChars, - ); + } + i.e. the compiler prints "variable x is used as a type" + which isn't a particularly good error message (x is a variable?). + */ + Dsymbol varDecl = mtype.toDsymbol(sc); + const(Loc) varDeclLoc = varDecl.getLoc(); + Module varDeclModule = varDecl.getModule(); //This can be null - .errorSupplemental(varDeclLoc, "variable `%s` is declared here", varDecl.toChars); + .error(loc, "variable `%s` is used as a type", mtype.toChars()); + //Check for null to avoid https://issues.dlang.org/show_bug.cgi?id=22574 + if ((varDeclModule !is null) && varDeclModule != sc._module) // variable is imported + { + const(Loc) varDeclModuleImportLoc = varDeclModule.getLoc(); + .errorSupplemental( + varDeclModuleImportLoc, + "variable `%s` is imported here from: `%s`", + varDecl.toChars, + varDeclModule.toPrettyChars, + ); } - else - .error(loc, "`%s` is used as a type", mtype.toChars()); - return error(); + + .errorSupplemental(varDeclLoc, "variable `%s` is declared here", varDecl.toChars); } + else + .error(loc, "`%s` is used as a type", mtype.toChars()); + return error(); } Type visitInstance(TypeInstance mtype) @@ -3216,37 +3220,36 @@ Type merge(Type type) } //printf("merge(%s)\n", toChars()); - if (!type.deco) - { - OutBuffer buf; - buf.reserve(32); + if (type.deco) + return type; - mangleToBuffer(type, buf); + OutBuffer buf; + buf.reserve(32); - auto sv = type.stringtable.update(buf[]); - if (sv.value) - { - Type t = sv.value; - debug - { - import core.stdc.stdio; - if (!t.deco) - printf("t = %s\n", t.toChars()); - } - assert(t.deco); - //printf("old value, deco = '%s' %p\n", t.deco, t.deco); - return t; - } - else + mangleToBuffer(type, buf); + + auto sv = type.stringtable.update(buf[]); + if (sv.value) + { + Type t = sv.value; + debug { - Type t = stripDefaultArgs(type); - sv.value = t; - type.deco = t.deco = cast(char*)sv.toDchars(); - //printf("new value, deco = '%s' %p\n", t.deco, t.deco); - return t; + import core.stdc.stdio; + if (!t.deco) + printf("t = %s\n", t.toChars()); } + assert(t.deco); + //printf("old value, deco = '%s' %p\n", t.deco, t.deco); + return t; + } + else + { + Type t = stripDefaultArgs(type); + sv.value = t; + type.deco = t.deco = cast(char*)sv.toDchars(); + //printf("new value, deco = '%s' %p\n", t.deco, t.deco); + return t; } - return type; } /************************************* @@ -3300,14 +3303,14 @@ Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden const sz = mt.size(loc); if (sz == SIZE_INVALID) return ErrorExp.get(); - e = new IntegerExp(loc, sz, Type.tsize_t); + return new IntegerExp(loc, sz, Type.tsize_t); } else if (ident == Id.__xalignof) { const explicitAlignment = mt.alignment(); const naturalAlignment = mt.alignsize(); const actualAlignment = (explicitAlignment.isDefault() ? naturalAlignment : explicitAlignment.get()); - e = new IntegerExp(loc, actualAlignment, Type.tsize_t); + return new IntegerExp(loc, actualAlignment, Type.tsize_t); } else if (ident == Id._init) { @@ -3317,13 +3320,14 @@ Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden { e.isStructLiteralExp().useStaticInit = true; } + return e; } else if (ident == Id._mangleof) { if (!mt.deco) { error(loc, "forward reference of type `%s.mangleof`", mt.toChars()); - e = ErrorExp.get(); + return ErrorExp.get(); } else { @@ -3332,6 +3336,7 @@ Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden sc.eSink = global.errorSink; e = e.expressionSemantic(&sc); } + return e; } else if (ident == Id.stringof) { @@ -3340,74 +3345,74 @@ Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden Scope sc; sc.eSink = global.errorSink; e = e.expressionSemantic(&sc); + return e; } else if (flag && mt != Type.terror) { return null; } + + Dsymbol s = null; + if (mt.ty == Tstruct || mt.ty == Tclass || mt.ty == Tenum) + s = mt.toDsymbol(null); + if (s) + s = s.search_correct(ident); + if (s && !symbolIsVisible(scope_, s)) + s = null; + + if (mt == Type.terror) + return ErrorExp.get(); + + if (s) + error(loc, "no property `%s` for type `%s`, did you mean `%s`?", ident.toChars(), mt.toChars(), s.toPrettyChars()); + else if (ident == Id.call && mt.ty == Tclass) + error(loc, "no property `%s` for type `%s`, did you mean `new %s`?", ident.toChars(), mt.toChars(), mt.toPrettyChars()); + + else if (const n = importHint(ident.toString())) + error(loc, "no property `%s` for type `%s`, perhaps `import %.*s;` is needed?", ident.toChars(), mt.toChars(), cast(int)n.length, n.ptr); else { - Dsymbol s = null; - if (mt.ty == Tstruct || mt.ty == Tclass || mt.ty == Tenum) - s = mt.toDsymbol(null); - if (s) - s = s.search_correct(ident); - if (s && !symbolIsVisible(scope_, s)) - s = null; - if (mt != Type.terror) - { - if (s) - error(loc, "no property `%s` for type `%s`, did you mean `%s`?", ident.toChars(), mt.toChars(), s.toPrettyChars()); - else if (ident == Id.call && mt.ty == Tclass) - error(loc, "no property `%s` for type `%s`, did you mean `new %s`?", ident.toChars(), mt.toChars(), mt.toPrettyChars()); - - else if (const n = importHint(ident.toString())) - error(loc, "no property `%s` for type `%s`, perhaps `import %.*s;` is needed?", ident.toChars(), mt.toChars(), cast(int)n.length, n.ptr); - else + if (src) + { + error(loc, "no property `%s` for `%s` of type `%s`", + ident.toChars(), src.toChars(), mt.toPrettyChars(true)); + auto s2 = scope_.search_correct(ident); + // UFCS + if (s2 && s2.isFuncDeclaration) + errorSupplemental(loc, "did you mean %s `%s`?", + s2.kind(), s2.toChars()); + else if (src.type.ty == Tpointer) { - if (src) + // structPtr.field + auto tn = (cast(TypeNext) src.type).nextOf(); + if (auto as = tn.isAggregate()) { - error(loc, "no property `%s` for `%s` of type `%s`", - ident.toChars(), src.toChars(), mt.toPrettyChars(true)); - auto s2 = scope_.search_correct(ident); - // UFCS - if (s2 && s2.isFuncDeclaration) - errorSupplemental(loc, "did you mean %s `%s`?", - s2.kind(), s2.toChars()); - else if (src.type.ty == Tpointer) + if (auto s3 = as.search_correct(ident)) { - // structPtr.field - auto tn = (cast(TypeNext) src.type).nextOf(); - if (auto as = tn.isAggregate()) - { - if (auto s3 = as.search_correct(ident)) - { - errorSupplemental(loc, "did you mean %s `%s`?", - s3.kind(), s3.toChars()); - } - } + errorSupplemental(loc, "did you mean %s `%s`?", + s3.kind(), s3.toChars()); } } - else - error(loc, "no property `%s` for type `%s`", ident.toChars(), mt.toPrettyChars(true)); + } + } + else + error(loc, "no property `%s` for type `%s`", ident.toChars(), mt.toPrettyChars(true)); - if (auto dsym = mt.toDsymbol(scope_)) - { - if (auto sym = dsym.isAggregateDeclaration()) - { - if (auto fd = search_function(sym, Id.opDispatch)) - errorSupplemental(loc, "potentially malformed `opDispatch`. Use an explicit instantiation to get a better error message"); - else if (!sym.members) - errorSupplemental(sym.loc, "`%s %s` is opaque and has no members.", sym.kind, mt.toPrettyChars(true)); - } - errorSupplemental(dsym.loc, "%s `%s` defined here", - dsym.kind, dsym.toChars()); - } + if (auto dsym = mt.toDsymbol(scope_)) + { + if (auto sym = dsym.isAggregateDeclaration()) + { + if (auto fd = search_function(sym, Id.opDispatch)) + errorSupplemental(loc, "potentially malformed `opDispatch`. Use an explicit instantiation to get a better error message"); + else if (!sym.members) + errorSupplemental(sym.loc, "`%s %s` is opaque and has no members.", sym.kind, mt.toPrettyChars(true)); } + errorSupplemental(dsym.loc, "%s `%s` defined here", + dsym.kind, dsym.toChars()); } - e = ErrorExp.get(); } - return e; + + return ErrorExp.get(); } Expression visitError(TypeError) diff --git a/gcc/d/dmd/foreachvar.d b/gcc/d/dmd/visitor/foreachvar.d index 53b3c04..229ade5 100644 --- a/gcc/d/dmd/foreachvar.d +++ b/gcc/d/dmd/visitor/foreachvar.d @@ -9,7 +9,7 @@ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/foreachvar.d */ -module dmd.foreachvar; +module dmd.visitor.foreachvar; import core.stdc.stdio; import core.stdc.stdlib; @@ -32,13 +32,13 @@ import dmd.identifier; import dmd.init; import dmd.initsem; import dmd.mtype; -import dmd.postordervisitor; import dmd.printast; import dmd.root.array; import dmd.rootobject; import dmd.statement; import dmd.tokens; import dmd.visitor; +import dmd.visitor.postorder; /********************************************* * Visit each Expression in e, and call dgVar() on each variable declared in it. diff --git a/gcc/d/dmd/visitor.d b/gcc/d/dmd/visitor/package.d index 9082909..8c1d034 100644 --- a/gcc/d/dmd/visitor.d +++ b/gcc/d/dmd/visitor/package.d @@ -12,12 +12,10 @@ module dmd.visitor; import dmd.astcodegen; -import dmd.astenums; -import dmd.parsetimevisitor; import dmd.tokens; -import dmd.transitivevisitor; -import dmd.expression; import dmd.rootobject; +import dmd.visitor.parsetime; +import dmd.visitor.transitive; /** * Classic Visitor class which implements visit methods for all the AST @@ -151,10 +149,11 @@ extern (C++) class SemanticTimeTransitiveVisitor : SemanticTimePermissiveVisitor { // CTFE can generate struct literals that contain an AddrExp pointing to themselves, // need to avoid infinite recursion. - if (!(e.stageflags & stageToCBuffer)) + alias flag = ASTCodegen.StructLiteralExp.StageFlags.toCBuffer; + if (!(e.stageflags & flag)) { const old = e.stageflags; - e.stageflags |= stageToCBuffer; + e.stageflags |= flag; foreach (el; *e.elements) if (el) el.accept(this); @@ -250,7 +249,7 @@ extern (C++) class SemanticTimeTransitiveVisitor : SemanticTimePermissiveVisitor override void visit(ASTCodegen.LoweredAssignExp e) { e.lowering.accept(this); - visit(cast(AssignExp)e); + visit(cast(ASTCodegen.AssignExp)e); } } diff --git a/gcc/d/dmd/parsetimevisitor.d b/gcc/d/dmd/visitor/parsetime.d index c03f78d..914ca41 100644 --- a/gcc/d/dmd/parsetimevisitor.d +++ b/gcc/d/dmd/visitor/parsetime.d @@ -7,7 +7,7 @@ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/parsetimevisitor.d */ -module dmd.parsetimevisitor; +module dmd.visitor.parsetime; /** Basic and dumm visitor which implements a visit method for each AST node * implemented in AST. This visitor is the parent of strict, transitive diff --git a/gcc/d/dmd/permissivevisitor.d b/gcc/d/dmd/visitor/permissive.d index 5d7f3fc..ef1f279 100644 --- a/gcc/d/dmd/permissivevisitor.d +++ b/gcc/d/dmd/visitor/permissive.d @@ -5,9 +5,9 @@ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/permissivevisitor.d */ -module dmd.permissivevisitor; +module dmd.visitor.permissive; -import dmd.parsetimevisitor; +import dmd.visitor.parsetime; /** PermissiveVisitor overrides all the visit methods in the parent class * that assert(0) in order to facilitate the traversal of subsets of the AST. diff --git a/gcc/d/dmd/sapply.d b/gcc/d/dmd/visitor/postorder.d index 340fbad..af12f1e 100644 --- a/gcc/d/dmd/sapply.d +++ b/gcc/d/dmd/visitor/postorder.d @@ -1,19 +1,29 @@ /** - * Provides a depth-first statement visitor. + * A depth-first visitor for expressions and statements. * * 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/sparse.d, _sparse.d) - * Documentation: https://dlang.org/phobos/dmd_sapply.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/sapply.d + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/apply.d, _apply.d) + * Documentation: https://dlang.org/phobos/dmd_apply.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/apply.d */ -module dmd.sapply; +module dmd.visitor.postorder; +import dmd.dtemplate; +import dmd.expression; +import dmd.root.array; import dmd.statement; import dmd.visitor; +bool walkPostorder(Expression e, StoppableVisitor v) +{ + scope PostorderExpressionVisitor pv = new PostorderExpressionVisitor(v); + e.accept(pv); + return v.stop; +} + bool walkPostorder(Statement s, StoppableVisitor v) { scope PostorderStatementVisitor pv = new PostorderStatementVisitor(v); @@ -21,6 +31,135 @@ bool walkPostorder(Statement s, StoppableVisitor v) return v.stop; } +private: +/************************************** + * An Expression tree walker that will visit each Expression e in the tree, + * in depth-first evaluation order, and call fp(e,param) on it. + * fp() signals whether the walking continues with its return value: + * Returns: + * 0 continue + * 1 done + * It's a bit slower than using virtual functions, but more encapsulated and less brittle. + * Creating an iterator for this would be much more complex. + */ +extern (C++) final class PostorderExpressionVisitor : StoppableVisitor +{ + alias visit = typeof(super).visit; +public: + StoppableVisitor v; + + extern (D) this(StoppableVisitor v) scope @safe + { + this.v = v; + } + + bool doCond(Expression e) + { + if (!stop && e) + e.accept(this); + return stop; + } + + extern(D) bool doCond(Expression[] e) + { + for (size_t i = 0; i < e.length && !stop; i++) + doCond(e[i]); + return stop; + } + + bool applyTo(Expression e) + { + e.accept(v); + stop = v.stop; + return true; + } + + override void visit(Expression e) + { + applyTo(e); + } + + override void visit(NewExp e) + { + //printf("NewExp::apply(): %s\n", toChars()); + doCond(e.thisexp) || doCond(e.arguments.peekSlice()) || applyTo(e); + } + + override void visit(NewAnonClassExp e) + { + //printf("NewAnonClassExp::apply(): %s\n", toChars()); + doCond(e.thisexp) || doCond(e.arguments.peekSlice()) || applyTo(e); + } + + override void visit(TypeidExp e) + { + doCond(isExpression(e.obj)) || applyTo(e); + } + + override void visit(UnaExp e) + { + doCond(e.e1) || applyTo(e); + } + + override void visit(BinExp e) + { + doCond(e.e1) || doCond(e.e2) || applyTo(e); + } + + override void visit(AssertExp e) + { + //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); + doCond(e.e1) || doCond(e.msg) || applyTo(e); + } + + override void visit(CallExp e) + { + //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); + doCond(e.e1) || doCond(e.arguments.peekSlice()) || applyTo(e); + } + + override void visit(ArrayExp e) + { + //printf("ArrayExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); + doCond(e.e1) || doCond(e.arguments.peekSlice()) || applyTo(e); + } + + override void visit(SliceExp e) + { + doCond(e.e1) || doCond(e.lwr) || doCond(e.upr) || applyTo(e); + } + + override void visit(ArrayLiteralExp e) + { + doCond(e.basis) || doCond(e.elements.peekSlice()) || applyTo(e); + } + + override void visit(AssocArrayLiteralExp e) + { + doCond(e.keys.peekSlice()) || doCond(e.values.peekSlice()) || applyTo(e); + } + + override void visit(StructLiteralExp e) + { + if (e.stageflags & StructLiteralExp.StageFlags.apply) + return; + const old = e.stageflags; + e.stageflags |= StructLiteralExp.StageFlags.apply; + doCond(e.elements.peekSlice()) || applyTo(e); + e.stageflags = old; + } + + override void visit(TupleExp e) + { + doCond(e.e0) || doCond(e.exps.peekSlice()) || applyTo(e); + } + + override void visit(CondExp e) + { + doCond(e.econd) || doCond(e.e1) || doCond(e.e2) || applyTo(e); + } +} + /************************************** * A Statement tree walker that will visit each Statement s in the tree, * in depth-first evaluation order, and call fp(s,param) on it. @@ -31,7 +170,7 @@ bool walkPostorder(Statement s, StoppableVisitor v) * It's a bit slower than using virtual functions, but more encapsulated and less brittle. * Creating an iterator for this would be much more complex. */ -private extern (C++) final class PostorderStatementVisitor : StoppableVisitor +extern (C++) final class PostorderStatementVisitor : StoppableVisitor { alias visit = typeof(super).visit; public: diff --git a/gcc/d/dmd/statement_rewrite_walker.d b/gcc/d/dmd/visitor/statement_rewrite_walker.d index 221c502..aeb5ac8 100644 --- a/gcc/d/dmd/statement_rewrite_walker.d +++ b/gcc/d/dmd/visitor/statement_rewrite_walker.d @@ -9,7 +9,7 @@ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/statement_rewrite_walker.d */ -module dmd.statement_rewrite_walker; +module dmd.visitor.statement_rewrite_walker; import core.stdc.stdio; diff --git a/gcc/d/dmd/transitivevisitor.d b/gcc/d/dmd/visitor/transitive.d index bf1d38e..952460c 100644 --- a/gcc/d/dmd/transitivevisitor.d +++ b/gcc/d/dmd/visitor/transitive.d @@ -3,10 +3,10 @@ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/transitivevisitor.d */ -module dmd.transitivevisitor; +module dmd.visitor.transitive; import dmd.astenums; -import dmd.permissivevisitor; +import dmd.visitor.permissive; import dmd.tokens; import dmd.rootobject; @@ -26,7 +26,7 @@ extern(C++) class ParseTimeTransitiveVisitor(AST) : PermissiveVisitor!AST * is used for semantic time AST node traversal, so in order to not duplicate the code, * the template mixin is used. */ -package mixin template ParseVisitMethods(AST) +package(dmd.visitor) mixin template ParseVisitMethods(AST) { import dmd.root.array; @@ -854,7 +854,7 @@ package mixin template ParseVisitMethods(AST) override void visit(AST.FuncLiteralDeclaration f) { //printf("Visiting FuncLiteralDeclaration\n"); - if (f.type.ty == Terror) + if (f.type.isTypeError()) return; auto tf = f.type.isTypeFunction(); if (!f.inferRetType && tf.next) diff --git a/gcc/testsuite/gdc.test/compilable/imports/imp23812.c b/gcc/testsuite/gdc.test/compilable/imports/imp23812.c new file mode 100644 index 0000000..26a600a --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/imports/imp23812.c @@ -0,0 +1,35 @@ + +void funcDefault(void); + +#pragma attribute(push, nothrow) +void funcNothrow(void); +#pragma attribute(pop) + +#pragma attribute(push, nogc) +void funcNogc(void); +#pragma attribute(pop) + +#pragma attribute(push, pure) +void funcPure(void); +#pragma attribute(pop) + +#pragma attribute(push, nothrow, nogc) +void funcNothrowNogc(void); +#pragma attribute(pop) + +void funcDefault2(void); + +#pragma attribute(push, nothrow) +#pragma attribute(push, nogc) +void funcNothrowNogc2(void); +#pragma attribute(pop) +void funcNothrow2(void); +#pragma attribute(pop) + +#pragma attribute(push, nothrow) +void funcWithCallback(void (*f)(void)); +struct Callbacks +{ + void (*f)(void); +}; +#pragma attribute(pop) diff --git a/gcc/testsuite/gdc.test/compilable/test23812.d b/gcc/testsuite/gdc.test/compilable/test23812.d new file mode 100644 index 0000000..502a9d0 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test23812.d @@ -0,0 +1,75 @@ +// EXTRA_FILES: imports/imp23812.c + +import imports.imp23812; + +void callDefault() +{ + funcDefault(); + funcDefault2(); +} + +static assert(!__traits(compiles, () nothrow { funcDefault(); } )); +static assert(!__traits(compiles, () @nogc { funcDefault(); } )); +static assert(!__traits(compiles, () pure { funcDefault(); } )); + +static assert(!__traits(compiles, () nothrow { funcDefault2(); } )); +static assert(!__traits(compiles, () @nogc { funcDefault2(); } )); +static assert(!__traits(compiles, () pure { funcDefault2(); } )); + +void callNothrow() nothrow +{ + funcNothrow(); + funcNothrow2(); +} + +static assert(!__traits(compiles, () @nogc { funcNothrow(); } )); +static assert(!__traits(compiles, () pure { funcNothrow(); } )); + +static assert(!__traits(compiles, () @nogc { funcNothrow2(); } )); +static assert(!__traits(compiles, () pure { funcNothrow2(); } )); + +void callNogc() @nogc +{ + funcNogc(); +} + +static assert(!__traits(compiles, () nothrow { funcNogc(); } )); +static assert(!__traits(compiles, () pure { funcNogc(); } )); + +void callPure() pure +{ + funcPure(); +} + +static assert(!__traits(compiles, () nothrow { funcPure(); } )); +static assert(!__traits(compiles, () @nogc { funcPure(); } )); + +void callNothrowNogc() nothrow @nogc +{ + funcNothrowNogc(); + funcNothrowNogc2(); +} + +static assert(!__traits(compiles, () pure { funcNothrowNogc(); } )); + +static assert(!__traits(compiles, () pure { funcNothrowNogc2(); } )); + +extern(C) void callbackDefault() +{ +} + +extern(C) void callbackNothrow() nothrow +{ +} + +void callFuncWithCallback() nothrow +{ + funcWithCallback(&callbackNothrow); + + Callbacks callbacks; + callbacks.f = &callbackNothrow; +} + +static assert(!__traits(compiles, () { funcWithCallback(&callbackDefault); } )); + +static assert(!__traits(compiles, () { Callbacks callbacks; callbacks.f = &callbackDefault; } )); diff --git a/gcc/testsuite/gdc.test/compilable/test24762.d b/gcc/testsuite/gdc.test/compilable/test24762.d new file mode 100644 index 0000000..91df3c5 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test24762.d @@ -0,0 +1,11 @@ +// https://issues.dlang.org/show_bug.cgi?id=24762 + +struct S { int m; } + +string m() { return "m"; } +@nogc void f() +{ + S s; + enum t = m(); + auto x = __traits(getMember, s, m()); // Error: `@nogc` function `nogc.f` cannot call non-@nogc function `nogc.m` +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail18243.d b/gcc/testsuite/gdc.test/fail_compilation/fail18243.d index f31319b..016fff9 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail18243.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail18243.d @@ -1,8 +1,10 @@ /* -EXTRA_FILES: imports/a18243.d +EXTRA_FILES: imports/a18243.d imports/b18243.d TEST_OUTPUT: --- -fail_compilation/fail18243.d(15): Error: none of the overloads of `isNaN` are callable using argument types `!()(float)` +fail_compilation/fail18243.d(17): Error: none of the overloads of `isNaN` are callable using argument types `!()(float)` +fail_compilation/imports/b18243.d(3): Candidates are: `isNaN(T)(T x)` +fail_compilation/imports/a18243.d(5): `imports.a18243.isNaN()` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail_arrayexp.d b/gcc/testsuite/gdc.test/fail_compilation/fail_arrayexp.d index 1a766ff..c205413 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail_arrayexp.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail_arrayexp.d @@ -6,6 +6,7 @@ fail_compilation/fail_arrayexp.d(25): Error: cannot use `[]` operator on express fail_compilation/fail_arrayexp.d(26): Error: static array of `const(int)[]` with multiple lengths not allowed fail_compilation/fail_arrayexp.d(27): Error: only one index allowed to index `string` fail_compilation/fail_arrayexp.d(28): Error: no `[]` operator overload for type `U` +fail_compilation/fail_arrayexp.d(16): `fail_arrayexp.U` declared here fail_compilation/fail_arrayexp.d(29): Error: only one index allowed to index `(int, string)` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail_opover.d b/gcc/testsuite/gdc.test/fail_compilation/fail_opover.d index 2a62f53..368755a 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail_opover.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail_opover.d @@ -3,8 +3,34 @@ /* TEST_OUTPUT: --- -fail_compilation/fail_opover.d(13): Error: no `[]` operator overload for type `object.Object` -fail_compilation/fail_opover.d(17): Error: no `[]` operator overload for type `TestS` +fail_compilation/fail_opover.d(39): Error: no `[]` operator overload for type `object.Object` +$p:object.d$(110): `object.Object` declared here +fail_compilation/fail_opover.d(43): Error: no `[]` operator overload for type `TestS` +fail_compilation/fail_opover.d(41): `fail_opover.test1.TestS` declared here +fail_compilation/fail_opover.d(55): Error: no `[]` operator overload for type `S` +fail_compilation/fail_opover.d(48): `fail_opover.test2.S` declared here +fail_compilation/fail_opover.d(56): Error: no `[]` operator overload for type `S` +fail_compilation/fail_opover.d(48): `fail_opover.test2.S` declared here +fail_compilation/fail_opover.d(57): Error: no `[]` operator overload for type `S` +fail_compilation/fail_opover.d(48): `fail_opover.test2.S` declared here +fail_compilation/fail_opover.d(58): Error: no `[]` operator overload for type `S` +fail_compilation/fail_opover.d(48): `fail_opover.test2.S` declared here +fail_compilation/fail_opover.d(59): Error: no `[]` operator overload for type `S` +fail_compilation/fail_opover.d(48): `fail_opover.test2.S` declared here +fail_compilation/fail_opover.d(60): Error: no `[]` operator overload for type `S` +fail_compilation/fail_opover.d(48): `fail_opover.test2.S` declared here +fail_compilation/fail_opover.d(61): Error: no `[]` operator overload for type `S` +fail_compilation/fail_opover.d(48): `fail_opover.test2.S` declared here +fail_compilation/fail_opover.d(62): Error: no `[]` operator overload for type `S` +fail_compilation/fail_opover.d(48): `fail_opover.test2.S` declared here +fail_compilation/fail_opover.d(63): Error: no `[]` operator overload for type `S` +fail_compilation/fail_opover.d(48): `fail_opover.test2.S` declared here +fail_compilation/fail_opover.d(64): Error: no `[]` operator overload for type `S` +fail_compilation/fail_opover.d(48): `fail_opover.test2.S` declared here +fail_compilation/fail_opover.d(65): Error: no `[]` operator overload for type `S` +fail_compilation/fail_opover.d(48): `fail_opover.test2.S` declared here +fail_compilation/fail_opover.d(66): Error: no `[]` operator overload for type `S` +fail_compilation/fail_opover.d(48): `fail_opover.test2.S` declared here --- */ void test1() @@ -17,23 +43,6 @@ void test1() s[] = error; } -/* -TEST_OUTPUT: ---- -fail_compilation/fail_opover.d(46): Error: no `[]` operator overload for type `S` -fail_compilation/fail_opover.d(47): Error: no `[]` operator overload for type `S` -fail_compilation/fail_opover.d(48): Error: no `[]` operator overload for type `S` -fail_compilation/fail_opover.d(49): Error: no `[]` operator overload for type `S` -fail_compilation/fail_opover.d(50): Error: no `[]` operator overload for type `S` -fail_compilation/fail_opover.d(51): Error: no `[]` operator overload for type `S` -fail_compilation/fail_opover.d(52): Error: no `[]` operator overload for type `S` -fail_compilation/fail_opover.d(53): Error: no `[]` operator overload for type `S` -fail_compilation/fail_opover.d(54): Error: no `[]` operator overload for type `S` -fail_compilation/fail_opover.d(55): Error: no `[]` operator overload for type `S` -fail_compilation/fail_opover.d(56): Error: no `[]` operator overload for type `S` -fail_compilation/fail_opover.d(57): Error: no `[]` operator overload for type `S` ---- -*/ void test2() { struct S diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice15788.d b/gcc/testsuite/gdc.test/fail_compilation/ice15788.d index 941b179..6694254 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice15788.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice15788.d @@ -2,7 +2,9 @@ EXTRA_FILES: imports/range15788.d TEST_OUTPUT: --- -fail_compilation/ice15788.d(18): Error: none of the overloads of `iota` are callable using argument types `!()(S, S)` +fail_compilation/ice15788.d(20): Error: none of the overloads of `iota` are callable using argument types `!()(S, S)` +fail_compilation/imports/range15788.d(3): Candidates are: `iota(B, E, S)(B, E, S)` +fail_compilation/ice15788.d(13): `ice15788.iota()` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/imphint.d b/gcc/testsuite/gdc.test/fail_compilation/imphint.d index 101e786..7ca81f47 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/imphint.d +++ b/gcc/testsuite/gdc.test/fail_compilation/imphint.d @@ -1,53 +1,60 @@ /* TEST_OUTPUT: --- -fail_compilation/imphint.d(53): Error: `printf` is not defined, perhaps `import core.stdc.stdio;` is needed? -fail_compilation/imphint.d(54): Error: `writeln` is not defined, perhaps `import std.stdio;` is needed? -fail_compilation/imphint.d(55): Error: `sin` is not defined, perhaps `import std.math;` is needed? -fail_compilation/imphint.d(56): Error: `cos` is not defined, perhaps `import std.math;` is needed? -fail_compilation/imphint.d(57): Error: `sqrt` is not defined, perhaps `import std.math;` is needed? -fail_compilation/imphint.d(58): Error: `fabs` is not defined, perhaps `import std.math;` is needed? -fail_compilation/imphint.d(61): Error: `AliasSeq` is not defined, perhaps `import std.meta;` is needed? -fail_compilation/imphint.d(62): Error: `appender` is not defined, perhaps `import std.array;` is needed? -fail_compilation/imphint.d(63): Error: `array` is not defined, perhaps `import std.array;` is needed? -fail_compilation/imphint.d(64): Error: `calloc` is not defined, perhaps `import core.stdc.stdlib;` is needed? -fail_compilation/imphint.d(65): Error: `chdir` is not defined, perhaps `import std.file;` is needed? -fail_compilation/imphint.d(66): Error: `dirEntries` is not defined, perhaps `import std.file;` is needed? -fail_compilation/imphint.d(67): Error: `drop` is not defined, perhaps `import std.range;` is needed? -fail_compilation/imphint.d(68): Error: `each` is not defined, perhaps `import std.algorithm;` is needed? -fail_compilation/imphint.d(69): Error: `empty` is not defined, perhaps `import std.range;` is needed? -fail_compilation/imphint.d(70): Error: `enumerate` is not defined, perhaps `import std.range;` is needed? -fail_compilation/imphint.d(71): Error: `endsWith` is not defined, perhaps `import std.algorithm;` is needed? -fail_compilation/imphint.d(72): Error: `enforce` is not defined, perhaps `import std.exception;` is needed? -fail_compilation/imphint.d(73): Error: `equal` is not defined, perhaps `import std.algorithm;` is needed? -fail_compilation/imphint.d(74): Error: `exists` is not defined, perhaps `import std.file;` is needed? -fail_compilation/imphint.d(75): Error: `filter` is not defined, perhaps `import std.algorithm;` is needed? -fail_compilation/imphint.d(76): Error: `format` is not defined, perhaps `import std.format;` is needed? -fail_compilation/imphint.d(77): Error: `free` is not defined, perhaps `import core.stdc.stdlib;` is needed? -fail_compilation/imphint.d(78): Error: `front` is not defined, perhaps `import std.range;` is needed? -fail_compilation/imphint.d(79): Error: `iota` is not defined, perhaps `import std.range;` is needed? -fail_compilation/imphint.d(80): Error: `isDir` is not defined, perhaps `import std.file;` is needed? -fail_compilation/imphint.d(81): Error: `isFile` is not defined, perhaps `import std.file;` is needed? -fail_compilation/imphint.d(82): Error: `join` is not defined, perhaps `import std.array;` is needed? -fail_compilation/imphint.d(83): Error: `joiner` is not defined, perhaps `import std.algorithm;` is needed? -fail_compilation/imphint.d(84): Error: `malloc` is not defined, perhaps `import core.stdc.stdlib;` is needed? -fail_compilation/imphint.d(85): Error: `map` is not defined, perhaps `import std.algorithm;` is needed? -fail_compilation/imphint.d(86): Error: `max` is not defined, perhaps `import std.algorithm;` is needed? -fail_compilation/imphint.d(87): Error: `min` is not defined, perhaps `import std.algorithm;` is needed? -fail_compilation/imphint.d(88): Error: `mkdir` is not defined, perhaps `import std.file;` is needed? -fail_compilation/imphint.d(89): Error: `popFront` is not defined, perhaps `import std.range;` is needed? -fail_compilation/imphint.d(90): Error: `realloc` is not defined, perhaps `import core.stdc.stdlib;` is needed? -fail_compilation/imphint.d(91): Error: `replace` is not defined, perhaps `import std.array;` is needed? -fail_compilation/imphint.d(92): Error: `rmdir` is not defined, perhaps `import std.file;` is needed? -fail_compilation/imphint.d(93): Error: `sort` is not defined, perhaps `import std.algorithm;` is needed? -fail_compilation/imphint.d(94): Error: `split` is not defined, perhaps `import std.array;` is needed? -fail_compilation/imphint.d(95): Error: `startsWith` is not defined, perhaps `import std.algorithm;` is needed? -fail_compilation/imphint.d(96): Error: `take` is not defined, perhaps `import std.range;` is needed? -fail_compilation/imphint.d(97): Error: `text` is not defined, perhaps `import std.conv;` is needed? -fail_compilation/imphint.d(98): Error: `to` is not defined, perhaps `import std.conv;` is needed? +fail_compilation/imphint.d(60): Error: `printf` is not defined, perhaps `import core.stdc.stdio;` is needed? +fail_compilation/imphint.d(61): Error: `writeln` is not defined, perhaps `import std.stdio;` is needed? +fail_compilation/imphint.d(62): Error: `sin` is not defined, perhaps `import std.math;` is needed? +fail_compilation/imphint.d(63): Error: `cos` is not defined, perhaps `import std.math;` is needed? +fail_compilation/imphint.d(64): Error: `sqrt` is not defined, perhaps `import std.math;` is needed? +fail_compilation/imphint.d(65): Error: `fabs` is not defined, perhaps `import std.math;` is needed? +fail_compilation/imphint.d(68): Error: `AliasSeq` is not defined, perhaps `import std.meta;` is needed? +fail_compilation/imphint.d(69): Error: `appender` is not defined, perhaps `import std.array;` is needed? +fail_compilation/imphint.d(70): Error: `array` is not defined, perhaps `import std.array;` is needed? +fail_compilation/imphint.d(71): Error: `calloc` is not defined, perhaps `import core.stdc.stdlib;` is needed? +fail_compilation/imphint.d(72): Error: `chdir` is not defined, perhaps `import std.file;` is needed? +fail_compilation/imphint.d(73): Error: `dirEntries` is not defined, perhaps `import std.file;` is needed? +fail_compilation/imphint.d(74): Error: `drop` is not defined, perhaps `import std.range;` is needed? +fail_compilation/imphint.d(75): Error: `each` is not defined, perhaps `import std.algorithm;` is needed? +fail_compilation/imphint.d(76): Error: `empty` is not defined, perhaps `import std.range;` is needed? +fail_compilation/imphint.d(77): Error: `enumerate` is not defined, perhaps `import std.range;` is needed? +fail_compilation/imphint.d(78): Error: `endsWith` is not defined, perhaps `import std.algorithm;` is needed? +fail_compilation/imphint.d(79): Error: `enforce` is not defined, perhaps `import std.exception;` is needed? +fail_compilation/imphint.d(80): Error: `equal` is not defined, perhaps `import std.algorithm;` is needed? +fail_compilation/imphint.d(81): Error: `exists` is not defined, perhaps `import std.file;` is needed? +fail_compilation/imphint.d(82): Error: `filter` is not defined, perhaps `import std.algorithm;` is needed? +fail_compilation/imphint.d(83): Error: `format` is not defined, perhaps `import std.format;` is needed? +fail_compilation/imphint.d(84): Error: `free` is not defined, perhaps `import core.stdc.stdlib;` is needed? +fail_compilation/imphint.d(85): Error: `front` is not defined, perhaps `import std.range;` is needed? +fail_compilation/imphint.d(86): Error: `iota` is not defined, perhaps `import std.range;` is needed? +fail_compilation/imphint.d(87): Error: `isDir` is not defined, perhaps `import std.file;` is needed? +fail_compilation/imphint.d(88): Error: `isFile` is not defined, perhaps `import std.file;` is needed? +fail_compilation/imphint.d(89): Error: `join` is not defined, perhaps `import std.array;` is needed? +fail_compilation/imphint.d(90): Error: `joiner` is not defined, perhaps `import std.algorithm;` is needed? +fail_compilation/imphint.d(91): Error: `malloc` is not defined, perhaps `import core.stdc.stdlib;` is needed? +fail_compilation/imphint.d(92): Error: `map` is not defined, perhaps `import std.algorithm;` is needed? +fail_compilation/imphint.d(93): Error: `max` is not defined, perhaps `import std.algorithm;` is needed? +fail_compilation/imphint.d(94): Error: `min` is not defined, perhaps `import std.algorithm;` is needed? +fail_compilation/imphint.d(95): Error: `mkdir` is not defined, perhaps `import std.file;` is needed? +fail_compilation/imphint.d(96): Error: `popFront` is not defined, perhaps `import std.range;` is needed? +fail_compilation/imphint.d(97): Error: `realloc` is not defined, perhaps `import core.stdc.stdlib;` is needed? +fail_compilation/imphint.d(98): Error: `replace` is not defined, perhaps `import std.array;` is needed? +fail_compilation/imphint.d(99): Error: `rmdir` is not defined, perhaps `import std.file;` is needed? +fail_compilation/imphint.d(100): Error: `sort` is not defined, perhaps `import std.algorithm;` is needed? +fail_compilation/imphint.d(101): Error: `split` is not defined, perhaps `import std.array;` is needed? +fail_compilation/imphint.d(102): Error: `startsWith` is not defined, perhaps `import std.algorithm;` is needed? +fail_compilation/imphint.d(103): Error: `take` is not defined, perhaps `import std.range;` is needed? +fail_compilation/imphint.d(104): Error: `text` is not defined, perhaps `import std.conv;` is needed? +fail_compilation/imphint.d(105): Error: `to` is not defined, perhaps `import std.conv;` is needed? +fail_compilation/imphint.d(107): Error: `InterpolationHeader` is not defined, perhaps `import core.interpolation;` ? +fail_compilation/imphint.d(108): Error: template `heresy` is not callable using argument types `!()(InterpolationHeader, InterpolationFooter)` +fail_compilation/imphint.d(107): Candidate is: `heresy(Args...)(InterpolationHeader header, Args args, InterpolationFooter footer)` --- */ + + + + void foo() { printf("hello world\n"); @@ -96,4 +103,7 @@ void foo() take(); text(); to(); + + void heresy(Args...)(InterpolationHeader header, Args args, InterpolationFooter footer) {} + heresy(i""); } diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/a18243.d b/gcc/testsuite/gdc.test/fail_compilation/imports/a18243.d index 73df751..35da731 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/imports/a18243.d +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/a18243.d @@ -1,5 +1,5 @@ -module a18243; +module imports.a18243; -import std.math : isNaN; +import imports.b18243 : isNaN; public bool isNaN() { return false; } diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/b18243.d b/gcc/testsuite/gdc.test/fail_compilation/imports/b18243.d new file mode 100644 index 0000000..d59128f --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/b18243.d @@ -0,0 +1,3 @@ +module imports.b18243; + +bool isNaN(T)(T x) { return false; } diff --git a/gcc/testsuite/gdc.test/fail_compilation/nested_template_constraint.d b/gcc/testsuite/gdc.test/fail_compilation/nested_template_constraint.d new file mode 100644 index 0000000..9a89c0a --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/nested_template_constraint.d @@ -0,0 +1,18 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/nested_template_constraint.d(17): Error: template `foo` is not callable using argument types `!()(string, int)` +fail_compilation/nested_template_constraint.d(10): Candidate is: `foo(int x = 0)` +fail_compilation/nested_template_constraint.d(11): - Containing: `foo(T, U)(T t, U u)` +--- +*/ + +template foo(int x = 0) { + void foo(T, U)(T t, U u) + if (is(T == int) && is(U == int)) {} +} + +void main() +{ + foo("hello", 4); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test16443.d b/gcc/testsuite/gdc.test/fail_compilation/test16443.d new file mode 100644 index 0000000..a83dc53 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test16443.d @@ -0,0 +1,12 @@ +/* TEST_OUTPUT: +--- +fail_compilation/test16443.d(10): Error: incompatible types for `(null) + (null)`: both operands are of type `typeof(null)` +fail_compilation/test16443.d(11): Error: incompatible types for `(null) - (null)`: both operands are of type `typeof(null)` +--- +*/ + +void foo() +{ + auto a = null + null; + auto b = null - null; +} |