@@ -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)
@@ -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;
@@ -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.
@@ -98,14 +98,14 @@
| 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 @@
|---------------------------------------------------------------------------------|---------------------------------------------------------------|
| [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 |
@@ -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.
@@ -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)
{
@@ -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();
+ }
+
//}
/******************************************************************************/
@@ -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
*/
@@ -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();
@@ -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;
/*********************************
@@ -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;
}
@@ -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))
{
@@ -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;
}
/***************************************
@@ -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)
@@ -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;
@@ -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))
@@ -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();
@@ -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();
@@ -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:
@@ -1142,75 +1021,6 @@ extern (C++) class FuncDeclaration : Declaration
return true;
}
- /***********************************************
- * 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
{
@@ -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;
@@ -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
@@ -250,6 +250,7 @@ struct Param
DString resfile;
DString exefile;
DString mapfile;
+ bool fullyQualifiedObjectFiles;
bool timeTrace;
uint32_t timeTraceGranularityUs;
const char* timeTraceFile;
@@ -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;
}
@@ -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",
];
}
@@ -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)
{
@@ -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); }
};
@@ -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;
@@ -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); }
@@ -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();
+}
@@ -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;
@@ -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,728 +372,909 @@ 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)
- {
- id = Id.opBinary;
- id_r = Id.opBinaryRight;
- tiargs = opToArg(sc, e.op);
}
+ break;
}
- if (!s && !s_r)
+ if (search_function(ad, Id.index))
{
- // Try the D1-style operators, deprecated
- if (ad1 && id)
- {
- 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 (ad2 && id_r)
+ // 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)
{
- 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();
- }
+ return Expression.combine(e0, result);
}
}
- Expressions* args1 = new Expressions();
- Expressions* args2 = new Expressions();
- if (s || s_r)
+ Lfallback:
+ if (maybeSlice && ae.e1.op == EXP.type)
{
- /* 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()))
- {
- return ErrorExp.get();
- }
- }
- FuncDeclaration lastf = m.lastf;
- if (s_r)
+ 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))
+ {
+ // Deal with $
+ result = resolveOpDollar(sc, ae, ie, &e0);
+
+ if (result.op == EXP.error)
{
- 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();
+ if (!e0 && !search_function(ad, Id.dollar)) {
+ ae.loc.errorSupplemental("Aggregate declaration '%s' does not define 'opDollar'", ae.e1.toChars());
}
+ return result;
}
- 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
+ /* Rewrite a[i..j] as:
+ * a.opSlice(i, j)
+ */
+ auto a = new Expressions();
+ if (ie)
{
- // 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);
+ 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;
}
- L1:
- version (all)
+ // Didn't find it. Forward to aliasthis
+ if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type))
{
- // Retained for D1 compatibility
- if (isCommutative(e.op) && !tiargs)
- {
- s = null;
- s_r = null;
- if (ad1 && id_r)
- {
- 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)
- {
- 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()))
- {
- 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)
- {
- 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;
- }
- }
- }
+ //printf("att arr e1 = %s\n", this.e1.type.toChars());
+ /* 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;
+ return result;
+ }
- Expression rewrittenLhs;
- if (!(e.op == EXP.assign && ad2 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943
+ /***********************************************
+ * 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)
{
- if (Expression result = checkAliasThisForLhs(ad1, sc, e))
+ version (all)
{
- /* 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 dve = ae.e1.isDotVarExp();
- if (auto ad = dve.var.isMember2())
+ // Backwards compatibility with D1 if opCast is a function, not a template
+ if (fd.isFuncDeclaration())
{
- // 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;
- }
+ // Rewrite as: e1.opCast()
+ return build_overload(e.loc, sc, e.e1, null, fd);
}
- rewrittenLhs = ae.e1;
}
+ 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;
}
- if (!(e.op == EXP.assign && ad1 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943
+ // Didn't find it. Forward to aliasthis
+ if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type))
{
- if (Expression result = checkAliasThisForRhs(ad2, sc, e))
+ /* 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;
+ }
}
- 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;
}
+ return result;
+ }
- Expression visitEqual(EqualExp e)
+ 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)
{
- //printf("EqualExp::op_overload() (%s)\n", e.toChars());
- Type t1 = e.e1.type.toBasetype();
- Type t2 = e.e2.type.toBasetype();
-
- /* 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))
+ 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;
}
-
- /* 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
+ }
+ 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;
- }
-
- /* Check for class equality.
+ }
+ 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 (t1.ty == Tclass && t2.ty == Tclass)
+ if (ad1)
{
- ClassDeclaration cd1 = t1.isClassHandle();
- ClassDeclaration cd2 = t2.isClassHandle();
- if (!(cd1.classKind == ClassKind.cpp || cd2.classKind == ClassKind.cpp))
+ s = search_function(ad1, Id.opBinary);
+ if (s && !s.isTemplateDeclaration())
{
- /* 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;
- }
-
- 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;
+ error(e.e1.loc, "`%s.opBinary` isn't a template", e.e1.toChars());
+ return ErrorExp.get();
}
}
-
- if (Expression result = compare_overload(e, sc, Id.eq, null))
+ if (ad2)
{
- if (lastComma(result).op == EXP.call && e.op == EXP.notEqual)
+ s_r = search_function(ad2, Id.opBinaryRight);
+ if (s_r && !s_r.isTemplateDeclaration())
{
- result = new NotExp(result.loc, result);
- result = result.expressionSemantic(sc);
+ error(e.e2.loc, "`%s.opBinaryRight` isn't a template", e.e2.toChars());
+ return ErrorExp.get();
}
- return result;
+ if (s_r && s_r == s) // https://issues.dlang.org/show_bug.cgi?id=12778
+ s_r = null;
}
-
- /* Check for pointer equality.
- */
- if (t1.ty == Tpointer || t2.ty == Tpointer)
+ // Set tiargs, the template argument list, which will be the operator string
+ if (s || s_r)
{
- /* Rewrite:
- * ptr1 == ptr2
- * as:
- * ptr1 is ptr2
- *
- * This is just a rewriting for deterministic AST representation
- * as the backend input.
- */
- 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);
+ id = Id.opBinary;
+ id_r = Id.opBinaryRight;
+ tiargs = opToArg(sc, e.op);
}
-
- /* Check for struct equality without opEquals.
- */
- if (t1.ty == Tstruct && t2.ty == Tstruct)
+ }
+ if (!s && !s_r)
+ {
+ // Try the D1-style operators, deprecated
+ if (ad1 && id)
{
- auto sd = t1.isTypeStruct().sym;
- if (sd != t2.isTypeStruct().sym)
- return null;
-
- import dmd.clone : needOpEquals;
- if (global.params.fieldwise != FeatureState.enabled && !needOpEquals(sd))
+ s = search_function(ad1, id);
+ if (s && id != Id.assign)
{
- // 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);
+ // @@@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();
}
-
- /* 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)
+ if (ad2 && id_r)
{
- auto tup1 = e.e1.isTupleExp();
- auto tup2 = e.e2.isTupleExp();
- size_t dim = tup1.exps.length;
- if (dim != tup2.exps.length)
+ 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)
{
- error(e.loc, "mismatched sequence lengths, `%d` and `%d`",
- cast(int)dim, cast(int)tup2.exps.length);
+ // @@@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();
}
-
- Expression result;
- if (dim == 0)
+ }
+ }
+ 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()))
{
- // zero-length tuple comparison should always return true or false.
- result = IntegerExp.createBool(e.op == EXP.equal);
+ 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()))
{
- 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);
+ return ErrorExp.get();
}
- result = Expression.combine(tup1.e0, tup2.e0, result);
- result = result.expressionSemantic(sc);
-
- return result;
}
- return null;
- }
-
- Expression visitCmp(CmpExp e)
- {
- //printf("CmpExp:: () (%s)\n", e.toChars());
- return compare_overload(e, sc, Id.cmp, pop);
+ 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
+ {
+ // 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);
+ }
}
-
- /*********************************
- * Operator overloading for op=
- */
- Expression visitBinAssign(BinAssignExp e)
+ L1:
+ version (all)
{
- //printf("BinAssignExp::op_overload() (%s)\n", e.toChars());
- if (auto ae = e.e1.isArrayExp())
+ // Retained for D1 compatibility
+ if (isCommutative(e.op) && !tiargs)
{
- 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)
+ s = null;
+ s_r = null;
+ if (ad1 && id_r)
{
- ie = (*ae.arguments)[0].isIntervalExp();
+ s_r = search_function(ad1, id_r);
}
- Type att = null; // first cyclic `alias this` type
- while (true)
+ if (ad2 && id)
{
- if (ae.e1.op == EXP.error)
+ 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)
{
- return ae.e1;
+ args1.setDim(1);
+ (*args1)[0] = e.e1;
+ expandTuples(args1);
+ args2.setDim(1);
+ (*args2)[0] = e.e2;
+ expandTuples(args2);
}
- 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))
+ MatchAccumulator m;
+ if (s_r)
{
- // 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)
+ functionResolve(m, s_r, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2));
+ if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
{
- return Expression.combine(e0, result);
+ return ErrorExp.get();
}
}
- Lfallback:
- if (maybeSlice && search_function(ad, Id.opSliceOpAssign))
+ FuncDeclaration lastf = m.lastf;
+ if (s)
{
- // 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)
+ functionResolve(m, s, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1));
+ if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
{
- a.push(ie.lwr);
- a.push(ie.upr);
+ return ErrorExp.get();
}
- 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);
+ }
+ 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;
}
- // Didn't find it. Forward to aliasthis
- if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type))
+ }
+ }
+ }
+
+ 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))
+ {
+ /* 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 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))
{
- /* Rewrite (a[arguments] op= e2) as:
- * a.aliasthis[arguments] op= e2
- */
- ae.e1 = resolveAliasThis(sc, ae1save, true);
- if (ae.e1)
- continue;
+ if (dve.var == ad.aliasthis.sym)
+ return result;
}
- break;
}
- ae.e1 = ae1old; // recovery
- ae.lengthVar = null;
+ rewrittenLhs = ae.e1;
}
- Expression result = e.binSemanticProp(sc);
- if (result)
+ }
+ 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;
- // Don't attempt 'alias this' if an error occurred
- if (e.e1.type.ty == Terror || e.e2.type.ty == Terror)
+ }
+ 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)
+ {
+ //printf("EqualExp::op_overload() (%s)\n", e.toChars());
+ Type t1 = e.e1.type.toBasetype();
+ Type t2 = e.e2.type.toBasetype();
+
+ /* 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 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))
{
- return ErrorExp.get();
+ /* 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;
+ }
+
+ 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;
}
- Identifier id = opId(e);
- Expressions* args2 = new Expressions();
- AggregateDeclaration ad1 = isAggregate(e.e1.type);
- Dsymbol s = null;
- Objects* tiargs = null;
- /* Try opOpAssign
+ }
+
+ if (Expression result = compare_overload(e, sc, Id.eq, null))
+ {
+ if (lastComma(result).op == EXP.call && e.op == EXP.notEqual)
+ {
+ result = new NotExp(result.loc, result);
+ result = result.expressionSemantic(sc);
+ }
+ return result;
+ }
+
+ /* 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 (ad1)
+ 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))
{
- 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();
- }
+ // 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);
}
- // Set tiargs, the template argument list, which will be the operator string
- if (s)
+
+ /* 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)
{
- id = Id.opOpAssign;
- tiargs = opToArg(sc, e.op);
+ error(e.loc, "mismatched sequence lengths, `%d` and `%d`",
+ cast(int)dim, cast(int)tup2.exps.length);
+ return ErrorExp.get();
}
- // Try D1-style operator overload, deprecated
- if (!s && ad1 && id)
+ Expression result;
+ if (dim == 0)
{
- s = search_function(ad1, id);
- if (s)
+ // 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++)
{
- // @@@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();
+ 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);
}
+ result = Expression.combine(tup1.e0, tup2.e0, result);
+ result = result.expressionSemantic(sc);
- if (s)
+ return result;
+ }
+ return null;
+ }
+
+ 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())
+ {
+ 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)
{
- /* 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()))
+ ie = (*ae.arguments)[0].isIntervalExp();
+ }
+ Type att = null; // first cyclic `alias this` type
+ while (true)
+ {
+ if (ae.e1.op == EXP.error)
{
- return ErrorExp.get();
+ return ae.e1;
}
- if (m.count > 1)
+ 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))
{
- // 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());
+ // 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);
+ }
}
- else if (m.last == MATCH.nomatch)
+ Lfallback:
+ if (maybeSlice && search_function(ad, Id.opSliceOpAssign))
{
- if (tiargs)
- goto L1;
- m.lastf = null;
+ // 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);
+ return result;
}
- // Rewrite (e1 op e2) as e1.opOpAssign(e2)
- return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s);
+ // 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;
}
- 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;
+ 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();
+ }
+ }
+ // Set tiargs, the template argument list, which will be the operator string
+ if (s)
+ {
+ id = Id.opOpAssign;
+ tiargs = opToArg(sc, e.op);
+ }
- return checkAliasThisForRhs(isAggregate(e.e2.type), sc, e);
+ // Try D1-style operator overload, deprecated
+ if (!s && ad1 && id)
+ {
+ 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();
+ }
}
+ 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()))
+ {
+ 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);
+ }
+ 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;
@@ -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)[])
@@ -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;
}
deleted file mode 100644
@@ -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);
- }
-}
@@ -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;
}
@@ -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;
+}
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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();
@@ -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)
similarity index 99%
rename from gcc/d/dmd/foreachvar.d
rename to 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.
similarity index 97%
rename from gcc/d/dmd/visitor.d
rename to 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);
}
}
similarity index 99%
rename from gcc/d/dmd/parsetimevisitor.d
rename to 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
similarity index 93%
rename from gcc/d/dmd/permissivevisitor.d
rename to 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.
similarity index 50%
rename from gcc/d/dmd/sapply.d
rename to 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:
similarity index 98%
rename from gcc/d/dmd/statement_rewrite_walker.d
rename to 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;
similarity index 99%
rename from gcc/d/dmd/transitivevisitor.d
rename to 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)
new file mode 100644
@@ -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)
new file mode 100644
@@ -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; } ));
new file mode 100644
@@ -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`
+}
@@ -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()`
---
*/
@@ -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)`
---
*/
@@ -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
@@ -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()`
---
*/
@@ -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"");
}
@@ -1,5 +1,5 @@
-module a18243;
+module imports.a18243;
-import std.math : isNaN;
+import imports.b18243 : isNaN;
public bool isNaN() { return false; }
new file mode 100644
@@ -0,0 +1,3 @@
+module imports.b18243;
+
+bool isNaN(T)(T x) { return false; }
new file mode 100644
@@ -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);
+}
new file mode 100644
@@ -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;
+}
@@ -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.
@@ -51,3 +51,46 @@ version (LDC)
/// Writes `s` to `stderr` during CTFE (does nothing at runtime).
void __ctfeWrite(scope const(char)[] s) @nogc @safe pure nothrow {}
+
+version (GNU)
+{
+ /// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005fexpect
+ alias expect = __builtin_expect;
+ /// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005ftrap
+ alias trap = __builtin_trap;
+}
+else version (LDC)
+{
+ /// https://llvm.org/docs/LangRef.html#llvm-expect-intrinsic
+ alias expect = llvm_expect;
+ debug
+ /// https://llvm.org/docs/LangRef.html#llvm-debugtrap-intrinsic
+ alias trap = llvm_debugtrap;
+ else
+ /// https://llvm.org/docs/LangRef.html#llvm-trap-intrinsic
+ alias trap = llvm_trap;
+}
+else version (DigitalMars)
+{
+ pragma(inline, true)
+ T expect(T)(T val, T expected) if (__traits(isIntegral, T))
+ {
+ return val;
+ }
+
+ pragma(inline, true)
+ void trap()
+ {
+ debug
+ {
+ version(D_InlineAsm_X86)
+ asm nothrow @nogc pure @trusted { int 3; }
+ }
+ assert(0);
+ }
+}
+
+/// Provide static branch hints
+pragma(inline, true) bool likely(bool b) { return !!expect(b, true); }
+///
+pragma(inline, true) bool unlikely(bool b) { return !!expect(b, false); }
@@ -432,6 +432,14 @@ else version (CRuntime_Musl)
}
alias ushort fexcept_t;
}
+ else version (LoongArch64)
+ {
+ struct fenv_t
+ {
+ uint __cw;
+ }
+ alias uint fexcept_t;
+ }
else
{
static assert(false, "Architecture not supported.");
@@ -2758,6 +2758,11 @@ else version (CRuntime_Musl)
enum MINSIGSTKSZ = 2048;
enum SIGSTKSZ = 8192;
}
+ else version (LoongArch64)
+ {
+ enum MINSIGSTKSZ = 4096;
+ enum SIGSTKSZ = 16384;
+ }
else
static assert(0, "unimplemented");
@@ -405,7 +405,7 @@ const(SOCKET)* stop = start + set.fd_count;
// Adds.
-void FD_SET(SOCKET fd, fd_set* set) pure @nogc
+void FD_SET(SOCKET fd, fd_set* set) pure @nogc
{
uint c = set.fd_count;
set.fd_array.ptr[c] = fd;
@@ -547,13 +547,29 @@ enum: uint
enum: int
{
- AI_PASSIVE = 0x1,
- AI_CANONNAME = 0x2,
- AI_NUMERICHOST = 0x4,
- AI_ADDRCONFIG = 0x0400,
- AI_NON_AUTHORITATIVE = 0x04000,
- AI_SECURE = 0x08000,
- AI_RETURN_PREFERRED_NAMES = 0x010000,
+ AI_PASSIVE = 0x1, // Socket address will be used in bind() call
+ AI_CANONNAME = 0x2, // Return canonical name in first ai_canonname
+ AI_NUMERICHOST = 0x4, // Nodename must be a numeric address string
+ AI_NUMERICSERV = 0x8, // Servicename must be a numeric port number
+ AI_DNS_ONLY = 0x10, // Restrict queries to unicast DNS only (no LLMNR, netbios, etc.)
+ AI_FORCE_CLEAR_TEXT = 0x20, // Force clear text DNS query
+ AI_BYPASS_DNS_CACHE = 0x40, // Bypass DNS cache
+ AI_RETURN_TTL = 0x80, // Return record TTL
+ AI_ALL = 0x0100, // Query both IP6 and IP4 with AI_V4MAPPED
+ AI_ADDRCONFIG = 0x0400, // Resolution only if global address configured
+ AI_V4MAPPED = 0x0800, // On v6 failure, query v4 and convert to V4MAPPED format
+ AI_NON_AUTHORITATIVE = 0x04000, // LUP_NON_AUTHORITATIVE
+ AI_SECURE = 0x08000, // LUP_SECURE
+ AI_RETURN_PREFERRED_NAMES = 0x010000, // LUP_RETURN_PREFERRED_NAMES
+ AI_FQDN = 0x00020000, // Return the FQDN in ai_canonname
+ AI_FILESERVER = 0x00040000, // Resolving fileserver name resolution
+ AI_DISABLE_IDN_ENCODING = 0x00080000, // Disable Internationalized Domain Names handling
+ AI_SECURE_WITH_FALLBACK = 0x00100000, // Forces clear text fallback if the secure DNS query fails
+ AI_EXCLUSIVE_CUSTOM_SERVERS = 0x00200000, // Use exclusively the custom DNS servers
+ AI_RETURN_RESPONSE_FLAGS = 0x10000000, // Requests extra information about the DNS results
+ AI_REQUIRE_SECURE = 0x20000000, // Forces the DNS query to be done over seucre protocols
+ AI_RESOLUTION_HANDLE = 0x40000000, // Request resolution handle
+ AI_EXTENDED = 0x80000000, // Indicates this is extended ADDRINFOEX(2/..) struct
}
@@ -702,12 +718,12 @@ struct hostent
// Note: These are Winsock2!!
struct WSAOVERLAPPED;
alias LPWSAOVERLAPPED = WSAOVERLAPPED*;
-alias LPWSAOVERLAPPED_COMPLETION_ROUTINE = void function(uint, uint, LPWSAOVERLAPPED, uint);
+alias LPWSAOVERLAPPED_COMPLETION_ROUTINE = void function(uint, uint, LPWSAOVERLAPPED, uint) nothrow @nogc;
int WSAIoctl(SOCKET s, uint dwIoControlCode,
void* lpvInBuffer, uint cbInBuffer,
void* lpvOutBuffer, uint cbOutBuffer,
uint* lpcbBytesReturned,
- LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
+ LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) @nogc;
enum IOC_VENDOR = 0x18000000;
@@ -720,3 +736,31 @@ struct tcp_keepalive
uint keepalivetime;
uint keepaliveinterval;
}
+
+
+struct pollfd
+{
+ SOCKET fd; // Socket handle
+ short events; // Requested events to monitor
+ short revents; // Returned events indicating status
+}
+alias WSAPOLLFD = pollfd;
+alias PWSAPOLLFD = pollfd*;
+alias LPWSAPOLLFD = pollfd*;
+
+enum: short {
+ POLLRDNORM = 0x0100,
+ POLLRDBAND = 0x0200,
+ POLLIN = (POLLRDNORM | POLLRDBAND),
+ POLLPRI = 0x0400,
+
+ POLLWRNORM = 0x0010,
+ POLLOUT = (POLLWRNORM),
+ POLLWRBAND = 0x0020,
+
+ POLLERR = 0x0001,
+ POLLHUP = 0x0002,
+ POLLNVAL = 0x0004
+}
+
+int WSAPoll(LPWSAPOLLFD fdArray, uint fds, int timeout) @nogc;
@@ -1154,52 +1154,6 @@ unittest
t.join();
}
-unittest
-{
- // NOTE: This entire test is based on the assumption that no
- // memory is allocated after the child thread is
- // started. If an allocation happens, a collection could
- // trigger, which would cause the synchronization below
- // to cause a deadlock.
- // NOTE: DO NOT USE LOCKS IN CRITICAL REGIONS IN NORMAL CODE.
-
- import core.sync.semaphore;
-
- auto sema = new Semaphore(),
- semb = new Semaphore();
-
- auto thr = new Thread(
- {
- thread_enterCriticalRegion();
- assert(thread_inCriticalRegion());
- sema.notify();
-
- semb.wait();
- assert(thread_inCriticalRegion());
-
- thread_exitCriticalRegion();
- assert(!thread_inCriticalRegion());
- sema.notify();
-
- semb.wait();
- assert(!thread_inCriticalRegion());
- });
-
- thr.start();
-
- sema.wait();
- synchronized (ThreadBase.criticalRegionLock)
- assert(thr.m_isInCriticalRegion);
- semb.notify();
-
- sema.wait();
- synchronized (ThreadBase.criticalRegionLock)
- assert(!thr.m_isInCriticalRegion);
- semb.notify();
-
- thr.join();
-}
-
// https://issues.dlang.org/show_bug.cgi?id=22124
unittest
{
@@ -1212,36 +1166,6 @@ unittest
static assert(!__traits(compiles, () @nogc => fun(thread, 3) ));
}
-unittest
-{
- import core.sync.semaphore;
-
- shared bool inCriticalRegion;
- auto sema = new Semaphore(),
- semb = new Semaphore();
-
- auto thr = new Thread(
- {
- thread_enterCriticalRegion();
- inCriticalRegion = true;
- sema.notify();
- semb.wait();
-
- Thread.sleep(dur!"msecs"(1));
- inCriticalRegion = false;
- thread_exitCriticalRegion();
- });
- thr.start();
-
- sema.wait();
- assert(inCriticalRegion);
- semb.notify();
-
- thread_suspendAll();
- assert(!inCriticalRegion);
- thread_resumeAll();
-}
-
@nogc @safe nothrow
unittest
{
@@ -1740,21 +1664,11 @@ private extern(D) void* getStackBottom() nothrow @nogc
*/
private extern (D) bool suspend( Thread t ) nothrow @nogc
{
- Duration waittime = dur!"usecs"(10);
- Lagain:
if (!t.isRunning)
{
Thread.remove(t);
return false;
}
- else if (t.m_isInCriticalRegion)
- {
- Thread.criticalRegionLock.unlock_nothrow();
- Thread.sleep(waittime);
- if (waittime < dur!"msecs"(10)) waittime *= 2;
- Thread.criticalRegionLock.lock_nothrow();
- goto Lagain;
- }
version (Windows)
{
@@ -1962,6 +1876,13 @@ private extern (D) bool suspend( Thread t ) nothrow @nogc
return true;
}
+/**
+ * Runs the necessary operations required before stopping the world.
+ */
+extern (C) void thread_preStopTheWorld() nothrow {
+ Thread.slock.lock_nothrow();
+}
+
/**
* Suspend all threads but the calling thread for "stop the world" garbage
* collection runs. This function may be called multiple times, and must
@@ -1994,13 +1915,11 @@ extern (C) void thread_suspendAll() nothrow
return;
}
- Thread.slock.lock_nothrow();
+ thread_preStopTheWorld();
{
if ( ++suspendDepth > 1 )
return;
- Thread.criticalRegionLock.lock_nothrow();
- scope (exit) Thread.criticalRegionLock.unlock_nothrow();
size_t cnt;
bool suspendedSelf;
Thread t = ThreadBase.sm_tbeg.toThread;
@@ -2466,6 +2385,39 @@ else version (Posix)
__gshared sem_t suspendCount;
+ extern (C) bool thread_preSuspend( void* sp ) nothrow {
+ // NOTE: Since registers are being pushed and popped from the
+ // stack, any other stack data used by this function should
+ // be gone before the stack cleanup code is called below.
+ Thread obj = Thread.getThis();
+ if (obj is null)
+ {
+ return false;
+ }
+
+ if ( !obj.m_lock )
+ {
+ obj.m_curr.tstack = sp;
+ }
+
+ return true;
+ }
+
+ extern (C) bool thread_postSuspend() nothrow {
+ Thread obj = Thread.getThis();
+ if (obj is null)
+ {
+ return false;
+ }
+
+ if ( !obj.m_lock )
+ {
+ obj.m_curr.tstack = obj.m_curr.bstack;
+ }
+
+ return true;
+ }
+
extern (C) void thread_suspendHandler( int sig ) nothrow
in
{
@@ -2475,15 +2427,13 @@ else version (Posix)
{
void op(void* sp) nothrow
{
- // NOTE: Since registers are being pushed and popped from the
- // stack, any other stack data used by this function should
- // be gone before the stack cleanup code is called below.
- Thread obj = Thread.getThis();
- assert(obj !is null);
+ bool supported = thread_preSuspend(getStackTop());
+ assert(supported, "Tried to suspend a detached thread!");
- if ( !obj.m_lock )
+ scope(exit)
{
- obj.m_curr.tstack = getStackTop();
+ supported = thread_postSuspend();
+ assert(supported, "Tried to suspend a detached thread!");
}
sigset_t sigres = void;
@@ -2499,11 +2449,6 @@ else version (Posix)
assert( status == 0 );
sigsuspend( &sigres );
-
- if ( !obj.m_lock )
- {
- obj.m_curr.tstack = obj.m_curr.bstack;
- }
}
callWithStackShell(&op);
}
@@ -2641,11 +2586,7 @@ private
bool ll_dllHasExternalReferences() nothrow
{
- version (CRuntime_DigitalMars)
- enum internalReferences = 1; // only the watchdog thread
- else
- int internalReferences = msvcUsesUCRT ? 1 + ll_countLowLevelThreadsWithDLLUnloadCallback() : 1;
-
+ int internalReferences = msvcUsesUCRT ? 1 + ll_countLowLevelThreadsWithDLLUnloadCallback() : 1;
int refcnt = dll_getRefCount(ll_dllModule);
return refcnt > internalReferences;
}
@@ -2706,10 +2647,7 @@ private
// if a thread is created from a DLL, the MS runtime (starting with VC2015) increments the DLL reference count
// to avoid the DLL being unloaded while the thread is still running. Mimick this behavior here for all
// runtimes not doing this
- version (CRuntime_DigitalMars)
- enum needRef = true;
- else
- bool needRef = !msvcUsesUCRT;
+ bool needRef = !msvcUsesUCRT;
if (needRef)
{
@@ -456,7 +456,6 @@ package:
string m_name;
size_t m_sz;
bool m_isDaemon;
- bool m_isInCriticalRegion;
Throwable m_unhandled;
///////////////////////////////////////////////////////////////////////////
@@ -564,25 +563,17 @@ package(core.thread):
return cast(Mutex)_slock.ptr;
}
- @property static Mutex criticalRegionLock() nothrow @nogc
- {
- return cast(Mutex)_criticalRegionLock.ptr;
- }
-
__gshared align(mutexAlign) void[mutexClassInstanceSize] _slock;
- __gshared align(mutexAlign) void[mutexClassInstanceSize] _criticalRegionLock;
static void initLocks() @nogc nothrow
{
import core.lifetime : emplace;
emplace!Mutex(_slock[]);
- emplace!Mutex(_criticalRegionLock[]);
}
static void termLocks() @nogc nothrow
{
(cast(Mutex)_slock.ptr).__dtor();
- (cast(Mutex)_criticalRegionLock.ptr).__dtor();
}
__gshared StackContext* sm_cbeg;
@@ -978,6 +969,13 @@ package __gshared uint suspendDepth = 0;
private alias resume = externDFunc!("core.thread.osthread.resume", void function(ThreadBase) nothrow @nogc);
+/**
+ * Run the necessary operation required after the world was resumed.
+ */
+extern (C) void thread_postRestartTheWorld() nothrow {
+ ThreadBase.slock.unlock_nothrow();
+}
+
/**
* Resume all threads but the calling thread for "stop the world" garbage
* collection runs. This function must be called once for each preceding
@@ -1004,7 +1002,7 @@ do
return;
}
- scope(exit) ThreadBase.slock.unlock_nothrow();
+ scope(exit) thread_postRestartTheWorld();
{
if (--suspendDepth > 0)
return;
@@ -1144,75 +1142,6 @@ extern (C) void thread_scanAll(scope ScanAllThreadsFn scan) nothrow
private alias thread_yield = externDFunc!("core.thread.osthread.thread_yield", void function() @nogc nothrow);
-/**
- * Signals that the code following this call is a critical region. Any code in
- * this region must finish running before the calling thread can be suspended
- * by a call to thread_suspendAll.
- *
- * This function is, in particular, meant to help maintain garbage collector
- * invariants when a lock is not used.
- *
- * A critical region is exited with thread_exitCriticalRegion.
- *
- * $(RED Warning):
- * Using critical regions is extremely error-prone. For instance, using locks
- * inside a critical region can easily result in a deadlock when another thread
- * holding the lock already got suspended.
- *
- * The term and concept of a 'critical region' comes from
- * $(LINK2 https://github.com/mono/mono/blob/521f4a198e442573c400835ef19bbb36b60b0ebb/mono/metadata/sgen-gc.h#L925, Mono's SGen garbage collector).
- *
- * In:
- * The calling thread must be attached to the runtime.
- */
-extern (C) void thread_enterCriticalRegion() @nogc
-in
-{
- assert(ThreadBase.getThis());
-}
-do
-{
- synchronized (ThreadBase.criticalRegionLock)
- ThreadBase.getThis().m_isInCriticalRegion = true;
-}
-
-
-/**
- * Signals that the calling thread is no longer in a critical region. Following
- * a call to this function, the thread can once again be suspended.
- *
- * In:
- * The calling thread must be attached to the runtime.
- */
-extern (C) void thread_exitCriticalRegion() @nogc
-in
-{
- assert(ThreadBase.getThis());
-}
-do
-{
- synchronized (ThreadBase.criticalRegionLock)
- ThreadBase.getThis().m_isInCriticalRegion = false;
-}
-
-
-/**
- * Returns true if the current thread is in a critical region; otherwise, false.
- *
- * In:
- * The calling thread must be attached to the runtime.
- */
-extern (C) bool thread_inCriticalRegion() @nogc
-in
-{
- assert(ThreadBase.getThis());
-}
-do
-{
- synchronized (ThreadBase.criticalRegionLock)
- return ThreadBase.getThis().m_isInCriticalRegion;
-}
-
/**
* A callback for thread errors in D during collections. Since an allocation is not possible
@@ -1233,22 +1162,6 @@ package void onThreadError(string msg) nothrow @nogc
throw error;
}
-unittest
-{
- assert(!thread_inCriticalRegion());
-
- {
- thread_enterCriticalRegion();
-
- scope (exit)
- thread_exitCriticalRegion();
-
- assert(thread_inCriticalRegion());
- }
-
- assert(!thread_inCriticalRegion());
-}
-
/**
* Indicates whether an address has been marked by the GC.
@@ -1,4 +1,4 @@
-48d581a1f509a7a302ea893e28939edb5b130622
+eab6595ade1dab9a757bc0efe2f4e92f39cab0f7
The first line of this file holds the git revision number of the last
merge done from the dlang/phobos repository.
@@ -2385,7 +2385,11 @@ private template TimSortImpl(alias pred, R)
size_t stackLen = 0;
// Allocate temporary memory if not provided by user
- if (temp.length < minTemp) temp = () @trusted { return uninitializedArray!(T[])(minTemp); }();
+ if (temp.length < minTemp)
+ {
+ static if (hasElaborateAssign!T) temp = new T[](minTemp);
+ else temp = () @trusted { return uninitializedArray!(T[])(minTemp); }();
+ }
for (size_t i = 0; i < range.length; )
{
@@ -3076,6 +3080,20 @@ private template TimSortImpl(alias pred, R)
array.sort!("a < b", SwapStrategy.stable);
}
+// https://issues.dlang.org/show_bug.cgi?id=24773
+@safe unittest
+{
+ static struct S
+ {
+ int i = 42;
+ ~this() { assert(i == 42); }
+ }
+
+ auto array = new S[](400);
+ array.sort!((a, b) => false, SwapStrategy.stable);
+}
+
+
// schwartzSort
/**
Alternative sorting method that should be used when comparing keys involves an
@@ -60,7 +60,7 @@ Limitation: This package does not support localization, but
adheres to the rounding mode of the floating point unit, if
available.
-$(SECTION3 Format Strings)
+$(H3 $(LNAME2 format-strings, Format Strings))
The functions contained in this package use $(I format strings). A
format string describes the layout of another string for reading or
@@ -144,7 +144,7 @@ recommended to assign (lower- and uppercase) letters.
Note: The $(I Parameters) of a $(I CompoundIndicator) are currently
limited to a $(B '-') flag.
-$(SECTION4 Format Indicator)
+$(H4 $(LNAME2 format-indicator, Format Indicator))
The $(I format indicator) can either be a single character or an
expression surrounded by $(B %\() and $(B %\)). It specifies the
@@ -205,7 +205,7 @@ Note: Inside a $(I compound indicator), strings and characters are
escaped automatically. To avoid this behavior, use `"%-$(LPAREN)"`
instead of `"%$(LPAREN)"`.
-$(SECTION4 Flags)
+$(H4 $(LNAME2 flags, Flags))
There are several flags that affect the outcome of the formatting.
@@ -244,7 +244,7 @@ $(BOOKTABLE ,
sections below for more information.))
)
-$(SECTION4 Width$(COMMA) Precision and Separator)
+$(H4 $(LNAME2 width-precision-separator, Width, Precision and Separator))
The $(I width) parameter specifies the minimum width of the result.
@@ -269,7 +269,7 @@ The $(I separator) can also be followed by a $(B '?'). In that case,
an additional argument is used to specify the symbol that should be
used to separate the chunks.
-$(SECTION4 Position)
+$(H4 $(LNAME2 position, Position))
By default, the arguments are processed in the provided order. With
the $(I position) parameter it is possible to address arguments
@@ -282,7 +282,7 @@ It's also possible to use positional arguments for $(I width), $(I
precision) and $(I separator) by adding a number and a $(B
'$(DOLLAR)') after the $(B '*').
-$(SECTION4 Types)
+$(H4 $(LNAME2 types, Types))
This section describes the result of combining types with format
characters. It is organized in 2 subsections: a list of general
@@ -28,7 +28,7 @@ $(TR $(TD $(I delegates)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH))
Enums can be used with all format characters of the base type.
-$(SECTION3 Structs$(COMMA) Unions$(COMMA) Classes$(COMMA) and Interfaces)
+$(H3 $(LNAME2 aggregates, Structs, Unions, Classes, and Interfaces))
Aggregate types can define various `toString` functions. If this
function takes a $(REF_ALTTEXT FormatSpec, FormatSpec, std, format,
@@ -7005,7 +7005,7 @@ private enum TransformRes
// Note, getting GB1 (break at start of text, unless text is empty) right
// relies on the user starting grapheme walking from beginning of the text, and
// not attempting to walk an empty text.
-private enum TransformRes
+private immutable TransformRes
function(ref GraphemeState, dchar) @safe pure nothrow @nogc [] graphemeTransforms =
[
GraphemeState.Start: (ref state, ch)
@@ -7243,9 +7243,7 @@ if (is(C : dchar))
static assert(c2 == 3); // \u0301 has 2 UTF-8 code units
}
-// TODO: make this @nogc. Probably no big deal since the state machine is
-// already GC-free.
-@safe pure nothrow unittest
+@safe pure nothrow @nogc unittest
{
// grinning face ~ emoji modifier fitzpatrick type-5 ~ grinning face
assert(graphemeStride("\U0001F600\U0001f3FE\U0001F600"d, 0) == 2);