@@ -113,6 +113,7 @@ D_FRONTEND_OBJS = \
d/declaration.o \
d/delegatize.o \
d/denum.o \
+ d/deps.o \
d/dimport.o \
d/dinterpret.o \
d/dmacro.o \
@@ -201,6 +202,7 @@ D_FRONTEND_OBJS = \
d/target.o \
d/templateparamsem.o \
d/templatesem.o \
+ d/timetrace.o \
d/tokens.o \
d/traits.o \
d/transitivevisitor.o \
@@ -727,7 +727,7 @@ public:
create_typeinfo (d->type, NULL);
TypeEnum *tc = d->type->isTypeEnum ();
- if (tc->sym->members && !d->type->isZeroInit ())
+ if (tc->sym->members && !dmd::isZeroInit (d->type))
{
/* Generate static initializer. */
d->sinit = enum_initializer_decl (d);
@@ -821,7 +821,7 @@ public:
DECL_INITIAL (decl) = build_expr (e, true);
}
}
- else if (!d->type->isZeroInit ())
+ else if (!dmd::isZeroInit (d->type))
{
/* Use default initializer for the type. */
if (TypeStruct *ts = d->type->isTypeStruct ())
@@ -1,4 +1,4 @@
-66b93fc24a7ab5e2a8aa7f53c613df4abddc188b
+34875cd6e1faa42e84ae953c0485ef524fe67e38
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.
@@ -43,6 +43,8 @@
| [errorsink.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/errorsink.d) | Error reporting interface |
| [target.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/target.d) | Manage target-specific parameters for cross-compiling (for LDC/GDC) |
| [compiler.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/compiler.d) | Describe a back-end compiler and implements compiler-specific actions |
+| [deps.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/deps.d) | Implement the `-deps` and `-makedeps` switches |
+| [timetrace.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/timetrace.d) | Build time profiling utility |
### Lexing / parsing
@@ -1 +1 @@
-v2.109.1
+v2.110.0-beta.1
@@ -163,7 +163,7 @@ private bool hasProtectedAccess(Scope *sc, Dsymbol s)
*/
bool checkAccess(Loc loc, Scope* sc, Expression e, Dsymbol d)
{
- if (sc.flags & SCOPE.noaccesscheck)
+ if (sc.noAccessCheck)
return false;
static if (LOG)
{
@@ -287,9 +287,8 @@ bool isUnaArrayOp(EXP op) @safe
case EXP.tilde:
return true;
default:
- break;
+ return false;
}
- return false;
}
/***********************************************
@@ -310,9 +309,8 @@ bool isBinArrayOp(EXP op) @safe
case EXP.pow:
return true;
default:
- break;
+ return false;
}
- return false;
}
/***********************************************
@@ -333,9 +331,8 @@ bool isBinAssignArrayOp(EXP op) @safe
case EXP.powAssign:
return true;
default:
- break;
+ return false;
}
- return false;
}
/***********************************************
@@ -237,8 +237,7 @@ extern (C++) class StorageClassDeclaration : AttribDeclaration
* before the semantic analysis of 'to', so that template overloading based on the
* 'this' pointer can be successful.
*/
- FuncDeclaration fd = ps.isFuncDeclaration();
- if (fd)
+ if (FuncDeclaration fd = ps.isFuncDeclaration())
{
/* Use storage_class2 instead of storage_class otherwise when we do .di generation
* we'll wind up with 'const const' rather than 'const'.
@@ -984,7 +983,7 @@ extern (C++) final class StaticForeachDeclaration : AttribDeclaration
* pragma(msg, mixin("x" ~ to!string(i))); // ok, all 10 symbols are visible as they were forwarded to the global scope
* }
*
- * static assert (!is(typeof(i))); // loop index variable is not visible outside of the static foreach loop
+ * static assert(!is(typeof(i))); // loop index variable is not visible outside of the static foreach loop
*
* A StaticForeachDeclaration generates one
* ForwardingAttribDeclaration for each expansion of its body. The
@@ -111,11 +111,9 @@ CT canThrow(Expression e, FuncDeclaration func, ErrorSink eSink)
if (ce.f && ce.arguments.length > 0)
{
Type tb = (*ce.arguments)[0].type.toBasetype();
- auto tbNext = tb.nextOf();
- if (tbNext)
+ if (auto tbNext = tb.nextOf())
{
- auto ts = tbNext.baseElemOf().isTypeStruct();
- if (ts)
+ if (auto ts = tbNext.baseElemOf().isTypeStruct())
{
auto sd = ts.sym;
const id = ce.f.ident;
@@ -99,8 +99,7 @@ StorageClass mergeFuncAttrs(StorageClass s1, const FuncDeclaration f) pure
*/
FuncDeclaration hasIdentityOpAssign(AggregateDeclaration ad, Scope* sc)
{
- Dsymbol assign = search_function(ad, Id.assign);
- if (assign)
+ if (Dsymbol assign = search_function(ad, Id.assign))
{
/* check identity opAssign exists
*/
@@ -825,7 +824,7 @@ FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc)
* Note that it would only be necessary if it has floating point fields.
* For now, we'll just not generate a toHash() for C files.
*/
- if (sc.flags & SCOPE.Cfile)
+ if (sc.inCfile)
return null;
//printf("StructDeclaration::buildXtoHash() %s\n", sd.toPrettyChars());
@@ -20,43 +20,79 @@ module dmd.common.bitfields;
extern (D) string generateBitFields(S, T)()
if (__traits(isUnsigned, T))
{
+ import core.bitop: bsr;
+
string result = "extern (C++) pure nothrow @nogc @safe final {";
- enum structName = __traits(identifier, S);
- string initialValue = "";
+ struct BitInfo
+ {
+ int[] offset;
+ int[] size;
+ T initialValue;
+ int totalSize;
+ }
+
+ // Iterate over members to compute bit offset and bit size for each of them
+ enum BitInfo bitInfo = () {
+ BitInfo result;
+ int bitOffset = 0;
+ foreach (size_t i, mem; __traits(allMembers, S))
+ {
+ alias memType = typeof(__traits(getMember, S, mem));
+ enum int bitSize = bsr(memType.max | 1) + 1;
+ result.offset ~= bitOffset;
+ result.size ~= bitSize;
+ result.initialValue |= cast(T) __traits(getMember, S.init, mem) << bitOffset;
+ bitOffset += bitSize;
+ }
+ result.totalSize = bitOffset;
+ return result;
+ } ();
+
+ alias TP = typeof(T.init + 0u); // type that `T` gets promoted to, uint or ulong
+ enum string toString(TP i) = i.stringof; // compile time 'integer to string'
+
+ static assert(bitInfo.totalSize <= T.sizeof * 8,
+ "sum of bit field size "~toString!(bitInfo.totalSize)~" exceeds storage type `"~T.stringof~"`");
+
foreach (size_t i, mem; __traits(allMembers, S))
{
- static assert(is(typeof(__traits(getMember, S, mem)) == bool));
- static assert(i < T.sizeof * 8, "too many fields for bit field storage of type `"~T.stringof~"`");
- enum mask = "(1 << "~i.stringof~")";
+ enum typeName = typeof(__traits(getMember, S, mem)).stringof;
+ enum shift = toString!(bitInfo.offset[i]);
+ enum sizeMask = toString!((1 << bitInfo.size[i]) - 1); // 0x01 for bool, 0xFF for ubyte etc.
result ~= "
- /// set or get the corresponding "~structName~" member
- bool "~mem~"() const scope { return !!(bitFields & "~mask~"); }
- /// ditto
- bool "~mem~"(bool v)
+ "~typeName~" "~mem~"() const scope { return cast("~typeName~") ((bitFields >>> "~shift~") & "~sizeMask~"); }
+ "~typeName~" "~mem~"("~typeName~" v) scope
{
- v ? (bitFields |= "~mask~") : (bitFields &= ~"~mask~");
+ bitFields &= ~("~sizeMask~" << "~shift~");
+ bitFields |= v << "~shift~";
return v;
}";
-
- initialValue = (__traits(getMember, S.init, mem) ? "1" : "0") ~ initialValue;
}
- return result ~ "}\n private "~T.stringof~" bitFields = 0b" ~ initialValue ~ ";\n";
+ enum TP initVal = bitInfo.initialValue;
+ return result ~ "\n}\n private "~T.stringof~" bitFields = " ~ toString!(initVal) ~ ";\n";
}
///
unittest
{
+ enum E
+ {
+ a, b, c,
+ }
+
static struct B
{
bool x;
bool y;
+ E e = E.c;
bool z = 1;
+ private ubyte w = 77;
}
static struct S
{
- mixin(generateBitFields!(B, ubyte));
+ mixin(generateBitFields!(B, ushort));
}
S s;
@@ -69,5 +105,13 @@ unittest
s.y = true;
assert(s.y);
assert(!s.x);
+
+ assert(s.e == E.c);
+ s.e = E.a;
+ assert(s.e == E.a);
+
assert(s.z);
+ assert(s.w == 77);
+ s.w = 3;
+ assert(s.w == 3);
}
@@ -725,8 +725,7 @@ private final class CppMangleVisitor : Visitor
*/
static Dsymbol getInstance(Dsymbol s)
{
- Dsymbol p = s.toParent();
- if (p)
+ if (Dsymbol p = s.toParent())
{
if (TemplateInstance ti = p.isTemplateInstance())
return ti;
@@ -827,8 +826,7 @@ private final class CppMangleVisitor : Visitor
return buf.writestring("St");
auto si = getInstance(s);
- Dsymbol p = getQualifier(si);
- if (p)
+ if (Dsymbol p = getQualifier(si))
{
if (isStd(p))
{
@@ -495,6 +495,12 @@ Covariant covariant(Type src, Type t, StorageClass* pstc = null, bool
return dmd.typesem.covariant(src, t, pstc, cppCovariant);
}
+bool isZeroInit(Type t, const ref Loc loc)
+{
+ import dmd.typesem;
+ return dmd.typesem.isZeroInit(t, loc);
+}
+
bool isBaseOf(Type tthis, Type t, int* poffset)
{
import dmd.typesem;
@@ -651,6 +657,12 @@ MATCH implicitConvTo(Type from, Type to)
return dmd.dcast.implicitConvTo(from, to);
}
+MATCH constConv(Type from, Type to)
+{
+ import dmd.typesem;
+ return dmd.typesem.constConv(from, to);
+}
+
/***********************************************************
* typinf.d
*/
@@ -71,7 +71,7 @@ Expression implicitCastTo(Expression e, Scope* sc, Type t)
Expression visit(Expression e)
{
//printf("Expression.implicitCastTo(%s of type %s) => %s\n", e.toChars(), e.type.toChars(), t.toChars());
- if (const match = (sc && sc.flags & SCOPE.Cfile) ? e.cimplicitConvTo(t) : e.implicitConvTo(t))
+ if (const match = (sc && sc.inCfile) ? e.cimplicitConvTo(t) : e.implicitConvTo(t))
{
// no need for an extra cast when matching is exact
@@ -2140,8 +2140,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
{
if (hasAliasThis)
{
- auto result = tryAliasThisCast();
- if (result)
+ if (auto result = tryAliasThisCast())
return result;
}
@@ -2235,8 +2234,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
*/
if (hasAliasThis)
{
- auto result = tryAliasThisCast();
- if (result)
+ if (auto result = tryAliasThisCast())
return result;
}
error(e.loc, "cannot cast expression `%s` of type `%s` to `%s`", e.toChars(), e.type.toChars(), t.toChars());
@@ -2306,7 +2304,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
//printf("StringExp::castTo(t = %s), '%s' committed = %d\n", t.toChars(), e.toChars(), e.committed);
if (!e.committed && t.ty == Tpointer && t.nextOf().ty == Tvoid &&
- (!sc || !(sc.flags & SCOPE.Cfile)))
+ (!sc || !sc.inCfile))
{
error(e.loc, "cannot convert string literal to `void*`");
return ErrorExp.get();
@@ -2437,9 +2435,9 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
if (e.committed)
goto Lcast;
- auto X(T, U)(T tf, U tt)
+ static auto X(T, U)(T tf, U tt)
{
- return (cast(int)tf * 256 + cast(int)tt);
+ return cast(int)tf * 256 + cast(int)tt;
}
{
@@ -2557,7 +2555,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
// See if need to truncate or extend the literal
if (auto tsa = tb.isTypeSArray())
{
- size_t dim2 = cast(size_t)tsa.dim.toInteger();
+ const dim2 = cast(size_t)tsa.dim.toInteger();
//printf("dim from = %d, to = %d\n", cast(int)se.len, cast(int)dim2);
// Changing dimensions
@@ -2637,8 +2635,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
tb.ty == Tpointer && tb.nextOf().ty == Tfunction)
{
auto ve = e.e1.isVarExp();
- auto f = ve.var.isFuncDeclaration();
- if (f)
+ if (auto f = ve.var.isFuncDeclaration())
{
assert(f.isImportedSymbol());
f = f.overloadExactMatch(tb.nextOf());
@@ -2946,8 +2943,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
{
if (e.func)
{
- auto f = e.func.overloadExactMatch(tb.nextOf());
- if (f)
+ if (auto f = e.func.overloadExactMatch(tb.nextOf()))
{
int offset;
if (f.tintro && f.tintro.nextOf().isBaseOf(f.type.nextOf(), &offset) && offset)
@@ -3360,7 +3356,7 @@ Type typeMerge(Scope* sc, EXP op, ref Expression pe1, ref Expression pe2)
Type t1b = e1.type.toBasetype();
Type t2b = e2.type.toBasetype();
- if (sc && sc.flags & SCOPE.Cfile)
+ if (sc && sc.inCfile)
{
// Integral types can be implicitly converted to pointers
if ((t1b.ty == Tpointer) != (t2b.ty == Tpointer))
@@ -4220,7 +4216,7 @@ Expression integralPromotions(Expression e, Scope* sc)
void fix16997(Scope* sc, UnaExp ue)
{
- if (global.params.fix16997 || sc.flags & SCOPE.Cfile)
+ if (global.params.fix16997 || sc.inCfile)
ue.e1 = integralPromotions(ue.e1, sc); // desired C-like behavor
else
{
@@ -490,8 +490,7 @@ extern (C++) class ClassDeclaration : AggregateDeclaration
return null;
if (cdb.ident.equals(ident))
return cdb;
- auto result = cdb.searchBase(ident);
- if (result)
+ if (auto result = cdb.searchBase(ident))
return result;
}
return null;
@@ -353,8 +353,7 @@ extern (C++) abstract class Declaration : Dsymbol
// is an overload in the overload set that isn't
if (isAliasedDeclaration)
{
- FuncDeclaration fd = isFuncDeclaration();
- if (fd)
+ if (FuncDeclaration fd = isFuncDeclaration())
{
for (FuncDeclaration ovl = fd; ovl; ovl = cast(FuncDeclaration)ovl.overnext)
if (!(ovl.storage_class & STC.disable))
@@ -397,7 +396,7 @@ extern (C++) abstract class Declaration : Dsymbol
{
for (Scope* scx = sc; scx; scx = scx.enclosing)
{
- if (scx.func == parent && (scx.flags & SCOPE.contract))
+ if (scx.func == parent && scx.contract != Contract.none)
{
const(char)* s = isParameter() && parent.ident != Id.ensure ? "parameter" : "result";
if (!(flag & ModifyFlags.noError))
@@ -412,7 +411,7 @@ extern (C++) abstract class Declaration : Dsymbol
VarDeclaration vthis = e1.isThisExp().var;
for (Scope* scx = sc; scx; scx = scx.enclosing)
{
- if (scx.func == vthis.parent && (scx.flags & SCOPE.contract))
+ if (scx.func == vthis.parent && scx.contract != Contract.none)
{
if (!(flag & ModifyFlags.noError))
error(loc, "%s `%s` cannot modify parameter `this` in contract", kind, toPrettyChars);
@@ -1137,7 +1136,6 @@ extern (C++) class VarDeclaration : Declaration
VarDeclaration lastVar; // Linked list of variables for goto-skips-init detection
Expression edtor; // if !=null, does the destruction of the variable
IntRange* range; // if !=null, the variable is known to be within the range
- VarDeclarations* maybes; // maybeScope variables that are assigned to this maybeScope variable
uint endlinnum; // line number of end of scope that this var lives in
uint offset;
@@ -1239,8 +1237,7 @@ extern (C++) class VarDeclaration : Declaration
*/
for (auto s = cast(Dsymbol)this; s; s = s.parent)
{
- auto ad = (cast(inout)s).isMember();
- if (ad)
+ if (auto ad = (cast(inout)s).isMember())
return ad;
if (!s.parent || !s.parent.isTemplateMixin())
break;
@@ -1580,7 +1577,7 @@ extern (C++) class VarDeclaration : Declaration
extern (D) final bool checkNestedReference(Scope* sc, Loc loc)
{
//printf("VarDeclaration::checkNestedReference() %s\n", toChars());
- if (sc.intypeof == 1 || (sc.flags & SCOPE.ctfe))
+ if (sc.intypeof == 1 || sc.ctfe)
return false;
if (!parent || parent == sc.parent)
return false;
@@ -1615,7 +1612,7 @@ extern (C++) class VarDeclaration : Declaration
}
// Add this VarDeclaration to fdv.closureVars[] if not already there
- if (!sc.intypeof && !(sc.flags & SCOPE.compile) &&
+ if (!sc.intypeof && !sc.traitsCompiles &&
// https://issues.dlang.org/show_bug.cgi?id=17605
(fdv.skipCodegen || !fdthis.skipCodegen))
{
@@ -237,7 +237,6 @@ public:
VarDeclaration *lastVar; // Linked list of variables for goto-skips-init detection
Expression *edtor; // if !=NULL, does the destruction of the variable
IntRange *range; // if !NULL, the variable is known to be within the range
- VarDeclarations *maybes; // STCmaybescope variables that are assigned to this STCmaybescope variable
unsigned endlinnum; // line number of end of scope that this var lives in
unsigned offset;
@@ -639,12 +638,10 @@ public:
bool nothrowInprocess(bool v);
bool nogcInprocess() const;
bool nogcInprocess(bool v);
- bool returnInprocess() const;
- bool returnInprocess(bool v);
+ bool scopeInprocess() const;
+ bool scopeInprocess(bool v);
bool inlineScanned() const;
bool inlineScanned(bool v);
- bool inferScope() const;
- bool inferScope(bool v);
bool hasCatches() const;
bool hasCatches(bool v);
bool skipCodegen() const;
@@ -215,15 +215,13 @@ bool lambdaCheckForNestedRef(Expression e, Scope* sc)
override void visit(SymOffExp e)
{
- VarDeclaration v = e.var.isVarDeclaration();
- if (v)
+ if (VarDeclaration v = e.var.isVarDeclaration())
result = v.checkNestedReference(sc, Loc.initial);
}
override void visit(VarExp e)
{
- VarDeclaration v = e.var.isVarDeclaration();
- if (v)
+ if (VarDeclaration v = e.var.isVarDeclaration())
result = v.checkNestedReference(sc, Loc.initial);
}
@@ -235,8 +233,7 @@ bool lambdaCheckForNestedRef(Expression e, Scope* sc)
override void visit(DeclarationExp e)
{
- VarDeclaration v = e.declaration.isVarDeclaration();
- if (v)
+ if (VarDeclaration v = e.declaration.isVarDeclaration())
{
result = v.checkNestedReference(sc, Loc.initial);
if (result)
new file mode 100644
@@ -0,0 +1,141 @@
+/**
+ * Implement the `-deps` and `-makedeps` switches, which output dependencies of modules for build tools.
+ *
+ * The grammar of the `-deps` output is:
+ * ---
+ * ImportDeclaration
+ * ::= BasicImportDeclaration [ " : " ImportBindList ] [ " -> "
+ * ModuleAliasIdentifier ] "\n"
+ *
+ * BasicImportDeclaration
+ * ::= ModuleFullyQualifiedName " (" FilePath ") : " Protection|"string"
+ * " [ " static" ] : " ModuleFullyQualifiedName " (" FilePath ")"
+ *
+ * FilePath
+ * - any string with '(', ')' and '\' escaped with the '\' character
+ * ---
+ *
+ * Make dependencies as generated by `-makedeps` look like this:
+ * ---
+ * source/app.d:
+ * source/importa.d \
+ * source/importb.d
+ * ---
+ *
+ * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved
+ * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/deps.d, makedeps.d)
+ * Documentation: https://dlang.org/phobos/dmd_deps.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/deps.d
+ */
+module dmd.deps;
+
+import core.stdc.stdio : printf;
+import core.stdc.string : strcmp;
+import dmd.common.outbuffer;
+import dmd.dimport : Import;
+import dmd.dmodule : Module;
+import dmd.globals : Param, Output;
+import dmd.hdrgen : visibilityToBuffer;
+import dmd.id : Id;
+import dmd.utils : escapePath;
+
+/**
+ * Add an import expression to module dependencies
+ * Params:
+ * moduleDeps = output settings for `-deps`
+ * makeDeps = output settings for `-makedeps`
+ * fileNameZ = 0-termminated string containing the import expression's resolved filename
+ * importString = raw string passed to import exp
+ * imod = module import exp is in
+ */
+void addImportExpDep(ref Output moduleDeps, ref Output makeDeps, const(char)[] fileNameZ, const(char)[] importString, Module imod)
+{
+ if (moduleDeps.buffer !is null)
+ {
+ OutBuffer* ob = moduleDeps.buffer;
+
+ if (!moduleDeps.name)
+ ob.writestring("depsFile ");
+ ob.writestring(imod.toPrettyChars());
+ ob.writestring(" (");
+ escapePath(ob, imod.srcfile.toChars());
+ ob.writestring(") : ");
+ if (moduleDeps.name)
+ ob.writestring("string : ");
+ ob.write(importString);
+ ob.writestring(" (");
+ escapePath(ob, fileNameZ.ptr);
+ ob.writestring(")");
+ ob.writenl();
+ }
+ if (makeDeps.doOutput)
+ {
+ makeDeps.files.push(fileNameZ.ptr);
+ }
+}
+
+/**
+ * Add an import statement to module dependencies
+ * Params:
+ * moduleDeps = output settings
+ * imp = import to add
+ * imod = module that the import is in
+ */
+void addImportDep(ref Output moduleDeps, Import imp, Module imod)
+{
+ // object self-imports itself, so skip that
+ // https://issues.dlang.org/show_bug.cgi?id=7547
+ // don't list pseudo modules __entrypoint.d, __main.d
+ // https://issues.dlang.org/show_bug.cgi?id=11117
+ // https://issues.dlang.org/show_bug.cgi?id=11164
+ if (moduleDeps.buffer is null || (imp.id == Id.object && imod.ident == Id.object) ||
+ strcmp(imod.ident.toChars(), "__main") == 0)
+ return;
+
+ OutBuffer* ob = moduleDeps.buffer;
+ if (!moduleDeps.name)
+ ob.writestring("depsImport ");
+ ob.writestring(imod.toPrettyChars());
+ ob.writestring(" (");
+ escapePath(ob, imod.srcfile.toChars());
+ ob.writestring(") : ");
+ // use visibility instead of sc.visibility because it couldn't be
+ // resolved yet, see the comment above
+ visibilityToBuffer(*ob, imp.visibility);
+ ob.writeByte(' ');
+ if (imp.isstatic)
+ {
+ ob.writestring("static ");
+ }
+ ob.writestring(": ");
+ foreach (pid; imp.packages)
+ {
+ ob.printf("%s.", pid.toChars());
+ }
+ ob.writestring(imp.id.toString());
+ ob.writestring(" (");
+ if (imp.mod)
+ escapePath(ob, imp.mod.srcfile.toChars());
+ else
+ ob.writestring("???");
+ ob.writeByte(')');
+ foreach (i, name; imp.names)
+ {
+ if (i == 0)
+ ob.writeByte(':');
+ else
+ ob.writeByte(',');
+ auto _alias = imp.aliases[i];
+ if (!_alias)
+ {
+ ob.printf("%s", name.toChars());
+ _alias = name;
+ }
+ else
+ ob.printf("%s=%s", _alias.toChars(), name.toChars());
+ }
+ if (imp.aliasId)
+ ob.printf(" -> %s", imp.aliasId.toChars());
+ ob.writenl();
+}
@@ -100,6 +100,10 @@ public Expression ctfeInterpret(Expression e)
auto rgnpos = ctfeGlobals.region.savePos();
+ import dmd.timetrace;
+ timeTraceBeginEvent(TimeTraceEventType.ctfe);
+ scope (exit) timeTraceEndEvent(TimeTraceEventType.ctfe, e);
+
Expression result = interpret(e, null);
// Report an error if the expression contained a `ThrowException` and
@@ -432,6 +436,27 @@ private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterSta
printf("\n********\n%s FuncDeclaration::interpret(istate = %p) %s\n", fd.loc.toChars(), istate, fd.toChars());
}
+ scope dlg = () {
+ import dmd.common.outbuffer;
+ auto strbuf = OutBuffer(20);
+ strbuf.writestring(fd.toPrettyChars());
+ strbuf.write("(");
+ if (arguments)
+ {
+ foreach (i, arg; *arguments)
+ {
+ if (i > 0)
+ strbuf.write(", ");
+ strbuf.writestring(arg.toChars());
+ }
+ }
+ strbuf.write(")");
+ return strbuf.extractSlice();
+ };
+ import dmd.timetrace;
+ timeTraceBeginEvent(TimeTraceEventType.ctfeCall);
+ scope (exit) timeTraceEndEvent(TimeTraceEventType.ctfeCall, fd, dlg);
+
void fdError(const(char)* msg)
{
error(fd.loc, "%s `%s` %s", fd.kind, fd.toPrettyChars, msg);
@@ -16,8 +16,6 @@ import core.stdc.string;
import dmd.common.outbuffer;
import dmd.root.rmem;
-@trusted:
-
struct MacroTable
{
/**********************************
@@ -303,7 +301,7 @@ struct Macro
* copy allocated with mem.xmalloc()
*/
-char[] memdup(const(char)[] p) nothrow pure
+char[] memdup(const(char)[] p) nothrow pure @trusted
{
size_t len = p.length;
return (cast(char*)memcpy(mem.xmalloc(len), p.ptr, len))[0 .. len];
@@ -318,7 +316,7 @@ char[] memdup(const(char)[] p) nothrow pure
* 1..9: get nth argument
* -1: get 2nd through end
*/
-size_t extractArgN(const(char)[] buf, out const(char)[] marg, int n) @nogc nothrow pure
+size_t extractArgN(const(char)[] buf, out const(char)[] marg, int n) @nogc nothrow pure @safe
{
/* Scan forward for matching right parenthesis.
* Nest parentheses.
@@ -334,7 +332,7 @@ size_t extractArgN(const(char)[] buf, out const(char)[] marg, int n) @nogc nothr
uint inexp = 0;
uint argn = 0;
size_t v = 0;
- const p = buf.ptr;
+ const p = buf;
const end = buf.length;
Largstart:
// Skip first space, if any, to find the start of the macro argument
@@ -715,6 +715,11 @@ extern (C++) final class Module : Package
{
const(char)* srcname = srcfile.toChars();
//printf("Module::parse(srcname = '%s')\n", srcname);
+
+ import dmd.timetrace;
+ timeTraceBeginEvent(TimeTraceEventType.parse);
+ scope (exit) timeTraceEndEvent(TimeTraceEventType.parse, this);
+
isPackageFile = isPackageFileName(srcfile);
const(char)[] buf = processSource(src, this);
// an error happened on UTF conversion
@@ -45,37 +45,45 @@ import dmd.tokens;
//version=LOGSEARCH;
-
-// List of flags that can be applied to this `Scope`
-enum SCOPE
+/// What kind of contract function we're in, if any
+enum Contract : ubyte
{
- ctor = 0x0001, /// constructor type
- noaccesscheck = 0x0002, /// don't do access checks
- condition = 0x0004, /// inside static if/assert condition
- debug_ = 0x0008, /// inside debug conditional
- constraint = 0x0010, /// inside template constraint
- invariant_ = 0x0020, /// inside invariant code
- require = 0x0040, /// inside in contract code
- ensure = 0x0060, /// inside out contract code
- contract = 0x0060, /// [mask] we're inside contract code
- ctfe = 0x0080, /// inside a ctfe-only expression
- compile = 0x0100, /// inside __traits(compile)
- ignoresymbolvisibility = 0x0200, /// ignore symbol visibility
- /// https://issues.dlang.org/show_bug.cgi?id=15907
- Cfile = 0x0800, /// C semantics apply
- free = 0x8000, /// is on free list
-
- fullinst = 0x10000, /// fully instantiate templates
- ctfeBlock = 0x20000, /// inside a `if (__ctfe)` block
- dip1000 = 0x40000, /// dip1000 errors enabled for this scope
- dip25 = 0x80000, /// dip25 errors enabled for this scope
+ none = 0,
+ invariant_ = 1,
+ require = 2, // in contract
+ ensure = 3, // out contract
}
-/// Flags that are carried along with a scope push()
-private enum PersistentFlags =
- SCOPE.contract | SCOPE.debug_ | SCOPE.ctfe | SCOPE.compile | SCOPE.constraint |
- SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility |
- SCOPE.Cfile | SCOPE.ctfeBlock | SCOPE.dip1000 | SCOPE.dip25;
+private extern (D) struct BitFields
+{
+ bool ctor; /// constructor type
+ bool noAccessCheck; /// don't do access checks
+ bool condition; /// inside static if/assert condition
+ bool debug_; /// inside debug conditional
+ bool inTemplateConstraint; /// inside template constraint
+ Contract contract;
+ bool ctfe; /// inside a ctfe-only expression
+ bool traitsCompiles; /// inside __traits(compile)
+
+ /// ignore symbol visibility
+ /// https://issues.dlang.org/show_bug.cgi?id=15907
+ bool ignoresymbolvisibility;
+
+ bool _padding0; // To keep the layout the same as when the old `SCOPE` enum bitflags were used
+
+ bool inCfile; /// C semantics apply
+
+ bool _padding1;
+ bool _padding2;
+ bool _padding3;
+
+ bool canFree; /// is on free list
+
+ bool fullinst; /// fully instantiate templates
+ bool ctfeBlock; /// inside a `if (__ctfe)` block
+ bool dip1000; /// dip1000 errors enabled for this scope
+ bool dip25; /// dip25 errors enabled for this scope
+}
extern (C++) struct Scope
{
@@ -136,7 +144,8 @@ extern (C++) struct Scope
DeprecatedDeclaration depdecl; /// customized deprecation message
- uint flags;
+ import dmd.common.bitfields : generateBitFields;
+ mixin(generateBitFields!(BitFields, uint));
// user defined attributes
UserAttributeDeclaration userAttribDecl;
@@ -157,8 +166,8 @@ extern (C++) struct Scope
Scope* s = freelist;
freelist = s.enclosing;
//printf("freelist %p\n", s);
- assert(s.flags & SCOPE.free);
- s.flags &= ~SCOPE.free;
+ assert(s.canFree);
+ s.canFree = false;
return s;
}
return new Scope();
@@ -181,11 +190,11 @@ extern (C++) struct Scope
m.addMember(null, sc.scopesym);
m.parent = null; // got changed by addMember()
if (global.params.useDIP1000 == FeatureState.enabled)
- sc.flags |= SCOPE.dip1000;
+ sc.dip1000 = true;
if (global.params.useDIP25 == FeatureState.enabled)
- sc.flags |= SCOPE.dip25;
+ sc.dip25 = true;
if (_module.filetype == FileType.c)
- sc.flags |= SCOPE.Cfile;
+ sc.inCfile = true;
// Create the module scope underneath the global scope
sc = sc.push(_module);
sc.parent = _module;
@@ -207,13 +216,13 @@ extern (C++) struct Scope
{
Scope* s = copy();
//printf("Scope::push(this = %p) new = %p\n", this, s);
- assert(!(flags & SCOPE.free));
+ assert(!this.canFree);
s.scopesym = null;
s.enclosing = &this;
debug
{
if (enclosing)
- assert(!(enclosing.flags & SCOPE.free));
+ assert(!enclosing.canFree);
if (s == enclosing)
{
printf("this = %p, enclosing = %p, enclosing.enclosing = %p\n", s, &this, enclosing);
@@ -223,12 +232,38 @@ extern (C++) struct Scope
s.slabel = null;
s.nofree = false;
s.ctorflow.fieldinit = ctorflow.fieldinit.arraydup;
- s.flags = (flags & PersistentFlags);
+
+ // Only keep persistent flags
+ s.resetAllFlags();
+ s.contract = this.contract;
+ s.debug_ = this.debug_;
+ s.ctfe = this.ctfe;
+ s.traitsCompiles = this.traitsCompiles;
+ s.inTemplateConstraint = this.inTemplateConstraint;
+ s.noAccessCheck = this.noAccessCheck;
+ s.ignoresymbolvisibility = this.ignoresymbolvisibility;
+ s.inCfile = this.inCfile;
+ s.ctfeBlock = this.ctfeBlock;
+ s.dip1000 = this.dip1000;
+ s.dip25 = this.dip25;
+
s.lastdc = null;
assert(&this != s);
return s;
}
+ /// Copy flags from scope `other`
+ extern(D) void copyFlagsFrom(Scope* other)
+ {
+ this.bitFields = other.bitFields;
+ }
+
+ /// Set all scope flags to their initial value
+ extern(D) void resetAllFlags()
+ {
+ this.bitFields = 0;
+ }
+
extern (D) Scope* push(ScopeDsymbol ss)
{
//printf("Scope::push(%s)\n", ss.toChars());
@@ -251,7 +286,7 @@ extern (C++) struct Scope
this = this.init;
enclosing = freelist;
freelist = &this;
- flags |= SCOPE.free;
+ this.canFree = true;
}
return enc;
}
@@ -270,7 +305,8 @@ extern (C++) struct Scope
extern (D) Scope* startCTFE()
{
Scope* sc = this.push();
- sc.flags = this.flags | SCOPE.ctfe;
+ sc.copyFlagsFrom(&this);
+ sc.ctfe = true;
version (none)
{
/* TODO: Currently this is not possible, because we need to
@@ -300,7 +336,7 @@ extern (C++) struct Scope
extern (D) Scope* endCTFE()
{
- assert(flags & SCOPE.ctfe);
+ assert(this.ctfe);
return pop();
}
@@ -470,7 +506,7 @@ extern (C++) struct Scope
if (sc.scopesym.isModule())
flags |= SearchOpt.unqualifiedModule; // tell Module.search() that SearchOpt.localsOnly is to be obeyed
- else if (sc.flags & SCOPE.Cfile && sc.scopesym.isStructDeclaration())
+ else if (sc.inCfile && sc.scopesym.isStructDeclaration())
continue; // C doesn't have struct scope
if (Dsymbol s = sc.scopesym.search(loc, ident, flags))
@@ -495,8 +531,7 @@ extern (C++) struct Scope
if (global.params.fixAliasThis)
{
Expression exp = new ThisExp(loc);
- Dsymbol aliasSym = checkAliasThis(sc.scopesym.isAggregateDeclaration(), ident, flags, &exp);
- if (aliasSym)
+ if (Dsymbol aliasSym = checkAliasThis(sc.scopesym.isAggregateDeclaration(), ident, flags, &exp))
{
//printf("found aliassym: %s\n", aliasSym.toChars());
pscopesym = new ExpressionDsymbol(exp);
@@ -511,7 +546,7 @@ extern (C++) struct Scope
return null;
}
- if (this.flags & SCOPE.ignoresymbolvisibility)
+ if (this.ignoresymbolvisibility)
flags |= SearchOpt.ignoreVisibility;
// First look in local scopes
@@ -658,7 +693,7 @@ extern (C++) struct Scope
//printf("\t\tscopesym = %p\n", scopesym);
if (!scopesym.symtab)
scopesym.symtab = new DsymbolTable();
- if (!(flags & SCOPE.Cfile))
+ if (!this.inCfile)
return scopesym.symtabInsert(s);
// ImportC insert
@@ -753,7 +788,7 @@ extern (C++) struct Scope
{
//printf("\tsc = %p\n", sc);
sc.nofree = true;
- assert(!(flags & SCOPE.free));
+ assert(!this.canFree);
//assert(sc != sc.enclosing);
//assert(!sc.enclosing || sc != sc.enclosing.enclosing);
//if (++i == 10)
@@ -814,7 +849,7 @@ extern (C++) struct Scope
*/
extern (D) bool isFromSpeculativeSemanticContext() scope
{
- return this.intypeof || this.flags & SCOPE.compile;
+ return this.intypeof || this.traitsCompiles;
}
@@ -824,19 +859,19 @@ extern (C++) struct Scope
*/
extern (D) bool needsCodegen()
{
- return (flags & (SCOPE.ctfe | SCOPE.ctfeBlock | SCOPE.compile)) == 0;
+ return !this.ctfe && !this.ctfeBlock && !this.traitsCompiles;
}
/// Returns: whether to raise DIP1000 warnings (FeatureStabe.default) or errors (FeatureState.enabled)
extern (D) FeatureState useDIP1000()
{
- return (flags & SCOPE.dip1000 || hasEdition(Edition.v2024)) ? FeatureState.enabled : FeatureState.disabled;
+ return (this.dip1000 || hasEdition(Edition.v2024)) ? FeatureState.enabled : FeatureState.disabled;
}
/// Returns: whether to raise DIP25 warnings (FeatureStabe.default) or errors (FeatureState.enabled)
extern (D) FeatureState useDIP25()
{
- return (flags & SCOPE.dip25 || hasEdition(Edition.v2024)) ? FeatureState.enabled : FeatureState.disabled;
+ return (this.dip25 || hasEdition(Edition.v2024)) ? FeatureState.enabled : FeatureState.disabled;
}
/// Returns: whether this scope compiles with `edition` or later
@@ -265,10 +265,9 @@ extern (C++) class Dsymbol : ASTNode
Identifier ident;
Dsymbol parent;
Symbol* csym; // symbol for code generator
- const Loc loc; // where defined
Scope* _scope; // !=null means context to use for semantic()
- const(char)* prettystring; // cached value of toPrettyChars()
private DsymbolAttributes* atts; /// attached attribute declarations
+ const Loc loc; // where defined
bool errors; // this symbol failed to pass semantic()
PASS semanticRun = PASS.initial;
ushort localNum; /// perturb mangled name to avoid collisions with those in FuncDeclaration.localsymtab
@@ -396,8 +395,7 @@ extern (C++) class Dsymbol : ASTNode
while (s)
{
//printf("\ts = %s '%s'\n", s.kind(), s.toPrettyChars());
- Module m = s.isModule();
- if (m)
+ if (Module m = s.isModule())
return m;
s = s.parent;
}
@@ -428,8 +426,7 @@ extern (C++) class Dsymbol : ASTNode
while (s)
{
//printf("\ts = %s '%s'\n", s.kind(), s.toPrettyChars());
- Module m = s.isModule();
- if (m)
+ if (Module m = s.isModule())
return m;
TemplateInstance ti = s.isTemplateInstance();
if (ti && ti.enclosing)
@@ -657,15 +654,10 @@ extern (C++) class Dsymbol : ASTNode
const(char)* toPrettyChars(bool QualifyTypes = false)
{
- if (prettystring && !QualifyTypes)
- return prettystring; // value cached for speed
-
//printf("Dsymbol::toPrettyChars() '%s'\n", toChars());
if (!parent)
{
auto s = toChars();
- if (!QualifyTypes)
- prettystring = s;
return s;
}
@@ -685,8 +677,6 @@ extern (C++) class Dsymbol : ASTNode
addQualifiers(this);
auto s = buf.extractSlice(true).ptr;
- if (!QualifyTypes)
- prettystring = s;
return s;
}
@@ -192,12 +192,11 @@ public:
Identifier *ident;
Dsymbol *parent;
Symbol *csym; // symbol for code generator
- Loc loc; // where defined
Scope *_scope; // !=NULL means context to use for semantic()
- const utf8_t *prettystring;
private:
DsymbolAttributes* atts;
public:
+ Loc loc; // where defined
d_bool errors; // this symbol failed to pass semantic()
PASS semanticRun;
unsigned short localNum; // perturb mangled name to avoid collisions with those in FuncDeclaration.localsymtab
@@ -24,10 +24,12 @@ import dmd.attrib;
import dmd.attribsem;
import dmd.clone;
import dmd.cond;
+import dmd.timetrace;
import dmd.dcast;
import dmd.dclass;
import dmd.declaration;
import dmd.denum;
+import dmd.deps;
import dmd.dimport;
import dmd.dinterpret;
import dmd.dmodule;
@@ -59,6 +61,7 @@ import dmd.objc;
import dmd.opover;
import dmd.optimize;
import dmd.parse;
+debug import dmd.printast;
import dmd.root.array;
import dmd.root.filename;
import dmd.common.outbuffer;
@@ -127,7 +130,7 @@ AlignDeclaration getAlignment(AlignDeclaration ad, Scope* sc)
else
{
auto n = e.toInteger();
- if (sc.flags & SCOPE.Cfile && n == 0) // C11 6.7.5-6 allows 0 for alignment
+ if (sc.inCfile && n == 0) // C11 6.7.5-6 allows 0 for alignment
continue;
if (n < 1 || n & (n - 1) || ushort.max < n || !e.type.isintegral())
@@ -179,7 +182,7 @@ bool checkDeprecated(Dsymbol d, const ref Loc loc, Scope* sc)
return false;
// Don't complain if we're inside a template constraint
// https://issues.dlang.org/show_bug.cgi?id=21831
- if (sc.flags & SCOPE.constraint)
+ if (sc.inTemplateConstraint)
return false;
const(char)* message = null;
@@ -596,7 +599,17 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
}
if (dsym.storage_class & STC.extern_ && dsym._init)
- .error(dsym.loc, "%s `%s` extern symbols cannot have initializers", dsym.kind, dsym.toPrettyChars);
+ {
+ if (sc.inCfile)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=24447
+ // extern int x = 3; is allowed in C
+ dsym.storage_class &= ~STC.extern_;
+ }
+ else
+ .error(dsym.loc, "%s `%s` extern symbols cannot have initializers", dsym.kind, dsym.toPrettyChars);
+
+ }
AggregateDeclaration ad = dsym.isThis();
if (ad)
@@ -614,12 +627,12 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
bool needctfe = (dsym.storage_class & (STC.manifest | STC.static_)) != 0 || !sc.func;
if (needctfe)
{
- sc.flags |= SCOPE.condition;
+ sc.condition = true;
sc = sc.startCTFE();
}
//printf("inferring type for %s with init %s\n", dsym.toChars(), dsym._init.toChars());
dsym._init = dsym._init.inferType(sc);
- dsym.type = dsym._init.initializerToExpression(null, (sc.flags & SCOPE.Cfile) != 0).type;
+ dsym.type = dsym._init.initializerToExpression(null, sc.inCfile).type;
if (needctfe)
sc = sc.endCTFE();
@@ -728,7 +741,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
* and add those.
*/
size_t nelems = Parameter.dim(tt.arguments);
- Expression ie = (dsym._init && !dsym._init.isVoidInitializer()) ? dsym._init.initializerToExpression(null, (sc.flags & SCOPE.Cfile) != 0) : null;
+ Expression ie = (dsym._init && !dsym._init.isVoidInitializer()) ? dsym._init.initializerToExpression(null, sc.inCfile) : null;
if (ie)
ie = ie.expressionSemantic(sc);
if (nelems > 0 && ie)
@@ -1001,9 +1014,9 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
}
}
- if ((dsym.storage_class & (STC.ref_ | STC.parameter | STC.foreach_ | STC.temp | STC.result)) == STC.ref_ && dsym.ident != Id.This)
+ if ((dsym.storage_class & (STC.ref_ | STC.field)) == (STC.ref_ | STC.field) && dsym.ident != Id.This)
{
- .error(dsym.loc, "%s `%s` - only parameters, functions and `foreach` declarations can be `ref`", dsym.kind, dsym.toPrettyChars);
+ .error(dsym.loc, "%s `%s` - field declarations cannot be `ref`", dsym.kind, dsym.toPrettyChars);
}
if (dsym.type.hasWild())
@@ -1052,8 +1065,15 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
}
}
+ bool dsymIsRef = (dsym.storage_class & (STC.ref_ | STC.field | STC.parameter | STC.temp | STC.foreach_)) == STC.ref_;
+ if (dsymIsRef)
+ {
+ if (!dsym._init && dsym.ident != Id.This)
+ .error(dsym.loc, "%s `%s` - initializer is required for `ref` variable", dsym.kind, dsym.toPrettyChars);
+ }
+
FuncDeclaration fd = parent.isFuncDeclaration();
- if (dsym.type.isscope() && !(dsym.storage_class & STC.nodtor))
+ if (dsym.type.isScopeClass() && !(dsym.storage_class & STC.nodtor))
{
if (dsym.storage_class & (STC.field | STC.out_ | STC.ref_ | STC.static_ | STC.manifest | STC.gshared) || !fd)
{
@@ -1122,7 +1142,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
bool isBlit = false;
uinteger_t sz;
- if (sc.flags & SCOPE.Cfile && !dsym._init)
+ if (sc.inCfile && !dsym._init)
{
addDefaultCInitializer(dsym);
}
@@ -1187,7 +1207,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
sc = sc.push();
sc.stc &= ~(STC.TYPECTOR | STC.pure_ | STC.nothrow_ | STC.nogc | STC.ref_ | STC.disable);
- if (sc.flags & SCOPE.Cfile &&
+ if (sc.inCfile &&
dsym.type.isTypeSArray() &&
dsym.type.isTypeSArray().isIncomplete() &&
dsym._init.isVoidInitializer() &&
@@ -1217,12 +1237,12 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
if (ai && tb.ty == Taarray)
e = ai.toAssocArrayLiteral();
else
- e = dsym._init.initializerToExpression(null, (sc.flags & SCOPE.Cfile) != 0);
+ e = dsym._init.initializerToExpression(null, sc.inCfile);
if (!e)
{
// Run semantic, but don't need to interpret
dsym._init = dsym._init.initializerSemantic(sc, dsym.type, INITnointerpret);
- e = dsym._init.initializerToExpression(null, (sc.flags & SCOPE.Cfile) != 0);
+ e = dsym._init.initializerToExpression(null, sc.inCfile);
if (!e)
{
.error(dsym.loc, "%s `%s` is not a static and cannot have static initializer", dsym.kind, dsym.toPrettyChars);
@@ -1232,7 +1252,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
ei = new ExpInitializer(dsym._init.loc, e);
dsym._init = ei;
}
- else if (sc.flags & SCOPE.Cfile && dsym.type.isTypeSArray() &&
+ else if (sc.inCfile && dsym.type.isTypeSArray() &&
dsym.type.isTypeSArray().isIncomplete())
{
// C11 6.7.9-22 determine the size of the incomplete array,
@@ -1283,21 +1303,48 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
Expression exp = ei.exp;
Expression e1 = new VarExp(dsym.loc, dsym);
- if (isBlit)
- exp = new BlitExp(dsym.loc, e1, exp);
+ if (dsymIsRef) // follow logic similar to typesem.argumentMatchParameter() and statementsem.visitForeach()
+ {
+ dsym.storage_class |= STC.nodtor;
+ exp = exp.expressionSemantic(sc);
+ Type tp = dsym.type;
+ Type ta = exp.type;
+ if (!exp.isLvalue())
+ {
+ .error(dsym.loc, "rvalue `%s` cannot be assigned to `ref %s`", exp.toChars(), dsym.toChars());
+ exp = ErrorExp.get();
+ }
+ else if (!ta.constConv(tp))
+ {
+ .error(dsym.loc, "type `%s` cannot be assigned to `ref %s %s`", ta.toChars(), tp.toChars(), dsym.toChars());
+ exp = ErrorExp.get();
+ }
+ else
+ {
+ exp = new ConstructExp(dsym.loc, e1, exp);
+ dsym.canassign++;
+ exp = exp.expressionSemantic(sc);
+ dsym.canassign--;
+ }
+ }
else
- exp = new ConstructExp(dsym.loc, e1, exp);
- dsym.canassign++;
- exp = exp.expressionSemantic(sc);
- dsym.canassign--;
- exp = exp.optimize(WANTvalue);
+ {
+ if (isBlit)
+ exp = new BlitExp(dsym.loc, e1, exp);
+ else
+ exp = new ConstructExp(dsym.loc, e1, exp);
+ dsym.canassign++;
+ exp = exp.expressionSemantic(sc);
+ dsym.canassign--;
+ }
+
if (exp.op == EXP.error)
{
dsym._init = new ErrorInitializer();
ei = null;
}
else
- ei.exp = exp;
+ ei.exp = exp.optimize(WANTvalue);
}
else
{
@@ -1321,7 +1368,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
}
else if (dsym.storage_class & (STC.const_ | STC.immutable_ | STC.manifest) ||
dsym.type.isConst() || dsym.type.isImmutable() ||
- sc.flags & SCOPE.Cfile)
+ sc.inCfile)
{
/* Because we may need the results of a const declaration in a
* subsequent type, such as an array dimension, before semantic2()
@@ -1466,7 +1513,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
if (dsym.errors)
return;
- if (!(global.params.bitfields || sc.flags & SCOPE.Cfile))
+ if (!(global.params.bitfields || sc.inCfile))
{
version (IN_GCC)
.error(dsym.loc, "%s `%s` use `-fpreview=bitfields` for bitfield support", dsym.kind, dsym.toPrettyChars);
@@ -1514,6 +1561,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
override void visit(Import imp)
{
+ timeTraceBeginEvent(TimeTraceEventType.sema1Import);
+ scope (exit) timeTraceEndEvent(TimeTraceEventType.sema1Import, imp);
static if (LOG)
{
printf("Import::semantic('%s') %s\n", imp.toPrettyChars(), imp.id.toChars());
@@ -1635,75 +1684,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
}
imp.semanticRun = PASS.semanticdone;
-
- // object self-imports itself, so skip that
- // https://issues.dlang.org/show_bug.cgi?id=7547
- // don't list pseudo modules __entrypoint.d, __main.d
- // https://issues.dlang.org/show_bug.cgi?id=11117
- // https://issues.dlang.org/show_bug.cgi?id=11164
- if (global.params.moduleDeps.buffer is null || (imp.id == Id.object && sc._module.ident == Id.object) ||
- strcmp(sc._module.ident.toChars(), "__main") == 0)
- return;
-
- /* The grammar of the file is:
- * ImportDeclaration
- * ::= BasicImportDeclaration [ " : " ImportBindList ] [ " -> "
- * ModuleAliasIdentifier ] "\n"
- *
- * BasicImportDeclaration
- * ::= ModuleFullyQualifiedName " (" FilePath ") : " Protection|"string"
- * " [ " static" ] : " ModuleFullyQualifiedName " (" FilePath ")"
- *
- * FilePath
- * - any string with '(', ')' and '\' escaped with the '\' character
- */
- OutBuffer* ob = global.params.moduleDeps.buffer;
- Module imod = sc._module;
- if (!global.params.moduleDeps.name)
- ob.writestring("depsImport ");
- ob.writestring(imod.toPrettyChars());
- ob.writestring(" (");
- escapePath(ob, imod.srcfile.toChars());
- ob.writestring(") : ");
- // use visibility instead of sc.visibility because it couldn't be
- // resolved yet, see the comment above
- visibilityToBuffer(*ob, imp.visibility);
- ob.writeByte(' ');
- if (imp.isstatic)
- {
- stcToBuffer(*ob, STC.static_);
- ob.writeByte(' ');
- }
- ob.writestring(": ");
- foreach (pid; imp.packages)
- {
- ob.printf("%s.", pid.toChars());
- }
- ob.writestring(imp.id.toString());
- ob.writestring(" (");
- if (imp.mod)
- escapePath(ob, imp.mod.srcfile.toChars());
- else
- ob.writestring("???");
- ob.writeByte(')');
- foreach (i, name; imp.names)
- {
- if (i == 0)
- ob.writeByte(':');
- else
- ob.writeByte(',');
- Identifier _alias = imp.aliases[i];
- if (!_alias)
- {
- ob.printf("%s", name.toChars());
- _alias = name;
- }
- else
- ob.printf("%s=%s", _alias.toChars(), name.toChars());
- }
- if (imp.aliasId)
- ob.printf(" -> %s", imp.aliasId.toChars());
- ob.writenl();
+ addImportDep(global.params.moduleDeps, imp, sc._module);
}
void attribSemantic(AttribDeclaration ad)
@@ -1754,7 +1735,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
sc = sc.push();
sc.stc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.gshared);
sc.inunion = scd.isunion ? scd : null;
- sc.flags = 0;
+ sc.resetAllFlags();
for (size_t i = 0; i < scd.decl.length; i++)
{
Dsymbol s = (*scd.decl)[i];
@@ -1911,6 +1892,25 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
{
if (sa.semanticRun < PASS.semanticdone)
sa.semanticRun = PASS.semanticdone;
+ else
+ return;
+
+ // https://issues.dlang.org/show_bug.cgi?id=24645
+ // This is a short-circuit. Usually, static assert conditions are evaluated
+ // in semantic2, but it's not uncommon to use this pattern:
+ // ---
+ // version(X)
+ // {}
+ // else
+ // static assert(false, "unsupported platform");
+ // ---
+ // However, without this short-circuit, the static assert error may get drowned
+ // out by subsequent semantic1 (import) errors. Only short-circuit at module scope though,
+ // inside mixin templates you want an instantiation trace (which you don't get here).
+ if (sc.parent && sc.parent.isModule())
+ if (auto i = sa.exp.isIntegerExp())
+ if (i.toInteger() == 0)
+ staticAssertFail(sa, sc);
}
override void visit(DebugSymbol ds)
@@ -1936,6 +1936,10 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
{
if (m.semanticRun != PASS.initial)
return;
+
+ timeTraceBeginEvent(TimeTraceEventType.sema1Module);
+ scope (exit) timeTraceEndEvent(TimeTraceEventType.sema1Module, m);
+
//printf("+Module::semantic(this = %p, '%s'): parent = %p\n", this, toChars(), parent);
m.semanticRun = PASS.semantic;
// Note that modules get their own scope, from scratch.
@@ -2782,7 +2786,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
sc = sc.push();
sc.stc &= ~STC.static_; // not a static invariant
sc.stc |= STC.const_; // invariant() is always const
- sc.flags = (sc.flags & ~SCOPE.contract) | SCOPE.invariant_;
+ sc.contract = Contract.invariant_;
sc.linkage = LINK.d;
funcDeclarationSemantic(sc, invd);
@@ -3001,7 +3005,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
buildOpAssign(sd, sc2);
buildOpEquals(sd, sc2);
- if (!(sc2.flags & SCOPE.Cfile) &&
+ if (!sc2.inCfile &&
global.params.useTypeInfo && Type.dtypeinfo) // these functions are used for TypeInfo
{
sd.xeq = buildXopEquals(sd, sc2);
@@ -4137,7 +4141,7 @@ private extern(C++) class AddMemberVisitor : Visitor
}
// If using C tag/prototype/forward declaration rules
- if (sc.flags & SCOPE.Cfile && !dsym.isImport())
+ if (sc.inCfile && !dsym.isImport())
{
if (handleTagSymbols(*sc, dsym, s2, sds))
return;
@@ -4158,7 +4162,7 @@ private extern(C++) class AddMemberVisitor : Visitor
if (sds.isAggregateDeclaration() || sds.isEnumDeclaration())
{
if (dsym.ident == Id.__sizeof ||
- !(sc && sc.flags & SCOPE.Cfile) && (dsym.ident == Id.__xalignof || dsym.ident == Id._mangleof))
+ !(sc && sc.inCfile) && (dsym.ident == Id.__xalignof || dsym.ident == Id._mangleof))
{
.error(dsym.loc, "%s `%s` `.%s` property cannot be redefined", dsym.kind, dsym.toPrettyChars, dsym.ident.toChars());
dsym.errors = true;
@@ -4434,7 +4438,7 @@ private extern(C++) class AddMemberVisitor : Visitor
*/
void addEnumMembersToSymtab(EnumDeclaration ed, Scope* sc, ScopeDsymbol sds)
{
- const bool isCEnum = (sc.flags & SCOPE.Cfile) != 0; // it's an ImportC enum
+ const bool isCEnum = sc.inCfile; // it's an ImportC enum
//printf("addEnumMembersToSymtab(ed: %s added: %d Cfile: %d)\n", ed.toChars(), ed.added, isCEnum);
if (ed.added)
return;
@@ -4465,6 +4469,8 @@ void addEnumMembersToSymtab(EnumDeclaration ed, Scope* sc, ScopeDsymbol sds)
* the enum members to both symbol tables.
*/
em.addMember(sc, ed); // add em to ed's symbol table
+ if (em.errors)
+ return;
em.addMember(sc, sds); // add em to symbol table that ed is in
em.parent = ed; // restore it after previous addMember() changed it
}
@@ -4970,7 +4976,7 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList
if (global.errors != errorsave)
goto Laftersemantic;
- if ((sc.func || (sc.flags & SCOPE.fullinst)) && !tempinst.tinst)
+ if ((sc.func || sc.fullinst) && !tempinst.tinst)
{
/* If a template is instantiated inside function, the whole instantiation
* should be done at that position. But, immediate running semantic3 of
@@ -7267,9 +7273,11 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor
}
else if (style == TargetC.BitFieldStyle.Gcc_Clang)
{
- // If the bit-field spans more units of alignment than its type,
- // start a new field at the next alignment boundary.
- if (fieldState.bitOffset == fieldState.fieldSize * 8 &&
+ // If the bit-field spans more units of alignment than its type
+ // and is at the alignment boundary, start a new field at the
+ // next alignment boundary. This affects when offsetof reports
+ // a higher number and bitoffsetof starts at zero again.
+ if (fieldState.bitOffset % (memalignsize * 8) == 0 &&
fieldState.bitOffset + bfd.fieldWidth > memsize * 8)
{
if (log) printf("more units of alignment than its type\n");
@@ -423,8 +423,7 @@ private size_t arrayObjectHash(ref Objects oa1)
hash = mixHash(hash, expressionHash(e1));
else if (auto s1 = isDsymbol(o1))
{
- auto fa1 = s1.isFuncAliasDeclaration();
- if (fa1)
+ if (auto fa1 = s1.isFuncAliasDeclaration())
s1 = fa1.toAliasFunc();
hash = mixHash(hash, mixHash(cast(size_t)cast(void*)s1.getIdent(), cast(size_t)cast(void*)s1.parent));
}
@@ -1012,6 +1011,11 @@ size_t templateParameterLookup(Type tparam, TemplateParameters* parameters)
return IDX_NOTFOUND;
}
+private auto X(T, U)(T m, U n)
+{
+ return (m << 4) | n;
+}
+
ubyte deduceWildHelper(Type t, Type* at, Type tparam)
{
if ((tparam.mod & MODFlags.wild) == 0)
@@ -1019,11 +1023,6 @@ ubyte deduceWildHelper(Type t, Type* at, Type tparam)
*at = null;
- auto X(T, U)(T U, U T)
- {
- return (U << 4) | T;
- }
-
switch (X(tparam.mod, t.mod))
{
case X(MODFlags.wild, 0):
@@ -1098,12 +1097,6 @@ private Type rawTypeMerge(Type t1, Type t2)
MATCH deduceTypeHelper(Type t, out Type at, Type tparam)
{
// 9*9 == 81 cases
-
- auto X(T, U)(T U, U T)
- {
- return (U << 4) | T;
- }
-
switch (X(tparam.mod, t.mod))
{
case X(0, 0):
@@ -2557,8 +2550,7 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, ref TemplateParameters pa
*/
private void deduceBaseClassParameters(ref BaseClass b, Scope* sc, Type tparam, ref TemplateParameters parameters, ref Objects dedtypes, ref Objects best, ref int numBaseClassMatches)
{
- TemplateInstance parti = b.sym ? b.sym.parent.isTemplateInstance() : null;
- if (parti)
+ if (TemplateInstance parti = b.sym ? b.sym.parent.isTemplateInstance() : null)
{
// Make a temporary copy of dedtypes so we don't destroy it
auto tmpdedtypes = new Objects(dedtypes.length);
@@ -192,7 +192,7 @@ void enumSemantic(Scope* sc, EnumDeclaration ed)
return;
}
- if (!(sc.flags & SCOPE.Cfile)) // C enum remains incomplete until members are done
+ if (!sc.inCfile) // C enum remains incomplete until members are done
ed.semanticRun = PASS.semanticdone;
version (none)
@@ -219,8 +219,7 @@ void enumSemantic(Scope* sc, EnumDeclaration ed)
*/
ed.members.foreachDsymbol( (s)
{
- EnumMember em = s.isEnumMember();
- if (em)
+ if (EnumMember em = s.isEnumMember())
em._scope = sce;
});
@@ -230,7 +229,7 @@ void enumSemantic(Scope* sc, EnumDeclaration ed)
*/
addEnumMembersToSymtab(ed, sc, sc.getScopesym());
- if (sc.flags & SCOPE.Cfile)
+ if (sc.inCfile)
{
/* C11 6.7.2.2
*/
@@ -386,8 +385,7 @@ Expression getDefaultValue(EnumDeclaration ed, const ref Loc loc)
foreach (const i; 0 .. ed.members.length)
{
- EnumMember em = (*ed.members)[i].isEnumMember();
- if (em)
+ if (EnumMember em = (*ed.members)[i].isEnumMember())
{
if (em.semanticRun < PASS.semanticdone)
{
@@ -378,32 +378,24 @@ bool checkParamArgumentEscape(ref Scope sc, FuncDeclaration fdc, Identifier parI
void onValue(VarDeclaration v)
{
if (log) printf("byvalue %s\n", v.toChars());
- if (parStc & STC.scope_ || v.isDataseg())
+ if (parStc & STC.scope_)
return;
- Dsymbol p = v.toParent2();
-
- notMaybeScope(v, vPar);
+ doNotInferScope(v, vPar);
if (v.isScope())
{
unsafeAssign!"scope variable"(v);
}
- else if (v.isTypesafeVariadicArray && p == sc.func)
- {
- unsafeAssign!"variadic variable"(v);
- }
}
void onRef(VarDeclaration v, bool retRefTransition)
{
if (log) printf("byref %s\n", v.toChars());
- if (v.isDataseg())
- return;
Dsymbol p = v.toParent2();
- notMaybeScope(v, arg);
+ doNotInferScope(v, arg);
if (checkScopeVarAddr(v, arg, sc, gag))
{
result = true;
@@ -432,7 +424,7 @@ bool checkParamArgumentEscape(ref Scope sc, FuncDeclaration fdc, Identifier parI
Dsymbol p = v.toParent2();
- notMaybeScope(v, arg);
+ doNotInferScope(v, arg);
if ((v.isReference() || v.isScope()) && p == sc.func)
{
@@ -667,6 +659,13 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef)
va = null;
}
+ if (e.op == EXP.construct && va && (va.storage_class & STC.temp) && va._init)
+ {
+ // Initializing a temporary is safe, `escapeExp` will forward such vars
+ // to their `va._init` if needed.
+ return false;
+ }
+
if (log && va) printf("va: %s\n", va.toChars());
FuncDeclaration fd = sc.func;
@@ -701,32 +700,19 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef)
void onValue(VarDeclaration v)
{
if (log) printf("byvalue: %s\n", v.toChars());
- if (v.isDataseg())
- return;
if (v == va)
return;
Dsymbol p = v.toParent2();
- if (va && !vaIsRef && !va.isScope() && !v.isScope() &&
- !v.isTypesafeVariadicArray && !va.isTypesafeVariadicArray &&
- (va.isParameter() && va.maybeScope && v.isParameter() && v.maybeScope) &&
- p == fd)
- {
- /* Add v to va's list of dependencies
- */
- va.addMaybe(v);
- return;
- }
-
if (vaIsFirstRef && p == fd)
{
inferReturn(fd, v, /*returnScope:*/ true);
}
if (!(va && va.isScope()) || vaIsRef)
- notMaybeScope(v, e);
+ doNotInferScope(v, e);
if (v.isScope())
{
@@ -785,27 +771,20 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef)
}
result |= sc.setUnsafeDIP1000(gag, ae.loc, "scope variable `%s` assigned to non-scope `%s`", v, e1);
}
- else if (v.isTypesafeVariadicArray && p == fd)
- {
- if (inferScope(va))
- return;
- result |= sc.setUnsafeDIP1000(gag, ae.loc, "variadic variable `%s` assigned to non-scope `%s`", v, e1);
- }
else
{
/* v is not 'scope', and we didn't check the scope of where we assigned it to.
* It may escape via that assignment, therefore, v can never be 'scope'.
*/
//printf("no infer for %s in %s, %d\n", v.toChars(), fd.ident.toChars(), __LINE__);
- doNotInferScope(v, e);
+ if (!v.isParameter)
+ doNotInferScope(v, e);
}
}
void onRef(VarDeclaration v, bool retRefTransition)
{
if (log) printf("byref: %s\n", v.toChars());
- if (v.isDataseg())
- return;
if (checkScopeVarAddr(v, ae, sc, gag))
{
@@ -845,7 +824,7 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef)
}
if (!(va && va.isScope()))
- notMaybeScope(v, e);
+ doNotInferScope(v, e);
if (p != sc.func)
return;
@@ -856,8 +835,6 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef)
va.storage_class |= STC.return_ | STC.returninferred;
return;
}
- if (e1.op == EXP.structLiteral)
- return;
result |= sc.setUnsafeDIP1000(gag, ae.loc, "reference to local variable `%s` assigned to non-scope `%s`", v, e1);
}
@@ -884,7 +861,7 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef)
Dsymbol p = v.toParent2();
if (!(va && va.isScope()))
- notMaybeScope(v, e);
+ doNotInferScope(v, e);
if (!(v.isReference() || v.isScope()) || p != fd)
return;
@@ -909,8 +886,7 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef)
/* Do not allow slicing of a static array returned by a function
*/
- if (ee.op == EXP.call && ee.type.toBasetype().isTypeSArray() && e1.type.toBasetype().isTypeDArray() &&
- !(va && va.storage_class & STC.temp))
+ if (ee.op == EXP.call && ee.type.toBasetype().isTypeSArray() && e1.type.toBasetype().isTypeDArray())
{
if (!gag)
sc.eSink.deprecation(ee.loc, "slice of static array temporary returned by `%s` assigned to longer lived variable `%s`",
@@ -919,31 +895,11 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef)
return;
}
- if (ee.op == EXP.call && ee.type.toBasetype().isTypeStruct() &&
- (!va || !(va.storage_class & STC.temp) && !va.isScope()))
- {
- if (sc.setUnsafeDIP1000(gag, ee.loc, "address of struct temporary returned by `%s` assigned to longer lived variable `%s`", ee, e1))
- {
- result = true;
- return;
- }
- }
-
- if (ee.op == EXP.structLiteral &&
- (!va || !(va.storage_class & STC.temp)))
- {
- if (sc.setUnsafeDIP1000(gag, ee.loc, "address of struct literal `%s` assigned to longer lived variable `%s`", ee, e1))
- {
- result = true;
- return;
- }
- }
-
- if (inferScope(va))
- return;
+ const(char)* msg = (ee.op == EXP.structLiteral) ?
+ "address of struct literal `%s` assigned to `%s` with longer lifetime" :
+ "address of expression temporary returned by `%s` assigned to `%s` with longer lifetime";
- result |= sc.setUnsafeDIP1000(gag, ee.loc,
- "reference to stack allocated value returned by `%s` assigned to non-scope `%s`", ee, e1);
+ result |= sc.setUnsafeDIP1000(gag, ee.loc, msg, ee, e1);
}
scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp);
@@ -977,9 +933,6 @@ bool checkThrowEscape(ref Scope sc, Expression e, bool gag)
void onValue(VarDeclaration v)
{
//printf("byvalue %s\n", v.toChars());
- if (v.isDataseg())
- return;
-
if (v.isScope() && !v.iscatchvar) // special case: allow catch var to be rethrown
// despite being `scope`
{
@@ -989,7 +942,7 @@ bool checkThrowEscape(ref Scope sc, Expression e, bool gag)
}
else
{
- notMaybeScope(v, new ThrowExp(e.loc, e));
+ doNotInferScope(v, new ThrowExp(e.loc, e));
}
}
void onFunc(FuncDeclaration fd, bool called) {}
@@ -1025,8 +978,6 @@ bool checkNewEscape(ref Scope sc, Expression e, bool gag)
void onValue(VarDeclaration v)
{
if (log) printf("byvalue `%s`\n", v.toChars());
- if (v.isDataseg())
- return;
Dsymbol p = v.toParent2();
@@ -1049,15 +1000,10 @@ bool checkNewEscape(ref Scope sc, Expression e, bool gag)
return;
}
}
- else if (v.isTypesafeVariadicArray && p == sc.func)
- {
- result |= sc.setUnsafeDIP1000(gag, e.loc,
- "copying `%s` into allocated memory escapes a reference to variadic parameter `%s`", e, v);
- }
else
{
//printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__);
- notMaybeScope(v, e);
+ doNotInferScope(v, e);
}
}
@@ -1075,9 +1021,6 @@ bool checkNewEscape(ref Scope sc, Expression e, bool gag)
return setUnsafePreview(&sc, fs, gag, e.loc, msg, e, v);
}
- if (v.isDataseg())
- return;
-
Dsymbol p = v.toParent2();
if (!v.isReference())
@@ -1124,7 +1067,12 @@ bool checkNewEscape(ref Scope sc, Expression e, bool gag)
}
}
- void onFunc(FuncDeclaration fd, bool called) {}
+ void onFunc(FuncDeclaration fd, bool called)
+ {
+ if (called)
+ result |= sc.setUnsafeDIP1000(gag, e.loc,
+ "nested function `%s` returns `scope` values and escapes them into allocated memory", fd);
+ }
void onExp(Expression ee, bool retRefTransition)
{
@@ -1203,8 +1151,6 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g
void onValue(VarDeclaration v)
{
if (log) printf("byvalue `%s`\n", v.toChars());
- if (v.isDataseg())
- return;
const vsr = buildScopeRef(v.storage_class);
@@ -1215,7 +1161,13 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g
return;
}
- if (v.isScope())
+ if (v.isTypesafeVariadicArray && p == sc.func)
+ {
+ if (!gag)
+ sc.eSink.error(e.loc, "returning `%s` escapes a reference to variadic parameter `%s`", e.toChars(), v.toChars());
+ result = false;
+ }
+ else if (v.isScope())
{
/* If `return scope` applies to v.
*/
@@ -1270,13 +1222,7 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g
}
}
}
- else if (v.isTypesafeVariadicArray && p == sc.func)
- {
- if (!gag)
- sc.eSink.error(e.loc, "returning `%s` escapes a reference to variadic parameter `%s`", e.toChars(), v.toChars());
- result = false;
- }
- else
+ else if (p == sc.func || !v.isParameter())
{
//printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__);
doNotInferScope(v, e);
@@ -1332,24 +1278,15 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g
}
}
- if (v.isDataseg())
- return;
-
const vsr = buildScopeRef(v.storage_class);
Dsymbol p = v.toParent2();
// https://issues.dlang.org/show_bug.cgi?id=19965
- if (!refs)
+ if (!refs && checkScopeVarAddr(v, e, sc, gag))
{
- if (sc.func.vthis == v)
- notMaybeScope(v, e);
-
- if (checkScopeVarAddr(v, e, sc, gag))
- {
- result = true;
- return;
- }
+ result = true;
+ return;
}
if (!v.isReference())
@@ -1360,7 +1297,7 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g
return;
}
FuncDeclaration fd = p.isFuncDeclaration();
- if (fd && sc.func.returnInprocess)
+ if (fd && sc.func.scopeInprocess)
{
/* Code like:
* int x;
@@ -1488,7 +1425,7 @@ private bool inferReturn(FuncDeclaration fd, VarDeclaration v, bool returnScope)
if (!v.isParameter() || v.isTypesafeVariadicArray || (returnScope && v.doNotInferReturn))
return false;
- if (!fd.returnInprocess)
+ if (!fd.scopeInprocess)
return false;
if (returnScope && !(v.isScope() || v.maybeScope))
@@ -1546,10 +1483,20 @@ private bool inferReturn(FuncDeclaration fd, VarDeclaration v, bool returnScope)
* Params:
* e = expression to be returned by value
* er = where to place collected data
- * retRefTransition = if `e` is returned through a `return (ref) scope` function call
*/
public
-void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransition = false)
+void escapeByValue(Expression e, ref scope EscapeByResults er)
+{
+ escapeExp(e, er, 0);
+}
+
+// Unified implementation of `escapeByValue` and `escapeByRef`
+// deref = derference level, if `p` has deref 0, then `*p` has deref 1, `&p` has -1, and `**p` has 2 etc.
+// For escapeByValue, deref = 0
+// For escapeByRef, deref = -1
+// Currently, `scope` is not transitive, so deref > 0 means no escaping, but `@live` does do transitive checking,
+// and future enhancements might add some form of transitive scope.
+void escapeExp(Expression e, ref scope EscapeByResults er, int deref)
{
//printf("[%s] escapeByValue, e: %s\n", e.loc.toChars(), e.toChars());
@@ -1563,55 +1510,92 @@ void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransi
* allowed, but CTFE can generate one out of a new expression,
* but it'll be placed in static data so no need to check it.
*/
- if (e.e1.op != EXP.structLiteral)
- escapeByRef(e.e1, er, retRefTransition);
+ if (deref == 0 && e.e1.op != EXP.structLiteral)
+ escapeExp(e.e1, er, deref - 1);
}
void visitSymOff(SymOffExp e)
{
- VarDeclaration v = e.var.isVarDeclaration();
- if (v)
- er.byRef(v, retRefTransition);
+ if (VarDeclaration v = e.var.isVarDeclaration())
+ er.varDeref(v, deref - 1);
}
void visitVar(VarExp e)
{
if (auto v = e.var.isVarDeclaration())
{
- if (v.type.hasPointers() || // not tracking non-pointers
- v.storage_class & STC.lazy_) // lazy variables are actually pointers
- er.byValue(v);
+ const refAddr = deref < 0 && v.storage_class & STC.ref_ ;
+ const tempVar = deref == 0 && v.storage_class & STC.temp;
+ if ((refAddr || tempVar) && v._init && v != er.lastTemp)
+ {
+ // If compiler generated ref temporary
+ // (ref v = ex; ex)
+ // e.g. to extract side effects of `Tuple!(int, int).modify().expand[0]`
+ // look at the initializer instead
+ if (ExpInitializer ez = v._init.isExpInitializer())
+ {
+ // Prevent endless loops. Consider:
+ // `__field0 = (S __tup1 = S(x, y);) , __field0 = __tup1.__fields_field_0`
+ // escapeExp would recurse on the lhs of the last assignment, which is __field0
+ // again. In this case, we want the rhs.
+ // Also consider appending a struct with a `return scope` constructor:
+ // __appendtmp34 = __appendtmp34.this(null)
+ // In that case we just break the cycle using `lastTemp`.
+ auto lc = ez.exp.lastComma();
+ auto restoreLastTemp = er.lastTemp;
+ er.lastTemp = v;
+ // printf("%s %s TO %s\n", e.loc.toChars, e.toChars, lc.toChars);
+ if (lc.isAssignExp || lc.isConstructExp || lc.isBlitExp)
+ escapeExp(lc.isBinExp().e2, er, deref);
+ else
+ escapeExp(ez.exp, er, deref);
+
+ er.lastTemp = restoreLastTemp;
+ return;
+ }
+ }
+
+ if (deref < 0 || e.type.hasPointers())
+ er.varDeref(v, deref);
}
}
void visitThis(ThisExp e)
{
+ // Special case because `__this2` isn't `ref` internally
+ if (deref == -1 && e.var && e.var.toParent2().isFuncDeclaration().hasDualContext())
+ {
+ escapeByValue(e, er);
+ return;
+ }
+
if (e.var)
- er.byValue(e.var);
+ er.varDeref(e.var, deref);
}
void visitPtr(PtrExp e)
{
- if (er.live && e.type.hasPointers())
- escapeByValue(e.e1, er, retRefTransition);
+ if (deref < 0 || (er.live && e.type.hasPointers()))
+ escapeExp(e.e1, er, deref + 1);
}
void visitDotVar(DotVarExp e)
{
- auto t = e.e1.type.toBasetype();
- if (e.type.hasPointers() && (er.live || t.ty == Tstruct))
- {
- escapeByValue(e.e1, er, retRefTransition);
- }
+ auto t1b = e.e1.type.toBasetype();
+ // Accessing a class field dereferences the `this` pointer
+ if (t1b.isTypeClass())
+ escapeExp(e.e1, er, deref + 1);
+ else if (deref < 0 || e.type.hasPointers())
+ escapeExp(e.e1, er, deref);
}
void visitDelegate(DelegateExp e)
{
Type t = e.e1.type.toBasetype();
- if (t.ty == Tclass || t.ty == Tpointer)
- escapeByValue(e.e1, er, retRefTransition);
+ if (t.isTypeClass() || t.isTypePointer())
+ escapeByValue(e.e1, er);
else
- escapeByRef(e.e1, er, retRefTransition);
+ escapeByRef(e.e1, er);
er.byFunc(e.func, false);
}
@@ -1629,14 +1613,14 @@ void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransi
void visitArrayLiteral(ArrayLiteralExp e)
{
Type tb = e.type.toBasetype();
- if (tb.ty == Tsarray || tb.ty == Tarray)
+ if (tb.isTypeSArray() || tb.isTypeDArray())
{
if (e.basis)
- escapeByValue(e.basis, er, retRefTransition);
+ escapeExp(e.basis, er, deref);
foreach (el; *e.elements)
{
if (el)
- escapeByValue(el, er, retRefTransition);
+ escapeExp(el, er, deref);
}
}
}
@@ -1648,120 +1632,127 @@ void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransi
foreach (ex; *e.elements)
{
if (ex)
- escapeByValue(ex, er, retRefTransition);
+ escapeExp(ex, er, deref);
}
}
+ if (deref == -1)
+ {
+ er.byExp(e, er.inRetRefTransition > 0); //
+ }
}
void visitNew(NewExp e)
{
Type tb = e.newtype.toBasetype();
- if (tb.ty == Tstruct && !e.member && e.arguments)
+ if (tb.isTypeStruct() && !e.member && e.arguments)
{
foreach (ex; *e.arguments)
{
if (ex)
- escapeByValue(ex, er, retRefTransition);
+ escapeExp(ex, er, deref);
}
}
}
void visitCast(CastExp e)
{
- if (!e.type.hasPointers())
+ if (deref < 0 || !e.type.hasPointers())
return;
Type tb = e.type.toBasetype();
- if (tb.ty == Tarray && e.e1.type.toBasetype().ty == Tsarray)
- {
- escapeByRef(e.e1, er, retRefTransition);
- }
+ if (tb.isTypeDArray() && e.e1.type.toBasetype().isTypeSArray())
+ escapeExp(e.e1, er, deref - 1);
else
- escapeByValue(e.e1, er, retRefTransition);
+ escapeExp(e.e1, er, deref);
}
void visitSlice(SliceExp e)
{
- if (auto ve = e.e1.isVarExp())
- {
- VarDeclaration v = ve.var.isVarDeclaration();
- Type tb = e.type.toBasetype();
- if (v)
- {
- if (tb.ty == Tsarray)
- return;
- if (v.isTypesafeVariadicArray)
- {
- er.byValue(v);
- return;
- }
- }
- }
- Type t1b = e.e1.type.toBasetype();
- if (t1b.ty == Tsarray)
- {
- Type tb = e.type.toBasetype();
- if (tb.ty != Tsarray)
- escapeByRef(e.e1, er, retRefTransition);
- }
- else
- escapeByValue(e.e1, er, retRefTransition);
+ // Usually: slicing a static array escapes by ref, slicing a dynamic array escapes by value.
+ // However, slices with compile-time known length can implicitly converted to static arrays:
+ // int*[3] b = sa[0 .. 3];
+ // So we need to compare the type before slicing and after slicing
+ const bool staticBefore = e.e1.type.toBasetype().isTypeSArray() !is null;
+ const bool staticAfter = e.type.toBasetype().isTypeSArray() !is null;
+ escapeExp(e.e1, er, deref + staticAfter - staticBefore);
}
void visitIndex(IndexExp e)
{
- if (e.e1.type.toBasetype().ty == Tsarray ||
- er.live && e.type.hasPointers())
+ Type tb = e.e1.type.toBasetype();
+
+ if (tb.isTypeSArray())
+ {
+ escapeExp(e.e1, er, deref);
+ }
+ else if (tb.isTypeDArray())
{
- escapeByValue(e.e1, er, retRefTransition);
+ escapeExp(e.e1, er, deref + 1);
}
}
void visitBin(BinExp e)
{
- Type tb = e.type.toBasetype();
- if (tb.ty == Tpointer)
+ if (e.type.toBasetype().isTypePointer())
{
- escapeByValue(e.e1, er, retRefTransition);
- escapeByValue(e.e2, er, retRefTransition);
+ // The expression must be pointer arithmetic, e.g. `p + 1` or `1 + p`
+ escapeExp(e.e1, er, deref);
+ escapeExp(e.e2, er, deref);
}
}
void visitBinAssign(BinAssignExp e)
{
- escapeByValue(e.e1, er, retRefTransition);
+ escapeExp(e.e1, er, deref);
}
void visitAssign(AssignExp e)
{
- escapeByValue(e.e1, er, retRefTransition);
+ escapeExp(e.e1, er, deref);
}
void visitComma(CommaExp e)
{
- escapeByValue(e.e2, er, retRefTransition);
+ escapeExp(e.e2, er, deref);
}
void visitCond(CondExp e)
{
- escapeByValue(e.e1, er, retRefTransition);
- escapeByValue(e.e2, er, retRefTransition);
+ escapeExp(e.e1, er, deref);
+ escapeExp(e.e2, er, deref);
}
void visitCall(CallExp e)
{
//printf("CallExp(): %s\n", e.toChars());
- /* Check each argument that is
- * passed as 'return scope'.
- */
+ // Check each argument that is passed as 'return scope'.
TypeFunction tf = e.calledFunctionType();
- if (!tf || !e.type.hasPointers())
+ if (!tf)
+ return;
+
+ if (deref < 0 && !tf.isref)
+ {
+ er.byExp(e, er.inRetRefTransition > 0);
+ return;
+ }
+
+ // A function may have a return scope struct parameter, but only return an `int` field of that struct
+ if (deref >= 0 && !e.type.hasPointers())
return;
+ /// Given a `scope` / `return scope` / `return ref` annotation,
+ /// get the corresponding pointer dereference level
+ static int paramDeref(ScopeRef psr)
+ {
+ return
+ (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope) ? -1 :
+ (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope) ? 0 :
+ +1;
+ }
+
if (e.arguments && e.arguments.length)
{
- /* j=1 if _arguments[] is first argument,
- * skip it because it is not passed by ref
- */
+ // j=1 if _arguments[] is first argument,
+ // skip it because it is not passed by ref
int j = tf.isDstyleVariadic();
for (size_t i = j; i < e.arguments.length; ++i)
{
@@ -1772,139 +1763,73 @@ void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransi
Parameter p = tf.parameterList[i - j];
const stc = tf.parameterStorageClass(null, p);
ScopeRef psr = buildScopeRef(stc);
- if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope)
- {
- if (tf.isref)
- {
- /* ignore `ref` on struct constructor return because
- * struct S { this(return scope int* q) { this.p = q; } int* p; }
- * is different from:
- * ref char* front(return scope char** q) { return *q; }
- * https://github.com/dlang/dmd/pull/14869
- */
- if (auto dve = e.e1.isDotVarExp())
- if (auto fd = dve.var.isFuncDeclaration())
- if (fd.isCtorDeclaration() && tf.next.toBasetype().isTypeStruct())
- {
- escapeByValue(arg, er, retRefTransition);
- }
- }
- else
- escapeByValue(arg, er, true);
- }
- else if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope)
- {
- if (tf.isref)
- {
- /* Treat:
- * ref P foo(return ref P p)
- * as:
- * p;
- */
- escapeByValue(arg, er, retRefTransition);
- }
- else
- escapeByRef(arg, er, retRefTransition);
- }
+
+ // For struct constructors, `tf.isref` is true, but for escape analysis,
+ // it's as if they return `void` and escape through the first (`this`) parameter:
+ // void assign(ref S this, return scope constructorArgs...)
+ // If you then return the constructed result by value, it doesn't count
+ // as dereferencing the scope arguments, they're still escaped.
+ const isRef = tf.isref && !(tf.isctor && paramDeref(psr) == 0);
+ const maybeInaccurate = deref == 0 && paramDeref(psr) == 0;
+ er.inRetRefTransition += maybeInaccurate;
+ if (paramDeref(psr) <= 0)
+ escapeExp(arg, er, deref + paramDeref(psr) + isRef);
+ er.inRetRefTransition -= maybeInaccurate;
}
}
}
+
// If 'this' is returned, check it too
Type t1 = e.e1.type.toBasetype();
- if (e.e1.op == EXP.dotVariable && t1.ty == Tfunction)
+ DotVarExp dve = e.e1.isDotVarExp();
+ if (dve && t1.ty == Tfunction)
{
- DotVarExp dve = e.e1.isDotVarExp();
FuncDeclaration fd = dve.var.isFuncDeclaration();
- if (fd && fd.isThis())
- {
- /* Calling a non-static member function dve.var, which is returning `this`, and with dve.e1 representing `this`
- */
-
- /*****************************
- * Concoct storage class for member function's implicit `this` parameter.
- * Params:
- * fd = member function
- * Returns:
- * storage class for fd's `this`
- */
- StorageClass getThisStorageClass(FuncDeclaration fd)
- {
- StorageClass stc;
- auto tf = fd.type.toBasetype().isTypeFunction();
- if (tf.isreturn)
- stc |= STC.return_;
- if (tf.isreturnscope)
- stc |= STC.returnScope | STC.scope_;
- auto ad = fd.isThis();
- if (ad.isClassDeclaration() || tf.isScopeQual)
- stc |= STC.scope_;
- if (ad.isStructDeclaration())
- stc |= STC.ref_; // `this` for a struct member function is passed by `ref`
- return stc;
- }
-
- const psr = buildScopeRef(getThisStorageClass(fd));
- if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope)
- {
- if (!tf.isref || tf.isctor)
- escapeByValue(dve.e1, er, retRefTransition);
- }
- else if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope)
- {
- if (tf.isref)
- {
- /* Treat calling:
- * struct S { ref S foo() return; }
- * as:
- * this;
- */
- escapeByValue(dve.e1, er, retRefTransition);
- }
- else
- escapeByRef(dve.e1, er, psr == ScopeRef.ReturnRef_Scope);
- }
- }
+ if (!fd)
+ return;
- // If it's also a nested function that is 'return scope'
- if (fd && fd.isNested())
+ // https://issues.dlang.org/show_bug.cgi?id=20149#c10
+ if (deref < 0 && dve.var.isCtorDeclaration())
{
- if (tf.isreturn && tf.isScopeQual)
- {
- if (tf.isreturnscope)
- er.byFunc(fd, true);
- else
- er.byExp(e, false);
- }
+ er.byExp(e, false);
+ return;
}
+
+ // Calling a non-static member function dve.var, which is returning `this`, and with dve.e1 representing `this`
+ const psr = buildScopeRef(getThisStorageClass(fd));
+ er.inRetRefTransition += (psr == ScopeRef.ReturnRef_Scope);
+ if (paramDeref(psr) <= 0)
+ escapeExp(dve.e1, er, deref + paramDeref(psr) + (tf.isref && !tf.isctor));
+ er.inRetRefTransition -= (psr == ScopeRef.ReturnRef_Scope);
}
- /* If returning the result of a delegate call, the .ptr
- * field of the delegate must be checked.
- */
- if (t1.isTypeDelegate())
+ // The return value of a delegate call with return (scope) may point to a closure variable,
+ // so escape the delegate in case it's `scope` / stack allocated.
+ if (t1.isTypeDelegate() && tf.isreturn)
{
- if (tf.isreturn)
- escapeByValue(e.e1, er, retRefTransition);
+ escapeExp(e.e1, er, deref + tf.isref);
}
- /* If it's a nested function that is 'return scope'
- */
+ // If `fd` is a nested function that's return ref / return scope, check that
+ // it doesn't escape closure vars
if (auto ve = e.e1.isVarExp())
{
- FuncDeclaration fd = ve.var.isFuncDeclaration();
- if (fd && fd.isNested())
+ if (FuncDeclaration fd = ve.var.isFuncDeclaration())
{
- if (tf.isreturn && tf.isScopeQual)
+ if (fd.isNested() && tf.isreturn)
{
- if (tf.isreturnscope)
- er.byFunc(fd, true);
- else
- er.byExp(e, false);
+ er.byFunc(fd, true);
}
}
}
}
+ if (deref > 0 && !er.live)
+ return; // scope is not transitive currently, so dereferencing expressions don't escape
+
+ if (deref >= 0 && er.live && !e.type.hasPointers())
+ return; // can't escape non-pointer values by value
+
switch (e.op)
{
case EXP.address: return visitAddr(e.isAddrExp());
@@ -1929,14 +1854,36 @@ void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransi
case EXP.question: return visitCond(e.isCondExp());
case EXP.call: return visitCall(e.isCallExp());
default:
- if (auto b = e.isBinExp())
- return visitBin(b);
if (auto ba = e.isBinAssignExp())
return visitBinAssign(ba);
+ if (auto b = e.isBinExp())
+ return visitBin(b);
return visit(e);
}
}
+/*****************************
+ * Concoct storage class for member function's implicit `this` parameter.
+ * Params:
+ * fd = member function
+ * Returns:
+ * storage class for fd's `this`
+ */
+StorageClass getThisStorageClass(FuncDeclaration fd)
+{
+ StorageClass stc;
+ auto tf = fd.type.toBasetype().isTypeFunction();
+ if (tf.isreturn)
+ stc |= STC.return_;
+ if (tf.isreturnscope)
+ stc |= STC.returnScope | STC.scope_;
+ auto ad = fd.isThis();
+ if ((ad && ad.isClassDeclaration()) || tf.isScopeQual)
+ stc |= STC.scope_;
+ if (ad && ad.isStructDeclaration())
+ stc |= STC.ref_; // `this` for a struct member function is passed by `ref`
+ return stc;
+}
/****************************************
* e is an expression to be returned by 'ref'.
@@ -1954,238 +1901,10 @@ void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransi
* Params:
* e = expression to be returned by 'ref'
* er = where to place collected data
- * retRefTransition = if `e` is returned through a `return (ref) scope` function call
*/
-private
-void escapeByRef(Expression e, ref scope EscapeByResults er, bool retRefTransition = false)
+void escapeByRef(Expression e, ref scope EscapeByResults er)
{
- //printf("[%s] escapeByRef, e: %s, retRefTransition: %d\n", e.loc.toChars(), e.toChars(), retRefTransition);
- void visit(Expression e)
- {
- }
-
- void visitVar(VarExp e)
- {
- auto v = e.var.isVarDeclaration();
- if (v)
- {
- if (v.storage_class & STC.ref_ && v.storage_class & (STC.foreach_ | STC.temp) && v._init)
- {
- /* If compiler generated ref temporary
- * (ref v = ex; ex)
- * look at the initializer instead
- */
- if (ExpInitializer ez = v._init.isExpInitializer())
- {
- if (auto ce = ez.exp.isConstructExp())
- escapeByRef(ce.e2, er, retRefTransition);
- else
- escapeByRef(ez.exp, er, retRefTransition);
- }
- }
- else
- er.byRef(v, retRefTransition);
- }
- }
-
- void visitThis(ThisExp e)
- {
- if (e.var && e.var.toParent2().isFuncDeclaration().hasDualContext())
- escapeByValue(e, er, retRefTransition);
- else if (e.var)
- er.byRef(e.var, retRefTransition);
- }
-
- void visitPtr(PtrExp e)
- {
- escapeByValue(e.e1, er, retRefTransition);
- }
-
- void visitIndex(IndexExp e)
- {
- Type tb = e.e1.type.toBasetype();
- if (auto ve = e.e1.isVarExp())
- {
- VarDeclaration v = ve.var.isVarDeclaration();
- if (v && v.isTypesafeVariadicArray)
- {
- er.byRef(v, retRefTransition);
- return;
- }
- }
- if (tb.ty == Tsarray)
- {
- escapeByRef(e.e1, er, retRefTransition);
- }
- else if (tb.ty == Tarray)
- {
- escapeByValue(e.e1, er, retRefTransition);
- }
- }
-
- void visitStructLiteral(StructLiteralExp e)
- {
- if (e.elements)
- {
- foreach (ex; *e.elements)
- {
- if (ex)
- escapeByRef(ex, er, retRefTransition);
- }
- }
- er.byExp(e, retRefTransition);
- }
-
- void visitDotVar(DotVarExp e)
- {
- Type t1b = e.e1.type.toBasetype();
- if (t1b.ty == Tclass)
- escapeByValue(e.e1, er, retRefTransition);
- else
- escapeByRef(e.e1, er, retRefTransition);
- }
-
- void visitBinAssign(BinAssignExp e)
- {
- escapeByRef(e.e1, er, retRefTransition);
- }
-
- void visitAssign(AssignExp e)
- {
- escapeByRef(e.e1, er, retRefTransition);
- }
-
- void visitComma(CommaExp e)
- {
- escapeByRef(e.e2, er, retRefTransition);
- }
-
- void visitCond(CondExp e)
- {
- escapeByRef(e.e1, er, retRefTransition);
- escapeByRef(e.e2, er, retRefTransition);
- }
-
- void visitCall(CallExp e)
- {
- //printf("escapeByRef.CallExp(): %s\n", e.toChars());
- /* If the function returns by ref, check each argument that is
- * passed as 'return ref'.
- */
- TypeFunction tf = e.calledFunctionType();
- if (!tf)
- return;
- if (tf.isref)
- {
- if (e.arguments && e.arguments.length)
- {
- /* j=1 if _arguments[] is first argument,
- * skip it because it is not passed by ref
- */
- int j = tf.isDstyleVariadic();
- for (size_t i = j; i < e.arguments.length; ++i)
- {
- Expression arg = (*e.arguments)[i];
- size_t nparams = tf.parameterList.length;
- if (i - j < nparams && i >= j)
- {
- Parameter p = tf.parameterList[i - j];
- const stc = tf.parameterStorageClass(null, p);
- ScopeRef psr = buildScopeRef(stc);
- if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope)
- escapeByRef(arg, er, retRefTransition);
- else if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope)
- {
- if (auto de = arg.isDelegateExp())
- {
- if (de.func.isNested())
- er.byExp(de, false);
- }
- else
- escapeByValue(arg, er, retRefTransition);
- }
- }
- }
- }
- // If 'this' is returned by ref, check it too
- Type t1 = e.e1.type.toBasetype();
- if (e.e1.op == EXP.dotVariable && t1.ty == Tfunction)
- {
- DotVarExp dve = e.e1.isDotVarExp();
-
- // https://issues.dlang.org/show_bug.cgi?id=20149#c10
- if (dve.var.isCtorDeclaration())
- {
- er.byExp(e, false);
- return;
- }
-
- StorageClass stc = dve.var.storage_class & (STC.return_ | STC.scope_ | STC.ref_);
- if (tf.isreturn)
- stc |= STC.return_;
- if (tf.isref)
- stc |= STC.ref_;
- if (tf.isScopeQual)
- stc |= STC.scope_;
- if (tf.isreturnscope)
- stc |= STC.returnScope;
-
- const psr = buildScopeRef(stc);
- if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope)
- escapeByRef(dve.e1, er, psr == ScopeRef.ReturnRef_Scope);
- else if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope)
- escapeByValue(dve.e1, er, retRefTransition);
-
- // If it's also a nested function that is 'return ref'
- if (FuncDeclaration fd = dve.var.isFuncDeclaration())
- {
- if (fd.isNested() && tf.isreturn)
- {
- er.byExp(e, false);
- }
- }
- }
- // If it's a delegate, check it too
- if (e.e1.op == EXP.variable && t1.ty == Tdelegate)
- {
- escapeByValue(e.e1, er, retRefTransition);
- }
-
- /* If it's a nested function that is 'return ref'
- */
- if (auto ve = e.e1.isVarExp())
- {
- FuncDeclaration fd = ve.var.isFuncDeclaration();
- if (fd && fd.isNested())
- {
- if (tf.isreturn)
- er.byExp(e, false);
- }
- }
- }
- else
- er.byExp(e, retRefTransition);
- }
-
- switch (e.op)
- {
- case EXP.variable: return visitVar(e.isVarExp());
- case EXP.this_: return visitThis(e.isThisExp());
- case EXP.star: return visitPtr(e.isPtrExp());
- case EXP.structLiteral: return visitStructLiteral(e.isStructLiteralExp());
- case EXP.dotVariable: return visitDotVar(e.isDotVarExp());
- case EXP.index: return visitIndex(e.isIndexExp());
- case EXP.blit: return visitAssign(e.isBlitExp());
- case EXP.construct: return visitAssign(e.isConstructExp());
- case EXP.assign: return visitAssign(e.isAssignExp());
- case EXP.comma: return visitComma(e.isCommaExp());
- case EXP.question: return visitCond(e.isCondExp());
- case EXP.call: return visitCall(e.isCallExp());
- default:
- if (auto ba = e.isBinAssignExp())
- return visitBinAssign(ba);
- return visit(e);
- }
+ escapeExp(e, er, -1);
}
/************************************
@@ -2213,6 +1932,20 @@ struct EscapeByResults
void delegate(VarDeclaration, bool retRefTransition) byRef;
/// called on variables with values containing pointers
void delegate(VarDeclaration) byValue;
+
+ /// Switch over `byValue` and `byRef` based on `deref` level (-1 = by ref, 0 = by value, 1 = only for live currently)
+ private void varDeref(VarDeclaration var, int deref)
+ {
+ if (var.isDataseg())
+ return;
+ if (deref == -1)
+ byRef(var, inRetRefTransition > 0);
+ else if (deref == 0)
+ byValue(var);
+ else if (deref > 0 && live)
+ byValue(var);
+ }
+
/// called on nested functions that are turned into delegates
/// When `called` is true, it means the delegate escapes variables
/// from the closure through a call to it, while `false` means the
@@ -2223,6 +1956,14 @@ struct EscapeByResults
/// if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, etc., all return `p`.
bool live = false;
+
+ /// Incremented / decremented every time an ambiguous return ref/scope parameter is checked.
+ /// See retRefTransition above.
+ private int inRetRefTransition = 0;
+
+ /// When forwarding a temp var to its initializer,
+ /// keep track of the temp var to break endless loops
+ private VarDeclaration lastTemp = null;
}
/*************************
@@ -2266,7 +2007,7 @@ public void findAllOuterAccessedVariables(FuncDeclaration fd, VarDeclarations* v
* - `VarDeclaration` of a non-scope parameter it was assigned to
* - `null` for no reason
*/
-private void notMaybeScope(VarDeclaration v, RootObject o)
+private void doNotInferScope(VarDeclaration v, RootObject o)
{
if (v.maybeScope)
{
@@ -2276,23 +2017,6 @@ private void notMaybeScope(VarDeclaration v, RootObject o)
}
}
-/***********************************
- * Turn off `maybeScope` for variable `v` if it's not a parameter.
- *
- * This is for compatibility with the old system with both `STC.maybescope` and `VarDeclaration.doNotInferScope`,
- * which is now just `VarDeclaration.maybeScope`.
- * This function should probably be removed in future refactors.
- *
- * Params:
- * v = variable
- * o = reason for it being turned off
- */
-private void doNotInferScope(VarDeclaration v, RootObject o)
-{
- if (!v.isParameter)
- notMaybeScope(v, o);
-}
-
/***********************************
* After semantic analysis of the function body,
* try to infer `scope` / `return` on the parameters
@@ -2305,48 +2029,21 @@ private void doNotInferScope(VarDeclaration v, RootObject o)
public
void finishScopeParamInference(FuncDeclaration funcdecl, ref TypeFunction f)
{
-
- if (funcdecl.returnInprocess)
- {
- funcdecl.returnInprocess = false;
- if (funcdecl.storage_class & STC.return_)
- {
- if (funcdecl.type == f)
- f = cast(TypeFunction)f.copy();
- f.isreturn = true;
- f.isreturnscope = cast(bool) (funcdecl.storage_class & STC.returnScope);
- if (funcdecl.storage_class & STC.returninferred)
- f.isreturninferred = true;
- }
- }
-
- if (!funcdecl.inferScope)
+ if (!funcdecl.scopeInprocess)
return;
- funcdecl.inferScope = false;
+ funcdecl.scopeInprocess = false;
- // Eliminate maybescope's
+ if (funcdecl.storage_class & STC.return_)
{
- // Create and fill array[] with maybe candidates from the `this` and the parameters
- VarDeclaration[10] tmp = void;
- size_t dim = (funcdecl.vthis !is null) + (funcdecl.parameters ? funcdecl.parameters.length : 0);
-
- import dmd.common.smallbuffer : SmallBuffer;
- auto sb = SmallBuffer!VarDeclaration(dim, tmp[]);
- VarDeclaration[] array = sb[];
-
- size_t n = 0;
- if (funcdecl.vthis)
- array[n++] = funcdecl.vthis;
- if (funcdecl.parameters)
- {
- foreach (v; *funcdecl.parameters)
- {
- array[n++] = v;
- }
- }
- eliminateMaybeScopes(array[0 .. n]);
+ if (funcdecl.type == f)
+ f = cast(TypeFunction)f.copy();
+ f.isreturn = true;
+ f.isreturnscope = cast(bool) (funcdecl.storage_class & STC.returnScope);
+ if (funcdecl.storage_class & STC.returninferred)
+ f.isreturninferred = true;
}
+
// Infer STC.scope_
if (funcdecl.parameters && !funcdecl.errors)
{
@@ -2370,61 +2067,6 @@ void finishScopeParamInference(FuncDeclaration funcdecl, ref TypeFunction f)
}
}
-/**********************************************
- * Have some variables that are maybescopes that were
- * assigned values from other maybescope variables.
- * Now that semantic analysis of the function is
- * complete, we can finalize this by turning off
- * maybescope for array elements that cannot be scope.
- *
- * $(TABLE2 Scope Table,
- * $(THEAD `va`, `v`, =>, `va` , `v` )
- * $(TROW maybe, maybe, =>, scope, scope)
- * $(TROW scope, scope, =>, scope, scope)
- * $(TROW scope, maybe, =>, scope, scope)
- * $(TROW maybe, scope, =>, scope, scope)
- * $(TROW - , - , =>, - , - )
- * $(TROW - , maybe, =>, - , - )
- * $(TROW - , scope, =>, error, error)
- * $(TROW maybe, - , =>, scope, - )
- * $(TROW scope, - , =>, scope, - )
- * )
- * Params:
- * array = array of variables that were assigned to from maybescope variables
- */
-private void eliminateMaybeScopes(VarDeclaration[] array)
-{
- enum log = false;
- if (log) printf("eliminateMaybeScopes()\n");
- bool changes;
- do
- {
- changes = false;
- foreach (va; array)
- {
- if (log) printf(" va = %s\n", va.toChars());
- if (!(va.maybeScope || va.isScope()))
- {
- if (va.maybes)
- {
- foreach (v; *va.maybes)
- {
- if (log) printf(" v = %s\n", v.toChars());
- if (v.maybeScope)
- {
- // v cannot be scope since it is assigned to a non-scope va
- notMaybeScope(v, va);
- if (!v.isReference())
- v.storage_class &= ~(STC.return_ | STC.returninferred);
- changes = true;
- }
- }
- }
- }
- }
- } while (changes);
-}
-
/************************************************
* Is type a reference to a mutable value?
*
@@ -2552,24 +2194,6 @@ private EnclosedBy enclosesLifetimeOf(VarDeclaration va, VarDeclaration v)
return EnclosedBy.none;
}
-/***************************************
- * Add variable `v` to maybes[]
- *
- * When a maybescope variable `v` is assigned to a maybescope variable `va`,
- * we cannot determine if `this` is actually scope until the semantic
- * analysis for the function is completed. Thus, we save the data
- * until then.
- * Params:
- * v = a variable with `maybeScope == true` that was assigned to `this`
- */
-private void addMaybe(VarDeclaration va, VarDeclaration v)
-{
- //printf("add %s to %s's list of dependencies\n", v.toChars(), toChars());
- if (!va.maybes)
- va.maybes = new VarDeclarations();
- va.maybes.push(v);
-}
-
// `setUnsafePreview` partially evaluated for dip1000
public
bool setUnsafeDIP1000(ref Scope sc, bool gag, Loc loc, const(char)* msg,
@@ -2599,7 +2223,7 @@ private bool checkScopeVarAddr(VarDeclaration v, Expression e, ref Scope sc, boo
if (!v.isScope())
{
- notMaybeScope(v, e);
+ doNotInferScope(v, e);
return false;
}
@@ -33,6 +33,7 @@ import dmd.dclass;
import dmd.dcast;
import dmd.delegatize;
import dmd.denum;
+import dmd.deps;
import dmd.dimport;
import dmd.dinterpret;
import dmd.dmangle;
@@ -547,7 +548,7 @@ private Expression extractOpDollarSideEffect(Scope* sc, UnaExp ue)
// https://issues.dlang.org/show_bug.cgi?id=12585
// Extract the side effect part if ue.e1 is comma.
- if ((sc.flags & SCOPE.ctfe) ? hasSideEffect(e1) : !isTrivialExp(e1)) // match logic in extractSideEffect()
+ if (sc.ctfe ? hasSideEffect(e1) : !isTrivialExp(e1)) // match logic in extractSideEffect()
{
/* Even if opDollar is needed, 'e1' should be evaluate only once. So
* Rewrite:
@@ -948,7 +949,7 @@ private Expression searchUFCS(Scope* sc, UnaExp ue, Identifier ident)
SearchOptFlags flags = SearchOpt.all;
Dsymbol s;
- if (sc.flags & SCOPE.ignoresymbolvisibility)
+ if (sc.ignoresymbolvisibility)
flags |= SearchOpt.ignoreVisibility;
// First look in local scopes
@@ -965,8 +966,7 @@ private Expression searchUFCS(Scope* sc, UnaExp ue, Identifier ident)
FuncDeclaration f = s.isFuncDeclaration();
if (f)
{
- TemplateDeclaration td = getFuncTemplateDecl(f);
- if (td)
+ if (TemplateDeclaration td = getFuncTemplateDecl(f))
{
if (td.overroot)
td = td.overroot;
@@ -1351,7 +1351,7 @@ private Expression resolveUFCSProperties(Scope* sc, Expression e1, Expression e2
e = new CallExp(loc, e, arguments);
// https://issues.dlang.org/show_bug.cgi?id=24017
- if (sc.flags & SCOPE.debug_)
+ if (sc.debug_)
e.isCallExp().inDebugStatement = true;
e = e.expressionSemantic(sc);
@@ -1865,7 +1865,7 @@ private bool checkPurity(FuncDeclaration f, const ref Loc loc, Scope* sc)
return false;
if (sc.intypeof == 1)
return false;
- if (sc.flags & (SCOPE.ctfe | SCOPE.debug_))
+ if (sc.ctfe || sc.debug_)
return false;
// If the call has a pure parent, then the called func must be pure.
@@ -1974,7 +1974,7 @@ private bool checkPurity(VarDeclaration v, const ref Loc loc, Scope* sc)
return false;
if (sc.intypeof == 1)
return false; // allow violations inside typeof(expression)
- if (sc.flags & (SCOPE.ctfe | SCOPE.debug_))
+ if (sc.ctfe || sc.debug_)
return false; // allow violations inside compile-time evaluated expressions and debug conditionals
if (v.ident == Id.ctfe)
return false; // magic variable never violates pure and safe
@@ -2104,9 +2104,9 @@ private bool checkSafety(FuncDeclaration f, ref Loc loc, Scope* sc)
return false;
if (sc.intypeof == 1)
return false;
- if (sc.flags & SCOPE.debug_)
+ if (sc.debug_)
return false;
- if ((sc.flags & SCOPE.ctfe) && sc.func)
+ if (sc.ctfe && sc.func)
return false;
if (!sc.func)
@@ -2179,7 +2179,7 @@ private bool checkNogc(FuncDeclaration f, ref Loc loc, Scope* sc)
return false;
if (sc.intypeof == 1)
return false;
- if (sc.flags & (SCOPE.ctfe | SCOPE.debug_))
+ if (sc.ctfe || sc.debug_)
return false;
/* The original expressions (`new S(...)` or `new S[...]``) will be
* verified instead. This is to keep errors related to the original code
@@ -2391,7 +2391,7 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 =
tthis = dve.e1.type;
goto Lfd;
}
- else if (sc && sc.flags & SCOPE.Cfile && e1.isVarExp() && !e2)
+ else if (sc && sc.inCfile && e1.isVarExp() && !e2)
{
// ImportC: do not implicitly call function if no ( ) are present
}
@@ -3724,7 +3724,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
if (!e.type)
e.type = Type.tfloat64;
- else if (e.type.isimaginary && sc.flags & SCOPE.Cfile)
+ else if (e.type.isimaginary && sc.inCfile)
{
/* Convert to core.stdc.config.complex
*/
@@ -3861,8 +3861,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
if (global.params.fixAliasThis)
{
- ExpressionDsymbol expDsym = scopesym.isExpressionDsymbol();
- if (expDsym)
+ if (ExpressionDsymbol expDsym = scopesym.isExpressionDsymbol())
{
//printf("expDsym = %s\n", expDsym.exp.toChars());
result = expDsym.exp.expressionSemantic(sc);
@@ -3906,7 +3905,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
if (exp.ident == Id.ctfe)
{
- if (sc.flags & SCOPE.ctfe)
+ if (sc.ctfe)
{
error(exp.loc, "variable `__ctfe` cannot be read at compile time");
return setError();
@@ -4290,7 +4289,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
}
buffer.write4(0);
e.setData(buffer.extractData(), newlen, 4);
- if (sc && sc.flags & SCOPE.Cfile)
+ if (sc && sc.inCfile)
e.type = Type.tuns32.sarrayOf(e.len + 1);
else
e.type = Type.tdchar.immutableOf().arrayOf();
@@ -4315,7 +4314,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
}
buffer.writeUTF16(0);
e.setData(buffer.extractData(), newlen, 2);
- if (sc && sc.flags & SCOPE.Cfile)
+ if (sc && sc.inCfile)
e.type = Type.tuns16.sarrayOf(e.len + 1);
else
e.type = Type.twchar.immutableOf().arrayOf();
@@ -4327,7 +4326,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
goto default;
default:
- if (sc && sc.flags & SCOPE.Cfile)
+ if (sc && sc.inCfile)
e.type = Type.tchar.sarrayOf(e.len + 1);
else
e.type = Type.tchar.immutableOf().arrayOf();
@@ -4526,7 +4525,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
}
Type t = cle.type.typeSemantic(cle.loc, sc);
auto init = initializerSemantic(cle.initializer, sc, t, INITnointerpret);
- auto e = initializerToExpression(init, t, (sc.flags & SCOPE.Cfile) != 0);
+ auto e = initializerToExpression(init, t, sc.inCfile);
if (!e)
{
error(cle.loc, "cannot convert initializer `%s` to expression", toChars(init));
@@ -5099,7 +5098,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
return;
}
else if (sc.needsCodegen() && // interpreter doesn't need this lowered
- !exp.onstack && !exp.type.isscope()) // these won't use the GC
+ !exp.onstack && !exp.type.isScopeClass()) // these won't use the GC
{
/* replace `new T(arguments)` with `core.lifetime._d_newclassT!T(arguments)`
* or `_d_newclassTTrace`
@@ -5427,7 +5426,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
Expression d = new DeclarationExp(e.loc, e.cd);
sc = sc.push(); // just create new scope
- sc.flags &= ~SCOPE.ctfe; // temporary stop CTFE
+ sc.ctfe = false; // temporary stop CTFE
d = d.expressionSemantic(sc);
sc = sc.pop();
@@ -5595,7 +5594,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
uint olderrors;
sc = sc.push(); // just create new scope
- sc.flags &= ~SCOPE.ctfe; // temporary stop CTFE
+ sc.ctfe = false; // temporary stop CTFE
sc.visibility = Visibility(Visibility.Kind.public_); // https://issues.dlang.org/show_bug.cgi?id=12506
/* fd.treq might be incomplete type,
@@ -5822,7 +5821,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
return;
}
}
- if (sc.flags & SCOPE.Cfile)
+ if (sc.inCfile)
{
/* See if need to rewrite the AST because of cast/call ambiguity
*/
@@ -6005,7 +6004,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
// Rewrite (*fp)(arguments) to fp(arguments)
exp.e1 = (cast(PtrExp)exp.e1).e1;
}
- else if (exp.e1.op == EXP.type && (sc && sc.flags & SCOPE.Cfile))
+ else if (exp.e1.op == EXP.type && (sc && sc.inCfile))
{
const numArgs = exp.arguments ? exp.arguments.length : 0;
@@ -6622,7 +6621,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
if (exp.f.checkNestedReference(sc, exp.loc))
return setError();
}
- else if (sc.func && sc.intypeof != 1 && !(sc.flags & (SCOPE.ctfe | SCOPE.debug_)))
+ else if (sc.func && sc.intypeof != 1 && !(sc.ctfe || sc.debug_))
{
bool err = false;
if (!tf.purity && sc.func.setImpure(exp.loc, "`pure` %s `%s` cannot call impure `%s`", exp.e1))
@@ -6886,7 +6885,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
VarDeclaration v = s.isVarDeclaration();
if (v)
{
- if (sc.flags & SCOPE.Cfile)
+ if (sc.inCfile)
{
/* Do semantic() on the type before inserting v into the symbol table
*/
@@ -6920,7 +6919,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
return setError();
}
- if (v && (sc.flags & SCOPE.Cfile))
+ if (v && sc.inCfile)
{
/* Do semantic() on initializer last so this will be legal:
* int a = a;
@@ -6958,7 +6957,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
// The mangling change only works for D mangling
}
- if (!(sc.flags & SCOPE.Cfile))
+ if (!sc.inCfile)
{
/* https://issues.dlang.org/show_bug.cgi?id=21272
* If we are in a foreach body we need to extract the
@@ -7096,7 +7095,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
// 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.flags & SCOPE.compile)
+ if (sc.traitsCompiles)
{
genObjCode = false;
}
@@ -7179,7 +7178,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("IsExp::semantic(%s)\n", e.toChars());
}
- if (e.id && !(sc.flags & SCOPE.condition))
+ if (e.id && !sc.condition)
{
error(e.loc, "can only declare type aliases within `static if` conditionals or `static assert`s");
return setError();
@@ -7208,7 +7207,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
Scope* sc2 = sc.copy(); // keep sc.flags
sc2.tinst = null;
sc2.minst = null;
- sc2.flags |= SCOPE.fullinst;
+ sc2.fullinst = true;
Type t = dmd.typesem.trySemantic(e.targ, e.loc, sc2);
sc2.pop();
if (!t) // errors, so condition is false
@@ -7723,29 +7722,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
const slice = se.peekString();
message("file %.*s\t(%s)", cast(int)slice.length, slice.ptr, resolvedNamez.ptr);
}
- if (global.params.moduleDeps.buffer !is null)
- {
- OutBuffer* ob = global.params.moduleDeps.buffer;
- Module imod = sc._module;
- if (!global.params.moduleDeps.name)
- ob.writestring("depsFile ");
- ob.writestring(imod.toPrettyChars());
- ob.writestring(" (");
- escapePath(ob, imod.srcfile.toChars());
- ob.writestring(") : ");
- if (global.params.moduleDeps.name)
- ob.writestring("string : ");
- ob.write(se.peekString());
- ob.writestring(" (");
- escapePath(ob, resolvedNamez.ptr);
- ob.writestring(")");
- ob.writenl();
- }
- if (global.params.makeDeps.doOutput)
- {
- global.params.makeDeps.files.push(resolvedNamez.ptr);
- }
+ addImportExpDep(global.params.moduleDeps, global.params.makeDeps, resolvedNamez, se.peekString(), sc._module);
{
auto fileName = FileName(resolvedNamez);
@@ -8110,7 +8088,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
//printf("e1.op = %d, '%s'\n", e1.op, Token.toChars(e1.op));
}
- if (sc.flags & SCOPE.Cfile)
+ if (sc.inCfile)
{
/* See if need to rewrite the AST because of cast/call ambiguity
*/
@@ -8503,7 +8481,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
return;
}
- if (sc.flags & SCOPE.Cfile)
+ if (sc.inCfile)
{
/* Special handling for &"string"/&(T[]){0, 1}
* since C regards string/array literals as lvalues
@@ -8743,7 +8721,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
result = e;
return;
}
- if (sc.func && !sc.intypeof && !(sc.flags & SCOPE.debug_))
+ if (sc.func && !sc.intypeof && !sc.debug_)
{
sc.setUnsafe(false, exp.loc,
"`this` reference necessary to take address of member `%s` in `@safe` function `%s`",
@@ -8834,7 +8812,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
goto case Terror;
}
- if (sc.flags & SCOPE.Cfile && exp.type && exp.type.toBasetype().ty == Tvoid)
+ if (sc.inCfile && exp.type && exp.type.toBasetype().ty == Tvoid)
{
// https://issues.dlang.org/show_bug.cgi?id=23752
// `&*((void*)(0))` is allowed in C
@@ -9005,7 +8983,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
if (checkNonAssignmentArrayOp(e.e1))
return setError();
- e.type = (sc && sc.flags & SCOPE.Cfile) ? Type.tint32 : Type.tbool;
+ e.type = (sc && sc.inCfile) ? Type.tint32 : Type.tbool;
result = e;
}
@@ -9084,7 +9062,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
return;
}
- if ((sc && sc.flags & SCOPE.Cfile) &&
+ if ((sc && sc.inCfile) &&
exp.to && (exp.to.ty == Tident || exp.to.ty == Tsarray) &&
(exp.e1.op == EXP.address || exp.e1.op == EXP.star ||
exp.e1.op == EXP.uadd || exp.e1.op == EXP.negate))
@@ -9338,7 +9316,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
}
}
- if (sc && sc.flags & SCOPE.Cfile)
+ if (sc && sc.inCfile)
{
/* C11 6.5.4-5: A cast does not yield an lvalue.
* So ensure that castTo does not strip away the cast so that this
@@ -9744,7 +9722,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
}
assert(!exp.type);
- if (sc.flags & SCOPE.Cfile)
+ if (sc.inCfile)
{
/* See if need to rewrite the AST because of cast/call ambiguity
*/
@@ -9842,7 +9820,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
e.type = e.e2.type;
result = e;
- if (sc.flags & SCOPE.Cfile)
+ if (sc.inCfile)
return;
if (e.type is Type.tvoid)
@@ -10141,7 +10119,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
// OR it in, because it might already be set for C array indexing
exp.indexIsInBounds |= bounds.contains(getIntRange(exp.e2));
}
- else if (sc.flags & SCOPE.Cfile && t1b.ty == Tsarray)
+ else if (sc.inCfile && t1b.ty == Tsarray)
{
if (auto ve = exp.e1.isVarExp())
{
@@ -10172,7 +10150,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
return;
}
- if (sc.flags & SCOPE.Cfile)
+ if (sc.inCfile)
{
/* See if need to rewrite the AST because of cast/call ambiguity
*/
@@ -10348,7 +10326,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
if (auto e2comma = exp.e2.isCommaExp())
{
- if (!e2comma.isGenerated && !(sc.flags & SCOPE.Cfile))
+ if (!e2comma.isGenerated && !sc.inCfile)
error(exp.loc, "using the result of a comma expression is not allowed");
/* Rewrite to get rid of the comma from rvalue
@@ -10493,7 +10471,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
e1x = e;
}
- else if (sc.flags & SCOPE.Cfile && e1x.isDotIdExp())
+ else if (sc.inCfile && e1x.isDotIdExp())
{
auto die = e1x.isDotIdExp();
e1x = fieldLookup(die.e1, sc, die.ident, die.arrow);
@@ -11088,7 +11066,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
* string to match the size of e1.
*/
Type t2 = e2x.type.toBasetype();
- if (sc.flags & SCOPE.Cfile && e2x.isStringExp() && t2.isTypeSArray())
+ if (sc.inCfile && e2x.isStringExp() && t2.isTypeSArray())
{
uinteger_t dim1 = t1.isTypeSArray().dim.toInteger();
uinteger_t dim2 = t2.isTypeSArray().dim.toInteger();
@@ -13294,14 +13272,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
e1x = resolveProperties(sc, e1x);
e1x = e1x.toBoolean(sc);
- if (sc.flags & SCOPE.condition)
+ if (sc.condition)
{
/* If in static if, don't evaluate e2 if we don't have to.
*/
e1x = e1x.optimize(WANTvalue);
if (e1x.toBool().hasValue(exp.op == EXP.orOr))
{
- if (sc.flags & SCOPE.Cfile)
+ if (sc.inCfile)
result = new IntegerExp(exp.op == EXP.orOr);
else
result = IntegerExp.createBool(exp.op == EXP.orOr);
@@ -13349,7 +13327,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
if (e2x.type.ty == Tvoid)
exp.type = Type.tvoid;
else
- exp.type = (sc && sc.flags & SCOPE.Cfile) ? Type.tint32 : Type.tbool;
+ exp.type = (sc && sc.inCfile) ? Type.tint32 : Type.tbool;
exp.e1 = e1x;
exp.e2 = e2x;
@@ -13446,7 +13424,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
if (f1 || f2)
return setError();
- exp.type = (sc && sc.flags & SCOPE.Cfile) ? Type.tint32 : Type.tbool;
+ exp.type = (sc && sc.inCfile) ? Type.tint32 : Type.tbool;
// Special handling for array comparisons
Expression arrayLowering = null;
@@ -13741,7 +13719,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
if (f1 || f2)
return setError();
- exp.type = (sc && sc.flags & SCOPE.Cfile) ? Type.tint32 : Type.tbool;
+ exp.type = (sc && sc.inCfile) ? Type.tint32 : Type.tbool;
if (!isArrayComparison)
{
@@ -13957,7 +13935,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
// https://issues.dlang.org/show_bug.cgi?id=23767
// `cast(void*) 0` should be treated as `null` so the ternary expression
// gets the pointer type of the other branch
- if (sc.flags & SCOPE.Cfile)
+ if (sc.inCfile)
{
static void rewriteCNull(ref Expression e, ref Type t)
{
@@ -14273,7 +14251,7 @@ private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc)
if (Expression ex = unaSemantic(exp, sc))
return ex;
- if (!(sc.flags & SCOPE.Cfile) && exp.ident == Id._mangleof)
+ if (!sc.inCfile && exp.ident == Id._mangleof)
{
// symbol.mangleof
@@ -14409,7 +14387,7 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag)
//{ static int z; fflush(stdout); if (++z == 10) *(char*)0=0; }
- const cfile = (sc.flags & SCOPE.Cfile) != 0;
+ const cfile = sc.inCfile;
/* Special case: rewrite this.id and super.id
* to be classtype.id and baseclasstype.id
@@ -14464,13 +14442,13 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag)
*/
if (ie.sds.isModule() && ie.sds != sc._module)
flags |= SearchOpt.ignorePrivateImports;
- if (sc.flags & SCOPE.ignoresymbolvisibility)
+ if (sc.ignoresymbolvisibility)
flags |= SearchOpt.ignoreVisibility;
Dsymbol s = ie.sds.search(exp.loc, exp.ident, flags);
/* Check for visibility before resolving aliases because public
* aliases to private symbols are public.
*/
- if (s && !(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc._module, s))
+ if (s && !sc.ignoresymbolvisibility && !symbolIsVisible(sc._module, s))
{
s = null;
}
@@ -15128,7 +15106,7 @@ bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false)
if (global.params.noSharedAccess != FeatureState.enabled ||
!sc ||
sc.intypeof ||
- sc.flags & SCOPE.ctfe)
+ sc.ctfe)
{
return false;
}
@@ -15699,7 +15677,7 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action
Expression visitStructLiteral(StructLiteralExp _this)
{
- if (sc.flags & SCOPE.Cfile)
+ if (sc.inCfile)
return _this; // C struct literals are lvalues
else
return visit(_this);
@@ -15746,7 +15724,7 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action
auto e1 = _this.e1;
auto var = _this.var;
//printf("DotVarExp::toLvalue(%s)\n", toChars());
- if (sc && sc.flags & SCOPE.Cfile)
+ if (sc && sc.inCfile)
{
/* C11 6.5.2.3-3: A postfix expression followed by the '.' or '->' operator
* is an lvalue if the first expression is an lvalue.
@@ -15792,7 +15770,7 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action
Expression visitCast(CastExp _this)
{
- if (sc && sc.flags & SCOPE.Cfile)
+ if (sc && sc.inCfile)
{
/* C11 6.5.4-5: A cast does not yield an lvalue.
*/
@@ -16295,7 +16273,7 @@ bool checkAddressable(Expression e, Scope* sc)
continue;
case EXP.variable:
- if (sc.flags & SCOPE.Cfile)
+ if (sc.inCfile)
{
// C11 6.5.3.2: A variable that has its address taken cannot be
// stored in a register.
@@ -16597,7 +16575,7 @@ Expression getVarExp(EnumMember em, const ref Loc loc, Scope* sc)
return ErrorExp.get();
Expression e = new VarExp(loc, em);
e = e.expressionSemantic(sc);
- if (!(sc.flags & SCOPE.Cfile) && em.isCsymbol())
+ if (!sc.inCfile && em.isCsymbol())
{
/* C11 types them as int. But if in D file,
* type qualified names as the enum
@@ -16638,7 +16616,7 @@ Expression toBoolean(Expression exp, Scope* sc)
case EXP.construct:
case EXP.blit:
case EXP.loweredAssignExp:
- if (sc.flags & SCOPE.Cfile)
+ if (sc.inCfile)
return exp;
// Things like:
// if (a = b) ...
@@ -16777,7 +16755,7 @@ bool evalStaticCondition(Scope* sc, Expression original, Expression e, out bool
const uint nerrors = global.errors;
sc = sc.startCTFE();
- sc.flags |= SCOPE.condition;
+ sc.condition = true;
e = e.expressionSemantic(sc);
e = resolveProperties(sc, e);
@@ -119,9 +119,8 @@ private struct FUNCFLAG
bool safetyInprocess; /// working on determining safety
bool nothrowInprocess; /// working on determining nothrow
bool nogcInprocess; /// working on determining @nogc
- bool returnInprocess; /// working on inferring 'return' for parameters
+ bool scopeInprocess; /// infer `return` and `scope` for parameters
bool inlineScanned; /// function has been scanned for inline possibilities
- bool inferScope; /// infer 'scope' for parameters
bool hasCatches; /// function has try-catch statements
bool skipCodegen; /// do not generate code for this function.
bool printf; /// is a printf-like function
@@ -431,8 +430,7 @@ extern (C++) class FuncDeclaration : Declaration
{
//printf("FuncDeclaration::overloadInsert(s = %s) this = %s\n", s.toChars(), toChars());
assert(s != this);
- AliasDeclaration ad = s.isAliasDeclaration();
- if (ad)
+ if (AliasDeclaration ad = s.isAliasDeclaration())
{
if (overnext)
return overnext.overloadInsert(ad);
@@ -501,8 +499,7 @@ extern (C++) class FuncDeclaration : Declaration
while (f && f.overnext)
{
//printf("f.overnext = %p %s\n", f.overnext, f.overnext.toChars());
- TemplateDeclaration td = f.overnext.isTemplateDeclaration();
- if (td)
+ if (TemplateDeclaration td = f.overnext.isTemplateDeclaration())
return td;
f = f.overnext.isFuncDeclaration();
}
@@ -724,19 +721,16 @@ extern (C++) class FuncDeclaration : Declaration
if (!tf.isnogc)
nogcInprocess = true;
- if (!isVirtual() || this.isIntroducing())
- returnInprocess = true;
-
// Initialize for inferring STC.scope_
- inferScope = true;
+ scopeInprocess = true;
}
- extern (D) final uint flags()
+ extern (D) final uint saveFlags()
{
return bitFields;
}
- extern (D) final uint flags(uint f)
+ extern (D) final uint restoreFlags(uint f)
{
bitFields = f;
return bitFields;
@@ -1262,8 +1256,7 @@ extern (C++) class FuncDeclaration : Declaration
{
if (type)
{
- TypeFunction fdtype = type.isTypeFunction();
- if (fdtype) // Could also be TypeError
+ if (TypeFunction fdtype = type.isTypeFunction()) // Could also be TypeError
return fdtype.parameterList;
}
@@ -1677,7 +1670,7 @@ extern (C++) final class FuncLiteralDeclaration : FuncDeclaration
this.fes = fes;
// Always infer scope for function literals
// See https://issues.dlang.org/show_bug.cgi?id=20362
- this.inferScope = true;
+ this.scopeInprocess = true;
//printf("FuncLiteralDeclaration() id = '%s', type = '%s'\n", this.ident.toChars(), type.toChars());
}
@@ -1732,8 +1725,7 @@ extern (C++) final class FuncLiteralDeclaration : FuncDeclaration
{
if (parent)
{
- TemplateInstance ti = parent.isTemplateInstance();
- if (ti)
+ if (TemplateInstance ti = parent.isTemplateInstance())
return ti.tempdecl.toPrettyChars(QualifyTypes);
}
return Dsymbol.toPrettyChars(QualifyTypes);
@@ -214,11 +214,11 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl)
//printf("function storage_class = x%llx, sc.stc = x%llx, %x\n", storage_class, sc.stc, Declaration.isFinal());
- if (sc.flags & SCOPE.compile)
+ if (sc.traitsCompiles)
funcdecl.skipCodegen = true;
funcdecl._linkage = sc.linkage;
- if (sc.flags & SCOPE.Cfile && funcdecl.isFuncLiteralDeclaration())
+ if (sc.inCfile && funcdecl.isFuncLiteralDeclaration())
funcdecl._linkage = LINK.d; // so they are uniquely mangled
if (auto fld = funcdecl.isFuncLiteralDeclaration())
@@ -263,7 +263,7 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl)
return null;
}
- if (sc.flags & SCOPE.Cfile)
+ if (sc.inCfile)
{
/* C11 allows a function to be declared with a typedef, D does not.
*/
@@ -1047,8 +1047,7 @@ Ldone:
}
// If it's a member template
- ClassDeclaration cd = ti.tempdecl.isClassMember();
- if (cd)
+ if (ClassDeclaration cd = ti.tempdecl.isClassMember())
{
.error(funcdecl.loc, "%s `%s` cannot use template to add virtual function to class `%s`", funcdecl.kind, funcdecl.toPrettyChars, cd.toChars());
}
@@ -2182,7 +2181,7 @@ int getLevelAndCheck(FuncDeclaration fd, const ref Loc loc, Scope* sc, FuncDecla
if (level != fd.LevelError)
return level;
// Don't give error if in template constraint
- if (!(sc.flags & SCOPE.constraint))
+ if (!sc.inTemplateConstraint)
{
const(char)* xstatic = fd.isStatic() ? "`static` " : "";
// better diagnostics for static functions
@@ -2291,7 +2290,7 @@ bool checkNestedReference(FuncDeclaration fd, Scope* sc, const ref Loc loc)
if (!found)
{
//printf("\tadding sibling %s to %s\n", fdthis.toPrettyChars(), toPrettyChars());
- if (!sc.intypeof && !(sc.flags & SCOPE.compile))
+ if (!sc.intypeof && !sc.traitsCompiles)
{
fd.siblingCallers.push(fdthis);
fd.computedEscapingSiblings = false;
@@ -2777,7 +2776,7 @@ void modifyReturns(FuncLiteralDeclaration fld, Scope* sc, Type tret)
*/
bool isRootTraitsCompilesScope(Scope* sc)
{
- return (sc.flags & SCOPE.compile) && !(sc.func.flags & SCOPE.compile);
+ return (sc.traitsCompiles) && !sc.func.skipCodegen;
}
/**************************************
@@ -2801,7 +2800,7 @@ bool setUnsafe(Scope* sc,
if (sc.intypeof)
return false; // typeof(cast(int*)0) is safe
- if (sc.flags & SCOPE.debug_) // debug {} scopes are permissive
+ if (sc.debug_) // debug {} scopes are permissive
return false;
if (!sc.func)
@@ -248,6 +248,11 @@ extern (C++) struct Param
const(char)[] exefile;
const(char)[] mapfile;
+ // Time tracing
+ bool timeTrace = false; /// Whether profiling of compile time is enabled
+ uint timeTraceGranularityUs = 500; /// In microseconds, minimum event size to report
+ const(char)* timeTraceFile; /// File path of output file
+
///
bool parsingUnittestsRequired()
{
@@ -250,6 +250,9 @@ struct Param
DString resfile;
DString exefile;
DString mapfile;
+ bool timeTrace;
+ uint32_t timeTraceGranularityUs;
+ const char* timeTraceFile;
};
struct structalign_t
@@ -2682,10 +2682,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
void visitDotId(DotIdExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
- if (e.arrow)
- buf.writestring("->");
- else
- buf.writeByte('.');
+ buf.writeByte('.');
buf.writestring(e.ident.toString());
}
@@ -3927,7 +3924,7 @@ private void visitFuncIdentWithPrefix(TypeFunction t, const Identifier ident, Te
/* Use 'storage class' (prefix) style for attributes
*/
- if (t.mod)
+ if (t.mod && !(hgs.ddoc || hgs.hdrgen))
{
MODtoBuffer(buf, t.mod);
buf.writeByte(' ');
@@ -3977,6 +3974,12 @@ private void visitFuncIdentWithPrefix(TypeFunction t, const Identifier ident, Te
buf.writeByte(')');
}
parametersToBuffer(t.parameterList, buf, hgs);
+ // postfix this attributes are more readable
+ if (t.mod && (hgs.ddoc || hgs.hdrgen))
+ {
+ buf.writeByte(' ');
+ MODtoBuffer(buf, t.mod);
+ }
if (t.isreturnscope && !t.isreturninferred)
{
buf.writestring(" return scope");
@@ -4273,13 +4276,23 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs)
void visitTag(TypeTag t)
{
- if (t.mod & MODFlags.const_)
- buf.writestring("const ");
if (hgs.importcHdr && t.id)
{
+ // https://issues.dlang.org/show_bug.cgi?id=24670
+ // `const` must be parenthesized because it can be a return type
+ if (t.mod & MODFlags.const_)
+ buf.writestring("const(");
+
+ // For C to D translation, `struct S` or `enum S` simply becomes `S`
buf.writestring(t.id.toString());
+
+ if (t.mod & MODFlags.const_)
+ buf.writestring(")");
return;
}
+ // The following produces something like "const enum E : short"
+ if (t.mod & MODFlags.const_)
+ buf.writestring("const ");
buf.writestring(Token.toString(t.tok));
buf.writeByte(' ');
if (t.id)
@@ -41,7 +41,7 @@ import dmd.typesem;
*/
Type cAdjustParamType(Type t, Scope* sc)
{
- if (!(sc.flags & SCOPE.Cfile))
+ if (!sc.inCfile)
return t;
Type tb = t.toBasetype();
@@ -77,7 +77,7 @@ Type cAdjustParamType(Type t, Scope* sc)
Expression arrayFuncConv(Expression e, Scope* sc)
{
//printf("arrayFuncConv() %s\n", e.toChars());
- if (!(sc.flags & SCOPE.Cfile))
+ if (!sc.inCfile)
return e;
auto t = e.type.toBasetype();
@@ -121,7 +121,6 @@ Expression fieldLookup(Expression e, Scope* sc, Identifier id, bool arrow)
if (e.isErrorExp())
return e;
- Dsymbol s;
auto t = e.type;
if (t.isTypePointer())
{
@@ -131,6 +130,7 @@ Expression fieldLookup(Expression e, Scope* sc, Identifier id, bool arrow)
error(e.loc, "since `%s` is a pointer, use `%s->%s` instead of `%s.%s`", pe, pe, id.toChars(), pe, id.toChars());
e = new PtrExp(e.loc, e);
}
+ Dsymbol s;
if (auto ts = t.isTypeStruct())
s = ts.sym.search(e.loc, id, 0);
if (!s)
@@ -154,7 +154,7 @@ Expression fieldLookup(Expression e, Scope* sc, Identifier id, bool arrow)
*/
Expression carraySemantic(ArrayExp ae, Scope* sc)
{
- if (!(sc.flags & SCOPE.Cfile))
+ if (!sc.inCfile)
return null;
auto e1 = ae.e1.expressionSemantic(sc);
@@ -161,7 +161,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn
// Convert initializer to Expression `ex`
auto tm = fieldType.addMod(t.mod);
auto iz = i.value[j].initializerSemantic(sc, tm, needInterpret);
- auto ex = iz.initializerToExpression(null, (sc.flags & SCOPE.Cfile) != 0);
+ auto ex = iz.initializerToExpression(null, sc.inCfile);
if (ex.op != EXP.error)
i.value[j] = iz;
return ex;
@@ -305,7 +305,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn
}
if (auto tsa = t.isTypeSArray())
{
- if (sc.flags & SCOPE.Cfile && tsa.isIncomplete())
+ if (sc.inCfile && tsa.isIncomplete())
{
// Change to array of known length
auto tn = tsa.next.toBasetype();
@@ -369,7 +369,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn
// If the result will be implicitly cast, move the cast into CTFE
// to avoid premature truncation of polysemous types.
// eg real [] x = [1.1, 2.2]; should use real precision.
- if (i.exp.implicitConvTo(t) && !(sc.flags & SCOPE.Cfile))
+ if (i.exp.implicitConvTo(t) && !sc.inCfile)
{
i.exp = i.exp.implicitCastTo(sc, t);
}
@@ -377,7 +377,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn
{
return i;
}
- if (sc.flags & SCOPE.Cfile)
+ if (sc.inCfile)
{
/* the interpreter turns (char*)"string" into &"string"[0] which then
* it cannot interpret. Resolve that case by doing optimize() first
@@ -450,7 +450,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn
/* Lop off terminating 0 of initializer for:
* static char s[5] = "hello";
*/
- if (sc.flags & SCOPE.Cfile &&
+ if (sc.inCfile &&
typeb.ty == Tsarray &&
tynto.isSomeChar &&
tb.isTypeSArray().dim.toInteger() + 1 == typeb.isTypeSArray().dim.toInteger())
@@ -463,7 +463,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn
* Initialize an array of unknown size with a string.
* Change to static array of known size
*/
- if (sc.flags & SCOPE.Cfile && i.exp.isStringExp() &&
+ if (sc.inCfile && i.exp.isStringExp() &&
tb.isTypeSArray() && tb.isTypeSArray().isIncomplete())
{
StringExp se = i.exp.isStringExp();
@@ -549,7 +549,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn
{
i.exp = i.exp.implicitCastTo(sc, t);
}
- else if (sc.flags & SCOPE.Cfile && i.exp.isStringExp() &&
+ else if (sc.inCfile && i.exp.isStringExp() &&
tta && (tta.next.ty == Tint8 || tta.next.ty == Tuns8) &&
ti.ty == Tsarray && ti.nextOf().ty == Tchar)
{
@@ -499,7 +499,7 @@ class Lexer
clexerCharConstant(*t, c);
return;
}
- else if (p[1] == '\"') // C wide string literal
+ if (p[1] == '\"') // C wide string literal
{
const c = *p;
++p;
@@ -509,7 +509,7 @@ class Lexer
'd';
return;
}
- else if (p[1] == '8' && p[2] == '\"') // C UTF-8 string literal
+ if (p[1] == '8' && p[2] == '\"') // C UTF-8 string literal
{
p += 2;
escapeStringConstant(t);
@@ -542,14 +542,13 @@ class Lexer
delimitedStringConstant(t);
return;
}
- else if (p[1] == '{')
+ if (p[1] == '{')
{
p++;
tokenStringConstant(t);
return;
}
- else
- goto case_ident;
+ goto case_ident;
case 'i':
if (Ccompile)
goto case_ident;
@@ -559,20 +558,19 @@ class Lexer
escapeStringConstant(t, true);
return;
}
- else if (p[1] == '`')
+ if (p[1] == '`')
{
p++; // skip the i
wysiwygStringConstant(t, true);
return;
}
- else if (p[1] == 'q' && p[2] == '{')
+ if (p[1] == 'q' && p[2] == '{')
{
p += 2; // skip the i and q
tokenStringConstant(t, true);
return;
}
- else
- goto case_ident;
+ goto case_ident;
case '"':
escapeStringConstant(t);
return;
@@ -894,7 +892,7 @@ class Lexer
t.value = TOK.comment;
return;
}
- else if (doDocComment && t.ptr[2] == '*' && p - 4 != t.ptr)
+ if (doDocComment && t.ptr[2] == '*' && p - 4 != t.ptr)
{
// if /** but not /**/
getDocComment(t, lastLine == startLoc.linnum, startLoc.linnum - lastDocLine > 1);
@@ -21,7 +21,6 @@ import dmd.aggregate;
import dmd.arraytypes;
import dmd.astenums;
import dmd.ast_node;
-import dmd.dcast : implicitConvTo;
import dmd.dclass;
import dmd.declaration;
import dmd.denum;
@@ -58,6 +57,11 @@ static if (__VERSION__ < 2095)
private alias StringValueType = StringValue!Type;
}
+private auto X(T, U)(T m, U n)
+{
+ return (m << 4) | n;
+}
+
/***************************
* Return !=0 if modfrom can be implicitly converted to modto
*/
@@ -67,10 +71,6 @@ bool MODimplicitConv(MOD modfrom, MOD modto) pure nothrow @nogc @safe
return true;
//printf("MODimplicitConv(from = %x, to = %x)\n", modfrom, modto);
- auto X(T, U)(T m, U n)
- {
- return ((m << 4) | n);
- }
switch (X(modfrom & ~MODFlags.shared_, modto & ~MODFlags.shared_))
{
@@ -98,11 +98,6 @@ MATCH MODmethodConv(MOD modfrom, MOD modto) pure nothrow @nogc @safe
if (MODimplicitConv(modfrom, modto))
return MATCH.constant;
- auto X(T, U)(T m, U n)
- {
- return ((m << 4) | n);
- }
-
switch (X(modfrom, modto))
{
case X(0, MODFlags.wild):
@@ -682,7 +677,7 @@ extern (C++) abstract class Type : ASTNode
return false;
}
- bool isscope()
+ bool isScopeClass()
{
return false;
}
@@ -1282,24 +1277,6 @@ extern (C++) abstract class Type : ASTNode
return ((te = isTypeEnum()) !is null) ? te.toBasetype2() : this;
}
- /*******************************
- * Determine if converting 'this' to 'to' is an identity operation,
- * a conversion to const operation, or the types aren't the same.
- * Returns:
- * MATCH.exact 'this' == 'to'
- * MATCH.constant 'to' is const
- * MATCH.nomatch conversion to mutable or invariant
- */
- MATCH constConv(Type to)
- {
- //printf("Type::constConv(this = %s, to = %s)\n", toChars(), to.toChars());
- if (equals(to))
- return MATCH.exact;
- if (ty == to.ty && MODimplicitConv(mod, to.mod))
- return MATCH.constant;
- return MATCH.nomatch;
- }
-
/***************************************
* Compute MOD bits matching `this` argument type to wild parameter type.
* Params:
@@ -1362,12 +1339,6 @@ extern (C++) abstract class Type : ASTNode
return defaultInit(this, loc);
}
- // if initializer is 0
- bool isZeroInit(const ref Loc loc)
- {
- return false; // assume not
- }
-
/***************************************
* Return !=0 if the type or any of its subtypes is wild.
*/
@@ -1866,35 +1837,6 @@ extern (C++) abstract class TypeNext : Type
return t;
}
- override MATCH constConv(Type to)
- {
- //printf("TypeNext::constConv from = %s, to = %s\n", toChars(), to.toChars());
- if (equals(to))
- return MATCH.exact;
-
- if (!(ty == to.ty && MODimplicitConv(mod, to.mod)))
- return MATCH.nomatch;
-
- Type tn = to.nextOf();
- if (!(tn && next.ty == tn.ty))
- return MATCH.nomatch;
-
- MATCH m;
- if (to.isConst()) // whole tail const conversion
- {
- // Recursive shared level check
- m = next.constConv(tn);
- if (m == MATCH.exact)
- m = MATCH.constant;
- }
- else
- {
- //printf("\tnext => %s, to.next => %s\n", next.toChars(), tn.toChars());
- m = next.equals(tn) ? MATCH.constant : MATCH.nomatch;
- }
- return m;
- }
-
override final MOD deduceWild(Type t, bool isRef)
{
if (ty == Tfunction)
@@ -2125,28 +2067,6 @@ extern (C++) final class TypeBasic : Type
return (flags & TFlags.unsigned) != 0;
}
- override bool isZeroInit(const ref Loc loc)
- {
- switch (ty)
- {
- case Tchar:
- case Twchar:
- case Tdchar:
- case Timaginary32:
- case Timaginary64:
- case Timaginary80:
- case Tfloat32:
- case Tfloat64:
- case Tfloat80:
- case Tcomplex32:
- case Tcomplex64:
- case Tcomplex80:
- return false; // no
- default:
- return true; // yes
- }
- }
-
override bool hasUnsafeBitpatterns()
{
return ty == Tbool;
@@ -2246,11 +2166,6 @@ extern (C++) final class TypeVector : Type
return tb;
}
- override bool isZeroInit(const ref Loc loc)
- {
- return basetype.isZeroInit(loc);
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -2327,26 +2242,11 @@ extern (C++) final class TypeSArray : TypeArray
return nty.isSomeChar;
}
- override bool isZeroInit(const ref Loc loc)
- {
- return next.isZeroInit(loc);
- }
-
override structalign_t alignment()
{
return next.alignment();
}
- override MATCH constConv(Type to)
- {
- if (auto tsa = to.isTypeSArray())
- {
- if (!dim.equals(tsa.dim))
- return MATCH.nomatch;
- }
- return TypeNext.constConv(to);
- }
-
override Expression defaultInitLiteral(const ref Loc loc)
{
static if (LOGDEFAULTINIT)
@@ -2445,11 +2345,6 @@ extern (C++) final class TypeDArray : TypeArray
return nty.isSomeChar;
}
- override bool isZeroInit(const ref Loc loc)
- {
- return true;
- }
-
override bool isBoolean()
{
return true;
@@ -2496,28 +2391,11 @@ extern (C++) final class TypeAArray : TypeArray
return result;
}
- override bool isZeroInit(const ref Loc loc)
- {
- return true;
- }
-
override bool isBoolean()
{
return true;
}
- override MATCH constConv(Type to)
- {
- if (auto taa = to.isTypeAArray())
- {
- MATCH mindex = index.constConv(taa.index);
- MATCH mkey = next.constConv(taa.next);
- // Pick the worst match
- return mkey < mindex ? mkey : mindex;
- }
- return Type.constConv(to);
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -2554,28 +2432,11 @@ extern (C++) final class TypePointer : TypeNext
return result;
}
- override MATCH constConv(Type to)
- {
- if (next.ty == Tfunction)
- {
- if (to.nextOf() && next.equals((cast(TypeNext)to).next))
- return Type.constConv(to);
- else
- return MATCH.nomatch;
- }
- return TypeNext.constConv(to);
- }
-
override bool isscalar()
{
return true;
}
- override bool isZeroInit(const ref Loc loc)
- {
- return true;
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -2608,11 +2469,6 @@ extern (C++) final class TypeReference : TypeNext
return result;
}
- override bool isZeroInit(const ref Loc loc)
- {
- return true;
- }
-
override void accept(Visitor v)
{
v.visit(this);
@@ -2868,16 +2724,6 @@ extern (C++) final class TypeFunction : TypeNext
return newArgs;
}
- /** Extends TypeNext.constConv by also checking for matching attributes **/
- override MATCH constConv(Type to)
- {
- // Attributes need to match exactly, otherwise it's an implicit conversion
- if (this.ty != to.ty || !this.attributesEqual(cast(TypeFunction) to))
- return MATCH.nomatch;
-
- return super.constConv(to);
- }
-
extern (D) bool checkRetType(const ref Loc loc)
{
Type tb = next.toBasetype();
@@ -2994,11 +2840,6 @@ extern (C++) final class TypeDelegate : TypeNext
return target.ptrsize;
}
- override bool isZeroInit(const ref Loc loc)
- {
- return true;
- }
-
override bool isBoolean()
{
return true;
@@ -3392,13 +3233,6 @@ extern (C++) final class TypeStruct : Type
return structinit;
}
- override bool isZeroInit(const ref Loc loc)
- {
- // Determine zeroInit here, as this can be called before semantic2
- sym.determineSize(sym.loc);
- return sym.zeroInit;
- }
-
override bool isAssignable()
{
bool assignable = true;
@@ -3493,77 +3327,6 @@ extern (C++) final class TypeStruct : Type
return sym.hasInvariant() || sym.hasFieldWithInvariant;
}
- extern (D) MATCH implicitConvToWithoutAliasThis(Type to)
- {
- //printf("TypeStruct::implicitConvToWithoutAliasThis(%s => %s)\n", toChars(), to.toChars());
-
- auto tos = to.isTypeStruct();
- if (!(tos && sym == tos.sym))
- return MATCH.nomatch;
-
- if (mod == to.mod)
- return MATCH.exact;
-
- if (MODimplicitConv(mod, to.mod))
- return MATCH.constant;
-
- /* Check all the fields. If they can all be converted,
- * allow the conversion.
- */
- MATCH m = MATCH.constant;
- uint offset = ~0; // must never match a field offset
- foreach (v; sym.fields[])
- {
- /* Why are we only looking at the first member of a union?
- * The check should check for overlap of v with the previous field,
- * not just starting at the same point
- */
- if (!global.params.fixImmutableConv && v.offset == offset) // v is at same offset as previous field
- continue; // ignore
-
- Type tvf = v.type.addMod(mod); // from type
- Type tvt = v.type.addMod(to.mod); // to type
-
- // field match
- MATCH mf = tvf.implicitConvTo(tvt);
- //printf("\t%s => %s, match = %d\n", v.type.toChars(), tvt.toChars(), mf);
-
- if (mf == MATCH.nomatch)
- return MATCH.nomatch;
- if (mf < m) // if field match is worse
- m = mf;
- offset = v.offset;
- }
- return m;
- }
-
- extern (D) MATCH implicitConvToThroughAliasThis(Type to)
- {
- auto tos = to.isTypeStruct();
- if (!(tos && sym == tos.sym) &&
- sym.aliasthis &&
- !(att & AliasThisRec.tracing))
- {
- if (auto ato = aliasthisOf(this))
- {
- att = cast(AliasThisRec)(att | AliasThisRec.tracing);
- MATCH m = ato.implicitConvTo(to);
- att = cast(AliasThisRec)(att & ~AliasThisRec.tracing);
- return m;
- }
- }
- return MATCH.nomatch;
- }
-
- override MATCH constConv(Type to)
- {
- if (equals(to))
- return MATCH.exact;
- if (ty == to.ty && sym == (cast(TypeStruct)to).sym && MODimplicitConv(mod, to.mod))
- return MATCH.constant;
- return MATCH.nomatch;
- }
-
override MOD deduceWild(Type t, bool isRef)
{
if (ty == t.ty && sym == (cast(TypeStruct)t).sym)
@@ -3690,15 +3453,6 @@ extern (C++) final class TypeEnum : Type
return memType().needsNested();
}
- override MATCH constConv(Type to)
- {
- if (equals(to))
- return MATCH.exact;
- if (ty == to.ty && sym == (cast(TypeEnum)to).sym && MODimplicitConv(mod, to.mod))
- return MATCH.constant;
- return MATCH.nomatch;
- }
-
extern (D) Type toBasetype2()
{
if (!sym.members && !sym.memtype)
@@ -3707,11 +3461,6 @@ extern (C++) final class TypeEnum : Type
return tb.castMod(mod); // retain modifier bits from 'this'
}
- override bool isZeroInit(const ref Loc loc)
- {
- return sym.getDefaultValue(loc).toBool().hasValue(false);
- }
-
override bool hasVoidInitPointers()
{
return memType().hasVoidInitPointers();
@@ -3767,58 +3516,6 @@ extern (C++) final class TypeClass : Type
return sym;
}
- extern (D) MATCH implicitConvToWithoutAliasThis(Type to)
- {
- ClassDeclaration cdto = to.isClassHandle();
- MATCH m = constConv(to);
- if (m > MATCH.nomatch)
- return m;
-
- if (cdto && cdto.isBaseOf(sym, null) && MODimplicitConv(mod, to.mod))
- {
- //printf("'to' is base\n");
- return MATCH.convert;
- }
- return MATCH.nomatch;
- }
-
- extern (D) MATCH implicitConvToThroughAliasThis(Type to)
- {
- MATCH m;
- if (sym.aliasthis && !(att & AliasThisRec.tracing))
- {
- if (auto ato = aliasthisOf(this))
- {
- att = cast(AliasThisRec)(att | AliasThisRec.tracing);
- m = ato.implicitConvTo(to);
- att = cast(AliasThisRec)(att & ~AliasThisRec.tracing);
- }
- }
- return m;
- }
-
- override MATCH constConv(Type to)
- {
- if (equals(to))
- return MATCH.exact;
- if (ty == to.ty && sym == (cast(TypeClass)to).sym && MODimplicitConv(mod, to.mod))
- return MATCH.constant;
-
- /* Conversion derived to const(base)
- */
- int offset = 0;
- if (to.isBaseOf(this, &offset) && offset == 0 && MODimplicitConv(mod, to.mod))
- {
- // Disallow:
- // derived to base
- // inout(derived) to inout(base)
- if (!to.isMutable() && !to.isWild())
- return MATCH.convert;
- }
-
- return MATCH.nomatch;
- }
-
override MOD deduceWild(Type t, bool isRef)
{
ClassDeclaration cd = t.isClassHandle();
@@ -3840,12 +3537,7 @@ extern (C++) final class TypeClass : Type
return wm;
}
- override bool isZeroInit(const ref Loc loc)
- {
- return true;
- }
-
- override bool isscope()
+ override bool isScopeClass()
{
return sym.stack;
}
@@ -4088,12 +3780,6 @@ extern (C++) final class TypeNoreturn : Type
return this;
}
- override MATCH constConv(Type to)
- {
- // Either another noreturn or conversion to any type
- return this.implicitConvTo(to);
- }
-
override bool isBoolean()
{
return true; // bottom type can be implicitly converted to any other type
@@ -239,7 +239,7 @@ public:
virtual bool iscomplex();
virtual bool isscalar();
virtual bool isunsigned();
- virtual bool isscope();
+ virtual bool isScopeClass();
virtual bool isString();
virtual bool isAssignable();
virtual bool isBoolean();
@@ -264,13 +264,11 @@ public:
virtual Type *makeSharedWildConst();
virtual Type *makeMutable();
Type *toBasetype();
- virtual MATCH constConv(Type *to);
virtual unsigned char deduceWild(Type *t, bool isRef);
virtual ClassDeclaration *isClassHandle();
virtual structalign_t alignment();
virtual Expression *defaultInitLiteral(const Loc &loc);
- virtual bool isZeroInit(const Loc &loc = Loc()); // if initializer is 0
virtual int hasWild() const;
virtual bool hasVoidInitPointers();
virtual bool hasUnsafeBitpatterns();
@@ -340,7 +338,6 @@ public:
Type *makeSharedWild() override final;
Type *makeSharedWildConst() override final;
Type *makeMutable() override final;
- MATCH constConv(Type *to) override;
unsigned char deduceWild(Type *t, bool isRef) override final;
void transitive();
void accept(Visitor *v) override { v->visit(this); }
@@ -362,7 +359,6 @@ public:
bool iscomplex() override;
bool isscalar() override;
bool isunsigned() override;
- bool isZeroInit(const Loc &loc) override;
// For eliminating dynamic_cast
TypeBasic *isTypeBasic() override;
@@ -385,7 +381,6 @@ public:
bool isBoolean() override;
Expression *defaultInitLiteral(const Loc &loc) override;
TypeBasic *elementType();
- bool isZeroInit(const Loc &loc) override;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -407,9 +402,7 @@ public:
bool isIncomplete();
unsigned alignsize() override;
bool isString() override;
- bool isZeroInit(const Loc &loc) override;
structalign_t alignment() override;
- MATCH constConv(Type *to) override;
Expression *defaultInitLiteral(const Loc &loc) override;
bool hasUnsafeBitpatterns() override;
bool hasVoidInitPointers() override;
@@ -429,7 +422,6 @@ public:
TypeDArray *syntaxCopy() override;
unsigned alignsize() override;
bool isString() override;
- bool isZeroInit(const Loc &loc) override;
bool isBoolean() override;
void accept(Visitor *v) override { v->visit(this); }
@@ -444,9 +436,7 @@ public:
static TypeAArray *create(Type *t, Type *index);
const char *kind() override;
TypeAArray *syntaxCopy() override;
- bool isZeroInit(const Loc &loc) override;
bool isBoolean() override;
- MATCH constConv(Type *to) override;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -457,9 +447,7 @@ public:
static TypePointer *create(Type *t);
const char *kind() override;
TypePointer *syntaxCopy() override;
- MATCH constConv(Type *to) override;
bool isscalar() override;
- bool isZeroInit(const Loc &loc) override;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -469,7 +457,6 @@ class TypeReference final : public TypeNext
public:
const char *kind() override;
TypeReference *syntaxCopy() override;
- bool isZeroInit(const Loc &loc) override;
void accept(Visitor *v) override { v->visit(this); }
};
@@ -557,7 +544,6 @@ public:
bool hasLazyParameters();
bool isDstyleVariadic() const;
- MATCH constConv(Type *to) override;
bool isnothrow() const;
void isnothrow(bool v);
@@ -599,7 +585,6 @@ public:
const char *kind() override;
TypeDelegate *syntaxCopy() override;
unsigned alignsize() override;
- bool isZeroInit(const Loc &loc) override;
bool isBoolean() override;
void accept(Visitor *v) override { v->visit(this); }
@@ -709,7 +694,6 @@ public:
TypeStruct *syntaxCopy() override;
structalign_t alignment() override;
Expression *defaultInitLiteral(const Loc &loc) override;
- bool isZeroInit(const Loc &loc) override;
bool isAssignable() override;
bool isBoolean() override;
bool needsDestruction() override;
@@ -718,7 +702,6 @@ public:
bool hasVoidInitPointers() override;
bool hasUnsafeBitpatterns() override;
bool hasInvariant() override;
- MATCH constConv(Type *to) override;
unsigned char deduceWild(Type *t, bool isRef) override;
void accept(Visitor *v) override { v->visit(this); }
@@ -746,8 +729,6 @@ public:
bool needsDestruction() override;
bool needsCopyOrPostblit() override;
bool needsNested() override;
- MATCH constConv(Type *to) override;
- bool isZeroInit(const Loc &loc) override;
bool hasVoidInitPointers() override;
bool hasUnsafeBitpatterns() override;
bool hasInvariant() override;
@@ -766,10 +747,8 @@ public:
const char *kind() override;
TypeClass *syntaxCopy() override;
ClassDeclaration *isClassHandle() override;
- MATCH constConv(Type *to) override;
unsigned char deduceWild(Type *t, bool isRef) override;
- bool isZeroInit(const Loc &loc) override;
- bool isscope() override;
+ bool isScopeClass() override;
bool isBoolean() override;
void accept(Visitor *v) override { v->visit(this); }
@@ -820,7 +799,6 @@ class TypeNoreturn final : public Type
public:
const char *kind() override;
TypeNoreturn *syntaxCopy() override;
- MATCH constConv(Type* to) override;
bool isBoolean() override;
unsigned alignsize() override;
@@ -847,6 +825,7 @@ namespace dmd
bool equivalent(Type *src, Type *t);
Covariant covariant(Type *, Type *, StorageClass * = nullptr, bool = false);
bool isBaseOf(Type *tthis, Type *t, int *poffset);
+ bool isZeroInit(Type *t, const Loc &loc = Loc());
Type *trySemantic(Type *type, const Loc &loc, Scope *sc);
Type *pointerTo(Type *type);
Type *referenceTo(Type *type);
@@ -873,4 +852,5 @@ namespace dmd
uinteger_t size(Type *type);
uinteger_t size(Type *type, const Loc &loc);
MATCH implicitConvTo(Type* from, Type* to);
+ MATCH constConv(Type* from, Type* to);
}
@@ -201,7 +201,7 @@ public:
Expression checkGC(Scope* sc, Expression e)
{
- if (sc.flags & SCOPE.ctfeBlock) // ignore GC in ctfe blocks
+ if (sc.ctfeBlock) // ignore GC in ctfe blocks
return e;
/* If betterC, allow GC to happen in non-CTFE code.
@@ -211,10 +211,10 @@ Expression checkGC(Scope* sc, Expression e)
const betterC = !global.params.useGC;
FuncDeclaration f = sc.func;
if (e && e.op != EXP.error && f && sc.intypeof != 1 &&
- (!(sc.flags & SCOPE.ctfe) || betterC) &&
+ (!sc.ctfe || betterC) &&
(f.type.ty == Tfunction &&
(cast(TypeFunction)f.type).isnogc || f.nogcInprocess || global.params.v.gc) &&
- !(sc.flags & SCOPE.debug_))
+ !sc.debug_)
{
scope NOGCVisitor gcv = new NOGCVisitor(f);
gcv.checkOnly = betterC;
@@ -2001,7 +2001,7 @@ void escapeLive(Expression e, scope void delegate(VarDeclaration) onVar)
true,
);
- escapeByValue(e, er, true);
+ escapeByValue(e, er);
}
/***************************************
@@ -564,8 +564,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
{
//printf("CastExp::op_overload() (%s)\n", e.toChars());
Expression result;
- AggregateDeclaration ad = isAggregate(e.e1.type);
- if (ad)
+ if (AggregateDeclaration ad = isAggregate(e.e1.type))
{
Dsymbol fd = null;
/* Rewrite as:
@@ -1034,7 +1033,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof);
auto sc2 = sc.push();
- sc2.flags |= SCOPE.noaccesscheck;
+ sc2.noAccessCheck = true;
Expression r = e.expressionSemantic(sc2);
sc2.pop();
return r;
@@ -1412,8 +1411,7 @@ Expression build_overload(const ref Loc loc, Scope* sc, Expression ethis, Expres
{
assert(d);
Expression e;
- Declaration decl = d.isDeclaration();
- if (decl)
+ if (Declaration decl = d.isDeclaration())
e = new DotVarExp(loc, ethis, decl, false);
else
e = new DotIdExp(loc, ethis, d.ident);
@@ -1427,8 +1425,7 @@ Expression build_overload(const ref Loc loc, Scope* sc, Expression ethis, Expres
*/
Dsymbol search_function(ScopeDsymbol ad, Identifier funcid)
{
- Dsymbol s = ad.search(Loc.initial, funcid);
- if (s)
+ if (Dsymbol s = ad.search(Loc.initial, funcid))
{
//printf("search_function: s = '%s'\n", s.kind());
Dsymbol s2 = s.toAlias();
@@ -1436,8 +1433,7 @@ Dsymbol search_function(ScopeDsymbol ad, Identifier funcid)
FuncDeclaration fd = s2.isFuncDeclaration();
if (fd && fd.type.ty == Tfunction)
return fd;
- TemplateDeclaration td = s2.isTemplateDeclaration();
- if (td)
+ if (TemplateDeclaration td = s2.isTemplateDeclaration())
return td;
}
return null;
@@ -591,13 +591,13 @@ nothrow:
/***************************
* Free returned value with FileName::free()
*/
- extern (C++) static const(char)* defaultExt(const(char)* name, const(char)* ext)
+ extern (C++) static const(char)* defaultExt(const(char)* name, const(char)* ext) pure
{
return defaultExt(name.toDString, ext.toDString).ptr;
}
/// Ditto
- extern (D) static const(char)[] defaultExt(const char[] name, const char[] ext)
+ extern (D) static const(char)[] defaultExt(const char[] name, const char[] ext) pure
{
auto e = FileName.ext(name);
if (e.length) // it already has an extension
@@ -615,13 +615,13 @@ nothrow:
/***************************
* Free returned value with FileName::free()
*/
- extern (C++) static const(char)* forceExt(const(char)* name, const(char)* ext)
+ extern (C++) static const(char)* forceExt(const(char)* name, const(char)* ext) pure
{
return forceExt(name.toDString, ext.toDString).ptr;
}
/// Ditto
- extern (D) static const(char)[] forceExt(const char[] name, const char[] ext)
+ extern (D) static const(char)[] forceExt(const char[] name, const char[] ext) pure
{
if (auto e = FileName.ext(name))
return addExt(name[0 .. $ - e.length - 1], ext);
@@ -149,6 +149,7 @@ enum CHUNK_SIZE = (256 * 4096 - 64);
__gshared size_t heapleft = 0;
__gshared void* heapp;
+__gshared size_t heapTotal = 0; // Total amount of memory allocated using malloc
extern (D) void* allocmemoryNoFree(size_t m_size) nothrow @nogc
{
@@ -167,11 +168,13 @@ extern (D) void* allocmemoryNoFree(size_t m_size) nothrow @nogc
if (m_size > CHUNK_SIZE)
{
+ heapTotal += m_size;
return Mem.check(malloc(m_size));
}
heapleft = CHUNK_SIZE;
heapp = Mem.check(malloc(CHUNK_SIZE));
+ heapTotal += CHUNK_SIZE;
goto L1;
}
@@ -128,8 +128,8 @@ bool checkUnsafeAccess(Scope* sc, Expression e, bool readonly, bool printmsg)
if (hasPointers && v.type.toBasetype().ty != Tstruct)
{
- if ((!ad.type.alignment.isDefault() && ad.type.alignment.get() < target.ptrsize ||
- (v.offset & (target.ptrsize - 1))))
+ 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))
@@ -40,33 +40,15 @@ enum class CSX : uint16_t
halt = 0x20, // assert(0)
};
-enum class SCOPE
+enum class Contract : uint8_t
{
- // Flags that would not be inherited beyond scope nesting
- ctor = 0x0001, // constructor type
- noaccesscheck = 0x0002, // don't do access checks
- condition = 0x0004, // inside static if/assert condition
- debug_ = 0x0008, // inside debug conditional
-
- // Flags that would be inherited beyond scope nesting
- constraint = 0x0010, // inside template constraint
- invariant_ = 0x0020, // inside invariant code
- require = 0x0040, // inside in contract code
- ensure = 0x0060, // inside out contract code
- contract = 0x0060, // [mask] we're inside contract code
- ctfe = 0x0080, // inside a ctfe-only expression
- compile = 0x0100, // inside __traits(compile)
- ignoresymbolvisibility = 0x0200, // ignore symbol visibility (Bugzilla 15907)
-
- Cfile = 0x0800, // C semantics apply
- free = 0x8000, // is on free list
- fullinst = 0x10000, // fully instantiate templates
- ctfeBlock = 0x20000, // inside a `if (__ctfe)` block
- dip1000 = 0x40000, // dip1000 errors enabled for this scope
- dip25 = 0x80000, // dip25 errors enabled for this scope
+ none = 0u,
+ invariant_ = 1u,
+ require = 2u,
+ ensure = 3u,
};
-struct Scope
+struct Scope final
{
Scope *enclosing; // enclosing Scope
@@ -122,6 +104,37 @@ struct Scope
unsigned flags;
+ bool ctor() const;
+ bool ctor(bool v);
+ bool noAccessCheck() const;
+ bool noAccessCheck(bool v);
+ bool condition() const;
+ bool condition(bool v);
+ bool debug_() const;
+ bool debug_(bool v);
+ bool inTemplateConstraint() const;
+ bool inTemplateConstraint(bool v);
+ Contract contract() const;
+ Contract contract(Contract v);
+ bool ctfe() const;
+ bool ctfe(bool v);
+ bool traitsCompiles() const;
+ bool traitsCompiles(bool v);
+ bool ignoresymbolvisibility() const;
+ bool ignoresymbolvisibility(bool v);
+ bool inCfile() const;
+ bool inCfile(bool v);
+ bool canFree() const;
+ bool canFree(bool v);
+ bool fullinst() const;
+ bool fullinst(bool v);
+ bool ctfeBlock() const;
+ bool ctfeBlock(bool v);
+ bool dip1000() const;
+ bool dip1000(bool v);
+ bool dip25() const;
+ bool dip25(bool v);
+
UserAttributeDeclaration *userAttribDecl; // user defined attributes
DocComment *lastdc; // documentation comment for last symbol at this scope
@@ -21,6 +21,7 @@ import dmd.astcodegen;
import dmd.astenums;
import dmd.attrib;
import dmd.blockexit;
+import dmd.timetrace;
import dmd.clone;
import dmd.dcast;
import dmd.dclass;
@@ -56,6 +57,7 @@ import dmd.parse;
import dmd.root.filename;
import dmd.common.outbuffer;
import dmd.root.rmem;
+import dmd.root.string : toDString;
import dmd.rootobject;
import dmd.root.utf;
import dmd.sideeffect;
@@ -118,43 +120,7 @@ private extern(C++) final class Semantic2Visitor : Visitor
else if (result)
return;
- if (sa.msgs)
- {
- OutBuffer msgbuf;
- for (size_t i = 0; i < sa.msgs.length; i++)
- {
- Expression e = (*sa.msgs)[i];
- sc = sc.startCTFE();
- e = e.expressionSemantic(sc);
- e = resolveProperties(sc, e);
- sc = sc.endCTFE();
- e = ctfeInterpretForPragmaMsg(e);
- if (e.op == EXP.error)
- {
- errorSupplemental(sa.loc, "while evaluating `static assert` argument `%s`", (*sa.msgs)[i].toChars());
- return;
- }
- StringExp se = e.toStringExp();
- if (se)
- {
- const slice = se.toUTF8(sc).peekString();
- // Hack to keep old formatting to avoid changing error messages everywhere
- if (sa.msgs.length == 1)
- msgbuf.printf("\"%.*s\"", cast(int)slice.length, slice.ptr);
- else
- msgbuf.printf("%.*s", cast(int)slice.length, slice.ptr);
- }
- else
- msgbuf.printf("%s", e.toChars());
- }
- error(sa.loc, "static assert: %s", msgbuf.extractChars());
- }
- else
- error(sa.loc, "static assert: `%s` is false", sa.exp.toChars());
- if (sc.tinst)
- sc.tinst.printInstantiationTrace();
- if (!global.gag)
- fatal();
+ staticAssertFail(sa, sc);
}
override void visit(TemplateInstance tempinst)
@@ -282,7 +248,7 @@ private extern(C++) final class Semantic2Visitor : Visitor
// https://issues.dlang.org/show_bug.cgi?id=14166
// https://issues.dlang.org/show_bug.cgi?id=20417
// Don't run CTFE for the temporary variables inside typeof or __traits(compiles)
- vd._init = vd._init.initializerSemantic(sc, vd.type, sc.intypeof == 1 || sc.flags & SCOPE.compile ? INITnointerpret : INITinterpret);
+ vd._init = vd._init.initializerSemantic(sc, vd.type, sc.intypeof == 1 || sc.traitsCompiles ? INITnointerpret : INITinterpret);
lowerStaticAAs(vd, sc);
vd.inuse--;
}
@@ -330,7 +296,7 @@ private extern(C++) final class Semantic2Visitor : Visitor
{
// Cannot initialize a thread-local class or pointer to struct variable with a literal
// that itself is a thread-local reference and would need dynamic initialization also.
- if ((vd.type.ty == Tclass) && vd.type.isMutable() && !vd.type.isShared())
+ if (vd.type.ty == Tclass && vd.type.isMutable() && !vd.type.isShared())
{
ExpInitializer ei = vd._init.isExpInitializer();
if (ei && ei.exp.op == EXP.classReference)
@@ -393,6 +359,9 @@ private extern(C++) final class Semantic2Visitor : Visitor
assert(fd.semanticRun <= PASS.semantic2);
fd.semanticRun = PASS.semantic2;
+ timeTraceBeginEvent(TimeTraceEventType.sema2);
+ scope(exit) timeTraceEndEvent(TimeTraceEventType.sema2, fd);
+
//printf("FuncDeclaration::semantic2 [%s] fd: %s type: %s\n", fd.loc.toChars(), fd.toChars(), fd.type ? fd.type.toChars() : "".ptr);
// Only check valid functions which have a body to avoid errors
@@ -896,3 +865,51 @@ private extern(C++) final class StaticAAVisitor : SemanticTimeTransitiveVisitor
this.visit(crExp.value);
}
}
+
+/**
+ * Given a static assert with a failing condition, print an error
+ * Params:
+ * sa = Static assert with failing condition
+ * sc = scope for evaluating assert message and printing context
+ */
+void staticAssertFail(StaticAssert sa, Scope* sc)
+{
+ if (sa.msgs)
+ {
+ OutBuffer msgbuf;
+ for (size_t i = 0; i < sa.msgs.length; i++)
+ {
+ Expression e = (*sa.msgs)[i];
+ sc = sc.startCTFE();
+ e = e.expressionSemantic(sc);
+ e = resolveProperties(sc, e);
+ sc = sc.endCTFE();
+ e = ctfeInterpretForPragmaMsg(e);
+ if (e.op == EXP.error)
+ {
+ errorSupplemental(sa.loc, "while evaluating `static assert` argument `%s`", (*sa.msgs)[i].toChars());
+ if (!global.gag)
+ fatal();
+ return;
+ }
+ if (StringExp se = e.toStringExp())
+ {
+ const slice = se.toUTF8(sc).peekString();
+ // Hack to keep old formatting to avoid changing error messages everywhere
+ if (sa.msgs.length == 1)
+ msgbuf.printf("\"%.*s\"", cast(int)slice.length, slice.ptr);
+ else
+ msgbuf.printf("%.*s", cast(int)slice.length, slice.ptr);
+ }
+ else
+ msgbuf.printf("%s", e.toChars());
+ }
+ error(sa.loc, "static assert: %s", msgbuf.extractChars());
+ }
+ else
+ error(sa.loc, "static assert: `%s` is false", sa.exp.toChars());
+ if (sc.tinst)
+ sc.tinst.printInstantiationTrace();
+ if (!global.gag)
+ fatal();
+}
@@ -119,7 +119,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
sc.tinst = tempinst;
sc.minst = tempinst.minst;
- int needGagging = (tempinst.gagged && !global.gag);
+ bool needGagging = tempinst.gagged && !global.gag;
uint olderrors = global.errors;
int oldGaggedErrors = -1; // dead-store to prevent spurious warning
/* If this is a gagged instantiation, gag errors.
@@ -221,6 +221,11 @@ private extern(C++) final class Semantic3Visitor : Visitor
override void visit(FuncDeclaration funcdecl)
{
//printf("FuncDeclaration::semantic3(%s '%s', sc = %p)\n", funcdecl.kind(), funcdecl.toChars(), sc);
+ import dmd.timetrace;
+ import dmd.root.string : toDString;
+ timeTraceBeginEvent(TimeTraceEventType.sema3);
+ scope (exit) timeTraceEndEvent(TimeTraceEventType.sema3, funcdecl);
+
/* Determine if function should add `return 0;`
*/
bool addReturn0()
@@ -229,7 +234,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
auto f = funcdecl.type.isTypeFunction();
// C11 5.1.2.2.3
- if (sc.flags & SCOPE.Cfile && funcdecl.isCMain() && f.next.ty == Tint32)
+ if (sc.inCfile && funcdecl.isCMain() && f.next.ty == Tint32)
return true;
return f.next.ty == Tvoid && (funcdecl.isMain() || funcdecl.isCMain());
@@ -290,7 +295,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
}
}
- //printf(" sc.incontract = %d\n", (sc.flags & SCOPE.contract));
+ //printf(" sc.incontract = %d\n", sc.contract);
if (funcdecl.semanticRun >= PASS.semantic3)
return;
funcdecl.semanticRun = PASS.semantic3;
@@ -342,7 +347,10 @@ private extern(C++) final class Semantic3Visitor : Visitor
sc2.explicitVisibility = 0;
sc2.aligndecl = null;
if (funcdecl.ident != Id.require && funcdecl.ident != Id.ensure)
- sc2.flags = sc.flags & ~SCOPE.contract;
+ {
+ sc2.copyFlagsFrom(sc);
+ sc2.contract = Contract.none;
+ }
sc2.tf = null;
sc2.os = null;
sc2.inLoop = false;
@@ -543,8 +551,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
Statement fpreinv = null;
if (funcdecl.addPreInvariant())
{
- Expression e = addInvariant(funcdecl.isThis(), funcdecl.vthis);
- if (e)
+ if (Expression e = addInvariant(funcdecl.isThis(), funcdecl.vthis))
fpreinv = new ExpStatement(Loc.initial, e);
}
@@ -552,8 +559,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
Statement fpostinv = null;
if (funcdecl.addPostInvariant())
{
- Expression e = addInvariant(funcdecl.isThis(), funcdecl.vthis);
- if (e)
+ if (Expression e = addInvariant(funcdecl.isThis(), funcdecl.vthis))
fpostinv = new ExpStatement(Loc.initial, e);
}
@@ -820,7 +826,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
else
{
const(bool) inlineAsm = (funcdecl.hasReturnExp & 8) != 0;
- if ((blockexit & BE.fallthru) && f.next.ty != Tvoid && !inlineAsm && !(sc.flags & SCOPE.Cfile))
+ if ((blockexit & BE.fallthru) && f.next.ty != Tvoid && !inlineAsm && !sc.inCfile)
{
if (!funcdecl.hasReturnExp)
.error(funcdecl.loc, "%s `%s` has no `return` statement, but is expected to return a value of type `%s`", funcdecl.kind, funcdecl.toPrettyChars, f.next.toChars());
@@ -996,7 +1002,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
sym.parent = sc2.scopesym;
sym.endlinnum = funcdecl.endloc.linnum;
sc2 = sc2.push(sym);
- sc2.flags = (sc2.flags & ~SCOPE.contract) | SCOPE.require;
+ sc2.contract = Contract.require;
// BUG: need to error if accessing out parameters
// BUG: need to disallow returns
@@ -1041,7 +1047,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
}
sc2 = scout; //push
- sc2.flags = (sc2.flags & ~SCOPE.contract) | SCOPE.ensure;
+ sc2.contract = Contract.ensure;
// BUG: need to disallow returns and throws
@@ -1212,8 +1218,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
{
/* Wrap the entire function body in a synchronized statement
*/
- ClassDeclaration cd = funcdecl.toParentDecl().isClassDeclaration();
- if (cd)
+ if (ClassDeclaration cd = funcdecl.toParentDecl().isClassDeclaration())
{
if (target.libraryObjectMonitors(funcdecl, sbody))
{
@@ -408,7 +408,7 @@ Expression extractSideEffect(Scope* sc, const char[] name,
* https://issues.dlang.org/show_bug.cgi?id=17145
*/
if (!alwaysCopy &&
- ((sc.flags & SCOPE.ctfe) ? !hasSideEffect(e) : isTrivialExp(e)))
+ (sc.ctfe ? !hasSideEffect(e) : isTrivialExp(e)))
return e;
auto vd = copyToTemp(0, name, e);
@@ -82,13 +82,13 @@ version (DMDLIB)
*/
private Identifier fixupLabelName(Scope* sc, Identifier ident)
{
- uint flags = (sc.flags & SCOPE.contract);
+ Contract c = sc.contract;
const id = ident.toString();
- if (flags && flags != SCOPE.invariant_ &&
+ if (c != Contract.none && c != Contract.invariant_ &&
!(id.length >= 2 && id[0] == '_' && id[1] == '_')) // does not start with "__"
{
OutBuffer buf;
- buf.writestring(flags == SCOPE.require ? "__in_" : "__out_");
+ buf.writestring(c == Contract.require ? "__in_" : "__out_");
buf.writestring(ident.toString());
ident = Identifier.idPool(buf[]);
@@ -123,7 +123,7 @@ private LabelStatement checkLabeledLoop(Scope* sc, Statement statement) @safe
*/
private Expression checkAssignmentAsCondition(Expression e, Scope* sc)
{
- if (sc.flags & SCOPE.Cfile)
+ if (sc.inCfile)
return e;
auto ec = lastComma(e);
if (ec.op == EXP.assign)
@@ -205,7 +205,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
}
if (checkMustUse(s.exp, sc))
s.exp = ErrorExp.get();
- if (!(sc.flags & SCOPE.Cfile) && discardValue(s.exp))
+ if (!sc.inCfile && discardValue(s.exp))
s.exp = ErrorExp.get();
s.exp = s.exp.optimize(WANTvalue);
@@ -1697,7 +1697,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
if (ifs.isIfCtfeBlock())
{
Scope* scd2 = scd.push();
- scd2.flags |= SCOPE.ctfeBlock;
+ scd2.ctfeBlock = true;
ifs.ifbody = ifs.ifbody.semanticNoScope(scd2);
scd2.pop();
}
@@ -1733,11 +1733,10 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
// This feature allows a limited form of conditional compilation.
if (cs.condition.include(sc))
{
- DebugCondition dc = cs.condition.isDebugCondition();
- if (dc)
+ if (DebugCondition dc = cs.condition.isDebugCondition())
{
sc = sc.push();
- sc.flags |= SCOPE.debug_;
+ sc.debug_ = true;
cs.ifbody = cs.ifbody.statementSemantic(sc);
sc.pop();
}
@@ -1964,7 +1963,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
!(!ss.isFinal || needswitcherror || global.params.useAssert == CHECKENABLE.on || sc.func.isSafe);
if (!ss.hasDefault)
{
- if (!ss.isFinal && (!ss._body || !ss._body.isErrorStatement()) && !(sc.flags & SCOPE.Cfile))
+ if (!ss.isFinal && (!ss._body || !ss._body.isErrorStatement()) && !sc.inCfile)
error(ss.loc, "`switch` statement without a `default`; use `final switch` or add `default: assert(0);` or add `default: break;`");
// Generate runtime error if the default is hit
@@ -1972,7 +1971,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
CompoundStatement cs;
Statement s;
- if (sc.flags & SCOPE.Cfile)
+ if (sc.inCfile)
{
s = new BreakStatement(ss.loc, null); // default for C is `default: break;`
}
@@ -2023,7 +2022,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
ss._body = cs;
}
- if (!(sc.flags & SCOPE.Cfile) && ss.checkLabel())
+ if (!sc.inCfile && ss.checkLabel())
{
sc.pop();
return setError();
@@ -2474,7 +2473,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
Expression e0 = null;
bool errors = false;
- if (sc.flags & SCOPE.contract)
+ if (sc.contract)
{
error(rs.loc, "`return` statements cannot be in contracts");
errors = true;
@@ -3331,7 +3330,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
/* If catch exception type is derived from Exception
*/
if (c.type.toBasetype().implicitConvTo(ClassDeclaration.exception.type) &&
- (!c.handler || !c.handler.comeFrom()) && !(sc.flags & SCOPE.debug_))
+ (!c.handler || !c.handler.comeFrom()) && !sc.debug_)
{
// Remove c from the array of catches
tcs.catches.remove(i);
@@ -3459,7 +3458,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
if (ds.statement)
{
sc = sc.push();
- sc.flags |= SCOPE.debug_;
+ sc.debug_ = true;
ds.statement = ds.statement.statementSemantic(sc);
sc.pop();
}
@@ -3480,7 +3479,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
gs.tf = sc.tf;
gs.os = sc.os;
gs.lastVar = sc.lastVar;
- gs.inCtfeBlock = (sc.flags & SCOPE.ctfeBlock) != 0;
+ gs.inCtfeBlock = sc.ctfeBlock;
if (!gs.label.statement && sc.fes)
{
@@ -3504,7 +3503,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
fd.gotos = new GotoStatements();
fd.gotos.push(gs);
}
- else if (!(sc.flags & SCOPE.Cfile) && gs.checkLabel())
+ else if (!sc.inCfile && gs.checkLabel())
return setError();
result = gs;
@@ -3520,7 +3519,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
ls.tf = sc.tf;
ls.os = sc.os;
ls.lastVar = sc.lastVar;
- ls.inCtfeBlock = (sc.flags & SCOPE.ctfeBlock) != 0;
+ ls.inCtfeBlock = sc.ctfeBlock;
LabelDsymbol ls2 = fd.searchLabel(ls.ident, ls.loc);
if (ls2.statement && !ls2.duplicated)
@@ -154,7 +154,7 @@ void templateDeclarationSemantic(Scope* sc, TemplateDeclaration tempdecl)
/* Calculate TemplateParameter.dependent
*/
- TemplateParameters tparams = TemplateParameters(1);
+ auto tparams = TemplateParameters(1);
for (size_t i = 0; i < tempdecl.parameters.length; i++)
{
TemplateParameter tp = (*tempdecl.parameters)[i];
@@ -457,7 +457,7 @@ bool evaluateConstraint(TemplateDeclaration td, TemplateInstance ti, Scope* sc,
// (previously, this was immediately before calling evalStaticCondition), so the
// semantic pass knows not to issue deprecation warnings for these throw-away decls.
// https://issues.dlang.org/show_bug.cgi?id=21831
- scx.flags |= SCOPE.constraint;
+ scx.inTemplateConstraint = true;
assert(!ti.symtab);
if (fd)
@@ -1337,10 +1337,10 @@ extern (D) MATCHpair deduceFunctionTemplateMatch(TemplateDeclaration td, Templat
* We also save/restore sc.func.flags to avoid messing up
* attribute inference in the evaluation.
*/
- const oldflags = sc.func ? sc.func.flags : 0;
+ const oldflags = sc.func ? sc.func.saveFlags : 0;
auto e = resolveAliasThis(sc, farg, true);
if (sc.func)
- sc.func.flags = oldflags;
+ sc.func.restoreFlags(oldflags);
if (e)
{
farg = e;
new file mode 100644
@@ -0,0 +1,82 @@
+/**
+Compilation time tracing, -ftime-trace.
+
+The time trace profile is output in the Chrome Trace Event Format, described
+here: https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview
+
+This file is originally from LDC (the LLVM D compiler).
+
+Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved
+Authors: Johan Engelen, Max Haughton, Dennis Korpel
+License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/common/timetrace.d, common/_timetrace.d)
+Documentation: https://dlang.org/phobos/dmd_common_timetrace.html
+Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/common/timetrace.d
+*/
+module dmd.timetrace;
+
+import dmd.dsymbol;
+import dmd.expression;
+
+/**
+ * Start a new time trace event
+ *
+ * Details of the event will be passed as delegates to `timeTraceEndEvent` so
+ * they're only generated when the event is actually written.
+ *
+ * Params:
+ * eventType = what compilation stage the event belongs to
+ * (redundant with the eventType of `timeTraceEndEvent` but used by GDC)
+ */
+extern (C++)
+void timeTraceBeginEvent(TimeTraceEventType eventType)
+{
+}
+
+/**
+ * End a time tracing event, optionally updating the event name and details
+ * with a delegate. Delegates are used to prevent spending time on string
+ * generation when an event is too small to be generated anyway.
+ *
+ * Params:
+ * eventType = what compilation stage the event belongs to
+ * sym = Dsymbol which was analyzed, used to generate 'name' and 'detail'
+ * e = Expression which was analyzed, used to generate 'name' and 'detail'
+ * detail = custom lazy string for 'detail' of event
+ */
+extern (C++)
+void timeTraceEndEvent(TimeTraceEventType eventType)
+{
+}
+
+/// ditto
+void timeTraceEndEvent(TimeTraceEventType eventType, Dsymbol sym, scope const(char)[] delegate() detail = null)
+{
+ return timeTraceEndEvent(eventType);
+}
+
+/// ditto
+extern (C++)
+void timeTraceEndEvent(TimeTraceEventType eventType, Expression e)
+{
+ return timeTraceEndEvent(eventType);
+}
+
+/// Identifies which compilation stage the event is associated to
+enum TimeTraceEventType
+{
+ generic,
+ parseGeneral,
+ parse,
+ semaGeneral,
+ sema1Import,
+ sema1Module,
+ sema2,
+ sema3,
+ ctfe,
+ ctfeCall,
+ codegenGlobal,
+ codegenModule,
+ codegenFunction,
+ link,
+}
@@ -736,7 +736,9 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
return dimError(1);
Scope* sc2 = sc.push();
- sc2.flags = sc.flags | SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility;
+ sc2.copyFlagsFrom(sc);
+ sc2.noAccessCheck = true;
+ sc2.ignoresymbolvisibility = true;
bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1);
sc2.pop();
if (!ok)
@@ -772,7 +774,9 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
return dimError(1);
Scope* sc2 = sc.push();
- sc2.flags = sc.flags | SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility;
+ sc2.copyFlagsFrom(sc);
+ sc2.noAccessCheck = true;
+ sc2.ignoresymbolvisibility = true;
bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1);
sc2.pop();
if (!ok)
@@ -1003,7 +1007,8 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
doSemantic:
// ignore symbol visibility and disable access checks for these traits
Scope* scx = sc.push();
- scx.flags |= SCOPE.ignoresymbolvisibility | SCOPE.noaccesscheck;
+ scx.ignoresymbolvisibility = true;
+ scx.noAccessCheck = true;
scope (exit) scx.pop();
if (e.ident == Id.hasMember)
@@ -1737,7 +1742,11 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
Scope* sc2 = sc.push();
sc2.tinst = null;
sc2.minst = null; // this is why code for these are not emitted to object file
- sc2.flags = (sc.flags & ~(SCOPE.ctfe | SCOPE.condition)) | SCOPE.compile | SCOPE.fullinst;
+ sc2.copyFlagsFrom(sc);
+ sc2.ctfe = false;
+ sc2.condition = false;
+ sc2.traitsCompiles = true;
+ sc2.fullinst = true;
bool err = false;
@@ -235,7 +235,7 @@ private void resolveHelper(TypeQualified mt, const ref Loc loc, Scope* sc, Dsymb
Dsymbol sm = s.searchX(loc, sc, id, flags);
if (sm)
{
- if (!(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc, sm))
+ if (!sc.ignoresymbolvisibility && !symbolIsVisible(sc, sm))
{
.error(loc, "`%s` is not visible from module `%s`", sm.toPrettyChars(), sc._module.toChars());
sm = null;
@@ -620,7 +620,7 @@ extern (D) bool checkComplexTransition(Type type, const ref Loc loc, Scope* sc)
return false;
// Don't complain if we're inside a template constraint
// https://issues.dlang.org/show_bug.cgi?id=21831
- if (sc.flags & SCOPE.constraint)
+ if (sc.inTemplateConstraint)
return false;
Type t = type.baseElemOf();
@@ -633,7 +633,7 @@ extern (D) bool checkComplexTransition(Type type, const ref Loc loc, Scope* sc)
if (t.isimaginary() || t.iscomplex())
{
- if (sc.flags & SCOPE.Cfile)
+ if (sc.inCfile)
return true; // complex/imaginary not deprecated in C code
Type rt;
switch (t.ty)
@@ -1012,7 +1012,7 @@ private extern(D) MATCH argumentMatchParameter (TypeFunction tf, Parameter p,
else
{
import dmd.dcast : cimplicitConvTo;
- m = (sc && sc.flags & SCOPE.Cfile) ? arg.cimplicitConvTo(tprm) : arg.implicitConvTo(tprm);
+ m = (sc && sc.inCfile) ? arg.cimplicitConvTo(tprm) : arg.implicitConvTo(tprm);
}
}
@@ -1445,6 +1445,163 @@ uinteger_t size(Type t, const ref Loc loc)
}
}
+/*******************************
+ * Determine if converting 'this' to 'to' is an identity operation,
+ * a conversion to const operation, or the types aren't the same.
+ * Returns:
+ * MATCH.exact 'this' == 'to'
+ * MATCH.constant 'to' is const
+ * MATCH.nomatch conversion to mutable or invariant
+ */
+MATCH constConv(Type from, Type to)
+{
+ MATCH visitType(Type from)
+ {
+ //printf("Type::constConv(this = %s, to = %s)\n", from.toChars(), to.toChars());
+ if (from.equals(to))
+ return MATCH.exact;
+ if (from.ty == to.ty && MODimplicitConv(from.mod, to.mod))
+ return MATCH.constant;
+ return MATCH.nomatch;
+ }
+
+ MATCH visitNext(TypeNext from)
+ {
+ //printf("TypeNext::constConv from = %s, to = %s\n", from.toChars(), to.toChars());
+ if (from.equals(to))
+ return MATCH.exact;
+
+ if (!(from.ty == to.ty && MODimplicitConv(from.mod, to.mod)))
+ return MATCH.nomatch;
+
+ Type tn = to.nextOf();
+ if (!(tn && from.next.ty == tn.ty))
+ return MATCH.nomatch;
+
+ MATCH m;
+ if (to.isConst()) // whole tail const conversion
+ {
+ // Recursive shared level check
+ m = from.next.constConv(tn);
+ if (m == MATCH.exact)
+ m = MATCH.constant;
+ }
+ else
+ {
+ //printf("\tnext => %s, to.next => %s\n", from.next.toChars(), tn.toChars());
+ m = from.next.equals(tn) ? MATCH.constant : MATCH.nomatch;
+ }
+ return m;
+ }
+
+ MATCH visitSArray(TypeSArray from)
+ {
+ if (auto tsa = to.isTypeSArray())
+ {
+ if (!from.dim.equals(tsa.dim))
+ return MATCH.nomatch;
+ }
+ return visitNext(from);
+ }
+
+ MATCH visitAArray(TypeAArray from)
+ {
+ if (auto taa = to.isTypeAArray())
+ {
+ MATCH mindex = from.index.constConv(taa.index);
+ MATCH mkey = from.next.constConv(taa.next);
+ // Pick the worst match
+ return mkey < mindex ? mkey : mindex;
+ }
+ return visitType(from);
+ }
+
+ MATCH visitPointer(TypePointer from)
+ {
+ if (from.next.ty == Tfunction)
+ {
+ if (to.nextOf() && from.next.equals((cast(TypeNext)to).next))
+ return visitType(from);
+ else
+ return MATCH.nomatch;
+ }
+ return visitNext(from);
+ }
+
+ MATCH visitFunction(TypeFunction from)
+ {
+ // Attributes need to match exactly, otherwise it's an implicit conversion
+ if (from.ty != to.ty || !from.attributesEqual(cast(TypeFunction) to))
+ return MATCH.nomatch;
+
+ return visitNext(from);
+ }
+
+ MATCH visitStruct(TypeStruct from)
+ {
+ if (from.equals(to))
+ return MATCH.exact;
+ if (from.ty == to.ty && from.sym == (cast(TypeStruct)to).sym && MODimplicitConv(from.mod, to.mod))
+ return MATCH.constant;
+ return MATCH.nomatch;
+ }
+
+ MATCH visitEnum(TypeEnum from)
+ {
+ if (from.equals(to))
+ return MATCH.exact;
+ if (from.ty == to.ty && from.sym == (cast(TypeEnum)to).sym && MODimplicitConv(from.mod, to.mod))
+ return MATCH.constant;
+ return MATCH.nomatch;
+ }
+
+ MATCH visitClass(TypeClass from)
+ {
+ if (from.equals(to))
+ return MATCH.exact;
+ if (from.ty == to.ty && from.sym == (cast(TypeClass)to).sym && MODimplicitConv(from.mod, to.mod))
+ return MATCH.constant;
+
+ /* Conversion derived to const(base)
+ */
+ int offset = 0;
+ if (to.isBaseOf(from, &offset) && offset == 0 && MODimplicitConv(from.mod, to.mod))
+ {
+ // Disallow:
+ // derived to base
+ // inout(derived) to inout(base)
+ if (!to.isMutable() && !to.isWild())
+ return MATCH.convert;
+ }
+
+ return MATCH.nomatch;
+ }
+
+ MATCH visitNoreturn(TypeNoreturn from)
+ {
+ // Either another noreturn or conversion to any type
+ return from.implicitConvTo(to);
+ }
+
+ switch(from.ty)
+ {
+ default: return visitType(from);
+ case Tsarray: return visitSArray(from.isTypeSArray());
+ case Taarray: return visitAArray(from.isTypeAArray());
+ case Treference:
+ case Tdelegate:
+ case Tslice:
+ case Tarray: return visitNext(cast(TypeNext)from);
+ case Tpointer: return visitPointer(from.isTypePointer());
+ case Tfunction: return visitFunction(from.isTypeFunction());
+ case Tstruct: return visitStruct(from.isTypeStruct());
+ case Tenum: return visitEnum(from.isTypeEnum());
+ case Tclass: return visitClass(from.isTypeClass());
+ case Tnoreturn: return visitNoreturn(from.isTypeNoreturn());
+ }
+}
+
+
/******************************************
* Perform semantic analysis on a type.
* Params:
@@ -1479,7 +1636,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
Type visitComplex(TypeBasic t)
{
- if (!(sc.flags & SCOPE.Cfile))
+ if (!sc.inCfile)
return visitType(t);
auto tc = getComplexLibraryType(loc, sc, t.ty);
@@ -1653,7 +1810,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
default:
break;
}
- if (tbn.isscope())
+ if (tbn.isScopeClass())
{
.error(loc, "cannot have array of scope `%s`", tbn.toChars());
return error();
@@ -1687,7 +1844,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
default:
break;
}
- if (tn.isscope())
+ if (tn.isScopeClass())
{
.error(loc, "cannot have array of scope `%s`", tn.toChars());
return error();
@@ -1893,7 +2050,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
default:
break;
}
- if (mtype.next.isscope())
+ if (mtype.next.isScopeClass())
{
.error(loc, "cannot have array of scope `%s`", mtype.next.toChars());
return error();
@@ -2059,7 +2216,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
tf.next = tf.next.typeSemantic(loc, sc);
sc = sc.pop();
errors |= tf.checkRetType(loc);
- if (tf.next.isscope() && !tf.isctor)
+ if (tf.next.isScopeClass() && !tf.isctor)
{
.error(loc, "functions cannot return `scope %s`", tf.next.toChars());
errors = true;
@@ -2457,7 +2614,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
}
if (tf.parameterList.varargs == VarArg.variadic && tf.linkage != LINK.d && tf.parameterList.length == 0 &&
- !(sc.flags & SCOPE.Cfile))
+ !sc.inCfile)
{
.error(loc, "variadic functions with non-D linkage must have at least one parameter");
errors = true;
@@ -3209,7 +3366,28 @@ Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden
else
{
if (src)
- error(loc, "no property `%s` for `%s` of type `%s`", ident.toChars(), src.toChars(), mt.toPrettyChars(true));
+ {
+ 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)
+ {
+ // 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());
+ }
+ }
+ }
+ }
else
error(loc, "no property `%s` for type `%s`", ident.toChars(), mt.toPrettyChars(true));
@@ -3897,7 +4075,7 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type
// compile time sequences are valid types
!mt.exp.type.isTypeTuple())
{
- if (!(sc.flags & SCOPE.Cfile) && // in (extended) C typeof may be used on types as with sizeof
+ if (!sc.inCfile && // in (extended) C typeof may be used on types as with sizeof
mt.exp.checkType())
goto Lerr;
@@ -4786,7 +4964,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag
assert(e.op != EXP.dot);
// https://issues.dlang.org/show_bug.cgi?id=14010
- if (!(sc.flags & SCOPE.Cfile) && ident == Id._mangleof)
+ if (!sc.inCfile && ident == Id._mangleof)
{
return mt.getProperty(sc, e.loc, ident, flag & 1);
}
@@ -4798,7 +4976,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag
/* Create a TupleExp out of the fields of the struct e:
* (e.field0, e.field1, e.field2, ...)
*/
- e = e.expressionSemantic(sc); // do this before turning on noaccesscheck
+ e = e.expressionSemantic(sc); // do this before turning on noAccessCheck
if (!mt.sym.determineFields())
{
@@ -4828,20 +5006,20 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag
e = new TupleExp(e.loc, e0, exps);
Scope* sc2 = sc.push();
- sc2.flags |= SCOPE.noaccesscheck;
+ sc2.noAccessCheck = true;
e = e.expressionSemantic(sc2);
sc2.pop();
return e;
}
- immutable flags = sc.flags & SCOPE.ignoresymbolvisibility ? SearchOpt.ignoreVisibility : 0;
+ immutable flags = sc.ignoresymbolvisibility ? SearchOpt.ignoreVisibility : 0;
s = mt.sym.search(e.loc, ident, flags | SearchOpt.ignorePrivateImports);
L1:
if (!s)
{
return noMember(mt, sc, e, ident, flag);
}
- if (!(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc, s))
+ if (!sc.ignoresymbolvisibility && !symbolIsVisible(sc, s))
{
return noMember(mt, sc, e, ident, flag);
}
@@ -5078,7 +5256,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag
/* Create a TupleExp
*/
- e = e.expressionSemantic(sc); // do this before turning on noaccesscheck
+ e = e.expressionSemantic(sc); // do this before turning on noAccessCheck
mt.sym.size(e.loc); // do semantic of type
@@ -5108,13 +5286,13 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag
e = new TupleExp(e.loc, e0, exps);
Scope* sc2 = sc.push();
- sc2.flags |= SCOPE.noaccesscheck;
+ sc2.noAccessCheck = true;
e = e.expressionSemantic(sc2);
sc2.pop();
return e;
}
- SearchOptFlags flags = sc.flags & SCOPE.ignoresymbolvisibility ? SearchOpt.ignoreVisibility : SearchOpt.all;
+ SearchOptFlags flags = sc.ignoresymbolvisibility ? SearchOpt.ignoreVisibility : SearchOpt.all;
s = mt.sym.search(e.loc, ident, flags | SearchOpt.ignorePrivateImports);
L1:
@@ -5267,7 +5445,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag
return noMember(mt, sc, e, ident, flag & 1);
}
- if (!(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc, s))
+ if (!sc.ignoresymbolvisibility && !symbolIsVisible(sc, s))
{
return noMember(mt, sc, e, ident, flag);
}
@@ -5542,6 +5720,74 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag
}
}
+// if initializer is 0
+bool isZeroInit(Type t, const ref Loc loc)
+{
+ bool visitType(Type _)
+ {
+ return false; // assume not
+ }
+
+ bool visitBasic(TypeBasic t)
+ {
+ switch (t.ty)
+ {
+ case Tchar:
+ case Twchar:
+ case Tdchar:
+ case Timaginary32:
+ case Timaginary64:
+ case Timaginary80:
+ case Tfloat32:
+ case Tfloat64:
+ case Tfloat80:
+ case Tcomplex32:
+ case Tcomplex64:
+ case Tcomplex80:
+ return false; // no
+ default:
+ return true; // yes
+ }
+ }
+
+ bool visitVector(TypeVector t)
+ {
+ return t.basetype.isZeroInit(loc);
+ }
+
+ bool visitSArray(TypeSArray t)
+ {
+ return t.next.isZeroInit(loc);
+ }
+
+ bool visitStruct(TypeStruct t)
+ {
+ // Determine zeroInit here, as this can be called before semantic2
+ t.sym.determineSize(t.sym.loc);
+ return t.sym.zeroInit;
+ }
+
+ bool visitEnum(TypeEnum t)
+ {
+ return t.sym.getDefaultValue(loc).toBool().hasValue(false);
+ }
+
+ switch(t.ty)
+ {
+ default: return t.isTypeBasic() ? visitBasic(cast(TypeBasic)t) : visitType(t);
+ case Tvector: return visitVector(t.isTypeVector());
+ case Tsarray: return visitSArray(t.isTypeSArray());
+ case Taarray:
+ case Tarray:
+ case Treference:
+ case Tdelegate:
+ case Tclass:
+ case Tpointer: return true;
+ case Tstruct: return visitStruct(t.isTypeStruct());
+ case Tenum: return visitEnum(t.isTypeEnum());
+ }
+}
+
/************************
* Get the default initialization expression for a type.
@@ -7136,6 +7382,100 @@ bool isRecursiveAliasThis(ref Type att, Type t)
return false;
}
+MATCH implicitConvToWithoutAliasThis(TypeStruct from, Type to)
+{
+ //printf("TypeStruct::implicitConvToWithoutAliasThis(%s => %s)\n", toChars(), to.toChars());
+
+ auto tos = to.isTypeStruct();
+ if (!(tos && from.sym == tos.sym))
+ return MATCH.nomatch;
+
+ if (from.mod == to.mod)
+ return MATCH.exact;
+
+ if (MODimplicitConv(from.mod, to.mod))
+ return MATCH.constant;
+
+ /* Check all the fields. If they can all be converted,
+ * allow the conversion.
+ */
+ MATCH m = MATCH.constant;
+ uint offset = ~0; // must never match a field offset
+ foreach (v; from.sym.fields[])
+ {
+ /* Why are we only looking at the first member of a union?
+ * The check should check for overlap of v with the previous field,
+ * not just starting at the same point
+ */
+ if (!global.params.fixImmutableConv && v.offset == offset) // v is at same offset as previous field
+ continue; // ignore
+
+ Type tvf = v.type.addMod(from.mod); // from type
+ Type tvt = v.type.addMod(to.mod); // to type
+
+ // field match
+ MATCH mf = tvf.implicitConvTo(tvt);
+ //printf("\t%s => %s, match = %d\n", v.type.toChars(), tvt.toChars(), mf);
+
+ if (mf == MATCH.nomatch)
+ return MATCH.nomatch;
+ if (mf < m) // if field match is worse
+ m = mf;
+ offset = v.offset;
+ }
+ return m;
+}
+
+MATCH implicitConvToWithoutAliasThis(TypeClass from, Type to)
+{
+ ClassDeclaration cdto = to.isClassHandle();
+ MATCH m = constConv(from, to);
+ if (m > MATCH.nomatch)
+ return m;
+
+ if (cdto && cdto.isBaseOf(from.sym, null) && MODimplicitConv(from.mod, to.mod))
+ {
+ //printf("'to' is base\n");
+ return MATCH.convert;
+ }
+ return MATCH.nomatch;
+}
+
+MATCH implicitConvToThroughAliasThis(TypeClass from, Type to)
+{
+ MATCH m;
+ if (from.sym.aliasthis && !(from.att & AliasThisRec.tracing))
+ {
+ if (auto ato = aliasthisOf(from))
+ {
+ from.att = cast(AliasThisRec)(from.att | AliasThisRec.tracing);
+ m = ato.implicitConvTo(to);
+ from.att = cast(AliasThisRec)(from.att & ~AliasThisRec.tracing);
+ }
+ }
+ return m;
+}
+
+MATCH implicitConvToThroughAliasThis(TypeStruct from, Type to)
+{
+ auto tos = to.isTypeStruct();
+ if (!(tos && from.sym == tos.sym) &&
+ from.sym.aliasthis &&
+ !(from.att & AliasThisRec.tracing))
+ {
+ if (auto ato = aliasthisOf(from))
+ {
+ from.att = cast(AliasThisRec)(from.att | AliasThisRec.tracing);
+ MATCH m = ato.implicitConvTo(to);
+ from.att = cast(AliasThisRec)(from.att & ~AliasThisRec.tracing);
+ return m;
+ }
+ }
+ return MATCH.nomatch;
+}
+
+
+
/******************************* Private *****************************************/
private:
@@ -7257,8 +7597,7 @@ Type stripDefaultArgs(Type t)
{
foreach (i, p; *parameters)
{
- Parameter ps = stripParameter(p);
- if (ps)
+ if (Parameter ps = stripParameter(p))
{
// Replace params with a copy we can modify
Parameters* nparams = new Parameters(parameters.length);
@@ -42,7 +42,7 @@ bool genTypeInfo(Expression e, const ref Loc loc, Type torig, Scope* sc)
// Even when compiling without `useTypeInfo` (e.g. -betterC) we should
// still be able to evaluate `TypeInfo` at compile-time, just not at runtime.
// https://issues.dlang.org/show_bug.cgi?id=18472
- if (!sc || !(sc.flags & SCOPE.ctfe))
+ if (!sc || !sc.ctfe)
{
if (!global.params.useTypeInfo)
{
@@ -124,7 +124,7 @@ bool ensurePathToNameExists(Loc loc, const(char)[] name)
* buf = Buffer to write the escaped path to
* fname = Path to escape
*/
-void escapePath(OutBuffer* buf, const(char)* fname)
+void escapePath(OutBuffer* buf, const(char)* fname) pure
{
while (1)
{
@@ -145,78 +145,6 @@ void escapePath(OutBuffer* buf, const(char)* fname)
}
}
-/**
- * Takes a path, and make it compatible with GNU Makefile format.
- *
- * GNU make uses a weird quoting scheme for white space.
- * A space or tab preceded by 2N+1 backslashes represents N backslashes followed by space;
- * a space or tab preceded by 2N backslashes represents N backslashes at the end of a file name;
- * and backslashes in other contexts should not be doubled.
- *
- * Params:
- * buf = Buffer to write the escaped path to
- * fname = Path to escape
- */
-void writeEscapedMakePath(ref OutBuffer buf, const(char)* fname)
-{
- uint slashes;
-
- while (*fname)
- {
- switch (*fname)
- {
- case '\\':
- slashes++;
- break;
- case '$':
- buf.writeByte('$');
- goto default;
- case ' ':
- case '\t':
- while (slashes--)
- buf.writeByte('\\');
- goto case;
- case '#':
- buf.writeByte('\\');
- goto default;
- case ':':
- // ':' not escaped on Windows because it can
- // create problems with absolute paths (e.g. C:\Project)
- version (Windows) {}
- else
- {
- buf.writeByte('\\');
- }
- goto default;
- default:
- slashes = 0;
- break;
- }
-
- buf.writeByte(*fname);
- fname++;
- }
-}
-
-///
-unittest
-{
- version (Windows)
- {
- enum input = `C:\My Project\file#4$.ext`;
- enum expected = `C:\My\ Project\file\#4$$.ext`;
- }
- else
- {
- enum input = `/foo\bar/weird$.:name#\ with spaces.ext`;
- enum expected = `/foo\bar/weird$$.\:name\#\\\ with\ spaces.ext`;
- }
-
- OutBuffer buf;
- buf.writeEscapedMakePath(input);
- assert(buf[] == expected);
-}
-
/**
* Convert string to integer.
*
@@ -2180,7 +2180,7 @@ public:
tree type = build_ctype (e->type);
tree length = size_int (sd->dsym->structsize);
tree ptr = (sd->dsym->isStructDeclaration ()
- && sd->dsym->type->isZeroInit (e->loc))
+ && dmd::isZeroInit (sd->dsym->type, e->loc))
? null_pointer_node : build_address (result);
this->result_ = d_array_value (type, length, ptr);
@@ -662,7 +662,7 @@ public:
this->layout_string (ed->toPrettyChars ());
/* Default initializer for enum. */
- if (ed->members && !d->tinfo->isZeroInit ())
+ if (ed->members && !dmd::isZeroInit (d->tinfo))
{
tree length = size_int (dmd::size (ed->type));
tree ptr = build_address (enum_initializer_decl (ed));
@@ -1415,7 +1415,7 @@ check_typeinfo_type (const Loc &loc, Scope *sc, Expression *expr)
{
/* Even when compiling without RTTI we should still be able to evaluate
TypeInfo at compile-time, just not at run-time. */
- if (!sc || !(sc->flags & unsigned(SCOPE::ctfe)))
+ if (!sc || !sc->ctfe ())
{
static int warned = 0;
@@ -13,7 +13,7 @@ struct FullCaseEntry
ubyte n;
ubyte size;
ubyte entry_len;
- auto const pure nothrow @nogc @property @trusted value() return
+ auto pure nothrow @nogc @property @trusted value() const return
{
return seq[0..entry_len];
}
@@ -262,3 +262,116 @@ struct S23669
a.length += 1;
}
}
+
+/************************************/
+
+// Calling `hasPointers` in escape.d at some point caused
+// a "circular `typeof` definition" error in std.typecons
+// https://github.com/dlang/dmd/pull/16719
+struct Unique
+{
+ int* p;
+ alias ValueType = typeof({ return p; } ());
+ static if (is(ValueType == int*)) {}
+}
+
+// Without handling tuple expansion in escape.d, this error occurs:
+// Error: returning `(ref Tuple!(int, int) __tup2 = this.tuple.get();) , &__tup2.__expand_field_0` escapes a reference to local variable `__tup2`
+struct Tuple(Types...)
+{
+ Types expand;
+ ref Tuple!Types get() { return this; }
+}
+
+struct S2
+{
+ Tuple!(int, int) tuple;
+ int* f() return { return &tuple.get().expand[0]; }
+}
+
+/************************************/
+
+// https://issues.dlang.org/show_bug.cgi?id=23300
+
+auto array(Range)(Range r)
+{
+ int[] result;
+ foreach (e; r)
+ result ~= e;
+ return result;
+}
+
+struct Range
+{
+ int* ptr;
+ int front = 3;
+ enum empty = true;
+ void popFront() @safe scope {}
+}
+
+auto f() @safe
+{
+ scope Range r;
+ return r.array;
+}
+
+/************************************/
+
+// Test that there's no endless loop forwarding `__appendtmp1 => __appendtmp1.this(null)`
+struct SD
+{
+ int* p;
+ this(int*) return scope {}
+}
+
+auto fsd() @safe
+{
+ SD[] a;
+ a ~= SD(null);
+}
+
+/************************************/
+
+// Test that `return scope` inference from assignment doesn't force the function to be `scope`
+struct Scape
+{
+ int* p;
+
+ this()(int* input)
+ {
+ p = input; // return scope inferred here
+ notScope(); // not-scope inferred here
+ }
+
+ void notScope() @safe
+ {
+ static int* g;
+ g = p;
+ }
+}
+
+@safe testScape()
+{
+ Scape(null);
+}
+
+/************************************/
+
+// Test `scope` inference in presence of nested function returning `this`:
+// `save()` can still be called on a `scope Result`
+struct Result
+{
+ int* source;
+ auto save()
+ {
+ int* saveI() { return this.source; }
+ auto saveResult = Result(saveI());
+ }
+}
+
+@safe escapeNested()
+{
+ int s;
+ auto r = Result(&s);
+ r.save();
+}
@@ -4,7 +4,7 @@ TEST_OUTPUT:
fail_compilation/bug9631.d(20): Error: cannot implicitly convert expression `F()` of type `bug9631.T1!().F` to `bug9631.T2!().F`
---
*/
-// DISABLED: win32
+
template T1()
{
struct F { }
@@ -1,13 +1,73 @@
-/*
+/* REQUIRED_ARGS: -verrors=0
TEST_OUTPUT:
---
-fail_compilation/diag9679.d(11): Error: variable `diag9679.main.n` - only parameters, functions and `foreach` declarations can be `ref`
-fail_compilation/diag9679.d(12): Error: variable `diag9679.main.n` - storage class `auto` has no effect if type is not inferred, did you mean `scope`?
+fail_compilation/diag9679.d(15): Error: rvalue `1` cannot be assigned to `ref n`
+fail_compilation/diag9679.d(16): Error: variable `diag9679.main.n` - storage class `auto` has no effect if type is not inferred, did you mean `scope`?
+fail_compilation/diag9679.d(17): Error: variable `diag9679.main.S.a` - field declarations cannot be `ref`
+fail_compilation/diag9679.d(24): Error: returning `r` escapes a reference to local variable `i`
---
*/
+
+
void main()
{
if (ref n = 1) {}
if (auto int n = 1) {}
+ struct S { ref int a; }
+}
+
+ref int test2()
+{
+ int i;
+ ref r = i;
+ return r;
+}
+
+ref int test3()
+{
+ extern int i;
+ ref r = i;
+ return r;
+}
+
+struct S { int a; }
+
+void test4()
+{
+ S s;
+ ref int r1 = s.a;
+ r1 = 3;
+ __gshared S t2;
+ ref int r2 = t2.a;
+ static S t3;
+ ref int r3 = t3.a;
+ extern S t4;
+ ref int r4 = t4.a;
+}
+
+/* TEST_OUTPUT:
+---
+fail_compilation/diag9679.d(60): Error: variable `diag9679.test5.r5` - initializer is required for `ref` variable
+fail_compilation/diag9679.d(60): Error: rvalue `0` cannot be assigned to `ref r5`
+fail_compilation/diag9679.d(65): Error: rvalue `4` cannot be assigned to `ref x`
+fail_compilation/diag9679.d(66): Error: returning `x` escapes a reference to local variable `x`
+fail_compilation/diag9679.d(71): Error: type `immutable(int)` cannot be assigned to `ref int x`
+---
+*/
+void test5()
+{
+ ref int r5;
+}
+
+ref int test6()
+{
+ ref int x = 4;
+ return x;
+}
+
+void test7(immutable int y)
+{
+ ref int x = y;
+ x = 5;
}
@@ -323,9 +323,9 @@ int[] testSlice2() { int[3] sa; int n; return sa[n..2][1..2]; }
/*
TEST_OUTPUT:
---
-fail_compilation/fail13902.d(324): Error: returning `vda[0]` escapes a reference to parameter `vda`
+fail_compilation/fail13902.d(324): Error: returning `vda[0]` escapes a reference to variadic parameter `vda`
+fail_compilation/fail13902.d(325): Error: returning `vda[]` escapes a reference to variadic parameter `vda`
---
-
*/
ref int testDynamicArrayVariadic1(int[] vda...) { return vda[0]; }
@safe int[] testDynamicArrayVariadic2(int[] vda...) { return vda[]; }
@@ -335,9 +335,28 @@ int[3] testDynamicArrayVariadic3(int[] vda...) { return vda[0..3]; } // no er
TEST_OUTPUT:
---
fail_compilation/fail13902.d(335): Error: returning `vsa[0]` escapes a reference to parameter `vsa`
-fail_compilation/fail13902.d(336): Error: returning `vsa[]` escapes a reference to variadic parameter `vsa`
+fail_compilation/fail13902.d(336): Error: returning `vsa[]` escapes a reference to parameter `vsa`
---
*/
ref int testStaticArrayVariadic1(int[3] vsa...) { return vsa[0]; }
int[] testStaticArrayVariadic2(int[3] vsa...) { return vsa[]; }
int[3] testStaticArrayVariadic3(int[3] vsa...) { return vsa[0..3]; } // no error
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail13902.d(355): Error: returning `match(st)` escapes a reference to local variable `st`
+---
+*/
+
+// This was reduced from a `static assert(!__traits(compiles, {...}))` test in `std.sumtype`
+// which was asserting that matchers couldn't escape sumtype members.
+// This should give an error even without `@safe` or `-preview=dip1000`
+
+int* match(return ref int i) { return &i; }
+
+int* escape()
+{
+ int st;
+ return match(st);
+}
@@ -1,5 +1,5 @@
// https://issues.dlang.org/show_bug.cgi?id=19948
-// DISABLED: win32
+
/*
TEST_OUTPUT:
---
@@ -8,7 +8,7 @@ fail_compilation/fail19948.d(16): cannot pass argument `X()` of type `fai
fail_compilation/fail19948.d(19): `fail19948.func(const(X))` declared here
---
*/
-// DISABLED: win32
+
struct X {}
void main()
{
@@ -3,15 +3,15 @@ TEST_OUTPUT:
---
fail_compilation/fail20183.d(1016): Error: function `addr` is not callable using argument types `(int)`
fail_compilation/fail20183.d(1016): cannot pass rvalue argument `S(0).i` of type `int` to parameter `return ref int b`
-fail_compilation/fail20183.d(1005): `fail20183.addr(return ref int b)` declared here
-fail_compilation/fail20183.d(1017): Error: address of struct temporary returned by `s()` assigned to longer lived variable `q`
+fail_compilation/fail20183.d(1004): `fail20183.addr(return ref int b)` declared here
+fail_compilation/fail20183.d(1017): Error: address of expression temporary returned by `s()` assigned to `q` with longer lifetime
+fail_compilation/fail20183.d(1018): Error: address of struct literal `S(0)` assigned to `r` with longer lifetime
---
*/
#line 1000
// https://issues.dlang.org/show_bug.cgi?id=20183
-
@safe:
int* addr(return ref int b) { return &b; }
@@ -19,20 +19,22 @@ int* addr(return ref int b) { return &b; }
struct S
{
int i;
+ S* addrOf() return => &this;
}
S s() { return S(); }
void test()
{
- int* p = addr(S().i); // struct literal
- int* q = addr(s().i); // struct temporary
+ scope int* p = addr(S().i); // struct literal
+ scope int* q = addr(s().i); // struct temporary
+ scope S* r = S().addrOf(); // struct literal
}
/*
TEST_OUTPUT:
---
-fail_compilation/fail20183.d(1107): Error: address of struct temporary returned by `s()` assigned to longer lived variable `this.ptr`
+fail_compilation/fail20183.d(1107): Error: address of expression temporary returned by `s()` assigned to `this.ptr` with longer lifetime
---
*/
#line 1100
@@ -2,9 +2,13 @@
EXTRA_FILES: imports/fail347a.d
TEST_OUTPUT:
---
-fail_compilation/fail347.d(22): Error: undefined identifier `bbr`, did you mean variable `bar`?
-fail_compilation/fail347.d(23): Error: no property `ofo` for type `S`, did you mean `fail347.S.foo`?
-fail_compilation/fail347.d(24): Error: undefined identifier `strlenx`, did you mean function `strlen`?
+fail_compilation/fail347.d(26): Error: undefined identifier `bbr`, did you mean variable `bar`?
+fail_compilation/fail347.d(27): Error: no property `ofo` for type `S`, did you mean `fail347.S.foo`?
+fail_compilation/fail347.d(29): Error: no property `fool` for `sp` of type `fail347.S*`
+fail_compilation/fail347.d(29): did you mean variable `foo`?
+fail_compilation/fail347.d(30): Error: undefined identifier `strlenx`, did you mean function `strlen`?
+fail_compilation/fail347.d(31): Error: no property `strlenx` for `"hello"` of type `string`
+fail_compilation/fail347.d(31): did you mean function `strlen`?
---
*/
@@ -21,5 +25,8 @@ void main()
S bar;
bbr.foo = 3;
bar.ofo = 4;
+ auto sp = &bar;
+ sp.fool = 5;
auto s = strlenx("hello");
+ auto q = "hello".strlenx();
}
@@ -7,7 +7,7 @@ fail_compilation/retscope.d(32): Error: returning `b ? nested1(& i) : nested2(&
fail_compilation/retscope.d(45): Error: scope variable `p` assigned to global variable `q`
fail_compilation/retscope.d(47): Error: address of variable `i` assigned to `q` with longer lifetime
fail_compilation/retscope.d(48): Error: scope variable `a` assigned to global variable `b`
-fail_compilation/retscope.d(49): Error: address of struct temporary returned by `(*fp2)()` assigned to longer lived variable `q`
+fail_compilation/retscope.d(49): Error: address of expression temporary returned by `(*fp2)()` assigned to `q` with longer lifetime
---
*/
@@ -299,15 +299,31 @@ struct S7
/***************************************************/
+/*
+TEST_OUTPUT:
+---
+fail_compilation/retscope.d(315): Error: scope parameter `p` may not be returned
+fail_compilation/retscope.d(316): Error: returning `p[]` escapes a reference to parameter `p`
+fail_compilation/retscope.d(319): Error: scope parameter `p` may not be returned
+---
+*/
+
int[3] escape8(scope int[] p) @safe { return p[0 .. 3]; } // should not error
char*[3] escape9(scope char*[] p) @safe { return p[0 .. 3]; }
+// https://issues.dlang.org/show_bug.cgi?id=24663
+ int*[3] escape8b(scope int*[3] p) @safe { return p[]; }
+ref int*[3] escape9b( int*[3] p) @safe { return p[]; }
+
+ref int[3] asStatic(return scope int[] p) @safe { return p[0 .. 3]; }
+ref int[3] asStatic2( scope int[] p) @safe { return p[0 .. 3]; }
+
/***************************************************/
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(319): Error: reference to local variable `i` assigned to non-scope `f`
+fail_compilation/retscope.d(335): Error: reference to local variable `i` assigned to non-scope `f`
---
*/
@@ -331,7 +347,7 @@ int* bar10( scope int** ptr ) @safe
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(342): Error: cannot take address of `scope` variable `aa` since `scope` applies to first indirection only
+fail_compilation/retscope.d(358): Error: cannot take address of `scope` variable `aa` since `scope` applies to first indirection only
---
*/
@@ -53,7 +53,7 @@ void bar4()
/*
TEST_OUTPUT:
---
-fail_compilation/retscope3.d(4003): Error: copying `u[]` into allocated memory escapes a reference to variadic parameter `u`
+fail_compilation/retscope3.d(4003): Error: copying `u[]` into allocated memory escapes a reference to parameter `u`
fail_compilation/retscope3.d(4016): Error: storing reference to outer local variable `i` into allocated memory causes it to escape
fail_compilation/retscope3.d(4025): Error: storing reference to stack allocated value returned by `makeSA()` into allocated memory causes it to escape
---
@@ -78,8 +78,11 @@ void foo() @safe
fail_compilation/retscope6.d(8016): Error: address of variable `i` assigned to `p` with longer lifetime
fail_compilation/retscope6.d(8031): Error: reference to local variable `i` assigned to non-scope parameter `p` calling `betty`
fail_compilation/retscope6.d(8031): Error: reference to local variable `j` assigned to non-scope parameter `q` calling `betty`
-fail_compilation/retscope6.d(8021): which is assigned to non-scope parameter `p`
+fail_compilation/retscope6.d(8023): which is not `scope` because of `p = q`
+fail_compilation/retscope6.d(8048): Error: reference to local variable `i` assigned to non-scope parameter `p` calling `archie`
+fail_compilation/retscope6.d(8039): which is not `scope` because of `r = p`
fail_compilation/retscope6.d(8048): Error: reference to local variable `j` assigned to non-scope parameter `q` calling `archie`
+fail_compilation/retscope6.d(8038): which is not `scope` because of `p = q`
---
*/
@@ -109,8 +112,8 @@ void testfrankly()
void betty()(int* p, int* q)
{
- p = q;
- escape(p);
+ p = q;
+ escape(p);
}
void testbetty()
@@ -124,9 +127,9 @@ void testbetty()
void archie()(int* p, int* q, int* r)
{
- p = q;
- r = p;
- escape(q);
+ p = q;
+ r = p;
+ escape(q);
}
void testarchie()
@@ -293,3 +296,26 @@ ref int escape23021() @safe
}
/******************************/
+
+/* TEST_OUTPUT:
+---
+fail_compilation/retscope6.d(14050): Error: scope variable `z` assigned to non-scope parameter `y` calling `f23294`
+fail_compilation/retscope6.d(14044): which is not `scope` because of `x = y`
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=23294
+
+@safe:
+int g23294;
+
+auto f23294(int* x, int* y)
+{
+ x = y;
+ g23294++; // make sure it's not inferring scope from pure
+}
+
+void escape23294(scope int* z)
+{
+ f23294(z, z); // passes
+}
new file mode 100644
@@ -0,0 +1,39 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/staticassert_sema1.d(17): Error: static assert: "unsupported OS"
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=24645
+// Test that a static assert(0) is not drowned out by subsequent import errors.
+
+version(_NONEXISTENT_OS)
+{
+
+}
+else
+{
+ static assert(0, msg);
+}
+
+import object: _NONEXISTENT;
+import object: _NONEXISTENT;
+import object: _NONEXISTENT;
+import object: _NONEXISTENT;
+import object: _NONEXISTENT;
+import object: _NONEXISTENT;
+import object: _NONEXISTENT;
+import object: _NONEXISTENT;
+import object: _NONEXISTENT;
+import object: _NONEXISTENT;
+import object: _NONEXISTENT;
+import object: _NONEXISTENT;
+import object: _NONEXISTENT;
+import object: _NONEXISTENT;
+import object: _NONEXISTENT;
+import object: _NONEXISTENT;
+import object: _NONEXISTENT;
+import object: _NONEXISTENT;
+
+enum msg = "unsupported OS";
@@ -1,17 +1,19 @@
/* REQUIRED_ARGS: -preview=dip1000
TEST_OUTPUT:
---
-fail_compilation/test14238.d(20): Error: scope parameter `fn` may not be returned
-fail_compilation/test14238.d(28): Error: escaping reference to stack allocated value returned by `&baz`
+fail_compilation/test14238.d(22): Error: scope parameter `fn` may not be returned
+fail_compilation/test14238.d(25): Error: function `test14238.bar` is `@nogc` yet allocates closure for `bar()` with the GC
+fail_compilation/test14238.d(27): function `test14238.bar.baz` closes over variable `x`
+fail_compilation/test14238.d(26): `x` declared here
---
*/
// https://issues.dlang.org/show_bug.cgi?id=14238
@safe:
-alias Fn = ref int delegate() return;
+alias Fn = ref int delegate() return @nogc;
-ref int foo(return scope Fn fn)
+ref int call(return scope Fn fn) @nogc
{
return fn(); // Ok
}
@@ -20,10 +22,14 @@ ref int foo2(scope Fn fn) {
return fn(); // Error
}
-ref int bar() {
+ref int bar() @nogc {
int x;
ref int baz() {
return x;
}
- return foo(&baz);
+
+ if (x == 0)
+ return call(&baz);
+ else
+ return (&baz)();
}
@@ -1,9 +1,9 @@
/* REQUIRED_ARGS: -preview=dip1000
TEST_OUTPUT:
---
-fail_compilation/test18644.d(15): Error: storing reference to stack allocated value returned by `foo()` into allocated memory causes it to escape
-fail_compilation/test18644.d(16): Error: escaping reference to stack allocated value returned by `foo()`
-fail_compilation/test18644.d(22): Error: escaping reference to stack allocated value returned by `foo()`
+fail_compilation/test18644.d(15): Error: nested function `foo` returns `scope` values and escapes them into allocated memory
+fail_compilation/test18644.d(16): Error: escaping local variable through nested function `foo`
+fail_compilation/test18644.d(22): Error: escaping local variable through nested function `foo`
---
*/
@@ -3,7 +3,7 @@ REQUIRED_ARGS: -preview=dip1000
TEST_OUTPUT:
---
fail_compilation/test22977.d(16): Error: escaping local variable through nested function `scfunc`
-fail_compilation/test22977.d(22): Error: escaping reference to stack allocated value returned by `scfunc2()`
+fail_compilation/test22977.d(22): Error: escaping local variable through nested function `scfunc2`
---
*/
@@ -2,7 +2,7 @@
REQUIRED_ARGS: -preview=dip1000
TEST_OUTPUT:
---
-fail_compilation/test23022.d(14): Error: scope parameter `p` may not be returned
+fail_compilation/test23022.d(14): Error: returning `p` escapes a reference to variadic parameter `p`
---
*/
new file mode 100644
@@ -0,0 +1,20 @@
+/**
+REQUIRED_ARGS: -preview=dip1000
+TEST_OUTPUT:
+---
+fail_compilation/test24680.d(19): Error: returning `c.peek(buf[])` escapes a reference to local variable `buf`
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=24680
+
+class C
+{
+ final auto peek(ubyte[] buf) { return buf; }
+}
+
+@safe escape(C c)
+{
+ ubyte[5] buf;
+ return c.peek(buf[]);
+}
@@ -17,14 +17,14 @@ fail_compilation/typeerrors.d(52): Error: cannot have associative array of `void
fail_compilation/typeerrors.d(54): Error: cannot have parameter of type `void`
fail_compilation/typeerrors.d(56): Error: slice `[1..5]` is out of range of [0..4]
fail_compilation/typeerrors.d(57): Error: slice `[2..1]` is out of range of [0..4]
+fail_compilation/typeerrors.d(59): Error: variable `typeerrors.foo.globalC` globals, statics, fields, manifest constants, ref and out parameters cannot be `scope`
+fail_compilation/typeerrors.d(59): Error: variable `typeerrors.foo.globalC` reference to `scope class` must be `scope`
+fail_compilation/typeerrors.d(60): Error: variable `typeerrors.foo.manifestC` globals, statics, fields, manifest constants, ref and out parameters cannot be `scope`
+fail_compilation/typeerrors.d(60): Error: variable `typeerrors.foo.manifestC` reference to `scope class` must be `scope`
---
*/
-
-
-
-
template tuple(T...) { alias T tuple; }
void bar();
@@ -55,4 +55,7 @@ void foo()
alias T2 = T[1 .. 5];
alias T3 = T[2 .. 1];
+
+ static C globalC;
+ enum C manifestC = new C();
}
@@ -36,5 +36,6 @@ fail_compilation/verrors5.d(6): Error: undefined identifier `T`
fail_compilation/verrors5.d(7): Error: undefined identifier `T`
fail_compilation/verrors5.d(8): Error: undefined identifier `T`
fail_compilation/verrors5.d(9): Error: undefined identifier `T`
+error limit (5) reached, use `-verrors=0` to show all
---
*/
@@ -406,6 +406,33 @@ void test13950()
/***************************************************/
+
+void testlocalref()
+{
+ int x = 4;
+ ref int rx = x;
+ rx = 5;
+ assert(x == 5);
+ ref int r2 = rx;
+ r2 = 6;
+ assert(x == 6);
+}
+
+/***************************************************/
+
+int global;
+
+ref int tgr() { return global; }
+
+void testglobalref()
+{
+ auto i = tgr();
+ i = 1;
+ assert(global == 0);
+}
+
+/***************************************************/
+
int main()
{
test6475();
@@ -419,6 +446,8 @@ int main()
test10142();
test11421();
test13950();
+ testlocalref();
+ testglobalref();
printf("Success\n");
return 0;
@@ -1,5 +1,5 @@
// EXTRA_FILES: imports/std11069array.d imports/std11069container.d imports/std11069range.d imports/std11069typecons.d
-// REQUIRED_ARGS: -noboundscheck
+// REQUIRED_ARGS: -boundscheck=off
// <-- To remove necessity of _D7imports13std11069array7__arrayZ
class Bar
@@ -1,4 +1,4 @@
-66b93fc24a7ab5e2a8aa7f53c613df4abddc188b
+34875cd6e1faa42e84ae953c0485ef524fe67e38
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.
@@ -1038,9 +1038,9 @@ bool hasCPUID()
void cpuidX86()
{
- datacache[0].size = 8;
- datacache[0].associativity = 2;
- datacache[0].lineSize = 32;
+ datacache[0].size = 8;
+ datacache[0].associativity = 2;
+ datacache[0].lineSize = 32;
}
}
@@ -3187,9 +3187,7 @@ private struct BufSlice
size_t from;
size_t to;
- @safe:
- pure:
- nothrow:
+ @safe pure nothrow:
@disable this();
@@ -3209,7 +3207,7 @@ private struct BufSlice
this.to = to;
}
- invariant()
+ invariant
{
if (buf is null)
{
@@ -62,7 +62,7 @@ Tret _d_arraycatnTX(Tret, Tarr...)(auto ref Tarr froms) @trusted
{
size_t i = 0;
foreach (ref from; froms)
- static if (is (typeof(from) : T))
+ static if (is(typeof(from) : T))
copyEmplace(cast(T) from, res[i++]);
else
{
@@ -530,7 +530,7 @@ Tarr _d_newarraymTX(Tarr : U[], T, U)(size_t[] dims, bool isShared=false) @trust
if (dims.length == 1)
{
auto r = _d_newarrayT!UnqT(dim, isShared);
- return *cast(void[]*)(&r);
+ return *cast(void[]*)&r;
}
auto allocSize = (void[]).sizeof * dim;
@@ -983,7 +983,7 @@ else version (GNU)
// Internal static mutex reference.
private AtomicMutex* _getAtomicMutex() @trusted @nogc nothrow
{
- __gshared static AtomicMutex mutex;
+ __gshared AtomicMutex mutex;
return &mutex;
}
@@ -695,7 +695,7 @@ const(ubyte)[] toUbyte(T)(const ref scope T val) if (__traits(isIntegral, T) &&
}
else
{
- return (cast(const(ubyte)*)(&val))[0 .. T.sizeof];
+ return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
}
}
else if (__ctfe)
@@ -715,7 +715,7 @@ const(ubyte)[] toUbyte(T)(const ref scope T val) if (__traits(isIntegral, T) &&
}
else
{
- return (cast(const(ubyte)*)(&val))[0 .. T.sizeof];
+ return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
}
}
@@ -134,7 +134,7 @@ struct GCBits
}
else
{
- auto pos = i >> BITS_SHIFT;
+ const pos = i >> BITS_SHIFT;
auto pdata = cast(shared)(data + pos);
auto mask = BITS_1 << (i & BITS_MASK);
auto state = *pdata;
@@ -404,7 +404,7 @@ class ConservativeGC : GC
p = sentinel_sub(p);
if (p != pool.findBase(p))
return 0;
- auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
+ const biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
oldb = pool.getBits(biti);
pool.setBits(biti, mask);
@@ -447,7 +447,7 @@ class ConservativeGC : GC
p = sentinel_sub(p);
if (p != pool.findBase(p))
return 0;
- auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
+ const biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
oldb = pool.getBits(biti);
pool.clrBits(biti, mask);
@@ -784,25 +784,25 @@ class ConservativeGC : GC
return 0;
auto lpool = cast(LargeObjectPool*) pool;
- size_t pagenum = lpool.pagenumOf(p);
+ const pagenum = lpool.pagenumOf(p);
if (lpool.pagetable[pagenum] != Bins.B_PAGE)
return 0;
- size_t psz = lpool.bPageOffsets[pagenum];
+ uint psz = lpool.bPageOffsets[pagenum];
assert(psz > 0);
- auto minsz = lpool.numPages(minsize);
- auto maxsz = lpool.numPages(maxsize);
+ const minsz = lpool.numPages(minsize);
+ const maxsz = lpool.numPages(maxsize);
if (pagenum + psz >= lpool.npages)
return 0;
if (lpool.pagetable[pagenum + psz] != Bins.B_FREE)
return 0;
- size_t freesz = lpool.bPageOffsets[pagenum + psz];
+ const freesz = lpool.bPageOffsets[pagenum + psz];
if (freesz < minsz)
return 0;
- size_t sz = freesz > maxsz ? maxsz : freesz;
+ const sz = freesz > maxsz ? maxsz : freesz;
invalidate((pool.baseAddr + (pagenum + psz) * PAGESIZE)[0 .. sz * PAGESIZE], 0xF0, true);
memset(lpool.pagetable + pagenum + psz, Bins.B_PAGEPLUS, sz);
lpool.bPageOffsets[pagenum] = cast(uint) (psz + sz);
@@ -1609,7 +1609,7 @@ struct Gcx
void Invariant() const { }
debug(INVARIANT)
- invariant()
+ invariant
{
if (initialized)
{
@@ -3171,8 +3171,7 @@ Lmark:
{
// first, we find the Pool this block is in, then check to see if the
// mark bit is clear.
- auto pool = findPool(addr);
- if (pool)
+ if (auto pool = findPool(addr))
{
auto offset = cast(size_t)(addr - pool.baseAddr);
auto pn = offset / PAGESIZE;
@@ -3924,7 +3923,7 @@ struct Pool
void Invariant() const {}
debug(INVARIANT)
- invariant()
+ invariant
{
if (baseAddr)
{
@@ -959,7 +959,7 @@ version (CRuntime_Microsoft) // supported since MSVCRT 12 (VS 2013) only
double num;
double denom;
}
- static __gshared immutable(Entry[5]) table =
+ static immutable Entry[5] table =
[ // Raise exception by evaluating num / denom:
{ FE_INVALID, 0.0, 0.0 },
{ FE_DIVBYZERO, 1.0, 0.0 },
@@ -18,6 +18,7 @@ module core.stdc.stdatomic;
import core.atomic : MemoryOrder;
import core.internal.atomic;
import core.stdc.config;
+import core.stdc.stddef;
import core.stdc.stdint;
@safe nothrow @nogc:
@@ -54,7 +55,7 @@ enum
///
ATOMIC_CHAR32_T_LOCK_FREE = IsAtomicLockFree!dchar ? 2 : 0,
///
- ATOMIC_WCHAR_T_LOCK_FREE = ATOMIC_CHAR16_T_LOCK_FREE,
+ ATOMIC_WCHAR_T_LOCK_FREE = IsAtomicLockFree!wchar_t ? 2 : 0,
///
ATOMIC_SHORT_LOCK_FREE = IsAtomicLockFree!short ? 2 : 0,
///
@@ -365,7 +366,7 @@ alias atomic_char16_t = shared(wchar);
///
alias atomic_char32_t = shared(dchar);
///
-alias atomic_wchar_t = shared(wchar);
+alias atomic_wchar_t = shared(wchar_t);
///
alias atomic_int_least8_t = shared(int_least8_t);
@@ -480,13 +480,21 @@ enum int_fast64_t INT_FAST64_MIN = int_fast64_t.min;
///
enum int_fast64_t INT_FAST64_MAX = int_fast64_t.max;
+///
+enum uint_fast8_t UINT_FAST8_MIN = uint_fast8_t.min;
///
enum uint_fast8_t UINT_FAST8_MAX = uint_fast8_t.max;
///
+enum uint_fast16_t UINT_FAST16_MIN = uint_fast16_t.min;
+///
enum uint_fast16_t UINT_FAST16_MAX = uint_fast16_t.max;
///
+enum uint_fast32_t UINT_FAST32_MIN = uint_fast32_t.min;
+///
enum uint_fast32_t UINT_FAST32_MAX = uint_fast32_t.max;
///
+enum uint_fast64_t UINT_FAST64_MIN = uint_fast64_t.min;
+///
enum uint_fast64_t UINT_FAST64_MAX = uint_fast64_t.max;
///
@@ -14,6 +14,7 @@
module core.stdc.wctype;
+import core.stdc.config;
public import core.stdc.wchar_; // for wint_t, WEOF
extern (C):
@@ -21,10 +22,41 @@ extern (C):
nothrow:
@nogc:
-///
-alias wchar_t wctrans_t;
-///
-alias wchar_t wctype_t;
+version (CRuntime_Glibc)
+{
+ ///
+ alias wctype_t = c_ulong;
+ ///
+ alias wctrans_t = const(int)*;
+}
+else version (CRuntime_Musl)
+{
+ ///
+ alias wctype_t = c_ulong;
+ ///
+ alias wctrans_t = const(int)*;
+}
+else version (FreeBSD)
+{
+ ///
+ alias wctype_t = c_ulong;
+ ///
+ alias wctrans_t = int;
+}
+else version (CRuntime_Bionic)
+{
+ ///
+ alias wctype_t = c_long;
+ ///
+ alias wctrans_t = const(void)*;
+}
+else
+{
+ ///
+ alias wchar_t wctrans_t;
+ ///
+ alias wchar_t wctype_t;
+}
///
pure int iswalnum(wint_t wc);
@@ -63,3 +95,17 @@ pure wint_t towupper(wint_t wc);
wint_t towctrans(wint_t wc, wctrans_t desc);
///
@system wctrans_t wctrans(const scope char* property);
+
+unittest
+{
+ assert(iswalpha('A'));
+ assert(!iswalpha('0'));
+ wctype_t alpha = wctype("alpha");
+ assert(alpha);
+ wctrans_t tolower = wctrans("tolower");
+ assert(tolower);
+ assert(iswctype('A', alpha));
+ assert(!iswctype('0', alpha));
+ assert(towctrans('A', tolower) == 'a');
+ assert(towctrans('0', tolower) == '0');
+}
@@ -37,7 +37,7 @@ extern(C++, struct) struct char_traits(CharT) {}
/**
* D language counterpart to C++ std::basic_string_view.
*
-* C++ reference: $(LINK2 hhttps://en.cppreference.com/w/cpp/string/basic_string_view)
+* C++ reference: $(LINK2 https://en.cppreference.com/w/cpp/string/basic_string_view, std::basic_string_view)
*/
extern(C++, class) struct basic_string_view(T, Traits = char_traits!T)
{
@@ -136,7 +136,7 @@ version (Solaris)
import core.sys.posix.unistd;
@property int SIGRTMIN() nothrow @nogc {
- __gshared static int sig = -1;
+ __gshared int sig = -1;
if (sig == -1) {
sig = cast(int)sysconf(_SC_SIGRT_MIN);
}
@@ -144,7 +144,7 @@ version (Solaris)
}
@property int SIGRTMAX() nothrow @nogc {
- __gshared static int sig = -1;
+ __gshared int sig = -1;
if (sig == -1) {
sig = cast(int)sysconf(_SC_SIGRT_MAX);
}
@@ -180,7 +180,7 @@ else version (linux)
}
@property int SIGRTMIN() nothrow @nogc {
- __gshared static int sig = -1;
+ __gshared int sig = -1;
if (sig == -1) {
sig = __libc_current_sigrtmin();
}
@@ -188,7 +188,7 @@ else version (linux)
}
@property int SIGRTMAX() nothrow @nogc {
- __gshared static int sig = -1;
+ __gshared int sig = -1;
if (sig == -1) {
sig = __libc_current_sigrtmax();
}
@@ -82,14 +82,14 @@ version (linux)
}
static if (__USE_FILE_OFFSET64)
- alias ulong rlim_t;
+ alias ulong rlim_t;
else
- alias c_ulong rlim_t;
+ alias c_ulong rlim_t;
static if (__USE_FILE_OFFSET64)
enum RLIM_INFINITY = 0xffffffffffffffffUL;
else
- enum RLIM_INFINITY = cast(c_ulong)(~0UL);
+ enum RLIM_INFINITY = cast(c_ulong)~0UL;
enum RLIM_SAVED_MAX = RLIM_INFINITY;
enum RLIM_SAVED_CUR = RLIM_INFINITY;
@@ -163,7 +163,7 @@ else version (Darwin)
enum
{
- RLIM_INFINITY = ((cast(ulong) 1 << 63) - 1),
+ RLIM_INFINITY = ((1UL << 63) - 1),
RLIM_SAVED_MAX = RLIM_INFINITY,
RLIM_SAVED_CUR = RLIM_INFINITY,
}
@@ -205,7 +205,7 @@ else version (FreeBSD)
enum
{
- RLIM_INFINITY = (cast(rlim_t)((cast(ulong) 1 << 63) - 1)),
+ RLIM_INFINITY = (cast(rlim_t)((1UL << 63) - 1)),
RLIM_SAVED_MAX = RLIM_INFINITY,
RLIM_SAVED_CUR = RLIM_INFINITY,
}
@@ -262,7 +262,7 @@ else version (NetBSD)
enum
{
- RLIM_INFINITY = (cast(rlim_t)((cast(ulong) 1 << 63) - 1)),
+ RLIM_INFINITY = (cast(rlim_t)((1UL << 63) - 1)),
RLIM_SAVED_MAX = RLIM_INFINITY,
RLIM_SAVED_CUR = RLIM_INFINITY,
}
@@ -319,7 +319,7 @@ else version (OpenBSD)
enum
{
- RLIM_INFINITY = (cast(rlim_t)((cast(ulong) 1 << 63) - 1)),
+ RLIM_INFINITY = (cast(rlim_t)((1UL << 63) - 1)),
RLIM_SAVED_MAX = RLIM_INFINITY,
RLIM_SAVED_CUR = RLIM_INFINITY,
}
@@ -378,7 +378,7 @@ else version (DragonFlyBSD)
enum
{
- RLIM_INFINITY = (cast(rlim_t)((cast(ulong) 1 << 63) - 1)),
+ RLIM_INFINITY = (cast(rlim_t)((1UL << 63) - 1)),
RLIM_SAVED_MAX = RLIM_INFINITY,
RLIM_SAVED_CUR = RLIM_INFINITY,
}
@@ -117,19 +117,19 @@ version (Win32)
// _NtdllBaseTag - tag used for RtlAllocateHeap
// _LdrpTlsList - root of the double linked list with TlsList entries
- static __gshared int* pNtdllBaseTag; // remembered for reusage in addTlsData
-
- static __gshared ubyte[] jmp_LdrpInitialize = [ 0x33, 0xED, 0xE9 ]; // xor ebp,ebp; jmp _LdrpInitialize
- static __gshared ubyte[] jmp__LdrpInitialize = [ 0x5D, 0xE9 ]; // pop ebp; jmp __LdrpInitialize
- static __gshared ubyte[] jmp__LdrpInitialize_xp64 = [ 0x5D, 0x90, 0x90, 0x90, 0x90, 0x90 ]; // pop ebp; nop; nop; nop; nop; nop;
- static __gshared ubyte[] call_LdrpInitializeThread = [ 0xFF, 0x75, 0x08, 0xE8 ]; // push [ebp+8]; call _LdrpInitializeThread
- static __gshared ubyte[] call_LdrpAllocateTls = [ 0x00, 0x00, 0xE8 ]; // jne 0xc3; call _LdrpAllocateTls
- static __gshared ubyte[] call_LdrpAllocateTls_svr03 = [ 0x65, 0xfc, 0x00, 0xE8 ]; // and [ebp+fc], 0; call _LdrpAllocateTls
- static __gshared ubyte[] jne_LdrpAllocateTls = [ 0x0f, 0x85 ]; // jne body_LdrpAllocateTls
- static __gshared ubyte[] mov_LdrpNumberOfTlsEntries = [ 0x8B, 0x0D ]; // mov ecx, _LdrpNumberOfTlsEntries
- static __gshared ubyte[] mov_NtdllBaseTag = [ 0x51, 0x8B, 0x0D ]; // push ecx; mov ecx, _NtdllBaseTag
- static __gshared ubyte[] mov_NtdllBaseTag_srv03 = [ 0x50, 0xA1 ]; // push eax; mov eax, _NtdllBaseTag
- static __gshared ubyte[] mov_LdrpTlsList = [ 0x8B, 0x3D ]; // mov edi, _LdrpTlsList
+ __gshared int* pNtdllBaseTag; // remembered for reusage in addTlsData
+
+ __gshared ubyte[] jmp_LdrpInitialize = [ 0x33, 0xED, 0xE9 ]; // xor ebp,ebp; jmp _LdrpInitialize
+ __gshared ubyte[] jmp__LdrpInitialize = [ 0x5D, 0xE9 ]; // pop ebp; jmp __LdrpInitialize
+ __gshared ubyte[] jmp__LdrpInitialize_xp64 = [ 0x5D, 0x90, 0x90, 0x90, 0x90, 0x90 ]; // pop ebp; nop; nop; nop; nop; nop;
+ __gshared ubyte[] call_LdrpInitializeThread = [ 0xFF, 0x75, 0x08, 0xE8 ]; // push [ebp+8]; call _LdrpInitializeThread
+ __gshared ubyte[] call_LdrpAllocateTls = [ 0x00, 0x00, 0xE8 ]; // jne 0xc3; call _LdrpAllocateTls
+ __gshared ubyte[] call_LdrpAllocateTls_svr03 = [ 0x65, 0xfc, 0x00, 0xE8 ]; // and [ebp+fc], 0; call _LdrpAllocateTls
+ __gshared ubyte[] jne_LdrpAllocateTls = [ 0x0f, 0x85 ]; // jne body_LdrpAllocateTls
+ __gshared ubyte[] mov_LdrpNumberOfTlsEntries = [ 0x8B, 0x0D ]; // mov ecx, _LdrpNumberOfTlsEntries
+ __gshared ubyte[] mov_NtdllBaseTag = [ 0x51, 0x8B, 0x0D ]; // push ecx; mov ecx, _NtdllBaseTag
+ __gshared ubyte[] mov_NtdllBaseTag_srv03 = [ 0x50, 0xA1 ]; // push eax; mov eax, _NtdllBaseTag
+ __gshared ubyte[] mov_LdrpTlsList = [ 0x8B, 0x3D ]; // mov edi, _LdrpTlsList
static LdrpTlsListEntry* addTlsListEntry( void** peb, void* tlsstart, void* tlsend, void* tls_callbacks_a, int* tlsindex ) nothrow
{
@@ -701,9 +701,10 @@ public:
version (CoreUnittest) unittest
{
- foreach (D; AliasSeq!(Duration, const Duration, immutable Duration))
+ alias Types = AliasSeq!(Duration, const Duration, immutable Duration);
+ foreach (D; Types)
{
- foreach (E; AliasSeq!(Duration, const Duration, immutable Duration))
+ foreach (E; Types)
{
assert((cast(D)Duration(5)) + (cast(E)Duration(7)) == Duration(12));
assert((cast(D)Duration(5)) - (cast(E)Duration(7)) == Duration(-2));
@@ -3191,6 +3191,10 @@ auto byValue(T : V[K], K, V)(T* aa) pure nothrow @nogc
sum += v;
assert(sum == 3);
+
+ foreach (ref v; dict.byValue)
+ v++;
+ assert(dict == ["k1": 2, "k2": 3]);
}
/***********************************
@@ -3214,7 +3218,7 @@ auto byValue(T : V[K], K, V)(T* aa) pure nothrow @nogc
*---
*
* Note that this is a low-level interface to iterating over the associative
- * array and is not compatible withth the
+ * array and is not compatible with the
* $(LINK2 $(ROOT_DIR)phobos/std_typecons.html#.Tuple,`Tuple`) type in Phobos.
* For compatibility with `Tuple`, use
* $(LINK2 $(ROOT_DIR)phobos/std_array.html#.byPair,std.array.byPair) instead.
@@ -3278,8 +3282,11 @@ auto byKeyValue(T : V[K], K, V)(T* aa) pure nothrow @nogc
assert(e.key[1] == e.value + '0');
sum += e.value;
}
-
assert(sum == 3);
+
+ foreach (e; dict.byKeyValue)
+ e.value++;
+ assert(dict == ["k1": 2, "k2": 3]);
}
/***********************************
@@ -3451,8 +3458,8 @@ Value[] values(T : Value[Key], Value, Key)(T *aa) @property
}
/***********************************
- * Looks up key; if it exists returns corresponding value else evaluates and
- * returns defaultValue.
+ * If `key` is in `aa`, returns corresponding value; otherwise it evaluates and
+ * returns `defaultValue`.
* Params:
* aa = The associative array.
* key = The key.
@@ -3481,8 +3488,8 @@ inout(V) get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue)
}
/***********************************
- * Looks up key; if it exists returns corresponding value else evaluates
- * value, adds it to the associative array and returns it.
+ * If `key` is in `aa`, returns corresponding value; otherwise it evaluates
+ * `value`, adds it to the associative array and returns it.
* Params:
* aa = The associative array.
* key = The key.
@@ -343,11 +343,11 @@ immutable(void)* rtinfoEntry(ref Impl aa, immutable(size_t)* keyinfo,
pos++;
}
if (keybits > 0)
- rtinfoData[pos] = mixin(src) & ((cast(size_t) 1 << keybits) - 1);
+ rtinfoData[pos] = mixin(src) & ((size_t(1) << keybits) - 1);
}
if (keyinfo is rtinfoHasPointers)
- copyKeyInfo!"~cast(size_t) 0"();
+ copyKeyInfo!"~size_t(0)"();
else if (keyinfo !is rtinfoNoPointers)
copyKeyInfo!"keyinfo[pos]"();
@@ -373,11 +373,11 @@ immutable(void)* rtinfoEntry(ref Impl aa, immutable(size_t)* keyinfo,
pos++;
}
if (endoff > 0)
- rtinfoData[dstpos] &= ((cast(size_t) 1 << endoff) - 1);
+ rtinfoData[dstpos] &= (size_t(1) << endoff) - 1;
}
if (valinfo is rtinfoHasPointers)
- copyValInfo!"~cast(size_t) 0"();
+ copyValInfo!"~size_t(0)"();
else if (valinfo !is rtinfoNoPointers)
copyValInfo!"valinfo[pos]"();
@@ -977,7 +977,7 @@ unittest
aa1 = null;
aa2 = null;
aa3 = null;
- GC.runFinalizers((cast(char*)(&entryDtor))[0 .. 1]);
+ GC.runFinalizers((cast(char*)&entryDtor)[0 .. 1]);
assert(T.dtor == 6 && T.postblit == 2);
}
@@ -167,7 +167,7 @@ void* _d_paint_cast(Object o, ClassInfo c)
{
/* If o is really an instance of c, just do a paint
*/
- auto p = (o && cast(void*)(areClassInfosEqual(typeid(o), c)) ? o : null);
+ auto p = o && cast(void*)(areClassInfosEqual(typeid(o), c)) ? o : null;
debug assert(cast(void*)p is cast(void*)_d_dynamic_cast(o, c));
return cast(void*)p;
}
@@ -129,8 +129,7 @@ string rt_envvarsOption(string opt, scope rt_configCallBack dg) @nogc nothrow
var[4 + i] = cast(char) toupper(c);
var[4 + opt.length] = 0;
- auto p = getenv(var.ptr);
- if (p)
+ if (auto p = getenv(var.ptr))
{
string s = dg(cast(string) p[0 .. strlen(p)]);
if (s != null)
@@ -607,7 +607,7 @@ extern (C) void _d_print_throwable(Throwable t)
void sink(in char[] s) scope nothrow
{
if (!s.length) return;
- int swlen = MultiByteToWideChar(
+ const swlen = MultiByteToWideChar(
CP_UTF8, 0, s.ptr, cast(int)s.length, null, 0);
if (!swlen) return;
@@ -615,7 +615,7 @@ extern (C) void _d_print_throwable(Throwable t)
(this.len + swlen + 1) * WCHAR.sizeof);
if (!newPtr) return;
ptr = newPtr;
- auto written = MultiByteToWideChar(
+ const written = MultiByteToWideChar(
CP_UTF8, 0, s.ptr, cast(int)s.length, ptr+len, swlen);
len += written;
}
@@ -633,10 +633,6 @@ extern (C) void _d_print_throwable(Throwable t)
return _fdToHandle(fd);
}
- auto hStdErr = windowsHandle(fileno(stderr));
- CONSOLE_SCREEN_BUFFER_INFO sbi;
- bool isConsole = GetConsoleScreenBufferInfo(hStdErr, &sbi) != 0;
-
// ensure the exception is shown at the beginning of the line, while also
// checking whether stderr is a valid file
int written = fprintf(stderr, "\n");
@@ -653,22 +649,22 @@ extern (C) void _d_print_throwable(Throwable t)
// Avoid static user32.dll dependency for console applications
// by loading it dynamically as needed
- auto user32 = LoadLibraryW("user32.dll");
- if (user32)
+ if (auto user32 = LoadLibraryW("user32.dll"))
{
alias typeof(&MessageBoxW) PMessageBoxW;
- auto pMessageBoxW = cast(PMessageBoxW)
- GetProcAddress(user32, "MessageBoxW");
- if (pMessageBoxW)
+ if (auto pMessageBoxW = cast(PMessageBoxW) GetProcAddress(user32, "MessageBoxW"))
pMessageBoxW(null, buf.get(), caption.get(), MB_ICONERROR);
+ FreeLibrary(user32);
}
- FreeLibrary(user32);
caption.free();
buf.free();
}
return;
}
- else if (isConsole)
+ auto hStdErr = windowsHandle(fileno(stderr));
+ CONSOLE_SCREEN_BUFFER_INFO sbi = void;
+ const isConsole = GetConsoleScreenBufferInfo(hStdErr, &sbi) != 0;
+ if (isConsole)
{
WSink buf;
formatThrowable(t, &buf.sink);
@@ -676,10 +672,9 @@ extern (C) void _d_print_throwable(Throwable t)
if (buf.ptr)
{
uint codepage = GetConsoleOutputCP();
- int slen = WideCharToMultiByte(codepage, 0,
+ const slen = WideCharToMultiByte(codepage, 0,
buf.ptr, cast(int)buf.len, null, 0, null, null);
- auto sptr = cast(char*)malloc(slen * char.sizeof);
- if (sptr)
+ if (auto sptr = cast(char*)malloc(slen * char.sizeof))
{
WideCharToMultiByte(codepage, 0,
buf.ptr, cast(int)buf.len, sptr, slen, null, null);
@@ -1941,15 +1941,15 @@ byte[] _d_arrayappendcTX(const TypeInfo ti, return scope ref byte[] px, size_t n
__setArrayAllocLength(info, newsize, isshared, tinext);
if (!isshared)
__insertBlkInfoCache(info, bic);
- auto newdata = cast(byte *)__arrayStart(info);
+ auto newdata = cast(byte*)__arrayStart(info);
memcpy(newdata, px.ptr, length * sizeelem);
// do postblit processing
__doPostblit(newdata, length * sizeelem, tinext);
- (cast(void **)(&px))[1] = newdata;
+ (cast(void**)&px)[1] = newdata;
}
L1:
- *cast(size_t *)&px = newlength;
+ *cast(size_t*)&px = newlength;
return px;
}
@@ -2417,14 +2417,14 @@ deprecated unittest
// associative arrays
import rt.aaA : entryDtor;
// throw away all existing AA entries with dtor
- GC.runFinalizers((cast(char*)(&entryDtor))[0..1]);
+ GC.runFinalizers((cast(char*)&entryDtor)[0..1]);
S1[int] aa1;
aa1[0] = S1(0);
aa1[1] = S1(1);
dtorCount = 0;
aa1 = null;
- GC.runFinalizers((cast(char*)(&entryDtor))[0..1]);
+ GC.runFinalizers((cast(char*)&entryDtor)[0..1]);
assert(dtorCount == 2);
int[S1] aa2;
@@ -2433,7 +2433,7 @@ deprecated unittest
aa2[S1(2)] = 2;
dtorCount = 0;
aa2 = null;
- GC.runFinalizers((cast(char*)(&entryDtor))[0..1]);
+ GC.runFinalizers((cast(char*)&entryDtor)[0..1]);
assert(dtorCount == 3);
S1[2][int] aa3;
@@ -2441,7 +2441,7 @@ deprecated unittest
aa3[1] = [S1(1),S1(3)];
dtorCount = 0;
aa3 = null;
- GC.runFinalizers((cast(char*)(&entryDtor))[0..1]);
+ GC.runFinalizers((cast(char*)&entryDtor)[0..1]);
assert(dtorCount == 4);
}
@@ -27,7 +27,7 @@ do
auto m = ensureMonitor(cast(Object) owner);
if (m.impl is null)
{
- atomicOp!("+=")(m.refs, cast(size_t) 1);
+ atomicOp!"+="(m.refs, size_t(1));
}
// Assume the monitor is garbage collected and simply copy the reference.
ownee.__monitor = owner.__monitor;
@@ -44,7 +44,7 @@ extern (C) void _d_monitordelete(Object h, bool det)
// let the GC collect the monitor
setMonitor(h, null);
}
- else if (!atomicOp!("-=")(m.refs, cast(size_t) 1))
+ else if (!atomicOp!"-="(m.refs, size_t(1)))
{
// refcount == 0 means unshared => no synchronization required
disposeEvent(cast(Monitor*) m, h);
@@ -65,7 +65,7 @@ extern (C) void _d_monitordelete_nogc(Object h) @nogc nothrow
// let the GC collect the monitor
setMonitor(h, null);
}
- else if (!atomicOp!("-=")(m.refs, cast(size_t) 1))
+ else if (!atomicOp!"-="(m.refs, size_t(1)))
{
// refcount == 0 means unshared => no synchronization required
deleteMonitor(cast(Monitor*) m);
@@ -231,7 +231,7 @@ private:
return *cast(shared Monitor**)&h.__monitor;
}
-private shared(Monitor)* getMonitor(Object h) pure @nogc
+shared(Monitor)* getMonitor(Object h) pure @nogc
{
return atomicLoad!(MemoryOrder.acq)(h.monitor);
}
@@ -203,10 +203,10 @@ detect if we need to override. The overriding initializer should be nonzero.
private class TypeInfoGeneric(T, Base = T) : Select!(is(T == Base), TypeInfo, TypeInfoGeneric!Base)
if (T.sizeof == Base.sizeof && T.alignof == Base.alignof)
{
- const: nothrow: pure: @trusted:
+ const: nothrow pure @trusted:
// Returns the type name.
- override string toString() const pure nothrow @safe { return T.stringof; }
+ override string toString() const @safe { return T.stringof; }
// `getHash` is the same for `Base` and `T`, introduce it just once.
static if (is(T == Base))
@@ -390,16 +390,16 @@ unittest
// void
class TypeInfo_v : TypeInfoGeneric!ubyte
{
- const: nothrow: pure: @trusted:
+ const nothrow pure @trusted:
- override string toString() const pure nothrow @safe { return "void"; }
+ override string toString() const @safe { return "void"; }
override size_t getHash(scope const void* p)
{
assert(0);
}
- override @property uint flags() nothrow pure
+ override @property uint flags()
{
return 1;
}
@@ -640,7 +640,7 @@ class TypeInfo_n : TypeInfo
override @property size_t tsize() { return typeof(null).sizeof; }
- override const(void)[] initializer() @trusted { return (cast(void *)null)[0 .. size_t.sizeof]; }
+ override const(void)[] initializer() @trusted { return (cast(void*)null)[0 .. size_t.sizeof]; }
override void swap(void*, void*) {}
@@ -1,4 +1,4 @@
-0c28620c301c9ae3136b1e1e5af55c290dbc7aae
+ebd24da8add9243c52e5cb346dcdf7acf3e6cbac
The first line of this file holds the git revision number of the last
merge done from the dlang/phobos repository.
@@ -1071,10 +1071,20 @@ Params:
copy is performed.
*/
void move(T)(ref T source, ref T target)
+if (__traits(compiles, target = T.init))
{
moveImpl(target, source);
}
+/// ditto
+template move(T)
+if (!__traits(compiles, imported!"std.traits".lvalueOf!T = T.init))
+{
+ ///
+ deprecated("Can't move into `target` as `" ~ T.stringof ~ "` can't be assigned")
+ void move(ref T source, ref T target) => moveImpl(target, source);
+}
+
/// For non-struct types, `move` just performs `target = source`:
@safe unittest
{
@@ -1184,6 +1194,19 @@ pure nothrow @safe @nogc unittest
assert(s53 is s51);
}
+@system unittest
+{
+ static struct S
+ {
+ immutable int i;
+ ~this() @safe {}
+ }
+ alias ol = __traits(getOverloads, std.algorithm.mutation, "move", true)[1];
+ static assert(__traits(isDeprecated, ol!S));
+ // uncomment after deprecation
+ //static assert(!__traits(compiles, { S a, b; move(a, b); }));
+}
+
/// Ditto
T move(T)(return scope ref T source)
{
@@ -2164,12 +2164,12 @@ private void quickSortImpl(alias less, Range)(Range r, size_t depth)
{
import std.algorithm.comparison : min, max;
import std.algorithm.mutation : swap, swapAt;
- import std.conv : to;
alias Elem = ElementType!(Range);
- enum size_t shortSortGetsBetter = max(32, 1024 / Elem.sizeof);
+ enum int size = Elem.sizeof;
+ enum size_t shortSortGetsBetter = max(32, 1024 / size);
static assert(shortSortGetsBetter >= 1, Elem.stringof ~ " "
- ~ to!string(Elem.sizeof));
+ ~ size.stringof);
// partition
while (r.length > shortSortGetsBetter)
@@ -295,6 +295,19 @@ if (is(Range == U*, U) && isIterable!U && !isAutodecodableString!Range && !isInf
R().array;
}
+// Test that `array(scope InputRange r)` returns a non-scope array
+// https://issues.dlang.org/show_bug.cgi?id=23300
+@safe pure nothrow unittest
+{
+ @safe int[] fun()
+ {
+ import std.algorithm.iteration : map;
+ int[3] arr = [1, 2, 3];
+ scope r = arr[].map!(x => x + 3);
+ return r.array;
+ }
+}
+
/**
Convert a narrow autodecoding string to an array type that fully supports
random access. This is handled as a special case and always returns an array
@@ -650,6 +663,8 @@ if (isInputRange!Values && isInputRange!Keys)
alias ValueElement = ElementType!Values;
static if (hasElaborateDestructor!ValueElement)
ValueElement.init.__xdtor();
+
+ aa[key] = values.front;
})))
{
() @trusted {
@@ -790,6 +805,20 @@ if (isInputRange!Values && isInputRange!Keys)
assert(assocArray(1.iota, [UnsafeElement()]) == [0: UnsafeElement()]);
}
+@safe unittest
+{
+ struct ValueRange
+ {
+ string front() const @system;
+ @safe:
+ void popFront() {}
+ bool empty() const { return false; }
+ }
+ int[] keys;
+ ValueRange values;
+ static assert(!__traits(compiles, assocArray(keys, values)));
+}
+
/**
Construct a range iterating over an associative array by key/value tuples.
@@ -1306,7 +1306,7 @@ public:
/**
Sets the bits of a slice of `BitArray` starting
- at index `start` and ends at index ($D end - 1)
+ at index `start` and ends at index $(D end - 1)
with the values specified by `val`.
*/
void opSliceAssign(bool val, size_t start, size_t end) @nogc pure nothrow
@@ -3776,7 +3776,7 @@ public:
enforceValid!"months"(cast(Month) month);
enforceValid!"days"(year, cast(Month) month, day);
- _year = cast(short) year;
+ _year = year.castToYear;
_month = cast(Month) month;
_day = cast(ubyte) day;
}
@@ -3814,6 +3814,7 @@ public:
assertThrown!DateTimeException(Date(1999, 10, 32));
assertThrown!DateTimeException(Date(1999, 11, 31));
assertThrown!DateTimeException(Date(1999, 12, 32));
+ assertThrown!DateTimeException(Date(short.max+1, 1, 1));
assertNotThrown!DateTimeException(Date(1999, 1, 31));
assertNotThrown!DateTimeException(Date(1999, 2, 28));
@@ -3839,6 +3840,7 @@ public:
assertThrown!DateTimeException(Date(-1, 2, 29));
assertThrown!DateTimeException(Date(-2, 2, 29));
assertThrown!DateTimeException(Date(-3, 2, 29));
+ assertThrown!DateTimeException(Date(short.min-1, 1, 1));
}
@@ -4128,7 +4130,7 @@ public:
@property void year(int year) @safe pure
{
enforceValid!"days"(year, _month, _day);
- _year = cast(short) year;
+ _year = year.castToYear;
}
///
@@ -4215,7 +4217,7 @@ public:
{
if (year <= 0)
throw new DateTimeException("The given year is not a year B.C.");
- _year = cast(short)((year - 1) * -1);
+ _year = castToYear((year - 1) * -1);
}
///
@@ -9689,6 +9691,16 @@ if (units == "days")
assert(!valid!"days"(2017, 2, 29));
}
+private short castToYear(int year, string file = __FILE__, size_t line = __LINE__) @safe pure
+{
+ import std.conv : to, ConvOverflowException;
+ import std.format : format;
+
+ try
+ return year.to!short;
+ catch (ConvOverflowException)
+ throw new DateTimeException(format("year %s doesn't fit to Date.", year), file, line);
+}
/++
Params:
@@ -3339,7 +3339,7 @@ else version (Posix)
Windows uses a different set of time zone names than the IANA time zone
database does, and how they correspond to one another changes over time
(particularly when Microsoft updates Windows).
- $(HTTP unicode.org/cldr/data/common/supplemental/windowsZones.xml, windowsZones.xml)
+ $(HTTP github.com/unicode-org/cldr/blob/main/common/supplemental/windowsZones.xml, windowsZones.xml)
provides the current conversions (which may or may not match up with what's
on a particular Windows box depending on how up-to-date it is), and
parseTZConversions reads in those conversions from windowsZones.xml so that
@@ -3358,7 +3358,7 @@ else version (Posix)
Params:
windowsZonesXMLText = The text from
- $(HTTP unicode.org/cldr/data/common/supplemental/windowsZones.xml, windowsZones.xml)
+ $(HTTP github.com/unicode-org/cldr/blob/main/common/supplemental/windowsZones.xml, windowsZones.xml)
Throws:
Exception if there is an error while parsing the given XML.
@@ -3372,7 +3372,7 @@ else version (Posix)
// and parse it so that it's guaranteed to be up-to-date, though
// that has the downside that the code needs to worry about the
// site being down or unicode.org changing the URL.
- auto url = "http://unicode.org/cldr/data/common/supplemental/windowsZones.xml";
+ auto url = "https://raw.githubusercontent.com/unicode-org/cldr/main/common/supplemental/windowsZones.xml";
auto conversions2 = parseTZConversions(std.net.curl.get(url));
--------------------
+/
@@ -3458,7 +3458,7 @@ TZConversions parseTZConversions(string windowsZonesXMLText) @safe pure
import std.algorithm.iteration : uniq;
import std.algorithm.sorting : isSorted;
- // Reduced text from http://unicode.org/cldr/data/common/supplemental/windowsZones.xml
+ // Reduced text from https://github.com/unicode-org/cldr/blob/main/common/supplemental/windowsZones.xml
auto sampleFileText =
`<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE supplementalData SYSTEM "../../common/dtd/ldmlSupplemental.dtd">
@@ -1540,9 +1540,9 @@ version (StdUnittest)
}
/+
-Returns true if the field at index `i` in ($D T) shares its address with another field.
+Returns true if the field at index `i` in $(D T) shares its address with another field.
-Note: This does not merelly check if the field is a member of an union, but also that
+Note: This does not merely check if the field is a member of an union, but also that
it is not a single child.
+/
package enum isUnionAliased(T, size_t i) = isUnionAliasedImpl!T(T.tupleof[i].offsetof);
@@ -43,7 +43,7 @@ alignedReallocate) APIs.))
$(TR $(TDC size_t goodAllocSize(size_t n);, $(POST $(RES) >= n)) $(TD Allocators
customarily allocate memory in discretely-sized chunks. Therefore, a request for
`n` bytes may result in a larger allocation. The extra memory allocated goes
-unused and adds to the so-called $(HTTP goo.gl/YoKffF,internal fragmentation).
+unused and adds to the so-called $(HTTPS en.wikipedia.org/wiki/Fragmentation_(computing)#Internal_fragmentation,internal fragmentation).
The function `goodAllocSize(n)` returns the actual number of bytes that would
be allocated upon a request for `n` bytes. This module defines a default
implementation that returns `n` rounded up to a multiple of the allocator's
@@ -137,7 +137,7 @@ thread-safe or not, this instance may be `shared`.))
$(H2 Sample Assembly)
-The example below features an _allocator modeled after $(HTTP goo.gl/m7329l,
+The example below features an _allocator modeled after $(HTTP jemalloc.net/,
jemalloc), which uses a battery of free-list allocators spaced so as to keep
internal fragmentation to a minimum. The `FList` definitions specify no
bounds for the freelist because the `Segregator` does all size selection in
@@ -904,7 +904,7 @@ version (DragonFlyBSD)
{
// sbrk is deprecated in favor of mmap (we could implement a mmap + MAP_NORESERVE + PROT_NONE version)
// brk has been removed
- // https://www.dragonflydigest.com/2019/02/22/22586.html
+ // https://web.archive.org/web/20221006070113/https://www.dragonflydigest.com/2019/02/22/22586.html
// http://gitweb.dragonflybsd.org/dragonfly.git/commitdiff/dc676eaefa61b0f47bbea1c53eab86fd5ccd78c6
// http://gitweb.dragonflybsd.org/dragonfly.git/commitdiff/4b5665564ef37dc939a3a9ffbafaab9894c18885
// http://gitweb.dragonflybsd.org/dragonfly.git/commitdiff/8618d94a0e2ff8303ad93c123a3fa598c26a116e
@@ -968,7 +968,7 @@ version (Posix) struct SbrkRegion(uint minAlign = platformAlignment)
scope(exit) pthread_mutex_unlock(cast(pthread_mutex_t*) &sbrkMutex) == 0
|| assert(0);
// Assume sbrk returns the old break. Most online documentation confirms
- // that, except for http://www.inf.udec.cl/~leo/Malloc_tutorial.pdf,
+ // that, except for https://web.archive.org/web/20171014020821/http://www.inf.udec.cl/~leo/Malloc_tutorial.pdf,
// which claims the returned value is not portable.
auto p = sbrk(rounded);
if (p == cast(void*) -1)
@@ -134,7 +134,7 @@ enum Options : ulong
bytesNotMoved = 1u << 17,
/**
Measures the sum of extra bytes allocated beyond the bytes requested, i.e.
- the $(HTTP goo.gl/YoKffF, internal fragmentation). This is the current
+ the $(HTTPS en.wikipedia.org/wiki/Fragmentation_(computing)#Internal_fragmentation, internal fragmentation). This is the current
effective number of slack bytes, and it goes up and down with time.
*/
bytesSlack = 1u << 18,
@@ -52,7 +52,7 @@ struct Mallocator
import core.memory : pureRealloc;
if (!s)
{
- // fuzzy area in the C standard, see http://goo.gl/ZpWeSE
+ // fuzzy area in the C standard, see https://stackoverflow.com/questions/6502077/malloc-and-realloc-functions
// so just deallocate and nullify the pointer
deallocate(b);
b = null;
@@ -60,7 +60,7 @@ struct MmapAllocator
// http://man7.org/linux/man-pages/man2/mmap.2.html
package alias allocateZeroed = allocate;
else version (NetBSD)
- // http://netbsd.gw.com/cgi-bin/man-cgi?mmap+2+NetBSD-current
+ // https://man.netbsd.org/mmap.2
package alias allocateZeroed = allocate;
else version (Solaris)
// https://docs.oracle.com/cd/E88353_01/html/E37841/mmap-2.html
@@ -161,15 +161,16 @@ if (isInputRange!Range && isSomeChar!T && !is(T == enum) && isSomeChar!(ElementT
enforceFmt(find(acceptedSpecs!T, spec.spec).length,
text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
- static if (T.sizeof == 1)
+ enum int size = T.sizeof;
+ static if (size == 1)
return unformatValue!ubyte(input, spec);
- else static if (T.sizeof == 2)
+ else static if (size == 2)
return unformatValue!ushort(input, spec);
- else static if (T.sizeof == 4)
+ else static if (size == 4)
return unformatValue!uint(input, spec);
else
static assert(false, T.stringof ~ ".sizeof must be 1, 2, or 4 not " ~
- to!string(T.sizeof));
+ size.stringof);
}
T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char fmt)
@@ -358,7 +358,7 @@ $(BOOKTABLE ,
Default precision is large enough to add all digits
of the integral value.
- In case of ($B 'a') and $(B 'A'), the integral digit can be
+ In case of $(B 'a') and $(B 'A'), the integral digit can be
any hexadecimal digit.
)
)
@@ -13,7 +13,7 @@ also $(LINK https://forum.dlang.org/post/dzfyaxypmkdrpakmycjv@forum.dlang.org).)
Copyright: Copyright Jeremie Pelletier 2008 - 2009.
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: Jeremie Pelletier, David Herberth
-References: $(LINK http://json.org/), $(LINK http://seriot.ch/parsing_json.html)
+References: $(LINK http://json.org/), $(LINK https://seriot.ch/projects/parsing_json.html)
Source: $(PHOBOSSRC std/json.d)
*/
/*
@@ -804,7 +804,22 @@ struct JSONValue
assert(j["author"].str == "Walter");
}
- ///
+ /**
+ * Compare two JSONValues for equality
+ *
+ * JSON arrays and objects are compared deeply. The order of object keys does not matter.
+ *
+ * Floating point numbers are compared for exact equality, not approximal equality.
+ *
+ * Different number types (unsigned, signed, and floating) will be compared by converting
+ * them to a common type, in the same way that comparison of built-in D `int`, `uint` and
+ * `float` works.
+ *
+ * Other than that, types must match exactly.
+ * Empty arrays are not equal to empty objects, and booleans are never equal to integers.
+ *
+ * Returns: whether this `JSONValue` is equal to `rhs`
+ */
bool opEquals(const JSONValue rhs) const @nogc nothrow pure @safe
{
return opEquals(rhs);
@@ -871,9 +886,13 @@ struct JSONValue
///
@safe unittest
{
- assert(JSONValue(0u) == JSONValue(0));
- assert(JSONValue(0u) == JSONValue(0.0));
- assert(JSONValue(0) == JSONValue(0.0));
+ assert(JSONValue(10).opEquals(JSONValue(10.0)));
+ assert(JSONValue(10) != (JSONValue(10.5)));
+
+ assert(JSONValue(1) != JSONValue(true));
+ assert(JSONValue.emptyArray != JSONValue.emptyObject);
+
+ assert(parseJSON(`{"a": 1, "b": 2}`).opEquals(parseJSON(`{"b": 2, "a": 1}`)));
}
/// Implements the foreach `opApply` interface for json arrays.
@@ -422,217 +422,7 @@ if (isIntegral!I && isFloatingPoint!F)
Unqual!(Largest!(F, G)) pow(F, G)(F x, G y) @nogc @trusted pure nothrow
if (isFloatingPoint!(F) && isFloatingPoint!(G))
{
- import core.math : fabs, sqrt;
- import std.math.traits : isInfinity, isNaN, signbit;
-
- alias Float = typeof(return);
-
- static real impl(real x, real y) @nogc pure nothrow
- {
- // Special cases.
- if (isNaN(y))
- return y;
- if (isNaN(x) && y != 0.0)
- return x;
-
- // Even if x is NaN.
- if (y == 0.0)
- return 1.0;
- if (y == 1.0)
- return x;
-
- if (isInfinity(y))
- {
- if (isInfinity(x))
- {
- if (!signbit(y) && !signbit(x))
- return F.infinity;
- else
- return F.nan;
- }
- else if (fabs(x) > 1)
- {
- if (signbit(y))
- return +0.0;
- else
- return F.infinity;
- }
- else if (fabs(x) == 1)
- {
- return F.nan;
- }
- else // < 1
- {
- if (signbit(y))
- return F.infinity;
- else
- return +0.0;
- }
- }
- if (isInfinity(x))
- {
- if (signbit(x))
- {
- long i = cast(long) y;
- if (y > 0.0)
- {
- if (i == y && i & 1)
- return -F.infinity;
- else if (i == y)
- return F.infinity;
- else
- return -F.nan;
- }
- else if (y < 0.0)
- {
- if (i == y && i & 1)
- return -0.0;
- else if (i == y)
- return +0.0;
- else
- return F.nan;
- }
- }
- else
- {
- if (y > 0.0)
- return F.infinity;
- else if (y < 0.0)
- return +0.0;
- }
- }
-
- if (x == 0.0)
- {
- if (signbit(x))
- {
- long i = cast(long) y;
- if (y > 0.0)
- {
- if (i == y && i & 1)
- return -0.0;
- else
- return +0.0;
- }
- else if (y < 0.0)
- {
- if (i == y && i & 1)
- return -F.infinity;
- else
- return F.infinity;
- }
- }
- else
- {
- if (y > 0.0)
- return +0.0;
- else if (y < 0.0)
- return F.infinity;
- }
- }
- if (x == 1.0)
- return 1.0;
-
- if (y >= F.max)
- {
- if ((x > 0.0 && x < 1.0) || (x > -1.0 && x < 0.0))
- return 0.0;
- if (x > 1.0 || x < -1.0)
- return F.infinity;
- }
- if (y <= -F.max)
- {
- if ((x > 0.0 && x < 1.0) || (x > -1.0 && x < 0.0))
- return F.infinity;
- if (x > 1.0 || x < -1.0)
- return 0.0;
- }
-
- if (x >= F.max)
- {
- if (y > 0.0)
- return F.infinity;
- else
- return 0.0;
- }
- if (x <= -F.max)
- {
- long i = cast(long) y;
- if (y > 0.0)
- {
- if (i == y && i & 1)
- return -F.infinity;
- else
- return F.infinity;
- }
- else if (y < 0.0)
- {
- if (i == y && i & 1)
- return -0.0;
- else
- return +0.0;
- }
- }
-
- // Integer power of x.
- long iy = cast(long) y;
- if (iy == y && fabs(y) < 32_768.0)
- return pow(x, iy);
-
- real sign = 1.0;
- if (x < 0)
- {
- // Result is real only if y is an integer
- // Check for a non-zero fractional part
- enum maxOdd = pow(2.0L, real.mant_dig) - 1.0L;
- static if (maxOdd > ulong.max)
- {
- // Generic method, for any FP type
- import std.math.rounding : floor;
- if (floor(y) != y)
- return sqrt(x); // Complex result -- create a NaN
-
- const hy = 0.5 * y;
- if (floor(hy) != hy)
- sign = -1.0;
- }
- else
- {
- // Much faster, if ulong has enough precision
- const absY = fabs(y);
- if (absY <= maxOdd)
- {
- const uy = cast(ulong) absY;
- if (uy != absY)
- return sqrt(x); // Complex result -- create a NaN
-
- if (uy & 1)
- sign = -1.0;
- }
- }
- x = -x;
- }
- version (INLINE_YL2X)
- {
- // If x > 0, x ^^ y == 2 ^^ ( y * log2(x) )
- // TODO: This is not accurate in practice. A fast and accurate
- // (though complicated) method is described in:
- // "An efficient rounding boundary test for pow(x, y)
- // in double precision", C.Q. Lauter and V. Lefèvre, INRIA (2007).
- return sign * exp2( core.math.yl2x(x, y) );
- }
- else
- {
- // If x > 0, x ^^ y == 2 ^^ ( y * log2(x) )
- // TODO: This is not accurate in practice. A fast and accurate
- // (though complicated) method is described in:
- // "An efficient rounding boundary test for pow(x, y)
- // in double precision", C.Q. Lauter and V. Lefèvre, INRIA (2007).
- Float w = exp2(y * log2(x));
- return sign * w;
- }
- }
- return impl(x, y);
+ return _powImpl(x, y);
}
///
@@ -802,6 +592,216 @@ if (isFloatingPoint!(F) && isFloatingPoint!(G))
assert(pow(-real.infinity, 0.0) == 1.0);
}
+private real _powImpl(real x, real y) @safe @nogc pure nothrow
+{
+ alias F = real;
+ import core.math : fabs, sqrt;
+ import std.math.traits : isInfinity, isNaN, signbit;
+
+ // Special cases.
+ if (isNaN(y))
+ return y;
+ if (isNaN(x) && y != 0.0)
+ return x;
+
+ // Even if x is NaN.
+ if (y == 0.0)
+ return 1.0;
+ if (y == 1.0)
+ return x;
+
+ if (isInfinity(y))
+ {
+ if (isInfinity(x))
+ {
+ if (!signbit(y) && !signbit(x))
+ return F.infinity;
+ else
+ return F.nan;
+ }
+ else if (fabs(x) > 1)
+ {
+ if (signbit(y))
+ return +0.0;
+ else
+ return F.infinity;
+ }
+ else if (fabs(x) == 1)
+ {
+ return F.nan;
+ }
+ else // < 1
+ {
+ if (signbit(y))
+ return F.infinity;
+ else
+ return +0.0;
+ }
+ }
+ if (isInfinity(x))
+ {
+ if (signbit(x))
+ {
+ long i = cast(long) y;
+ if (y > 0.0)
+ {
+ if (i == y && i & 1)
+ return -F.infinity;
+ else if (i == y)
+ return F.infinity;
+ else
+ return -F.nan;
+ }
+ else if (y < 0.0)
+ {
+ if (i == y && i & 1)
+ return -0.0;
+ else if (i == y)
+ return +0.0;
+ else
+ return F.nan;
+ }
+ }
+ else
+ {
+ if (y > 0.0)
+ return F.infinity;
+ else if (y < 0.0)
+ return +0.0;
+ }
+ }
+
+ if (x == 0.0)
+ {
+ if (signbit(x))
+ {
+ long i = cast(long) y;
+ if (y > 0.0)
+ {
+ if (i == y && i & 1)
+ return -0.0;
+ else
+ return +0.0;
+ }
+ else if (y < 0.0)
+ {
+ if (i == y && i & 1)
+ return -F.infinity;
+ else
+ return F.infinity;
+ }
+ }
+ else
+ {
+ if (y > 0.0)
+ return +0.0;
+ else if (y < 0.0)
+ return F.infinity;
+ }
+ }
+ if (x == 1.0)
+ return 1.0;
+
+ if (y >= F.max)
+ {
+ if ((x > 0.0 && x < 1.0) || (x > -1.0 && x < 0.0))
+ return 0.0;
+ if (x > 1.0 || x < -1.0)
+ return F.infinity;
+ }
+ if (y <= -F.max)
+ {
+ if ((x > 0.0 && x < 1.0) || (x > -1.0 && x < 0.0))
+ return F.infinity;
+ if (x > 1.0 || x < -1.0)
+ return 0.0;
+ }
+
+ if (x >= F.max)
+ {
+ if (y > 0.0)
+ return F.infinity;
+ else
+ return 0.0;
+ }
+ if (x <= -F.max)
+ {
+ long i = cast(long) y;
+ if (y > 0.0)
+ {
+ if (i == y && i & 1)
+ return -F.infinity;
+ else
+ return F.infinity;
+ }
+ else if (y < 0.0)
+ {
+ if (i == y && i & 1)
+ return -0.0;
+ else
+ return +0.0;
+ }
+ }
+
+ // Integer power of x.
+ long iy = cast(long) y;
+ if (iy == y && fabs(y) < 32_768.0)
+ return pow(x, iy);
+
+ real sign = 1.0;
+ if (x < 0)
+ {
+ // Result is real only if y is an integer
+ // Check for a non-zero fractional part
+ enum maxOdd = pow(2.0L, real.mant_dig) - 1.0L;
+ static if (maxOdd > ulong.max)
+ {
+ // Generic method, for any FP type
+ import std.math.rounding : floor;
+ if (floor(y) != y)
+ return sqrt(x); // Complex result -- create a NaN
+
+ const hy = 0.5 * y;
+ if (floor(hy) != hy)
+ sign = -1.0;
+ }
+ else
+ {
+ // Much faster, if ulong has enough precision
+ const absY = fabs(y);
+ if (absY <= maxOdd)
+ {
+ const uy = cast(ulong) absY;
+ if (uy != absY)
+ return sqrt(x); // Complex result -- create a NaN
+
+ if (uy & 1)
+ sign = -1.0;
+ }
+ }
+ x = -x;
+ }
+ version (INLINE_YL2X)
+ {
+ // If x > 0, x ^^ y == 2 ^^ ( y * log2(x) )
+ // TODO: This is not accurate in practice. A fast and accurate
+ // (though complicated) method is described in:
+ // "An efficient rounding boundary test for pow(x, y)
+ // in double precision", C.Q. Lauter and V. Lefèvre, INRIA (2007).
+ return sign * exp2( core.math.yl2x(x, y) );
+ }
+ else
+ {
+ // If x > 0, x ^^ y == 2 ^^ ( y * log2(x) )
+ // TODO: This is not accurate in practice. A fast and accurate
+ // (though complicated) method is described in:
+ // "An efficient rounding boundary test for pow(x, y)
+ // in double precision", C.Q. Lauter and V. Lefèvre, INRIA (2007).
+ auto w = exp2(y * log2(x));
+ return sign * w;
+ }
+}
+
/** Computes the value of a positive integer `x`, raised to the power `n`, modulo `m`.
*
* Params:
@@ -3405,7 +3405,7 @@ private:
// This algorithm works by performing the even and odd parts of our FFT
// using the "two for the price of one" method mentioned at
- // http://www.engineeringproductivitytools.com/stuff/T0001/PT10.HTM#Head521
+ // https://web.archive.org/web/20180312110051/http://www.engineeringproductivitytools.com/stuff/T0001/PT10.HTM#Head521
// by making the odd terms into the imaginary components of our new FFT,
// and then using symmetry to recombine them.
void fftImplPureReal(Ret, R)(R range, Ret buf) const
@@ -22,9 +22,10 @@ import std.traits : isSomeString;
* OutBuffer's byte order is the format native to the computer.
* To control the byte order (endianness), use a class derived
* from OutBuffer.
+ *
* OutBuffer's internal buffer is allocated with the GC. Pointers
* stored into the buffer are scanned by the GC, but you have to
- * ensure proper alignment, e.g. by using alignSize((void*).sizeof).
+ * ensure proper alignment, e.g. by using `alignSize((void*).sizeof)`.
*/
class OutBuffer
@@ -297,7 +298,7 @@ class OutBuffer
* Append output of C's vprintf() to internal buffer.
*/
- void vprintf(scope string format, va_list args) @trusted nothrow
+ void vprintf(scope string format, va_list args) @system nothrow
{
import core.stdc.stdio : vsnprintf;
import core.stdc.stdlib : alloca;
@@ -342,7 +343,7 @@ class OutBuffer
* Append output of C's printf() to internal buffer.
*/
- void printf(scope string format, ...) @trusted
+ void printf(scope string format, ...) @system
{
va_list ap;
va_start(ap, format);
@@ -475,7 +476,7 @@ class OutBuffer
buf.write("hello");
buf.write(cast(byte) 0x20);
buf.write("world");
- buf.printf(" %d", 62665);
+ buf.writef(" %d", 62665);
assert(cmp(buf.toString(), "hello world 62665") == 0);
buf.clear();
@@ -884,11 +884,26 @@ identical to the non-@safe case, but safety introduces some restrictions:
*/
@trusted auto task(F, Args...)(F fun, Args args)
-if (is(typeof(fun(args))) && isSafeTask!F)
+if (__traits(compiles, () @safe => fun(args)) && isSafeTask!F)
{
return new Task!(run, F, Args)(fun, args);
}
+@safe unittest
+{
+ static struct Oops {
+ int convert() {
+ *cast(int*) 0xcafebabe = 0xdeadbeef;
+ return 0;
+ }
+ alias convert this;
+ }
+ static void foo(int) @safe {}
+
+ static assert(!__traits(compiles, task(&foo, Oops.init)));
+ static assert(!__traits(compiles, scopedTask(&foo, Oops.init)));
+}
+
/**
These functions allow the creation of `Task` objects on the stack rather
than the GC heap. The lifetime of a `Task` created by `scopedTask`
@@ -928,7 +943,7 @@ if (is(typeof(delegateOrFp(args))) && !isSafeTask!F)
/// Ditto
@trusted auto scopedTask(F, Args...)(F fun, Args args)
-if (is(typeof(fun(args))) && isSafeTask!F)
+if (__traits(compiles, () @safe => fun(args)) && isSafeTask!F)
{
auto ret = Task!(run, F, Args)(fun, args);
ret.isScoped = true;
@@ -6070,10 +6070,13 @@ nothrow pure @system unittest
Generate lockstep's opApply function as a mixin string.
If withIndex is true prepend a size_t index to the delegate.
*/
-private string lockstepMixin(Ranges...)(bool withIndex, bool reverse)
+private struct LockstepMixin(Ranges...)
{
+ import std.conv : text;
import std.format : format;
+ string name;
+ string implName;
string[] params;
string[] emptyChecks;
string[] dgArgs;
@@ -6081,76 +6084,101 @@ private string lockstepMixin(Ranges...)(bool withIndex, bool reverse)
string indexDef;
string indexInc;
- if (withIndex)
+@safe pure:
+ this(bool withIndex, bool reverse)
{
- params ~= "size_t";
- dgArgs ~= "index";
- if (reverse)
+ if (withIndex)
{
- indexDef = q{
- size_t index = ranges[0].length-1;
- enforce(_stoppingPolicy == StoppingPolicy.requireSameLength,
- "lockstep can only be used with foreach_reverse when stoppingPolicy == requireSameLength");
+ params ~= "size_t";
+ dgArgs ~= "index";
+ if (reverse)
+ {
+ indexDef = q{
+ size_t index = ranges[0].length - 1;
+ enforce(this.stoppingPolicy == StoppingPolicy.requireSameLength,
+ "lockstep can only be used with foreach_reverse when stoppingPolicy == requireSameLength");
- foreach (range; ranges[1..$])
- enforce(range.length == ranges[0].length);
- };
- indexInc = "--index;";
+ foreach (range; ranges[1 .. $])
+ enforce(range.length == ranges[0].length);
+ };
+ indexInc = "--index;";
+ }
+ else
+ {
+ indexDef = "size_t index = 0;";
+ indexInc = "++index;";
+ }
}
- else
+
+ foreach (idx, Range; Ranges)
{
- indexDef = "size_t index = 0;";
- indexInc = "++index;";
+ params ~= format("%sElementType!(Ranges[%s])", hasLvalueElements!Range ? "ref " : "", idx);
+ emptyChecks ~= format("!ranges[%s].empty", idx);
+ if (reverse)
+ {
+ dgArgs ~= format("ranges[%s].back", idx);
+ popFronts ~= format("ranges[%s].popBack();", idx);
+ }
+ else
+ {
+ dgArgs ~= format("ranges[%s].front", idx);
+ popFronts ~= format("ranges[%s].popFront();", idx);
+ }
}
- }
- foreach (idx, Range; Ranges)
- {
- params ~= format("%sElementType!(Ranges[%s])", hasLvalueElements!Range ? "ref " : "", idx);
- emptyChecks ~= format("!ranges[%s].empty", idx);
if (reverse)
{
- dgArgs ~= format("ranges[%s].back", idx);
- popFronts ~= format("ranges[%s].popBack();", idx);
+ name = "opApplyReverse";
+ if (withIndex) implName = "opApplyReverseIdxImpl";
+ else implName = "opApplyReverseImpl";
}
else
{
- dgArgs ~= format("ranges[%s].front", idx);
- popFronts ~= format("ranges[%s].popFront();", idx);
+ name = "opApply";
+ if (withIndex) implName = "opApplyIdxImpl";
+ else implName = "opApplyImpl";
}
}
- string name = reverse ? "opApplyReverse" : "opApply";
-
- return format(
- q{
- int %s(scope int delegate(%s) dg)
- {
- import std.exception : enforce;
-
- auto ranges = _ranges;
- int res;
- %s
+const:
+ string getAlias()
+ {
+ return format(q{
+ alias %s = %s!(int delegate(%-(%s%|, %)));
+ },
+ name, implName, params
+ );
+ }
- while (%s)
+ string getImpl()
+ {
+ return format(q{
+ int %s(DG)(scope DG dg) scope
{
- res = dg(%s);
- if (res) break;
- %s
+ import std.exception : enforce;
+
+ auto ranges = this.ranges;
%s
- }
- if (_stoppingPolicy == StoppingPolicy.requireSameLength)
- {
- foreach (range; ranges)
- enforce(range.empty);
+ while (%-(%s%| && %))
+ {
+ if (int result = dg(%-(%s%|, %))) return result;
+ %-(%s%|
+ %)
+ %s
+ }
+
+ if (this.stoppingPolicy == StoppingPolicy.requireSameLength)
+ {
+ foreach (range; ranges)
+ enforce(range.empty);
+ }
+ return 0;
}
- return res;
- }
- }, name, params.join(", "), indexDef,
- emptyChecks.join(" && "), dgArgs.join(", "),
- popFronts.join("\n "),
- indexInc);
+ },
+ implName, indexDef, emptyChecks, dgArgs, popFronts, indexInc
+ );
+ }
}
/**
@@ -6170,10 +6198,6 @@ private string lockstepMixin(Ranges...)(bool withIndex, bool reverse)
By default `StoppingPolicy` is set to `StoppingPolicy.shortest`.
- Limitations: The `pure`, `@safe`, `@nogc`, or `nothrow` attributes cannot be
- inferred for `lockstep` iteration. $(LREF zip) can infer the first two due to
- a different implementation.
-
See_Also: $(LREF zip)
`lockstep` is similar to $(LREF zip), but `zip` bundles its
@@ -6184,41 +6208,53 @@ private string lockstepMixin(Ranges...)(bool withIndex, bool reverse)
struct Lockstep(Ranges...)
if (Ranges.length > 1 && allSatisfy!(isInputRange, Ranges))
{
+ private Ranges ranges;
+ private StoppingPolicy stoppingPolicy;
+
///
- this(R ranges, StoppingPolicy sp = StoppingPolicy.shortest)
+ this(Ranges ranges, StoppingPolicy sp = StoppingPolicy.shortest)
{
import std.exception : enforce;
- _ranges = ranges;
+ this.ranges = ranges;
enforce(sp != StoppingPolicy.longest,
- "Can't use StoppingPolicy.Longest on Lockstep.");
- _stoppingPolicy = sp;
+ "Can't use StoppingPolicy.Longest on Lockstep.");
+ this.stoppingPolicy = sp;
}
- mixin(lockstepMixin!Ranges(false, false));
- mixin(lockstepMixin!Ranges(true, false));
+ private enum lockstepMixinFF = LockstepMixin!Ranges(withIndex: false, reverse: false);
+ mixin(lockstepMixinFF.getImpl);
+
+ private enum lockstepMixinTF = LockstepMixin!Ranges(withIndex: true, reverse: false);
+ mixin(lockstepMixinTF.getImpl);
+
+ mixin(lockstepMixinFF.getAlias);
+ mixin(lockstepMixinTF.getAlias);
+
static if (allSatisfy!(isBidirectionalRange, Ranges))
{
- mixin(lockstepMixin!Ranges(false, true));
+ private enum lockstepMixinFT = LockstepMixin!Ranges(withIndex: false, reverse: true);
+ mixin(lockstepMixinFT.getImpl);
static if (allSatisfy!(hasLength, Ranges))
{
- mixin(lockstepMixin!Ranges(true, true));
+ private enum lockstepMixinTT = LockstepMixin!Ranges(withIndex: true, reverse: true);
+ mixin(lockstepMixinTT.getImpl);
+ mixin(lockstepMixinTT.getAlias);
}
else
{
- mixin(lockstepReverseFailMixin!Ranges(true));
+ mixin(lockstepReverseFailMixin!Ranges(withIndex: true));
+ alias opApplyReverse = opApplyReverseIdxFail;
}
+ mixin(lockstepMixinFT.getAlias);
}
else
{
- mixin(lockstepReverseFailMixin!Ranges(false));
- mixin(lockstepReverseFailMixin!Ranges(true));
+ mixin(lockstepReverseFailMixin!Ranges(withIndex: false));
+ mixin(lockstepReverseFailMixin!Ranges(withIndex: true));
+ alias opApplyReverse = opApplyReverseFail;
+ alias opApplyReverse = opApplyReverseIdxFail;
}
-
-private:
- alias R = Ranges;
- R _ranges;
- StoppingPolicy _stoppingPolicy;
}
/// Ditto
@@ -6238,33 +6274,39 @@ if (allSatisfy!(isInputRange, Ranges))
}
///
-@system unittest
+pure @safe unittest
{
- auto arr1 = [1,2,3,4,5,100];
- auto arr2 = [6,7,8,9,10];
+ int[6] arr1 = [1,2,3,4,5,100];
+ int[5] arr2 = [6,7,8,9,10];
- foreach (ref a, b; lockstep(arr1, arr2))
- {
- a += b;
- }
+ foreach (ref a, b; lockstep(arr1[], arr2[]))
+ {
+ a += b;
+ }
- assert(arr1 == [7,9,11,13,15,100]);
+ assert(arr1 == [7,9,11,13,15,100]);
+}
- /// Lockstep also supports iterating with an index variable:
- foreach (index, a, b; lockstep(arr1, arr2))
- {
- assert(arr1[index] == a);
- assert(arr2[index] == b);
- }
+/// Lockstep also supports iterating with an index variable:
+pure @safe unittest
+{
+ int[3] arr1 = [1,2,3];
+ int[3] arr2 = [4,5,6];
+
+ foreach (index, a, b; lockstep(arr1[], arr2[]))
+ {
+ assert(arr1[index] == a);
+ assert(arr2[index] == b);
+ }
}
// https://issues.dlang.org/show_bug.cgi?id=15860: foreach_reverse on lockstep
-@system unittest
+pure @safe unittest
{
auto arr1 = [0, 1, 2, 3];
auto arr2 = [4, 5, 6, 7];
- size_t n = arr1.length -1;
+ size_t n = arr1.length - 1;
foreach_reverse (index, a, b; lockstep(arr1, arr2, StoppingPolicy.requireSameLength))
{
assert(n == index);
@@ -6283,7 +6325,7 @@ if (allSatisfy!(isInputRange, Ranges))
}
}
-@system unittest
+pure @safe unittest
{
import std.algorithm.iteration : filter;
import std.conv : to;
@@ -6380,7 +6422,7 @@ if (allSatisfy!(isInputRange, Ranges))
foreach (x, y; lockstep(iota(0, 10), iota(0, 10))) { }
}
-@system unittest
+pure @safe unittest
{
struct RvalueRange
{
@@ -6436,11 +6478,11 @@ private string lockstepReverseFailMixin(Ranges...)(bool withIndex)
return format(
q{
- int opApplyReverse()(scope int delegate(%s) dg)
+ int opApplyReverse%sFail()(scope int delegate(%s) dg)
{
static assert(false, "%s");
}
- }, params.join(", "), message);
+ }, withIndex ? "Idx" : "" , params.join(", "), message);
}
// For generic programming, make sure Lockstep!(Range) is well defined for a
@@ -220,31 +220,6 @@ version (CRuntime_Microsoft)
private alias _FGETWC = _fgetwc_nolock;
private alias _FLOCK = _lock_file;
private alias _FUNLOCK = _unlock_file;
-
- // @@@DEPRECATED_2.107@@@
- deprecated("internal alias FPUTC was unintentionally available from "
- ~ "std.stdio and will be removed afer 2.107")
- alias FPUTC = _fputc_nolock;
- // @@@DEPRECATED_2.107@@@
- deprecated("internal alias FPUTWC was unintentionally available from "
- ~ "std.stdio and will be removed afer 2.107")
- alias FPUTWC = _fputwc_nolock;
- // @@@DEPRECATED_2.107@@@
- deprecated("internal alias FGETC was unintentionally available from "
- ~ "std.stdio and will be removed afer 2.107")
- alias FGETC = _fgetc_nolock;
- // @@@DEPRECATED_2.107@@@
- deprecated("internal alias FGETWC was unintentionally available from "
- ~ "std.stdio and will be removed afer 2.107")
- alias FGETWC = _fgetwc_nolock;
- // @@@DEPRECATED_2.107@@@
- deprecated("internal alias FLOCK was unintentionally available from "
- ~ "std.stdio and will be removed afer 2.107")
- alias FLOCK = _lock_file;
- // @@@DEPRECATED_2.107@@@
- deprecated("internal alias FUNLOCK was unintentionally available from "
- ~ "std.stdio and will be removed afer 2.107")
- alias FUNLOCK = _unlock_file;
}
else version (CRuntime_Glibc)
{
@@ -254,31 +229,6 @@ else version (CRuntime_Glibc)
private alias _FGETWC = fgetwc_unlocked;
private alias _FLOCK = core.sys.posix.stdio.flockfile;
private alias _FUNLOCK = core.sys.posix.stdio.funlockfile;
-
- // @@@DEPRECATED_2.107@@@
- deprecated("internal alias FPUTC was unintentionally available from "
- ~ "std.stdio and will be removed afer 2.107")
- alias FPUTC = fputc_unlocked;
- // @@@DEPRECATED_2.107@@@
- deprecated("internal alias FPUTWC was unintentionally available from "
- ~ "std.stdio and will be removed afer 2.107")
- alias FPUTWC = fputwc_unlocked;
- // @@@DEPRECATED_2.107@@@
- deprecated("internal alias FGETC was unintentionally available from "
- ~ "std.stdio and will be removed afer 2.107")
- alias FGETC = fgetc_unlocked;
- // @@@DEPRECATED_2.107@@@
- deprecated("internal alias FGETWC was unintentionally available from "
- ~ "std.stdio and will be removed afer 2.107")
- alias FGETWC = fgetwc_unlocked;
- // @@@DEPRECATED_2.107@@@
- deprecated("internal alias FLOCK was unintentionally available from "
- ~ "std.stdio and will be removed afer 2.107")
- alias FLOCK = core.sys.posix.stdio.flockfile;
- // @@@DEPRECATED_2.107@@@
- deprecated("internal alias FUNLOCK was unintentionally available from "
- ~ "std.stdio and will be removed afer 2.107")
- alias FUNLOCK = core.sys.posix.stdio.funlockfile;
}
else version (GENERIC_IO)
{
@@ -304,52 +254,6 @@ else version (GENERIC_IO)
{
static assert(0, "don't know how to lock files on GENERIC_IO");
}
-
- // @@@DEPRECATED_2.107@@@
- deprecated("internal function fputc_unlocked was unintentionally available "
- ~ "from std.stdio and will be removed afer 2.107")
- extern (C) pragma(mangle, fputc.mangleof) int fputc_unlocked(int c, _iobuf* fp);
- // @@@DEPRECATED_2.107@@@
- deprecated("internal function fputwc_unlocked was unintentionally available "
- ~ "from std.stdio and will be removed afer 2.107")
- extern (C) pragma(mangle, core.stdc.wchar_.fputwc.mangleof) int fputwc_unlocked(wchar_t c, _iobuf* fp);
- // @@@DEPRECATED_2.107@@@
- deprecated("internal function fgetc_unlocked was unintentionally available "
- ~ "from std.stdio and will be removed afer 2.107")
- extern (C) pragma(mangle, fgetc.mangleof) int fgetc_unlocked(_iobuf* fp);
- // @@@DEPRECATED_2.107@@@
- deprecated("internal function fgetwc_unlocked was unintentionally available "
- ~ "from std.stdio and will be removed afer 2.107")
- extern (C) pragma(mangle, core.stdc.wchar_.fgetwc.mangleof) int fgetwc_unlocked(_iobuf* fp);
-
- // @@@DEPRECATED_2.107@@@
- deprecated("internal alias FPUTC was unintentionally available from "
- ~ "std.stdio and will be removed afer 2.107")
- alias FPUTC = fputc_unlocked;
- // @@@DEPRECATED_2.107@@@
- deprecated("internal alias FPUTWC was unintentionally available from "
- ~ "std.stdio and will be removed afer 2.107")
- alias FPUTWC = fputwc_unlocked;
- // @@@DEPRECATED_2.107@@@
- deprecated("internal alias FGETC was unintentionally available from "
- ~ "std.stdio and will be removed afer 2.107")
- alias FGETC = fgetc_unlocked;
- // @@@DEPRECATED_2.107@@@
- deprecated("internal alias FGETWC was unintentionally available from "
- ~ "std.stdio and will be removed afer 2.107")
- alias FGETWC = fgetwc_unlocked;
-
- version (Posix)
- {
- // @@@DEPRECATED_2.107@@@
- deprecated("internal alias FLOCK was unintentionally available from "
- ~ "std.stdio and will be removed afer 2.107")
- alias FLOCK = core.sys.posix.stdio.flockfile;
- // @@@DEPRECATED_2.107@@@
- deprecated("internal alias FUNLOCK was unintentionally available from "
- ~ "std.stdio and will be removed afer 2.107")
- alias FUNLOCK = core.sys.posix.stdio.funlockfile;
- }
}
else
{
@@ -795,7 +699,7 @@ Throws: `ErrnoException` in case of error.
/**
Detaches from the current file (throwing on failure), and then runs a command
by calling the C standard library function $(HTTP
-opengroup.org/onlinepubs/007908799/xsh/_popen.html, _popen).
+pubs.opengroup.org/onlinepubs/7908799/xsh/popen.html, popen).
Throws: `ErrnoException` in case of error.
*/
@@ -813,8 +717,9 @@ The mode must be compatible with the mode of the file descriptor.
Throws: `ErrnoException` in case of error.
Params:
fd = File descriptor to associate with this `File`.
- stdioOpenmode = Mode to associate with this File. The mode has the same semantics
- semantics as in the C standard library $(CSTDIO fdopen) function,
+ stdioOpenmode = Mode to associate with this File. The mode has the same
+ semantics as in the POSIX library function $(HTTP
+ pubs.opengroup.org/onlinepubs/7908799/xsh/fdopen.html, fdopen)
and must be compatible with `fd`.
*/
void fdopen(int fd, scope const(char)[] stdioOpenmode = "rb") @safe
@@ -1135,6 +1040,9 @@ Throws: `ErrnoException` if the file is not opened or the call to `fread` fails.
assert(buf == "\r\n\n\r\n");
}
+ // https://issues.dlang.org/show_bug.cgi?id=24685
+ static assert(!__traits(compiles, (File f) @safe { int*[1] bar; f.rawRead(bar[]); }));
+
// https://issues.dlang.org/show_bug.cgi?id=21729
@system unittest
{
@@ -4576,11 +4484,11 @@ if ((isSomeFiniteCharInputRange!R1 || isSomeString!R1) &&
{
/*
* The new opengroup large file support API is transparently
- * included in the normal C bindings. http://opengroup.org/platform/lfs.html#1.0
+ * included in the normal C bindings. https://www.opengroup.org/platform/lfs.html#1.0
* if _FILE_OFFSET_BITS in druntime is 64, off_t is 64 bit and
* the normal functions work fine. If not, then large file support
* probably isn't available. Do not use the old transitional API
- * (the native extern(C) fopen64, http://www.unix.org/version2/whatsnew/lfs20mar.html#3.0)
+ * (the native extern(C) fopen64, https://unix.org/version2/whatsnew/lfs20mar.html#3.0)
*/
import core.sys.posix.stdio : fopen;
return fopen(namez, modez);
@@ -4629,6 +4537,13 @@ private auto trustedFwrite(T)(FILE* f, const T[] obj) @trusted
* Convenience function that forwards to `core.stdc.stdio.fread`
*/
private auto trustedFread(T)(FILE* f, T[] obj) @trusted
+if (!imported!"std.traits".hasIndirections!T)
+{
+ return fread(obj.ptr, T.sizeof, obj.length, f);
+}
+
+private auto trustedFread(T)(FILE* f, T[] obj) @system
+if (imported!"std.traits".hasIndirections!T)
{
return fread(obj.ptr, T.sizeof, obj.length, f);
}
@@ -3543,6 +3543,35 @@ struct Nullable(T)
format!"%s"(a);
}
+ /**
+ * Returns true if `this` has a value, otherwise false.
+ *
+ * Allows a `Nullable` to be used as the condition in an `if` statement:
+ *
+ * ---
+ * if (auto result = functionReturningNullable())
+ * {
+ * doSomethingWith(result.get);
+ * }
+ * ---
+ */
+ bool opCast(T : bool)() const
+ {
+ return !isNull;
+ }
+
+ /// Prevents `opCast` from disabling built-in conversions.
+ auto ref T opCast(T, this This)()
+ if (is(This : T) || This.sizeof == T.sizeof)
+ {
+ static if (is(This : T))
+ // Convert implicitly
+ return this;
+ else
+ // Reinterpret
+ return *cast(T*) &this;
+ }
+
/**
* Forces `this` to the null state.
*/
@@ -4400,6 +4429,26 @@ auto nullable(T)(T t)
assert(destroyed);
}
+// https://issues.dlang.org/show_bug.cgi?id=22293
+@safe unittest
+{
+ Nullable!int empty;
+ Nullable!int full = 123;
+
+ assert(cast(bool) empty == false);
+ assert(cast(bool) full == true);
+
+ if (empty) assert(0);
+ if (!full) assert(0);
+}
+
+// check that opCast doesn't break unsafe casts
+@system unittest
+{
+ Nullable!(const(int*)) a;
+ auto result = cast(immutable(Nullable!(int*))) a;
+}
+
/**
Just like `Nullable!T`, except that the null state is defined as a
particular value. For example, $(D Nullable!(uint, uint.max)) is an
@@ -10928,19 +10977,22 @@ struct RefCounted(T, RefCountedAutoInitialize autoInit =
swap(_refCounted._store, rhs._refCounted._store);
}
- void opAssign(T rhs)
+ static if (__traits(compiles, lvalueOf!T = T.init))
{
- import std.algorithm.mutation : move;
-
- static if (autoInit == RefCountedAutoInitialize.yes)
+ void opAssign(T rhs)
{
- _refCounted.ensureInitialized();
- }
- else
- {
- assert(_refCounted.isInitialized);
+ import std.algorithm.mutation : move;
+
+ static if (autoInit == RefCountedAutoInitialize.yes)
+ {
+ _refCounted.ensureInitialized();
+ }
+ else
+ {
+ assert(_refCounted.isInitialized);
+ }
+ move(rhs, _refCounted._store._payload);
}
- move(rhs, _refCounted._store._payload);
}
static if (autoInit == RefCountedAutoInitialize.yes)
@@ -53,7 +53,7 @@ $(TR $(TD Miscellaneous) $(TD
See_Also:
$(LINK2 http://en.wikipedia.org/wiki/Unicode, Wikipedia)<br>
$(LINK http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8)<br>
- $(LINK http://anubis.dkuug.dk/JTC1/SC2/WG2/docs/n1335)
+ $(LINK https://web.archive.org/web/20100113043530/https://anubis.dkuug.dk/JTC1/SC2/WG2/docs/n1335)
Copyright: Copyright The D Language Foundation 2000 - 2012.
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP digitalmars.com, Walter Bright) and