d: Merge dmd, druntime a6f1083699, phobos 31dedd7da
D front-end changes: - Import dmd v2.107.0. - Character postfixes can now also be used for integers of size two or four. D run-time changes: - Import druntime v2.107.0. Phobos changes: - Import phobos v2.107.0. gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd a6f1083699. * dmd/VERSION: Bump version to v2.107.0 * Make-lang.in (D_FRONTEND_OBJS): Add d/pragmasem.o. * d-builtins.cc (strip_type_modifiers): Update for new front-end interface. * d-codegen.cc (declaration_type): Likewise. (parameter_type): Likewise. * d-target.cc (TargetCPP::parameterType): Likewise. * expr.cc (ExprVisitor::visit (IndexExp *)): Likewise. (ExprVisitor::visit (VarExp *)): Likewise. (ExprVisitor::visit (AssocArrayLiteralExp *)): Likewise. * runtime.cc (get_libcall_type): Likewise. * typeinfo.cc (TypeInfoVisitor::visit (TypeInfoConstDeclaration *)): Likewise. (TypeInfoVisitor::visit (TypeInfoInvariantDeclaration *)): Likewise. (TypeInfoVisitor::visit (TypeInfoSharedDeclaration *)): Likewise. (TypeInfoVisitor::visit (TypeInfoWildDeclaration *)): Likewise. * types.cc (build_ctype): Likewise. libphobos/ChangeLog: * libdruntime/MERGE: Merge upstream druntime a6f1083699. * src/MERGE: Merge upstream phobos 31dedd7da.
This commit is contained in:
parent
435bed3f02
commit
c428454ece
74 changed files with 4553 additions and 4292 deletions
|
@ -163,6 +163,7 @@ D_FRONTEND_OBJS = \
|
|||
d/parsetimevisitor.o \
|
||||
d/permissivevisitor.o \
|
||||
d/postordervisitor.o \
|
||||
d/pragmasem.o \
|
||||
d/printast.o \
|
||||
d/root-aav.o \
|
||||
d/root-array.o \
|
||||
|
|
|
@ -690,7 +690,7 @@ strip_type_modifiers (Type *type)
|
|||
return tnext->pointerTo ();
|
||||
}
|
||||
|
||||
return type->castMod (0);
|
||||
return castMod (type, 0);
|
||||
}
|
||||
|
||||
/* Returns true if types T1 and T2 representing return types or types of
|
||||
|
|
|
@ -157,7 +157,7 @@ declaration_type (Declaration *decl)
|
|||
if (decl->isParameter () && valist_array_p (decl->type))
|
||||
{
|
||||
Type *valist = decl->type->nextOf ()->pointerTo ();
|
||||
valist = valist->castMod (decl->type->mod);
|
||||
valist = castMod (valist, decl->type->mod);
|
||||
return build_ctype (valist);
|
||||
}
|
||||
|
||||
|
@ -207,7 +207,7 @@ parameter_type (Parameter *arg)
|
|||
if (valist_array_p (arg->type))
|
||||
{
|
||||
Type *valist = arg->type->nextOf ()->pointerTo ();
|
||||
valist = valist->castMod (arg->type->mod);
|
||||
valist = castMod (valist, arg->type->mod);
|
||||
return build_ctype (valist);
|
||||
}
|
||||
|
||||
|
|
|
@ -381,11 +381,11 @@ TargetCPP::parameterType (Type *type)
|
|||
Type *tvalist = target.va_listType (Loc (), NULL);
|
||||
if (type->ty == TY::Tsarray && tvalist->ty == TY::Tsarray)
|
||||
{
|
||||
Type *tb = type->toBasetype ()->mutableOf ();
|
||||
Type *tb = mutableOf (type->toBasetype ());
|
||||
if (tb == tvalist)
|
||||
{
|
||||
tb = type->nextOf ()->pointerTo ();
|
||||
type = tb->castMod (type->mod);
|
||||
type = castMod (tb, type->mod);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
e7709452775d374c1e2dfb67566668ada3dec5fc
|
||||
a6f10836997d0b5526c8c363d781b4029c77f09f
|
||||
|
||||
The first line of this file holds the git revision number of the last
|
||||
merge done from the dlang/dmd repository.
|
||||
|
|
|
@ -119,6 +119,7 @@ Note that these groups have no strict meaning, the category assignments are a bi
|
|||
| [expressionsem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/expressionsem.d) | Do semantic analysis for expressions |
|
||||
| [statementsem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/statementsem.d) | Do semantic analysis for statements |
|
||||
| [initsem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/initsem.d) | Do semantic analysis for initializers |
|
||||
| [pragmasem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/pragmasem.d) | Do semantic analysis for pragmas |
|
||||
| [templatesem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/templatesem.d) | Do semantic analysis for templates |
|
||||
| [templateparamsem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/templateparamsem.d) | Do semantic analysis for template parameters |
|
||||
| [typesem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/typesem.d) | Do semantic analysis for types |
|
||||
|
|
|
@ -1 +1 @@
|
|||
v2.107.0-beta.1
|
||||
v2.107.0
|
||||
|
|
|
@ -1331,9 +1331,9 @@ int sliceCmpStringWithArray(const StringExp se1, ArrayLiteralExp ae2, size_t lo1
|
|||
{
|
||||
foreach (j; 0 .. len)
|
||||
{
|
||||
const val2 = cast(dchar)ae2[j + lo2].toInteger();
|
||||
const val1 = se1.getCodeUnit(j + lo1);
|
||||
const int c = val1 - val2;
|
||||
const val2 = ae2[j + lo2].toInteger();
|
||||
const val1 = se1.getIndex(j + lo1);
|
||||
const int c = (val1 > val2) - (val1 < val2);
|
||||
if (c)
|
||||
return c;
|
||||
}
|
||||
|
|
|
@ -5291,7 +5291,7 @@ final class CParser(AST) : Parser!AST
|
|||
auto ifn = new AST.ExpInitializer(loc, efn);
|
||||
auto lenfn = new AST.IntegerExp(loc, fn.length + 1, AST.Type.tuns32); // +1 for terminating 0
|
||||
auto tfn = new AST.TypeSArray(AST.Type.tchar, lenfn);
|
||||
efn.type = tfn.immutableOf();
|
||||
efn.type = tfn.makeImmutable();
|
||||
efn.committed = true;
|
||||
auto sfn = new AST.VarDeclaration(loc, tfn, Id.__func__, ifn, STC.gshared | STC.immutable_);
|
||||
auto e = new AST.DeclarationExp(loc, sfn);
|
||||
|
|
|
@ -34,6 +34,7 @@ import dmd.root.ctfloat;
|
|||
import dmd.root.port;
|
||||
import dmd.root.rmem;
|
||||
import dmd.tokens;
|
||||
import dmd.typesem;
|
||||
import dmd.visitor;
|
||||
|
||||
/****************************************************************/
|
||||
|
@ -640,7 +641,6 @@ bool isSafePointerCast(Type srcPointee, Type destPointee)
|
|||
// It's OK if function pointers differ only in safe/pure/nothrow
|
||||
if (srcPointee.ty == Tfunction && destPointee.ty == Tfunction)
|
||||
{
|
||||
import dmd.typesem : covariant;
|
||||
return srcPointee.covariant(destPointee) == Covariant.yes ||
|
||||
destPointee.covariant(srcPointee) == Covariant.yes;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import dmd.escape;
|
|||
import dmd.expression;
|
||||
import dmd.expressionsem;
|
||||
import dmd.func;
|
||||
import dmd.funcsem;
|
||||
import dmd.globals;
|
||||
import dmd.hdrgen;
|
||||
import dmd.location;
|
||||
|
@ -720,11 +721,6 @@ extern(C++) MATCH implicitConvTo(Expression e, Type t)
|
|||
return m;
|
||||
case Tint8:
|
||||
case Tuns8:
|
||||
if (e.hexString)
|
||||
{
|
||||
m = MATCH.convert;
|
||||
return m;
|
||||
}
|
||||
break;
|
||||
case Tenum:
|
||||
if (tn.isTypeEnum().sym.isSpecial())
|
||||
|
@ -739,6 +735,14 @@ extern(C++) MATCH implicitConvTo(Expression e, Type t)
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (e.hexString)
|
||||
{
|
||||
if (tn.isintegral && tn.size == e.sz)
|
||||
{
|
||||
m = MATCH.convert;
|
||||
return m;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -2185,7 +2189,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
|
|||
|
||||
if (auto f = isFuncAddress(e))
|
||||
{
|
||||
if (f.checkForwardRef(e.loc))
|
||||
if (checkForwardRef(f, e.loc))
|
||||
{
|
||||
return ErrorExp.get();
|
||||
}
|
||||
|
@ -2441,7 +2445,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
|
|||
|
||||
if (auto f = isFuncAddress(e))
|
||||
{
|
||||
if (f.checkForwardRef(e.loc))
|
||||
if (checkForwardRef(f, e.loc))
|
||||
{
|
||||
return ErrorExp.get();
|
||||
}
|
||||
|
@ -2496,7 +2500,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
|
|||
|
||||
if (auto f = isFuncAddress(e))
|
||||
{
|
||||
if (f.checkForwardRef(e.loc))
|
||||
if (checkForwardRef(f, e.loc))
|
||||
{
|
||||
return ErrorExp.get();
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import dmd.mtype;
|
|||
import dmd.objc;
|
||||
import dmd.root.rmem;
|
||||
import dmd.target;
|
||||
import dmd.typesem;
|
||||
import dmd.visitor;
|
||||
|
||||
/***********************************************************
|
||||
|
|
|
@ -703,7 +703,6 @@ public:
|
|||
Expressions *fdensureParams(Expressions *fdep);
|
||||
bool equals(const RootObject * const o) const override final;
|
||||
|
||||
int findVtblIndex(Dsymbols *vtbl, int dim);
|
||||
bool overloadInsert(Dsymbol *s) override;
|
||||
bool inUnittest();
|
||||
static MATCH leastAsSpecialized(FuncDeclaration *f, FuncDeclaration *g, Identifiers *names);
|
||||
|
|
|
@ -18,19 +18,16 @@ import core.stdc.stdio;
|
|||
|
||||
import dmd.astenums;
|
||||
import dmd.attrib;
|
||||
import dmd.errors;
|
||||
import dmd.gluelayer;
|
||||
import dmd.declaration;
|
||||
import dmd.dscope;
|
||||
import dmd.dsymbol;
|
||||
import dmd.dsymbolsem;
|
||||
import dmd.expression;
|
||||
import dmd.id;
|
||||
import dmd.identifier;
|
||||
import dmd.init;
|
||||
import dmd.location;
|
||||
import dmd.mtype;
|
||||
import dmd.typesem;
|
||||
import dmd.visitor;
|
||||
|
||||
/***********************************************************
|
||||
|
@ -66,6 +63,8 @@ extern (C++) final class EnumDeclaration : ScopeDsymbol
|
|||
import dmd.common.bitfields : generateBitFields;
|
||||
mixin(generateBitFields!(BitFields, ubyte));
|
||||
|
||||
Symbol* sinit;
|
||||
|
||||
extern (D) this(const ref Loc loc, Identifier ident, Type memtype)
|
||||
{
|
||||
super(loc, ident);
|
||||
|
@ -127,8 +126,6 @@ extern (C++) final class EnumDeclaration : ScopeDsymbol
|
|||
return this;
|
||||
}
|
||||
|
||||
Symbol* sinit;
|
||||
|
||||
override void accept(Visitor v)
|
||||
{
|
||||
v.visit(this);
|
||||
|
|
|
@ -50,6 +50,8 @@ import dmd.rootobject;
|
|||
import dmd.root.utf;
|
||||
import dmd.statement;
|
||||
import dmd.tokens;
|
||||
import dmd.typesem : mutableOf;
|
||||
import dmd.utils : arrayCastBigEndian;
|
||||
import dmd.visitor;
|
||||
|
||||
/*************************************
|
||||
|
@ -7744,44 +7746,3 @@ private void removeHookTraceImpl(ref CallExp ce, ref FuncDeclaration fd)
|
|||
if (global.params.v.verbose)
|
||||
message("strip %s =>\n %s", oldCE.toChars(), ce.toChars());
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast a `ubyte[]` to an array of larger integers as if we are on a big endian architecture
|
||||
* Params:
|
||||
* data = array with big endian data
|
||||
* size = 1 for ubyte[], 2 for ushort[], 4 for uint[], 8 for ulong[]
|
||||
* Returns: copy of `data`, with bytes shuffled if compiled for `version(LittleEndian)`
|
||||
*/
|
||||
ubyte[] arrayCastBigEndian(const ubyte[] data, size_t size)
|
||||
{
|
||||
ubyte[] impl(T)()
|
||||
{
|
||||
auto result = new T[](data.length / T.sizeof);
|
||||
foreach (i; 0 .. result.length)
|
||||
{
|
||||
result[i] = 0;
|
||||
foreach (j; 0 .. T.sizeof)
|
||||
{
|
||||
result[i] |= T(data[i * T.sizeof + j]) << ((T.sizeof - 1 - j) * 8);
|
||||
}
|
||||
}
|
||||
return cast(ubyte[]) result;
|
||||
}
|
||||
switch (size)
|
||||
{
|
||||
case 1: return data.dup;
|
||||
case 2: return impl!ushort;
|
||||
case 4: return impl!uint;
|
||||
case 8: return impl!ulong;
|
||||
default: assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
ubyte[] data = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22];
|
||||
assert(cast(ulong[]) arrayCastBigEndian(data, 8) == [0xAABBCCDDEEFF1122]);
|
||||
assert(cast(uint[]) arrayCastBigEndian(data, 4) == [0xAABBCCDD, 0xEEFF1122]);
|
||||
assert(cast(ushort[]) arrayCastBigEndian(data, 2) == [0xAABB, 0xCCDD, 0xEEFF, 0x1122]);
|
||||
assert(cast(ubyte[]) arrayCastBigEndian(data, 1) == data);
|
||||
}
|
||||
|
|
|
@ -951,6 +951,14 @@ public:
|
|||
OutBuffer tmp;
|
||||
const(char)[] q;
|
||||
|
||||
void mangleAsArray()
|
||||
{
|
||||
buf.writeByte('A');
|
||||
buf.print(e.len);
|
||||
foreach (i; 0 .. e.len)
|
||||
mangleInteger(e.getIndex(i));
|
||||
}
|
||||
|
||||
/* Write string in UTF-8 format
|
||||
*/
|
||||
switch (e.sz)
|
||||
|
@ -967,7 +975,7 @@ public:
|
|||
{
|
||||
dchar c;
|
||||
if (const s = utf_decodeWchar(slice, u, c))
|
||||
error(e.loc, "%.*s", cast(int)s.length, s.ptr);
|
||||
return mangleAsArray();
|
||||
else
|
||||
tmp.writeUTF8(c);
|
||||
}
|
||||
|
@ -981,7 +989,7 @@ public:
|
|||
foreach (c; slice)
|
||||
{
|
||||
if (!utf_isValidDchar(c))
|
||||
error(e.loc, "invalid UCS-32 char \\U%08x", c);
|
||||
return mangleAsArray();
|
||||
else
|
||||
tmp.writeUTF8(c);
|
||||
}
|
||||
|
@ -990,13 +998,7 @@ public:
|
|||
}
|
||||
case 8:
|
||||
// String of size 8 has to be hexstring cast to long[], mangle as array literal
|
||||
buf.writeByte('A');
|
||||
buf.print(e.len);
|
||||
foreach (i; 0 .. e.len)
|
||||
{
|
||||
mangleInteger(e.getIndex(i));
|
||||
}
|
||||
return;
|
||||
return mangleAsArray();
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
|
|
@ -147,7 +147,7 @@ enum
|
|||
|
||||
/* Flags for symbol search
|
||||
*/
|
||||
typedef uint SearchOptFlags;
|
||||
typedef unsigned SearchOptFlags;
|
||||
enum class SearchOpt : SearchOptFlags
|
||||
{
|
||||
all = 0x00, // default
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -816,194 +816,6 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
|
|||
return buf.extractChars();
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* Declare template parameter tp with value o, and install it in the scope sc.
|
||||
*/
|
||||
extern (D) RootObject declareParameter(Scope* sc, TemplateParameter tp, RootObject o)
|
||||
{
|
||||
//printf("TemplateDeclaration.declareParameter('%s', o = %p)\n", tp.ident.toChars(), o);
|
||||
Type ta = isType(o);
|
||||
Expression ea = isExpression(o);
|
||||
Dsymbol sa = isDsymbol(o);
|
||||
Tuple va = isTuple(o);
|
||||
|
||||
Declaration d;
|
||||
VarDeclaration v = null;
|
||||
|
||||
if (ea)
|
||||
{
|
||||
if (ea.op == EXP.type)
|
||||
ta = ea.type;
|
||||
else if (auto se = ea.isScopeExp())
|
||||
sa = se.sds;
|
||||
else if (auto te = ea.isThisExp())
|
||||
sa = te.var;
|
||||
else if (auto se = ea.isSuperExp())
|
||||
sa = se.var;
|
||||
else if (auto fe = ea.isFuncExp())
|
||||
{
|
||||
if (fe.td)
|
||||
sa = fe.td;
|
||||
else
|
||||
sa = fe.fd;
|
||||
}
|
||||
}
|
||||
|
||||
if (ta)
|
||||
{
|
||||
//printf("type %s\n", ta.toChars());
|
||||
auto ad = new AliasDeclaration(Loc.initial, tp.ident, ta);
|
||||
ad.storage_class |= STC.templateparameter;
|
||||
d = ad;
|
||||
}
|
||||
else if (sa)
|
||||
{
|
||||
//printf("Alias %s %s;\n", sa.ident.toChars(), tp.ident.toChars());
|
||||
auto ad = new AliasDeclaration(Loc.initial, tp.ident, sa);
|
||||
ad.storage_class |= STC.templateparameter;
|
||||
d = ad;
|
||||
}
|
||||
else if (ea)
|
||||
{
|
||||
// tdtypes.data[i] always matches ea here
|
||||
Initializer _init = new ExpInitializer(loc, ea);
|
||||
TemplateValueParameter tvp = tp.isTemplateValueParameter();
|
||||
Type t = tvp ? tvp.valType : null;
|
||||
v = new VarDeclaration(loc, t, tp.ident, _init);
|
||||
v.storage_class = STC.manifest | STC.templateparameter;
|
||||
d = v;
|
||||
}
|
||||
else if (va)
|
||||
{
|
||||
//printf("\ttuple\n");
|
||||
d = new TupleDeclaration(loc, tp.ident, &va.objects);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
d.storage_class |= STC.templateparameter;
|
||||
|
||||
if (ta)
|
||||
{
|
||||
Type t = ta;
|
||||
// consistent with Type.checkDeprecated()
|
||||
while (t.ty != Tenum)
|
||||
{
|
||||
if (!t.nextOf())
|
||||
break;
|
||||
t = (cast(TypeNext)t).next;
|
||||
}
|
||||
if (Dsymbol s = t.toDsymbol(sc))
|
||||
{
|
||||
if (s.isDeprecated())
|
||||
d.storage_class |= STC.deprecated_;
|
||||
}
|
||||
}
|
||||
else if (sa)
|
||||
{
|
||||
if (sa.isDeprecated())
|
||||
d.storage_class |= STC.deprecated_;
|
||||
}
|
||||
|
||||
if (!sc.insert(d))
|
||||
.error(loc, "%s `%s` declaration `%s` is already defined", kind, toPrettyChars, tp.ident.toChars());
|
||||
d.dsymbolSemantic(sc);
|
||||
/* So the caller's o gets updated with the result of semantic() being run on o
|
||||
*/
|
||||
if (v)
|
||||
o = v._init.initializerToExpression();
|
||||
return o;
|
||||
}
|
||||
|
||||
/*************************************************
|
||||
* Limited function template instantiation for using fd.leastAsSpecialized()
|
||||
*/
|
||||
extern (D) FuncDeclaration doHeaderInstantiation(TemplateInstance ti, Scope* sc2, FuncDeclaration fd, Type tthis, Expressions* fargs)
|
||||
{
|
||||
assert(fd);
|
||||
version (none)
|
||||
{
|
||||
printf("doHeaderInstantiation this = %s\n", toChars());
|
||||
}
|
||||
|
||||
// function body and contracts are not need
|
||||
if (fd.isCtorDeclaration())
|
||||
fd = new CtorDeclaration(fd.loc, fd.endloc, fd.storage_class, fd.type.syntaxCopy());
|
||||
else
|
||||
fd = new FuncDeclaration(fd.loc, fd.endloc, fd.ident, fd.storage_class, fd.type.syntaxCopy());
|
||||
fd.parent = ti;
|
||||
|
||||
assert(fd.type.ty == Tfunction);
|
||||
auto tf = fd.type.isTypeFunction();
|
||||
tf.fargs = fargs;
|
||||
|
||||
if (tthis)
|
||||
{
|
||||
// Match 'tthis' to any TemplateThisParameter's
|
||||
bool hasttp = false;
|
||||
foreach (tp; *parameters)
|
||||
{
|
||||
TemplateThisParameter ttp = tp.isTemplateThisParameter();
|
||||
if (ttp)
|
||||
hasttp = true;
|
||||
}
|
||||
if (hasttp)
|
||||
{
|
||||
tf = tf.addSTC(ModToStc(tthis.mod)).isTypeFunction();
|
||||
assert(!tf.deco);
|
||||
}
|
||||
}
|
||||
|
||||
Scope* scx = sc2.push();
|
||||
|
||||
// Shouldn't run semantic on default arguments and return type.
|
||||
foreach (ref params; *tf.parameterList.parameters)
|
||||
params.defaultArg = null;
|
||||
tf.incomplete = true;
|
||||
|
||||
if (fd.isCtorDeclaration())
|
||||
{
|
||||
// For constructors, emitting return type is necessary for
|
||||
// isReturnIsolated() in functionResolve.
|
||||
tf.isctor = true;
|
||||
|
||||
Dsymbol parent = toParentDecl();
|
||||
Type tret;
|
||||
AggregateDeclaration ad = parent.isAggregateDeclaration();
|
||||
if (!ad || parent.isUnionDeclaration())
|
||||
{
|
||||
tret = Type.tvoid;
|
||||
}
|
||||
else
|
||||
{
|
||||
tret = ad.handleType();
|
||||
assert(tret);
|
||||
tret = tret.addStorageClass(fd.storage_class | scx.stc);
|
||||
tret = tret.addMod(tf.mod);
|
||||
}
|
||||
tf.next = tret;
|
||||
if (ad && ad.isStructDeclaration())
|
||||
tf.isref = 1;
|
||||
//printf("tf = %s\n", tf.toChars());
|
||||
}
|
||||
else
|
||||
tf.next = null;
|
||||
fd.type = tf;
|
||||
fd.type = fd.type.addSTC(scx.stc);
|
||||
fd.type = fd.type.typeSemantic(fd.loc, scx);
|
||||
scx = scx.pop();
|
||||
|
||||
if (fd.type.ty != Tfunction)
|
||||
return null;
|
||||
|
||||
fd.originalType = fd.type; // for mangling
|
||||
//printf("\t[%s] fd.type = %s, mod = %x, ", loc.toChars(), fd.type.toChars(), fd.type.mod);
|
||||
//printf("fd.needThis() = %d\n", fd.needThis());
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
debug (FindExistingInstance)
|
||||
{
|
||||
__gshared uint nFound, nNotFound, nAdded, nRemoved;
|
||||
|
@ -1145,575 +957,6 @@ extern (C++) final class TypeDeduced : Type
|
|||
}
|
||||
|
||||
|
||||
/*************************************************
|
||||
* Given function arguments, figure out which template function
|
||||
* to expand, and return matching result.
|
||||
* Params:
|
||||
* m = matching result
|
||||
* dstart = the root of overloaded function templates
|
||||
* loc = instantiation location
|
||||
* sc = instantiation scope
|
||||
* tiargs = initial list of template arguments
|
||||
* tthis = if !NULL, the 'this' pointer argument
|
||||
* argumentList= arguments to function
|
||||
* errorHelper = delegate to send error message to if not null
|
||||
*/
|
||||
void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, Objects* tiargs,
|
||||
Type tthis, ArgumentList argumentList, void delegate(const(char)*) scope errorHelper = null)
|
||||
{
|
||||
version (none)
|
||||
{
|
||||
printf("functionResolve() dstart = %s\n", dstart.toChars());
|
||||
printf(" tiargs:\n");
|
||||
if (tiargs)
|
||||
{
|
||||
for (size_t i = 0; i < tiargs.length; i++)
|
||||
{
|
||||
RootObject arg = (*tiargs)[i];
|
||||
printf("\t%s\n", arg.toChars());
|
||||
}
|
||||
}
|
||||
printf(" fargs:\n");
|
||||
for (size_t i = 0; i < (fargs ? fargs.length : 0); i++)
|
||||
{
|
||||
Expression arg = (*fargs)[i];
|
||||
printf("\t%s %s\n", arg.type.toChars(), arg.toChars());
|
||||
//printf("\tty = %d\n", arg.type.ty);
|
||||
}
|
||||
//printf("stc = %llx\n", dstart._scope.stc);
|
||||
//printf("match:t/f = %d/%d\n", ta_last, m.last);
|
||||
}
|
||||
|
||||
// results
|
||||
int property = 0; // 0: uninitialized
|
||||
// 1: seen @property
|
||||
// 2: not @property
|
||||
size_t ov_index = 0;
|
||||
TemplateDeclaration td_best;
|
||||
TemplateInstance ti_best;
|
||||
MATCH ta_last = m.last != MATCH.nomatch ? MATCH.exact : MATCH.nomatch;
|
||||
Type tthis_best;
|
||||
|
||||
int applyFunction(FuncDeclaration fd)
|
||||
{
|
||||
// skip duplicates
|
||||
if (fd == m.lastf)
|
||||
return 0;
|
||||
// explicitly specified tiargs never match to non template function
|
||||
if (tiargs && tiargs.length > 0)
|
||||
return 0;
|
||||
|
||||
// constructors need a valid scope in order to detect semantic errors
|
||||
if (!fd.isCtorDeclaration &&
|
||||
fd.semanticRun < PASS.semanticdone)
|
||||
{
|
||||
Ungag ungag = fd.ungagSpeculative();
|
||||
fd.dsymbolSemantic(null);
|
||||
}
|
||||
if (fd.semanticRun < PASS.semanticdone)
|
||||
{
|
||||
.error(loc, "forward reference to template `%s`", fd.toChars());
|
||||
return 1;
|
||||
}
|
||||
//printf("fd = %s %s, fargs = %s\n", fd.toChars(), fd.type.toChars(), fargs.toChars());
|
||||
auto tf = fd.type.isTypeFunction();
|
||||
|
||||
int prop = tf.isproperty ? 1 : 2;
|
||||
if (property == 0)
|
||||
property = prop;
|
||||
else if (property != prop)
|
||||
error(fd.loc, "cannot overload both property and non-property functions");
|
||||
|
||||
/* For constructors, qualifier check will be opposite direction.
|
||||
* Qualified constructor always makes qualified object, then will be checked
|
||||
* that it is implicitly convertible to tthis.
|
||||
*/
|
||||
Type tthis_fd = fd.needThis() ? tthis : null;
|
||||
bool isCtorCall = tthis_fd && fd.isCtorDeclaration();
|
||||
if (isCtorCall)
|
||||
{
|
||||
//printf("%s tf.mod = x%x tthis_fd.mod = x%x %d\n", tf.toChars(),
|
||||
// tf.mod, tthis_fd.mod, fd.isReturnIsolated());
|
||||
if (MODimplicitConv(tf.mod, tthis_fd.mod) ||
|
||||
tf.isWild() && tf.isShared() == tthis_fd.isShared() ||
|
||||
fd.isReturnIsolated())
|
||||
{
|
||||
/* && tf.isShared() == tthis_fd.isShared()*/
|
||||
// Uniquely constructed object can ignore shared qualifier.
|
||||
// TODO: Is this appropriate?
|
||||
tthis_fd = null;
|
||||
}
|
||||
else
|
||||
return 0; // MATCH.nomatch
|
||||
}
|
||||
/* Fix Issue 17970:
|
||||
If a struct is declared as shared the dtor is automatically
|
||||
considered to be shared, but when the struct is instantiated
|
||||
the instance is no longer considered to be shared when the
|
||||
function call matching is done. The fix makes it so that if a
|
||||
struct declaration is shared, when the destructor is called,
|
||||
the instantiated struct is also considered shared.
|
||||
*/
|
||||
if (auto dt = fd.isDtorDeclaration())
|
||||
{
|
||||
auto dtmod = dt.type.toTypeFunction();
|
||||
auto shared_dtor = dtmod.mod & MODFlags.shared_;
|
||||
auto shared_this = tthis_fd !is null ?
|
||||
tthis_fd.mod & MODFlags.shared_ : 0;
|
||||
if (shared_dtor && !shared_this)
|
||||
tthis_fd = dtmod;
|
||||
else if (shared_this && !shared_dtor && tthis_fd !is null)
|
||||
tf.mod = tthis_fd.mod;
|
||||
}
|
||||
const(char)* failMessage;
|
||||
const(char)** pMessage = errorHelper ? &failMessage : null;
|
||||
MATCH mfa = tf.callMatch(tthis_fd, argumentList, 0, errorHelper, sc);
|
||||
//printf("test1: mfa = %d\n", mfa);
|
||||
if (failMessage)
|
||||
errorHelper(failMessage);
|
||||
if (mfa == MATCH.nomatch)
|
||||
return 0;
|
||||
|
||||
int firstIsBetter()
|
||||
{
|
||||
td_best = null;
|
||||
ti_best = null;
|
||||
ta_last = MATCH.exact;
|
||||
m.last = mfa;
|
||||
m.lastf = fd;
|
||||
tthis_best = tthis_fd;
|
||||
ov_index = 0;
|
||||
m.count = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mfa > m.last) return firstIsBetter();
|
||||
if (mfa < m.last) return 0;
|
||||
|
||||
/* See if one of the matches overrides the other.
|
||||
*/
|
||||
assert(m.lastf);
|
||||
if (m.lastf.overrides(fd)) return 0;
|
||||
if (fd.overrides(m.lastf)) return firstIsBetter();
|
||||
|
||||
/* Try to disambiguate using template-style partial ordering rules.
|
||||
* In essence, if f() and g() are ambiguous, if f() can call g(),
|
||||
* but g() cannot call f(), then pick f().
|
||||
* This is because f() is "more specialized."
|
||||
*/
|
||||
{
|
||||
MATCH c1 = FuncDeclaration.leastAsSpecialized(fd, m.lastf, argumentList.names);
|
||||
MATCH c2 = FuncDeclaration.leastAsSpecialized(m.lastf, fd, argumentList.names);
|
||||
//printf("c1 = %d, c2 = %d\n", c1, c2);
|
||||
if (c1 > c2) return firstIsBetter();
|
||||
if (c1 < c2) return 0;
|
||||
}
|
||||
|
||||
/* The 'overrides' check above does covariant checking only
|
||||
* for virtual member functions. It should do it for all functions,
|
||||
* but in order to not risk breaking code we put it after
|
||||
* the 'leastAsSpecialized' check.
|
||||
* In the future try moving it before.
|
||||
* I.e. a not-the-same-but-covariant match is preferred,
|
||||
* as it is more restrictive.
|
||||
*/
|
||||
if (!m.lastf.type.equals(fd.type))
|
||||
{
|
||||
//printf("cov: %d %d\n", m.lastf.type.covariant(fd.type), fd.type.covariant(m.lastf.type));
|
||||
const lastCovariant = m.lastf.type.covariant(fd.type);
|
||||
const firstCovariant = fd.type.covariant(m.lastf.type);
|
||||
|
||||
if (lastCovariant == Covariant.yes || lastCovariant == Covariant.no)
|
||||
{
|
||||
if (firstCovariant != Covariant.yes && firstCovariant != Covariant.no)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (firstCovariant == Covariant.yes || firstCovariant == Covariant.no)
|
||||
{
|
||||
return firstIsBetter();
|
||||
}
|
||||
}
|
||||
|
||||
/* If the two functions are the same function, like:
|
||||
* int foo(int);
|
||||
* int foo(int x) { ... }
|
||||
* then pick the one with the body.
|
||||
*
|
||||
* If none has a body then don't care because the same
|
||||
* real function would be linked to the decl (e.g from object file)
|
||||
*/
|
||||
if (tf.equals(m.lastf.type) &&
|
||||
fd.storage_class == m.lastf.storage_class &&
|
||||
fd.parent == m.lastf.parent &&
|
||||
fd.visibility == m.lastf.visibility &&
|
||||
fd._linkage == m.lastf._linkage)
|
||||
{
|
||||
if (fd.fbody && !m.lastf.fbody)
|
||||
return firstIsBetter();
|
||||
if (!fd.fbody)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// https://issues.dlang.org/show_bug.cgi?id=14450
|
||||
// Prefer exact qualified constructor for the creating object type
|
||||
if (isCtorCall && tf.mod != m.lastf.type.mod)
|
||||
{
|
||||
if (tthis.mod == tf.mod) return firstIsBetter();
|
||||
if (tthis.mod == m.lastf.type.mod) return 0;
|
||||
}
|
||||
|
||||
m.nextf = fd;
|
||||
m.count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int applyTemplate(TemplateDeclaration td)
|
||||
{
|
||||
//printf("applyTemplate(): td = %s\n", td.toChars());
|
||||
if (td == td_best) // skip duplicates
|
||||
return 0;
|
||||
|
||||
if (!sc)
|
||||
sc = td._scope; // workaround for Type.aliasthisOf
|
||||
|
||||
if (td.semanticRun == PASS.initial && td._scope)
|
||||
{
|
||||
// Try to fix forward reference. Ungag errors while doing so.
|
||||
Ungag ungag = td.ungagSpeculative();
|
||||
td.dsymbolSemantic(td._scope);
|
||||
}
|
||||
if (td.semanticRun == PASS.initial)
|
||||
{
|
||||
.error(loc, "forward reference to template `%s`", td.toChars());
|
||||
Lerror:
|
||||
m.lastf = null;
|
||||
m.count = 0;
|
||||
m.last = MATCH.nomatch;
|
||||
return 1;
|
||||
}
|
||||
//printf("td = %s\n", td.toChars());
|
||||
|
||||
if (argumentList.hasNames)
|
||||
{
|
||||
.error(loc, "named arguments with Implicit Function Template Instantiation are not supported yet");
|
||||
goto Lerror;
|
||||
}
|
||||
auto f = td.onemember ? td.onemember.isFuncDeclaration() : null;
|
||||
if (!f)
|
||||
{
|
||||
if (!tiargs)
|
||||
tiargs = new Objects();
|
||||
auto ti = new TemplateInstance(loc, td, tiargs);
|
||||
Objects dedtypes = Objects(td.parameters.length);
|
||||
assert(td.semanticRun != PASS.initial);
|
||||
MATCH mta = matchWithInstance(sc, td, ti, dedtypes, argumentList, 0);
|
||||
//printf("matchWithInstance = %d\n", mta);
|
||||
if (mta == MATCH.nomatch || mta < ta_last) // no match or less match
|
||||
return 0;
|
||||
|
||||
ti.templateInstanceSemantic(sc, argumentList);
|
||||
if (!ti.inst) // if template failed to expand
|
||||
return 0;
|
||||
|
||||
Dsymbol s = ti.inst.toAlias();
|
||||
FuncDeclaration fd;
|
||||
if (auto tdx = s.isTemplateDeclaration())
|
||||
{
|
||||
Objects dedtypesX; // empty tiargs
|
||||
|
||||
// https://issues.dlang.org/show_bug.cgi?id=11553
|
||||
// Check for recursive instantiation of tdx.
|
||||
for (TemplatePrevious* p = tdx.previous; p; p = p.prev)
|
||||
{
|
||||
if (arrayObjectMatch(*p.dedargs, dedtypesX))
|
||||
{
|
||||
//printf("recursive, no match p.sc=%p %p %s\n", p.sc, this, this.toChars());
|
||||
/* It must be a subscope of p.sc, other scope chains are not recursive
|
||||
* instantiations.
|
||||
*/
|
||||
for (Scope* scx = sc; scx; scx = scx.enclosing)
|
||||
{
|
||||
if (scx == p.sc)
|
||||
{
|
||||
error(loc, "recursive template expansion while looking for `%s.%s`", ti.toChars(), tdx.toChars());
|
||||
goto Lerror;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* BUG: should also check for ref param differences
|
||||
*/
|
||||
}
|
||||
|
||||
TemplatePrevious pr;
|
||||
pr.prev = tdx.previous;
|
||||
pr.sc = sc;
|
||||
pr.dedargs = &dedtypesX;
|
||||
tdx.previous = ≺ // add this to threaded list
|
||||
|
||||
fd = resolveFuncCall(loc, sc, s, null, tthis, argumentList, FuncResolveFlag.quiet);
|
||||
|
||||
tdx.previous = pr.prev; // unlink from threaded list
|
||||
}
|
||||
else if (s.isFuncDeclaration())
|
||||
{
|
||||
fd = resolveFuncCall(loc, sc, s, null, tthis, argumentList, FuncResolveFlag.quiet);
|
||||
}
|
||||
else
|
||||
goto Lerror;
|
||||
|
||||
if (!fd)
|
||||
return 0;
|
||||
|
||||
if (fd.type.ty != Tfunction)
|
||||
{
|
||||
m.lastf = fd; // to propagate "error match"
|
||||
m.count = 1;
|
||||
m.last = MATCH.nomatch;
|
||||
return 1;
|
||||
}
|
||||
|
||||
Type tthis_fd = fd.needThis() && !fd.isCtorDeclaration() ? tthis : null;
|
||||
|
||||
auto tf = fd.type.isTypeFunction();
|
||||
MATCH mfa = tf.callMatch(tthis_fd, argumentList, 0, null, sc);
|
||||
if (mfa < m.last)
|
||||
return 0;
|
||||
|
||||
if (mta < ta_last) goto Ltd_best2;
|
||||
if (mta > ta_last) goto Ltd2;
|
||||
|
||||
if (mfa < m.last) goto Ltd_best2;
|
||||
if (mfa > m.last) goto Ltd2;
|
||||
|
||||
// td_best and td are ambiguous
|
||||
//printf("Lambig2\n");
|
||||
m.nextf = fd;
|
||||
m.count++;
|
||||
return 0;
|
||||
|
||||
Ltd_best2:
|
||||
return 0;
|
||||
|
||||
Ltd2:
|
||||
// td is the new best match
|
||||
assert(td._scope);
|
||||
td_best = td;
|
||||
ti_best = null;
|
||||
property = 0; // (backward compatibility)
|
||||
ta_last = mta;
|
||||
m.last = mfa;
|
||||
m.lastf = fd;
|
||||
tthis_best = tthis_fd;
|
||||
ov_index = 0;
|
||||
m.nextf = null;
|
||||
m.count = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//printf("td = %s\n", td.toChars());
|
||||
for (size_t ovi = 0; f; f = f.overnext0, ovi++)
|
||||
{
|
||||
if (f.type.ty != Tfunction || f.errors)
|
||||
goto Lerror;
|
||||
|
||||
/* This is a 'dummy' instance to evaluate constraint properly.
|
||||
*/
|
||||
auto ti = new TemplateInstance(loc, td, tiargs);
|
||||
ti.parent = td.parent; // Maybe calculating valid 'enclosing' is unnecessary.
|
||||
|
||||
auto fd = f;
|
||||
MATCHpair x = td.deduceFunctionTemplateMatch(ti, sc, fd, tthis, argumentList);
|
||||
MATCH mta = x.mta;
|
||||
MATCH mfa = x.mfa;
|
||||
//printf("match:t/f = %d/%d\n", mta, mfa);
|
||||
if (!fd || mfa == MATCH.nomatch)
|
||||
continue;
|
||||
|
||||
Type tthis_fd = fd.needThis() ? tthis : null;
|
||||
|
||||
bool isCtorCall = tthis_fd && fd.isCtorDeclaration();
|
||||
if (isCtorCall)
|
||||
{
|
||||
// Constructor call requires additional check.
|
||||
auto tf = fd.type.isTypeFunction();
|
||||
assert(tf.next);
|
||||
if (MODimplicitConv(tf.mod, tthis_fd.mod) ||
|
||||
tf.isWild() && tf.isShared() == tthis_fd.isShared() ||
|
||||
fd.isReturnIsolated())
|
||||
{
|
||||
tthis_fd = null;
|
||||
}
|
||||
else
|
||||
continue; // MATCH.nomatch
|
||||
|
||||
// need to check here whether the constructor is the member of a struct
|
||||
// declaration that defines a copy constructor. This is already checked
|
||||
// in the semantic of CtorDeclaration, however, when matching functions,
|
||||
// the template instance is not expanded.
|
||||
// https://issues.dlang.org/show_bug.cgi?id=21613
|
||||
auto ad = fd.isThis();
|
||||
auto sd = ad.isStructDeclaration();
|
||||
if (checkHasBothRvalueAndCpCtor(sd, fd.isCtorDeclaration(), ti))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mta < ta_last) goto Ltd_best;
|
||||
if (mta > ta_last) goto Ltd;
|
||||
|
||||
if (mfa < m.last) goto Ltd_best;
|
||||
if (mfa > m.last) goto Ltd;
|
||||
|
||||
if (td_best)
|
||||
{
|
||||
// Disambiguate by picking the most specialized TemplateDeclaration
|
||||
MATCH c1 = leastAsSpecialized(sc, td, td_best, argumentList);
|
||||
MATCH c2 = leastAsSpecialized(sc, td_best, td, argumentList);
|
||||
//printf("1: c1 = %d, c2 = %d\n", c1, c2);
|
||||
if (c1 > c2) goto Ltd;
|
||||
if (c1 < c2) goto Ltd_best;
|
||||
}
|
||||
assert(fd && m.lastf);
|
||||
{
|
||||
// Disambiguate by tf.callMatch
|
||||
auto tf1 = fd.type.isTypeFunction();
|
||||
auto tf2 = m.lastf.type.isTypeFunction();
|
||||
MATCH c1 = tf1.callMatch(tthis_fd, argumentList, 0, null, sc);
|
||||
MATCH c2 = tf2.callMatch(tthis_best, argumentList, 0, null, sc);
|
||||
//printf("2: c1 = %d, c2 = %d\n", c1, c2);
|
||||
if (c1 > c2) goto Ltd;
|
||||
if (c1 < c2) goto Ltd_best;
|
||||
}
|
||||
{
|
||||
// Disambiguate by picking the most specialized FunctionDeclaration
|
||||
MATCH c1 = FuncDeclaration.leastAsSpecialized(fd, m.lastf, argumentList.names);
|
||||
MATCH c2 = FuncDeclaration.leastAsSpecialized(m.lastf, fd, argumentList.names);
|
||||
//printf("3: c1 = %d, c2 = %d\n", c1, c2);
|
||||
if (c1 > c2) goto Ltd;
|
||||
if (c1 < c2) goto Ltd_best;
|
||||
}
|
||||
|
||||
// https://issues.dlang.org/show_bug.cgi?id=14450
|
||||
// Prefer exact qualified constructor for the creating object type
|
||||
if (isCtorCall && fd.type.mod != m.lastf.type.mod)
|
||||
{
|
||||
if (tthis.mod == fd.type.mod) goto Ltd;
|
||||
if (tthis.mod == m.lastf.type.mod) goto Ltd_best;
|
||||
}
|
||||
|
||||
m.nextf = fd;
|
||||
m.count++;
|
||||
continue;
|
||||
|
||||
Ltd_best: // td_best is the best match so far
|
||||
//printf("Ltd_best\n");
|
||||
continue;
|
||||
|
||||
Ltd: // td is the new best match
|
||||
//printf("Ltd\n");
|
||||
assert(td._scope);
|
||||
td_best = td;
|
||||
ti_best = ti;
|
||||
property = 0; // (backward compatibility)
|
||||
ta_last = mta;
|
||||
m.last = mfa;
|
||||
m.lastf = fd;
|
||||
tthis_best = tthis_fd;
|
||||
ov_index = ovi;
|
||||
m.nextf = null;
|
||||
m.count = 1;
|
||||
continue;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto td = dstart.isTemplateDeclaration();
|
||||
if (td && td.funcroot)
|
||||
dstart = td.funcroot;
|
||||
overloadApply(dstart, (Dsymbol s)
|
||||
{
|
||||
if (s.errors)
|
||||
return 0;
|
||||
if (auto fd = s.isFuncDeclaration())
|
||||
return applyFunction(fd);
|
||||
if (auto td = s.isTemplateDeclaration())
|
||||
return applyTemplate(td);
|
||||
return 0;
|
||||
}, sc);
|
||||
|
||||
//printf("td_best = %p, m.lastf = %p\n", td_best, m.lastf);
|
||||
if (td_best && ti_best && m.count == 1)
|
||||
{
|
||||
// Matches to template function
|
||||
assert(td_best.onemember && td_best.onemember.isFuncDeclaration());
|
||||
/* The best match is td_best with arguments tdargs.
|
||||
* Now instantiate the template.
|
||||
*/
|
||||
assert(td_best._scope);
|
||||
if (!sc)
|
||||
sc = td_best._scope; // workaround for Type.aliasthisOf
|
||||
|
||||
auto ti = new TemplateInstance(loc, td_best, ti_best.tiargs);
|
||||
ti.templateInstanceSemantic(sc, argumentList);
|
||||
|
||||
m.lastf = ti.toAlias().isFuncDeclaration();
|
||||
if (!m.lastf)
|
||||
goto Lnomatch;
|
||||
if (ti.errors)
|
||||
{
|
||||
Lerror:
|
||||
m.count = 1;
|
||||
assert(m.lastf);
|
||||
m.last = MATCH.nomatch;
|
||||
return;
|
||||
}
|
||||
|
||||
// look forward instantiated overload function
|
||||
// Dsymbol.oneMembers is alredy called in TemplateInstance.semantic.
|
||||
// it has filled overnext0d
|
||||
while (ov_index--)
|
||||
{
|
||||
m.lastf = m.lastf.overnext0;
|
||||
assert(m.lastf);
|
||||
}
|
||||
|
||||
tthis_best = m.lastf.needThis() && !m.lastf.isCtorDeclaration() ? tthis : null;
|
||||
|
||||
if (m.lastf.type.ty == Terror)
|
||||
goto Lerror;
|
||||
auto tf = m.lastf.type.isTypeFunction();
|
||||
if (!tf.callMatch(tthis_best, argumentList, 0, null, sc))
|
||||
goto Lnomatch;
|
||||
|
||||
/* As https://issues.dlang.org/show_bug.cgi?id=3682 shows,
|
||||
* a template instance can be matched while instantiating
|
||||
* that same template. Thus, the function type can be incomplete. Complete it.
|
||||
*
|
||||
* https://issues.dlang.org/show_bug.cgi?id=9208
|
||||
* For auto function, completion should be deferred to the end of
|
||||
* its semantic3. Should not complete it in here.
|
||||
*/
|
||||
if (tf.next && !m.lastf.inferRetType)
|
||||
{
|
||||
m.lastf.type = tf.typeSemantic(loc, sc);
|
||||
}
|
||||
}
|
||||
else if (m.lastf)
|
||||
{
|
||||
// Matches to non template function,
|
||||
// or found matches were ambiguous.
|
||||
assert(m.count >= 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Lnomatch:
|
||||
m.count = 0;
|
||||
m.lastf = null;
|
||||
m.last = MATCH.nomatch;
|
||||
}
|
||||
}
|
||||
|
||||
/* ======================== Type ============================================ */
|
||||
|
||||
/****
|
||||
|
@ -6101,7 +5344,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol
|
|||
{
|
||||
TemplateParameter tp = (*tempdecl.parameters)[i];
|
||||
//printf("\ttdtypes[%d] = %p\n", i, o);
|
||||
tempdecl.declareParameter(sc, tp, o);
|
||||
declareParameter(tempdecl, sc, tp, o);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ import dmd.location;
|
|||
import dmd.root.filename;
|
||||
import dmd.visitor;
|
||||
import dmd.tokens;
|
||||
import dmd.typesem;
|
||||
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.utils;
|
||||
|
|
|
@ -503,6 +503,8 @@ void enumMemberSemantic(Scope* sc, EnumMember em)
|
|||
{
|
||||
Expression e = em.value;
|
||||
assert(e.dyncast() == DYNCAST.expression);
|
||||
if (em.ed.memtype)
|
||||
e = inferType(e, em.ed.memtype);
|
||||
e = e.expressionSemantic(sc);
|
||||
e = resolveProperties(sc, e);
|
||||
e = e.ctfeInterpret();
|
||||
|
@ -571,6 +573,10 @@ void enumMemberSemantic(Scope* sc, EnumMember em)
|
|||
em.origValue = e;
|
||||
}
|
||||
em.value = e;
|
||||
// https://issues.dlang.org/show_bug.cgi?id=24311
|
||||
// First enum member is .init value, which gets put into static segment
|
||||
if (first)
|
||||
lowerStaticAAs(e, sc);
|
||||
}
|
||||
else if (first)
|
||||
{
|
||||
|
|
|
@ -48,6 +48,7 @@ import dmd.root.string;
|
|||
import dmd.root.utf;
|
||||
import dmd.target;
|
||||
import dmd.tokens;
|
||||
import dmd.typesem;
|
||||
import dmd.visitor;
|
||||
|
||||
enum LOGSEMANTIC = false;
|
||||
|
@ -3851,7 +3852,7 @@ extern (C++) final class CastExp : UnaExp
|
|||
if (!e1.isLvalue())
|
||||
return false;
|
||||
return (to.ty == Tsarray && (e1.type.ty == Tvector || e1.type.ty == Tsarray)) ||
|
||||
e1.type.mutableOf().unSharedOf().equals(to.mutableOf().unSharedOf());
|
||||
e1.type.mutableOf.unSharedOf().equals(to.mutableOf().unSharedOf());
|
||||
}
|
||||
|
||||
override void accept(Visitor v)
|
||||
|
|
|
@ -55,6 +55,7 @@ Expression *resolveLoc(Expression *exp, const Loc &loc, Scope *sc);
|
|||
MATCH implicitConvTo(Expression *e, Type *t);
|
||||
Expression *toLvalue(Expression *_this, Scope *sc, const char* action);
|
||||
Expression *modifiableLvalue(Expression* exp, Scope *sc);
|
||||
Expression *optimize(Expression *exp, int result, bool keepLvalue = false);
|
||||
|
||||
typedef unsigned char OwnedBy;
|
||||
enum
|
||||
|
@ -108,8 +109,6 @@ public:
|
|||
Expression *addressOf();
|
||||
Expression *deref();
|
||||
|
||||
Expression *optimize(int result, bool keepLvalue = false);
|
||||
|
||||
int isConst();
|
||||
virtual bool isIdentical(const Expression *e) const;
|
||||
virtual Optional<bool> toBool();
|
||||
|
|
|
@ -85,6 +85,7 @@ import dmd.traits;
|
|||
import dmd.typesem;
|
||||
import dmd.typinf;
|
||||
import dmd.utils;
|
||||
import dmd.utils : arrayCastBigEndian;
|
||||
import dmd.visitor;
|
||||
|
||||
enum LOGSEMANTIC = false;
|
||||
|
@ -4242,7 +4243,33 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
size_t u;
|
||||
dchar c;
|
||||
|
||||
switch (e.postfix)
|
||||
if (e.hexString)
|
||||
{
|
||||
const data = cast(const ubyte[]) e.peekString();
|
||||
switch (e.postfix)
|
||||
{
|
||||
case 'd':
|
||||
e.sz = 4;
|
||||
e.type = Type.tdstring;
|
||||
break;
|
||||
case 'w':
|
||||
e.sz = 2;
|
||||
e.type = Type.twstring;
|
||||
break;
|
||||
case 'c':
|
||||
default:
|
||||
e.type = Type.tstring;
|
||||
e.sz = 1;
|
||||
break;
|
||||
}
|
||||
if ((e.len % e.sz) != 0)
|
||||
error(e.loc, "hex string with `%s` type needs to be multiple of %d bytes, not %d",
|
||||
e.type.toChars(), e.sz, cast(int) e.len);
|
||||
|
||||
e.setData(arrayCastBigEndian(data, e.sz).ptr, e.len / e.sz, e.sz);
|
||||
e.committed = true;
|
||||
}
|
||||
else switch (e.postfix)
|
||||
{
|
||||
case 'd':
|
||||
for (u = 0; u < e.len;)
|
||||
|
@ -6263,7 +6290,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
auto ad2 = b.sym;
|
||||
ue.e1 = ue.e1.castTo(sc, ad2.type.addMod(ue.e1.type.mod));
|
||||
ue.e1 = ue.e1.expressionSemantic(sc);
|
||||
auto vi = exp.f.findVtblIndex(&ad2.vtbl, cast(int)ad2.vtbl.length);
|
||||
auto vi = findVtblIndex(exp.f, ad2.vtbl[]);
|
||||
assert(vi >= 0);
|
||||
exp.f = ad2.vtbl[vi].isFuncDeclaration();
|
||||
assert(exp.f);
|
||||
|
|
172
gcc/d/dmd/func.d
172
gcc/d/dmd/func.d
|
@ -58,6 +58,7 @@ import dmd.semantic3;
|
|||
import dmd.statement_rewrite_walker;
|
||||
import dmd.statement;
|
||||
import dmd.statementsem;
|
||||
import dmd.templatesem;
|
||||
import dmd.tokens;
|
||||
import dmd.visitor;
|
||||
|
||||
|
@ -467,29 +468,6 @@ extern (C++) class FuncDeclaration : Declaration
|
|||
return f;
|
||||
}
|
||||
|
||||
/****************************************************
|
||||
* Check that this function type is properly resolved.
|
||||
* If not, report "forward reference error" and return true.
|
||||
*/
|
||||
extern (D) final bool checkForwardRef(const ref Loc loc)
|
||||
{
|
||||
if (!functionSemantic(this))
|
||||
return true;
|
||||
|
||||
/* No deco means the functionSemantic() call could not resolve
|
||||
* forward referenes in the type of this function.
|
||||
*/
|
||||
if (!type.deco)
|
||||
{
|
||||
bool inSemantic3 = (inferRetType && semanticRun >= PASS.semantic3);
|
||||
.error(loc, "forward reference to %s`%s`",
|
||||
(inSemantic3 ? "inferred return type of function " : "").ptr,
|
||||
toChars());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
override final bool equals(const RootObject o) const
|
||||
{
|
||||
if (this == o)
|
||||
|
@ -553,154 +531,6 @@ extern (C++) class FuncDeclaration : Declaration
|
|||
return result;
|
||||
}
|
||||
|
||||
/*************************************************
|
||||
* Find index of function in vtbl[0..length] that
|
||||
* this function overrides.
|
||||
* Prefer an exact match to a covariant one.
|
||||
* Params:
|
||||
* vtbl = vtable to use
|
||||
* dim = maximal vtable dimension
|
||||
* Returns:
|
||||
* -1 didn't find one
|
||||
* -2 can't determine because of forward references
|
||||
*/
|
||||
final int findVtblIndex(Dsymbols* vtbl, int dim)
|
||||
{
|
||||
//printf("findVtblIndex() %s\n", toChars());
|
||||
import dmd.typesem : covariant;
|
||||
|
||||
FuncDeclaration mismatch = null;
|
||||
StorageClass mismatchstc = 0;
|
||||
int mismatchvi = -1;
|
||||
int exactvi = -1;
|
||||
int bestvi = -1;
|
||||
for (int vi = 0; vi < dim; vi++)
|
||||
{
|
||||
FuncDeclaration fdv = (*vtbl)[vi].isFuncDeclaration();
|
||||
if (fdv && fdv.ident == ident)
|
||||
{
|
||||
if (type.equals(fdv.type)) // if exact match
|
||||
{
|
||||
if (fdv.parent.isClassDeclaration())
|
||||
{
|
||||
if (fdv.isFuture())
|
||||
{
|
||||
bestvi = vi;
|
||||
continue; // keep looking
|
||||
}
|
||||
return vi; // no need to look further
|
||||
}
|
||||
|
||||
if (exactvi >= 0)
|
||||
{
|
||||
.error(loc, "%s `%s` cannot determine overridden function", kind, toPrettyChars);
|
||||
return exactvi;
|
||||
}
|
||||
exactvi = vi;
|
||||
bestvi = vi;
|
||||
continue;
|
||||
}
|
||||
|
||||
StorageClass stc = 0;
|
||||
const cov = type.covariant(fdv.type, &stc);
|
||||
//printf("\tbaseclass cov = %d\n", cov);
|
||||
final switch (cov)
|
||||
{
|
||||
case Covariant.distinct:
|
||||
// types are distinct
|
||||
break;
|
||||
|
||||
case Covariant.yes:
|
||||
bestvi = vi; // covariant, but not identical
|
||||
break;
|
||||
// keep looking for an exact match
|
||||
|
||||
case Covariant.no:
|
||||
mismatchvi = vi;
|
||||
mismatchstc = stc;
|
||||
mismatch = fdv; // overrides, but is not covariant
|
||||
break;
|
||||
// keep looking for an exact match
|
||||
|
||||
case Covariant.fwdref:
|
||||
return -2; // forward references
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_linkage == LINK.cpp && bestvi != -1)
|
||||
{
|
||||
StorageClass stc = 0;
|
||||
FuncDeclaration fdv = (*vtbl)[bestvi].isFuncDeclaration();
|
||||
assert(fdv && fdv.ident == ident);
|
||||
if (type.covariant(fdv.type, &stc, /*cppCovariant=*/true) == Covariant.no)
|
||||
{
|
||||
/* https://issues.dlang.org/show_bug.cgi?id=22351
|
||||
* Under D rules, `type` and `fdv.type` are covariant, but under C++ rules, they are not.
|
||||
* For now, continue to allow D covariant rules to apply when `override` has been used,
|
||||
* but issue a deprecation warning that this behaviour will change in the future.
|
||||
* Otherwise, follow the C++ covariant rules, which will create a new vtable entry.
|
||||
*/
|
||||
if (isOverride())
|
||||
{
|
||||
/* @@@DEPRECATED_2.110@@@
|
||||
* After deprecation period has ended, be sure to remove this entire `LINK.cpp` branch,
|
||||
* but also the `cppCovariant` parameter from Type.covariant, and update the function
|
||||
* so that both `LINK.cpp` covariant conditions within are always checked.
|
||||
*/
|
||||
.deprecation(loc, "overriding `extern(C++)` function `%s%s` with `const` qualified function `%s%s%s` is deprecated",
|
||||
fdv.toPrettyChars(), fdv.type.toTypeFunction().parameterList.parametersTypeToChars(),
|
||||
toPrettyChars(), type.toTypeFunction().parameterList.parametersTypeToChars(), type.modToChars());
|
||||
|
||||
const char* where = type.isNaked() ? "parameters" : "type";
|
||||
deprecationSupplemental(loc, "Either remove `override`, or adjust the `const` qualifiers of the "
|
||||
~ "overriding function %s", where);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Treat as if Covariant.no
|
||||
mismatchvi = bestvi;
|
||||
mismatchstc = stc;
|
||||
mismatch = fdv;
|
||||
bestvi = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bestvi == -1 && mismatch)
|
||||
{
|
||||
//type.print();
|
||||
//mismatch.type.print();
|
||||
//printf("%s %s\n", type.deco, mismatch.type.deco);
|
||||
//printf("stc = %llx\n", mismatchstc);
|
||||
if (mismatchstc)
|
||||
{
|
||||
// Fix it by modifying the type to add the storage classes
|
||||
type = type.addStorageClass(mismatchstc);
|
||||
bestvi = mismatchvi;
|
||||
}
|
||||
}
|
||||
return bestvi;
|
||||
}
|
||||
|
||||
/*********************************
|
||||
* If function a function in a base class,
|
||||
* return that base class.
|
||||
* Returns:
|
||||
* base class if overriding, null if not
|
||||
*/
|
||||
extern (D) final BaseClass* overrideInterface()
|
||||
{
|
||||
for (ClassDeclaration cd = toParent2().isClassDeclaration(); cd; cd = cd.baseClass)
|
||||
{
|
||||
foreach (b; cd.interfaces)
|
||||
{
|
||||
auto v = findVtblIndex(&b.sym.vtbl, cast(int)b.sym.vtbl.length);
|
||||
if (v >= 0)
|
||||
return b;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/****************************************************
|
||||
* Overload this FuncDeclaration with the new one f.
|
||||
* Return true if successful; i.e. no conflict.
|
||||
|
|
1150
gcc/d/dmd/funcsem.d
1150
gcc/d/dmd/funcsem.d
File diff suppressed because it is too large
Load diff
|
@ -50,6 +50,7 @@ import dmd.root.string;
|
|||
import dmd.statement;
|
||||
import dmd.staticassert;
|
||||
import dmd.tokens;
|
||||
import dmd.typesem;
|
||||
import dmd.visitor;
|
||||
|
||||
struct HdrGenState
|
||||
|
@ -1552,7 +1553,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
|
|||
buf.writeByte('}');
|
||||
buf.writenl();
|
||||
|
||||
if (!hgs.importcHdr)
|
||||
if (!hgs.importcHdr || !d.ident)
|
||||
return;
|
||||
|
||||
/* C enums get their members inserted into the symbol table of the enum declaration.
|
||||
|
|
|
@ -30,6 +30,7 @@ import dmd.errors;
|
|||
import dmd.expression;
|
||||
import dmd.expressionsem;
|
||||
import dmd.func;
|
||||
import dmd.funcsem;
|
||||
import dmd.globals;
|
||||
import dmd.hdrgen;
|
||||
import dmd.id;
|
||||
|
@ -59,36 +60,33 @@ Expression toAssocArrayLiteral(ArrayInitializer ai)
|
|||
{
|
||||
//printf("ArrayInitializer::toAssocArrayInitializer(%s)\n", ai.toChars());
|
||||
//static int i; if (++i == 2) assert(0);
|
||||
const dim = ai.value.length;
|
||||
if (!dim)
|
||||
{
|
||||
error(ai.loc, "invalid associative array initializer `%s`, use `null` instead",
|
||||
toChars(ai));
|
||||
return ErrorExp.get();
|
||||
}
|
||||
|
||||
auto no(const char* format, Initializer i)
|
||||
{
|
||||
error(i.loc, format, toChars(i));
|
||||
return ErrorExp.get();
|
||||
}
|
||||
Expression e;
|
||||
auto keys = new Expressions(dim);
|
||||
|
||||
const dim = ai.value.length;
|
||||
if (!dim)
|
||||
return no("invalid associative array initializer `%s`, use `null` instead", ai);
|
||||
|
||||
auto keys = new Expressions(dim);
|
||||
auto values = new Expressions(dim);
|
||||
for (size_t i = 0; i < dim; i++)
|
||||
foreach (i, iz; ai.value[])
|
||||
{
|
||||
Initializer iz = ai.value[i];
|
||||
assert(iz);
|
||||
e = iz.initializerToExpression();
|
||||
if (!e)
|
||||
auto ev = iz.initializerToExpression();
|
||||
if (!ev)
|
||||
return no("invalid value `%s` in initializer", iz);
|
||||
(*values)[i] = e;
|
||||
e = ai.index[i];
|
||||
if (!e)
|
||||
(*values)[i] = ev;
|
||||
|
||||
auto ei = ai.index[i];
|
||||
if (!ei)
|
||||
return no("missing key for value `%s` in initializer", iz);
|
||||
(*keys)[i] = e;
|
||||
(*keys)[i] = ei;
|
||||
}
|
||||
e = new AssocArrayLiteralExp(ai.loc, keys, values);
|
||||
return e;
|
||||
return new AssocArrayLiteralExp(ai.loc, keys, values);
|
||||
}
|
||||
|
||||
/******************************************
|
||||
|
@ -138,8 +136,12 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
|
|||
/* This works by replacing the StructInitializer with an ExpInitializer.
|
||||
*/
|
||||
t = t.toBasetype();
|
||||
if (t.ty == Tsarray && t.nextOf().toBasetype().ty == Tstruct)
|
||||
t = t.nextOf().toBasetype();
|
||||
if (auto tsa = t.isTypeSArray())
|
||||
{
|
||||
auto ts = tsa.nextOf().toBasetype().isTypeStruct();
|
||||
if (ts)
|
||||
t = ts;
|
||||
}
|
||||
if (auto ts = t.isTypeStruct())
|
||||
{
|
||||
StructDeclaration sd = ts.sym;
|
||||
|
@ -154,16 +156,17 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
|
|||
if (sd.sizeok != Sizeok.done)
|
||||
return err();
|
||||
|
||||
Expression getExp(size_t j, Type fieldType)
|
||||
{
|
||||
// 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);
|
||||
if (ex.op != EXP.error)
|
||||
i.value[j] = iz;
|
||||
return ex;
|
||||
}
|
||||
Expression getExp(size_t j, Type fieldType)
|
||||
{
|
||||
// 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);
|
||||
if (ex.op != EXP.error)
|
||||
i.value[j] = iz;
|
||||
return ex;
|
||||
}
|
||||
|
||||
auto elements = resolveStructLiteralNamedArgs(sd, t, sc, i.loc, i.field[], &getExp, (size_t j) => i.value[j].loc);
|
||||
if (!elements)
|
||||
return err();
|
||||
|
@ -232,17 +235,19 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
|
|||
auto ei = new ExpInitializer(e.loc, e);
|
||||
return ei.initializerSemantic(sc, t, needInterpret);
|
||||
}
|
||||
|
||||
case Tpointer:
|
||||
if (t.nextOf().ty != Tfunction)
|
||||
break;
|
||||
goto default;
|
||||
if (t.nextOf().isTypeFunction())
|
||||
goto default;
|
||||
break;
|
||||
|
||||
default:
|
||||
error(i.loc, "cannot use array to initialize `%s`", t.toChars());
|
||||
return err();
|
||||
}
|
||||
i.type = t;
|
||||
length = 0;
|
||||
for (size_t j = 0; j < i.index.length; j++)
|
||||
for (size_t j = 0; j < i.index.length; j++) // don't replace with foreach; j is modified
|
||||
{
|
||||
Expression idx = i.index[j];
|
||||
if (idx)
|
||||
|
@ -277,9 +282,8 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
|
|||
TupleExp te = ei.exp.isTupleExp();
|
||||
i.index.remove(j);
|
||||
i.value.remove(j);
|
||||
for (size_t k = 0; k < te.exps.length; ++k)
|
||||
foreach (k, e; (*te.exps)[])
|
||||
{
|
||||
Expression e = (*te.exps)[k];
|
||||
i.index.insert(j + k, cast(Expression)null);
|
||||
i.value.insert(j + k, new ExpInitializer(e.loc, e));
|
||||
}
|
||||
|
@ -290,7 +294,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
|
|||
{
|
||||
i.value[j] = val;
|
||||
}
|
||||
length++;
|
||||
++length;
|
||||
if (length == 0)
|
||||
{
|
||||
error(i.loc, "array dimension overflow");
|
||||
|
@ -311,7 +315,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
|
|||
}
|
||||
else
|
||||
{
|
||||
uinteger_t edim = tsa.dim.toInteger();
|
||||
ulong edim = tsa.dim.toInteger();
|
||||
if (i.dim > edim)
|
||||
{
|
||||
error(i.loc, "array initializer has %u elements, but array length is %llu", i.dim, edim);
|
||||
|
@ -347,7 +351,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
|
|||
sc = sc.endCTFE();
|
||||
if (i.exp.op == EXP.error)
|
||||
return err();
|
||||
uint olderrors = global.errors;
|
||||
const olderrors = global.errors;
|
||||
|
||||
/* ImportC: convert arrays to pointers, functions to pointers to functions
|
||||
*/
|
||||
|
@ -1170,7 +1174,7 @@ Initializer inferType(Initializer init, Scope* sc)
|
|||
bool hasOverloads;
|
||||
if (auto f = isFuncAddress(init.exp, &hasOverloads))
|
||||
{
|
||||
if (f.checkForwardRef(init.loc))
|
||||
if (checkForwardRef(f, init.loc))
|
||||
{
|
||||
return new ErrorInitializer();
|
||||
}
|
||||
|
|
|
@ -52,6 +52,12 @@ enum LOGDEFAULTINIT = 0; // log ::defaultInit()
|
|||
|
||||
enum SIZE_INVALID = (~cast(uinteger_t)0); // error return from size() functions
|
||||
|
||||
static if (__VERSION__ < 2095)
|
||||
{
|
||||
// Fix linker errors when building with older compilers.
|
||||
// See: https://issues.dlang.org/show_bug.cgi?id=21299
|
||||
private alias StringValueType = StringValue!Type;
|
||||
}
|
||||
|
||||
/***************************
|
||||
* Return !=0 if modfrom can be implicitly converted to modto
|
||||
|
@ -299,7 +305,7 @@ extern (C++) abstract class Type : ASTNode
|
|||
Type swto; // MODFlags.shared_ | MODFlags.wild
|
||||
Type swcto; // MODFlags.shared_ | MODFlags.wildconst
|
||||
}
|
||||
private Mcache* mcache;
|
||||
Mcache* mcache;
|
||||
|
||||
Type pto; // merged pointer to this type
|
||||
Type rto; // reference to this type
|
||||
|
@ -440,7 +446,7 @@ extern (C++) abstract class Type : ASTNode
|
|||
|
||||
final bool equivalent(Type t)
|
||||
{
|
||||
return immutableOf().equals(t.immutableOf());
|
||||
return immutableOf(this).equals(t.immutableOf());
|
||||
}
|
||||
|
||||
// kludge for template.isType()
|
||||
|
@ -785,255 +791,6 @@ extern (C++) abstract class Type : ASTNode
|
|||
return t;
|
||||
}
|
||||
|
||||
/********************************
|
||||
* Convert to 'const'.
|
||||
*/
|
||||
final Type constOf()
|
||||
{
|
||||
//printf("Type::constOf() %p %s\n", this, toChars());
|
||||
if (mod == MODFlags.const_)
|
||||
return this;
|
||||
if (mcache && mcache.cto)
|
||||
{
|
||||
assert(mcache.cto.mod == MODFlags.const_);
|
||||
return mcache.cto;
|
||||
}
|
||||
Type t = makeConst();
|
||||
t = t.merge();
|
||||
t.fixTo(this);
|
||||
//printf("-Type::constOf() %p %s\n", t, t.toChars());
|
||||
return t;
|
||||
}
|
||||
|
||||
/********************************
|
||||
* Convert to 'immutable'.
|
||||
*/
|
||||
final Type immutableOf()
|
||||
{
|
||||
//printf("Type::immutableOf() %p %s\n", this, toChars());
|
||||
if (isImmutable())
|
||||
return this;
|
||||
if (mcache && mcache.ito)
|
||||
{
|
||||
assert(mcache.ito.isImmutable());
|
||||
return mcache.ito;
|
||||
}
|
||||
Type t = makeImmutable();
|
||||
t = t.merge();
|
||||
t.fixTo(this);
|
||||
//printf("\t%p\n", t);
|
||||
return t;
|
||||
}
|
||||
|
||||
/********************************
|
||||
* Make type mutable.
|
||||
*/
|
||||
final Type mutableOf()
|
||||
{
|
||||
//printf("Type::mutableOf() %p, %s\n", this, toChars());
|
||||
Type t = this;
|
||||
if (isImmutable())
|
||||
{
|
||||
getMcache();
|
||||
t = mcache.ito; // immutable => naked
|
||||
assert(!t || (t.isMutable() && !t.isShared()));
|
||||
}
|
||||
else if (isConst())
|
||||
{
|
||||
getMcache();
|
||||
if (isShared())
|
||||
{
|
||||
if (isWild())
|
||||
t = mcache.swcto; // shared wild const -> shared
|
||||
else
|
||||
t = mcache.sto; // shared const => shared
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isWild())
|
||||
t = mcache.wcto; // wild const -> naked
|
||||
else
|
||||
t = mcache.cto; // const => naked
|
||||
}
|
||||
assert(!t || t.isMutable());
|
||||
}
|
||||
else if (isWild())
|
||||
{
|
||||
getMcache();
|
||||
if (isShared())
|
||||
t = mcache.sto; // shared wild => shared
|
||||
else
|
||||
t = mcache.wto; // wild => naked
|
||||
assert(!t || t.isMutable());
|
||||
}
|
||||
if (!t)
|
||||
{
|
||||
t = makeMutable();
|
||||
t = t.merge();
|
||||
t.fixTo(this);
|
||||
}
|
||||
else
|
||||
t = t.merge();
|
||||
assert(t.isMutable());
|
||||
return t;
|
||||
}
|
||||
|
||||
final Type sharedOf()
|
||||
{
|
||||
//printf("Type::sharedOf() %p, %s\n", this, toChars());
|
||||
if (mod == MODFlags.shared_)
|
||||
return this;
|
||||
if (mcache && mcache.sto)
|
||||
{
|
||||
assert(mcache.sto.mod == MODFlags.shared_);
|
||||
return mcache.sto;
|
||||
}
|
||||
Type t = makeShared();
|
||||
t = t.merge();
|
||||
t.fixTo(this);
|
||||
//printf("\t%p\n", t);
|
||||
return t;
|
||||
}
|
||||
|
||||
final Type sharedConstOf()
|
||||
{
|
||||
//printf("Type::sharedConstOf() %p, %s\n", this, toChars());
|
||||
if (mod == (MODFlags.shared_ | MODFlags.const_))
|
||||
return this;
|
||||
if (mcache && mcache.scto)
|
||||
{
|
||||
assert(mcache.scto.mod == (MODFlags.shared_ | MODFlags.const_));
|
||||
return mcache.scto;
|
||||
}
|
||||
Type t = makeSharedConst();
|
||||
t = t.merge();
|
||||
t.fixTo(this);
|
||||
//printf("\t%p\n", t);
|
||||
return t;
|
||||
}
|
||||
|
||||
/********************************
|
||||
* Make type unshared.
|
||||
* 0 => 0
|
||||
* const => const
|
||||
* immutable => immutable
|
||||
* shared => 0
|
||||
* shared const => const
|
||||
* wild => wild
|
||||
* wild const => wild const
|
||||
* shared wild => wild
|
||||
* shared wild const => wild const
|
||||
*/
|
||||
final Type unSharedOf()
|
||||
{
|
||||
//printf("Type::unSharedOf() %p, %s\n", this, toChars());
|
||||
Type t = this;
|
||||
|
||||
if (isShared())
|
||||
{
|
||||
getMcache();
|
||||
if (isWild())
|
||||
{
|
||||
if (isConst())
|
||||
t = mcache.wcto; // shared wild const => wild const
|
||||
else
|
||||
t = mcache.wto; // shared wild => wild
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isConst())
|
||||
t = mcache.cto; // shared const => const
|
||||
else
|
||||
t = mcache.sto; // shared => naked
|
||||
}
|
||||
assert(!t || !t.isShared());
|
||||
}
|
||||
|
||||
if (!t)
|
||||
{
|
||||
t = this.nullAttributes();
|
||||
t.mod = mod & ~MODFlags.shared_;
|
||||
t.ctype = ctype;
|
||||
t = t.merge();
|
||||
t.fixTo(this);
|
||||
}
|
||||
else
|
||||
t = t.merge();
|
||||
assert(!t.isShared());
|
||||
return t;
|
||||
}
|
||||
|
||||
/********************************
|
||||
* Convert to 'wild'.
|
||||
*/
|
||||
final Type wildOf()
|
||||
{
|
||||
//printf("Type::wildOf() %p %s\n", this, toChars());
|
||||
if (mod == MODFlags.wild)
|
||||
return this;
|
||||
if (mcache && mcache.wto)
|
||||
{
|
||||
assert(mcache.wto.mod == MODFlags.wild);
|
||||
return mcache.wto;
|
||||
}
|
||||
Type t = makeWild();
|
||||
t = t.merge();
|
||||
t.fixTo(this);
|
||||
//printf("\t%p %s\n", t, t.toChars());
|
||||
return t;
|
||||
}
|
||||
|
||||
final Type wildConstOf()
|
||||
{
|
||||
//printf("Type::wildConstOf() %p %s\n", this, toChars());
|
||||
if (mod == MODFlags.wildconst)
|
||||
return this;
|
||||
if (mcache && mcache.wcto)
|
||||
{
|
||||
assert(mcache.wcto.mod == MODFlags.wildconst);
|
||||
return mcache.wcto;
|
||||
}
|
||||
Type t = makeWildConst();
|
||||
t = t.merge();
|
||||
t.fixTo(this);
|
||||
//printf("\t%p %s\n", t, t.toChars());
|
||||
return t;
|
||||
}
|
||||
|
||||
final Type sharedWildOf()
|
||||
{
|
||||
//printf("Type::sharedWildOf() %p, %s\n", this, toChars());
|
||||
if (mod == (MODFlags.shared_ | MODFlags.wild))
|
||||
return this;
|
||||
if (mcache && mcache.swto)
|
||||
{
|
||||
assert(mcache.swto.mod == (MODFlags.shared_ | MODFlags.wild));
|
||||
return mcache.swto;
|
||||
}
|
||||
Type t = makeSharedWild();
|
||||
t = t.merge();
|
||||
t.fixTo(this);
|
||||
//printf("\t%p %s\n", t, t.toChars());
|
||||
return t;
|
||||
}
|
||||
|
||||
final Type sharedWildConstOf()
|
||||
{
|
||||
//printf("Type::sharedWildConstOf() %p, %s\n", this, toChars());
|
||||
if (mod == (MODFlags.shared_ | MODFlags.wildconst))
|
||||
return this;
|
||||
if (mcache && mcache.swcto)
|
||||
{
|
||||
assert(mcache.swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
|
||||
return mcache.swcto;
|
||||
}
|
||||
Type t = makeSharedWildConst();
|
||||
t = t.merge();
|
||||
t.fixTo(this);
|
||||
//printf("\t%p %s\n", t, t.toChars());
|
||||
return t;
|
||||
}
|
||||
|
||||
/**********************************
|
||||
* For our new type 'this', which is type-constructed from t,
|
||||
* fill in the cto, ito, sto, scto, wto shortcuts.
|
||||
|
@ -1434,56 +1191,6 @@ extern (C++) abstract class Type : ASTNode
|
|||
return t;
|
||||
}
|
||||
|
||||
/************************************
|
||||
* Apply MODxxxx bits to existing type.
|
||||
*/
|
||||
final Type castMod(MOD mod)
|
||||
{
|
||||
Type t;
|
||||
switch (mod)
|
||||
{
|
||||
case 0:
|
||||
t = unSharedOf().mutableOf();
|
||||
break;
|
||||
|
||||
case MODFlags.const_:
|
||||
t = unSharedOf().constOf();
|
||||
break;
|
||||
|
||||
case MODFlags.wild:
|
||||
t = unSharedOf().wildOf();
|
||||
break;
|
||||
|
||||
case MODFlags.wildconst:
|
||||
t = unSharedOf().wildConstOf();
|
||||
break;
|
||||
|
||||
case MODFlags.shared_:
|
||||
t = mutableOf().sharedOf();
|
||||
break;
|
||||
|
||||
case MODFlags.shared_ | MODFlags.const_:
|
||||
t = sharedConstOf();
|
||||
break;
|
||||
|
||||
case MODFlags.shared_ | MODFlags.wild:
|
||||
t = sharedWildOf();
|
||||
break;
|
||||
|
||||
case MODFlags.shared_ | MODFlags.wildconst:
|
||||
t = sharedWildConstOf();
|
||||
break;
|
||||
|
||||
case MODFlags.immutable_:
|
||||
t = immutableOf();
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
/************************************
|
||||
* Add MODxxxx bits to existing type.
|
||||
* We're adding, not replacing, so adding const to
|
||||
|
@ -1506,16 +1213,16 @@ extern (C++) abstract class Type : ASTNode
|
|||
if (isShared())
|
||||
{
|
||||
if (isWild())
|
||||
t = sharedWildConstOf();
|
||||
t = this.sharedWildConstOf();
|
||||
else
|
||||
t = sharedConstOf();
|
||||
t = this.sharedConstOf();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isWild())
|
||||
t = wildConstOf();
|
||||
if (this.isWild())
|
||||
t = this.wildConstOf();
|
||||
else
|
||||
t = constOf();
|
||||
t = t.constOf();
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1523,63 +1230,63 @@ extern (C++) abstract class Type : ASTNode
|
|||
if (isShared())
|
||||
{
|
||||
if (isConst())
|
||||
t = sharedWildConstOf();
|
||||
t = this.sharedWildConstOf();
|
||||
else
|
||||
t = sharedWildOf();
|
||||
t = this.sharedWildOf();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isConst())
|
||||
t = wildConstOf();
|
||||
t = this.wildConstOf();
|
||||
else
|
||||
t = wildOf();
|
||||
t = this.wildOf();
|
||||
}
|
||||
break;
|
||||
|
||||
case MODFlags.wildconst:
|
||||
if (isShared())
|
||||
t = sharedWildConstOf();
|
||||
t = this.sharedWildConstOf();
|
||||
else
|
||||
t = wildConstOf();
|
||||
t = this.wildConstOf();
|
||||
break;
|
||||
|
||||
case MODFlags.shared_:
|
||||
if (isWild())
|
||||
{
|
||||
if (isConst())
|
||||
t = sharedWildConstOf();
|
||||
t = this.sharedWildConstOf();
|
||||
else
|
||||
t = sharedWildOf();
|
||||
t = this.sharedWildOf();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isConst())
|
||||
t = sharedConstOf();
|
||||
t = this.sharedConstOf();
|
||||
else
|
||||
t = sharedOf();
|
||||
t = this.sharedOf();
|
||||
}
|
||||
break;
|
||||
|
||||
case MODFlags.shared_ | MODFlags.const_:
|
||||
if (isWild())
|
||||
t = sharedWildConstOf();
|
||||
t = this.sharedWildConstOf();
|
||||
else
|
||||
t = sharedConstOf();
|
||||
t = this.sharedConstOf();
|
||||
break;
|
||||
|
||||
case MODFlags.shared_ | MODFlags.wild:
|
||||
if (isConst())
|
||||
t = sharedWildConstOf();
|
||||
t = this.sharedWildConstOf();
|
||||
else
|
||||
t = sharedWildOf();
|
||||
t = this.sharedWildOf();
|
||||
break;
|
||||
|
||||
case MODFlags.shared_ | MODFlags.wildconst:
|
||||
t = sharedWildConstOf();
|
||||
t = this.sharedWildConstOf();
|
||||
break;
|
||||
|
||||
case MODFlags.immutable_:
|
||||
t = immutableOf();
|
||||
t = this.immutableOf();
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -1990,7 +1697,7 @@ extern (C++) abstract class Type : ASTNode
|
|||
|
||||
final Type unqualify(uint m)
|
||||
{
|
||||
Type t = mutableOf().unSharedOf();
|
||||
Type t = this.mutableOf().unSharedOf();
|
||||
|
||||
Type tn = ty == Tenum ? null : nextOf();
|
||||
if (tn && tn.ty != Tfunction)
|
||||
|
|
|
@ -142,11 +142,7 @@ public:
|
|||
TY ty;
|
||||
MOD mod; // modifiers MODxxxx
|
||||
char *deco;
|
||||
|
||||
private:
|
||||
void* mcache;
|
||||
|
||||
public:
|
||||
Type *pto; // merged pointer to this type
|
||||
Type *rto; // reference to this type
|
||||
Type *arrayof; // array of this type
|
||||
|
@ -257,17 +253,6 @@ public:
|
|||
bool isSharedWild() const { return (mod & (MODshared | MODwild)) == (MODshared | MODwild); }
|
||||
bool isNaked() const { return mod == 0; }
|
||||
Type *nullAttributes() const;
|
||||
Type *constOf();
|
||||
Type *immutableOf();
|
||||
Type *mutableOf();
|
||||
Type *sharedOf();
|
||||
Type *sharedConstOf();
|
||||
Type *unSharedOf();
|
||||
Type *wildOf();
|
||||
Type *wildConstOf();
|
||||
Type *sharedWildOf();
|
||||
Type *sharedWildConstOf();
|
||||
Type *castMod(MOD mod);
|
||||
Type *addMod(MOD mod);
|
||||
virtual Type *addStorageClass(StorageClass stc);
|
||||
Type *pointerTo();
|
||||
|
@ -909,3 +894,14 @@ bool isBaseOf(Type *tthis, Type *t, int *poffset);
|
|||
Type *trySemantic(Type *type, const Loc &loc, Scope *sc);
|
||||
void purityLevel(TypeFunction *type);
|
||||
Type *merge2(Type *type);
|
||||
Type *constOf(Type *type);
|
||||
Type *immutableOf(Type *type);
|
||||
Type *mutableOf(Type *type);
|
||||
Type *sharedOf(Type *type);
|
||||
Type *sharedConstOf(Type *type);
|
||||
Type *unSharedOf(Type *type);
|
||||
Type *wildOf(Type *type);
|
||||
Type *wildConstOf(Type *type);
|
||||
Type *sharedWildOf(Type *type);
|
||||
Type *sharedWildConstOf(Type *type);
|
||||
Type *castMod(Type *type, MOD mod);
|
||||
|
|
|
@ -37,6 +37,7 @@ import dmd.location;
|
|||
import dmd.mtype;
|
||||
import dmd.optimize;
|
||||
import dmd.statement;
|
||||
import dmd.templatesem;
|
||||
import dmd.tokens;
|
||||
import dmd.typesem;
|
||||
import dmd.visitor;
|
||||
|
|
|
@ -33,6 +33,7 @@ import dmd.printast;
|
|||
import dmd.root.ctfloat;
|
||||
import dmd.sideeffect;
|
||||
import dmd.tokens;
|
||||
import dmd.typesem;
|
||||
import dmd.visitor;
|
||||
|
||||
/*************************************
|
||||
|
@ -272,7 +273,7 @@ package void setLengthVarIfKnown(VarDeclaration lengthVar, Type type)
|
|||
* Returns:
|
||||
* Constant folded version of `e`
|
||||
*/
|
||||
Expression optimize(Expression e, int result, bool keepLvalue = false)
|
||||
extern (C++) Expression optimize(Expression e, int result, bool keepLvalue = false)
|
||||
{
|
||||
//printf("optimize() e: %s result: %d keepLvalue %d\n", e.toChars(), result, keepLvalue);
|
||||
Expression ret = e;
|
||||
|
|
650
gcc/d/dmd/pragmasem.d
Normal file
650
gcc/d/dmd/pragmasem.d
Normal file
|
@ -0,0 +1,650 @@
|
|||
/**
|
||||
* Does semantic analysis for pragmas.
|
||||
*
|
||||
* Specification: $(LINK2 https://dlang.org/spec/pragma.html, Pragmas)
|
||||
*
|
||||
* Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
|
||||
* Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
|
||||
* License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
|
||||
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/pragmasem.d, _pragmasem.d)
|
||||
* Documentation: https://dlang.org/phobos/dmd_pragmasem.html
|
||||
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/pragmasem.d
|
||||
*/
|
||||
|
||||
module dmd.pragmasem;
|
||||
|
||||
import core.stdc.stdio;
|
||||
|
||||
import dmd.astenums;
|
||||
import dmd.arraytypes;
|
||||
import dmd.attrib;
|
||||
import dmd.dinterpret;
|
||||
import dmd.dscope;
|
||||
import dmd.dsymbol;
|
||||
import dmd.errors;
|
||||
import dmd.expression;
|
||||
import dmd.expressionsem;
|
||||
import dmd.globals;
|
||||
import dmd.location;
|
||||
import dmd.id;
|
||||
import dmd.statement;
|
||||
|
||||
/**
|
||||
* Run semantic on `pragma` declaration.
|
||||
*
|
||||
* Params:
|
||||
* pd = pragma declaration or statement to evaluate
|
||||
* sc = enclosing scope
|
||||
*/
|
||||
void pragmaDeclSemantic(PragmaDeclaration pd, Scope* sc)
|
||||
{
|
||||
import dmd.aggregate;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.dmangle;
|
||||
import dmd.dmodule;
|
||||
import dmd.dsymbolsem;
|
||||
import dmd.identifier;
|
||||
import dmd.root.rmem;
|
||||
import dmd.root.utf;
|
||||
import dmd.target;
|
||||
import dmd.utils;
|
||||
|
||||
StringExp verifyMangleString(ref Expression e)
|
||||
{
|
||||
auto se = semanticString(sc, e, "mangled name");
|
||||
if (!se)
|
||||
return null;
|
||||
e = se;
|
||||
if (!se.len)
|
||||
{
|
||||
.error(pd.loc, "%s `%s` - zero-length string not allowed for mangled name", pd.kind, pd.toPrettyChars);
|
||||
return null;
|
||||
}
|
||||
if (se.sz != 1)
|
||||
{
|
||||
.error(pd.loc, "%s `%s` - mangled name characters can only be of type `char`", pd.kind, pd.toPrettyChars);
|
||||
return null;
|
||||
}
|
||||
version (all)
|
||||
{
|
||||
/* Note: D language specification should not have any assumption about backend
|
||||
* implementation. Ideally pragma(mangle) can accept a string of any content.
|
||||
*
|
||||
* Therefore, this validation is compiler implementation specific.
|
||||
*/
|
||||
auto slice = se.peekString();
|
||||
for (size_t i = 0; i < se.len;)
|
||||
{
|
||||
dchar c = slice[i];
|
||||
if (c < 0x80)
|
||||
{
|
||||
if (c.isValidMangling)
|
||||
{
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
.error(pd.loc, "%s `%s` char 0x%02x not allowed in mangled name", pd.kind, pd.toPrettyChars, c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (const msg = utf_decodeChar(slice, i, c))
|
||||
{
|
||||
.error(pd.loc, "%s `%s` %.*s", pd.kind, pd.toPrettyChars, cast(int)msg.length, msg.ptr);
|
||||
break;
|
||||
}
|
||||
if (!isUniAlpha(c))
|
||||
{
|
||||
.error(pd.loc, "%s `%s` char `0x%04x` not allowed in mangled name", pd.kind, pd.toPrettyChars, c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return se;
|
||||
}
|
||||
void declarations()
|
||||
{
|
||||
if (!pd.decl)
|
||||
return;
|
||||
|
||||
Scope* sc2 = pd.newScope(sc);
|
||||
scope(exit)
|
||||
if (sc2 != sc)
|
||||
sc2.pop();
|
||||
|
||||
foreach (s; (*pd.decl)[])
|
||||
{
|
||||
if (pd.ident == Id.printf || pd.ident == Id.scanf)
|
||||
{
|
||||
s.setPragmaPrintf(pd.ident == Id.printf);
|
||||
s.dsymbolSemantic(sc2);
|
||||
continue;
|
||||
}
|
||||
|
||||
s.dsymbolSemantic(sc2);
|
||||
if (pd.ident != Id.mangle)
|
||||
continue;
|
||||
assert(pd.args);
|
||||
if (auto ad = s.isAggregateDeclaration())
|
||||
{
|
||||
Expression e = (*pd.args)[0];
|
||||
sc2 = sc2.startCTFE();
|
||||
e = e.expressionSemantic(sc);
|
||||
e = resolveProperties(sc2, e);
|
||||
sc2 = sc2.endCTFE();
|
||||
AggregateDeclaration agg;
|
||||
if (auto tc = e.type.isTypeClass())
|
||||
agg = tc.sym;
|
||||
else if (auto ts = e.type.isTypeStruct())
|
||||
agg = ts.sym;
|
||||
ad.pMangleOverride = new MangleOverride;
|
||||
void setString(ref Expression e)
|
||||
{
|
||||
if (auto se = verifyMangleString(e))
|
||||
{
|
||||
const name = (cast(const(char)[])se.peekData()).xarraydup;
|
||||
ad.pMangleOverride.id = Identifier.idPool(name);
|
||||
e = se;
|
||||
}
|
||||
else
|
||||
error(e.loc, "must be a string");
|
||||
}
|
||||
if (agg)
|
||||
{
|
||||
ad.pMangleOverride.agg = agg;
|
||||
if (pd.args.length == 2)
|
||||
{
|
||||
setString((*pd.args)[1]);
|
||||
}
|
||||
else
|
||||
ad.pMangleOverride.id = agg.ident;
|
||||
}
|
||||
else
|
||||
setString((*pd.args)[0]);
|
||||
}
|
||||
else if (auto td = s.isTemplateDeclaration())
|
||||
{
|
||||
.error(pd.loc, "%s `%s` cannot apply to a template declaration", pd.kind, pd.toPrettyChars);
|
||||
errorSupplemental(pd.loc, "use `template Class(Args...){ pragma(mangle, \"other_name\") class Class {} }`");
|
||||
}
|
||||
else if (auto se = verifyMangleString((*pd.args)[0]))
|
||||
{
|
||||
const name = (cast(const(char)[])se.peekData()).xarraydup;
|
||||
uint cnt = setMangleOverride(s, name);
|
||||
if (cnt > 1)
|
||||
.error(pd.loc, "%s `%s` can only apply to a single declaration", pd.kind, pd.toPrettyChars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void noDeclarations()
|
||||
{
|
||||
if (pd.decl)
|
||||
{
|
||||
.error(pd.loc, "%s `%s` is missing a terminating `;`", pd.kind, pd.toPrettyChars);
|
||||
declarations();
|
||||
// do them anyway, to avoid segfaults.
|
||||
}
|
||||
}
|
||||
|
||||
// Should be merged with PragmaStatement
|
||||
//printf("\tpragmaDeclSemantic '%s'\n", pd.toChars());
|
||||
if (target.supportsLinkerDirective())
|
||||
{
|
||||
if (pd.ident == Id.linkerDirective)
|
||||
{
|
||||
if (!pd.args || pd.args.length != 1)
|
||||
.error(pd.loc, "%s `%s` one string argument expected for pragma(linkerDirective)", pd.kind, pd.toPrettyChars);
|
||||
else
|
||||
{
|
||||
auto se = semanticString(sc, (*pd.args)[0], "linker directive");
|
||||
if (!se)
|
||||
return noDeclarations();
|
||||
(*pd.args)[0] = se;
|
||||
if (global.params.v.verbose)
|
||||
message("linkopt %.*s", cast(int)se.len, se.peekString().ptr);
|
||||
}
|
||||
return noDeclarations();
|
||||
}
|
||||
}
|
||||
if (pd.ident == Id.msg)
|
||||
{
|
||||
if (!pd.args)
|
||||
return noDeclarations();
|
||||
|
||||
if (!pragmaMsgSemantic(pd.loc, sc, pd.args))
|
||||
return;
|
||||
|
||||
return noDeclarations();
|
||||
}
|
||||
else if (pd.ident == Id.lib)
|
||||
{
|
||||
if (!pd.args || pd.args.length != 1)
|
||||
.error(pd.loc, "%s `%s` string expected for library name", pd.kind, pd.toPrettyChars);
|
||||
else
|
||||
{
|
||||
auto se = semanticString(sc, (*pd.args)[0], "library name");
|
||||
if (!se)
|
||||
return noDeclarations();
|
||||
(*pd.args)[0] = se;
|
||||
|
||||
auto name = se.peekString().xarraydup;
|
||||
if (global.params.v.verbose)
|
||||
message("library %s", name.ptr);
|
||||
if (global.params.moduleDeps.buffer && !global.params.moduleDeps.name)
|
||||
{
|
||||
OutBuffer* ob = global.params.moduleDeps.buffer;
|
||||
Module imod = sc._module;
|
||||
ob.writestring("depsLib ");
|
||||
ob.writestring(imod.toPrettyChars());
|
||||
ob.writestring(" (");
|
||||
escapePath(ob, imod.srcfile.toChars());
|
||||
ob.writestring(") : ");
|
||||
ob.writestring(name);
|
||||
ob.writenl();
|
||||
}
|
||||
mem.xfree(name.ptr);
|
||||
}
|
||||
return noDeclarations();
|
||||
}
|
||||
else if (pd.ident == Id.startaddress)
|
||||
{
|
||||
pragmaStartAddressSemantic(pd.loc, sc, pd.args);
|
||||
return noDeclarations();
|
||||
}
|
||||
else if (pd.ident == Id.Pinline)
|
||||
{
|
||||
// this pragma now gets evaluated on demand in function semantic
|
||||
|
||||
return declarations();
|
||||
}
|
||||
else if (pd.ident == Id.mangle)
|
||||
{
|
||||
if (!pd.args)
|
||||
pd.args = new Expressions();
|
||||
if (pd.args.length == 0 || pd.args.length > 2)
|
||||
{
|
||||
.error(pd.loc, pd.args.length == 0 ? "%s `%s` - string expected for mangled name"
|
||||
: "%s `%s` expected 1 or 2 arguments", pd.kind, pd.toPrettyChars);
|
||||
pd.args.setDim(1);
|
||||
(*pd.args)[0] = ErrorExp.get(); // error recovery
|
||||
}
|
||||
return declarations();
|
||||
}
|
||||
else if (pd.ident == Id.crt_constructor || pd.ident == Id.crt_destructor)
|
||||
{
|
||||
if (pd.args && pd.args.length != 0)
|
||||
.error(pd.loc, "%s `%s` takes no argument", pd.kind, pd.toPrettyChars);
|
||||
else
|
||||
{
|
||||
immutable isCtor = pd.ident == Id.crt_constructor;
|
||||
|
||||
static uint recurse(Dsymbol s, bool isCtor)
|
||||
{
|
||||
if (auto ad = s.isAttribDeclaration())
|
||||
{
|
||||
uint nestedCount;
|
||||
auto decls = ad.include(null);
|
||||
if (decls)
|
||||
{
|
||||
for (size_t i = 0; i < decls.length; ++i)
|
||||
nestedCount += recurse((*decls)[i], isCtor);
|
||||
}
|
||||
return nestedCount;
|
||||
}
|
||||
else if (auto f = s.isFuncDeclaration())
|
||||
{
|
||||
if (isCtor)
|
||||
f.isCrtCtor = true;
|
||||
else
|
||||
f.isCrtDtor = true;
|
||||
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (recurse(pd, isCtor) > 1)
|
||||
.error(pd.loc, "%s `%s` can only apply to a single declaration", pd.kind, pd.toPrettyChars);
|
||||
}
|
||||
return declarations();
|
||||
}
|
||||
else if (pd.ident == Id.printf || pd.ident == Id.scanf)
|
||||
{
|
||||
if (pd.args && pd.args.length != 0)
|
||||
.error(pd.loc, "%s `%s` takes no argument", pd.kind, pd.toPrettyChars);
|
||||
return declarations();
|
||||
}
|
||||
else if (!global.params.ignoreUnsupportedPragmas)
|
||||
{
|
||||
error(pd.loc, "unrecognized `pragma(%s)`", pd.ident.toChars());
|
||||
return declarations();
|
||||
}
|
||||
|
||||
if (!global.params.v.verbose)
|
||||
return declarations();
|
||||
|
||||
/* Print unrecognized pragmas
|
||||
*/
|
||||
OutBuffer buf;
|
||||
buf.writestring(pd.ident.toString());
|
||||
if (pd.args)
|
||||
{
|
||||
const errors_save = global.startGagging();
|
||||
for (size_t i = 0; i < pd.args.length; i++)
|
||||
{
|
||||
Expression e = (*pd.args)[i];
|
||||
sc = sc.startCTFE();
|
||||
e = e.expressionSemantic(sc);
|
||||
e = resolveProperties(sc, e);
|
||||
sc = sc.endCTFE();
|
||||
e = e.ctfeInterpret();
|
||||
if (i == 0)
|
||||
buf.writestring(" (");
|
||||
else
|
||||
buf.writeByte(',');
|
||||
buf.writestring(e.toChars());
|
||||
}
|
||||
if (pd.args.length)
|
||||
buf.writeByte(')');
|
||||
global.endGagging(errors_save);
|
||||
}
|
||||
message("pragma %s", buf.peekChars());
|
||||
return declarations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run semantic on `pragma` statement.
|
||||
*
|
||||
* Params:
|
||||
* ps = pragma statement to evaluate
|
||||
* sc = enclosing scope
|
||||
*
|
||||
* Returns : true if `pragma` is valid, or false if an error was found
|
||||
*/
|
||||
bool pragmaStmtSemantic(PragmaStatement ps, Scope* sc)
|
||||
{
|
||||
import dmd.statementsem;
|
||||
|
||||
/* https://dlang.org/spec/statement.html#pragma-statement
|
||||
*/
|
||||
// Should be merged with PragmaDeclaration
|
||||
|
||||
//printf("pragmaStmtSemantic() %s\n", ps.toChars());
|
||||
//printf("body = %p\n", ps._body);
|
||||
if (ps.ident == Id.msg)
|
||||
{
|
||||
if (!pragmaMsgSemantic(ps.loc, sc, ps.args))
|
||||
return false;
|
||||
}
|
||||
else if (ps.ident == Id.lib)
|
||||
{
|
||||
version (all)
|
||||
{
|
||||
/* Should this be allowed?
|
||||
*/
|
||||
error(ps.loc, "`pragma(lib)` not allowed as statement");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ps.args || ps.args.length != 1)
|
||||
{
|
||||
error(ps.loc, "`string` expected for library name");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto se = semanticString(sc, (*ps.args)[0], "library name");
|
||||
if (!se)
|
||||
return false;
|
||||
|
||||
if (global.params.v.verbose)
|
||||
{
|
||||
message("library %.*s", cast(int)se.len, se.string);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ps.ident == Id.linkerDirective)
|
||||
{
|
||||
/* Should this be allowed?
|
||||
*/
|
||||
error(ps.loc, "`pragma(linkerDirective)` not allowed as statement");
|
||||
return false;
|
||||
}
|
||||
else if (ps.ident == Id.startaddress)
|
||||
{
|
||||
if (!pragmaStartAddressSemantic(ps.loc, sc, ps.args))
|
||||
return false;
|
||||
}
|
||||
else if (ps.ident == Id.Pinline)
|
||||
{
|
||||
if (auto fd = sc.func)
|
||||
{
|
||||
fd.inlining = evalPragmaInline(ps.loc, sc, ps.args);
|
||||
}
|
||||
else
|
||||
{
|
||||
error(ps.loc, "`pragma(inline)` is not inside a function");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (ps.ident == Id.mangle)
|
||||
{
|
||||
auto es = ps._body ? ps._body.isExpStatement() : null;
|
||||
auto de = es ? es.exp.isDeclarationExp() : null;
|
||||
if (!de)
|
||||
{
|
||||
error(ps.loc, "`pragma(mangle)` must be attached to a declaration");
|
||||
return false;
|
||||
}
|
||||
const se = ps.args && (*ps.args).length == 1 ? semanticString(sc, (*ps.args)[0], "pragma mangle argument") : null;
|
||||
if (!se)
|
||||
{
|
||||
error(ps.loc, "`pragma(mangle)` takes a single argument that must be a string literal");
|
||||
return false;
|
||||
}
|
||||
const cnt = setMangleOverride(de.declaration, cast(const(char)[])se.peekData());
|
||||
if (cnt != 1)
|
||||
assert(0);
|
||||
}
|
||||
else if (!global.params.ignoreUnsupportedPragmas)
|
||||
{
|
||||
error(ps.loc, "unrecognized `pragma(%s)`", ps.ident.toChars());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ps._body)
|
||||
{
|
||||
if (ps.ident == Id.msg || ps.ident == Id.startaddress)
|
||||
{
|
||||
error(ps.loc, "`pragma(%s)` is missing a terminating `;`", ps.ident.toChars());
|
||||
return false;
|
||||
}
|
||||
ps._body = ps._body.statementSemantic(sc);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/***************************************
|
||||
* Interpret a `pragma(inline, x)`
|
||||
*
|
||||
* Params:
|
||||
* loc = location for error messages
|
||||
* sc = scope for evaluation of argument
|
||||
* args = pragma arguments
|
||||
* Returns: corresponding `PINLINE` state
|
||||
*/
|
||||
package PINLINE evalPragmaInline(Loc loc, Scope* sc, Expressions* args)
|
||||
{
|
||||
if (!args || args.length == 0)
|
||||
return PINLINE.default_;
|
||||
|
||||
if (args && args.length > 1)
|
||||
{
|
||||
.error(loc, "one boolean expression expected for `pragma(inline)`, not %llu", cast(ulong) args.length);
|
||||
args.setDim(1);
|
||||
(*args)[0] = ErrorExp.get();
|
||||
}
|
||||
|
||||
Expression e = (*args)[0];
|
||||
if (!e.type)
|
||||
{
|
||||
sc = sc.startCTFE();
|
||||
e = e.expressionSemantic(sc);
|
||||
e = resolveProperties(sc, e);
|
||||
sc = sc.endCTFE();
|
||||
e = e.ctfeInterpret();
|
||||
e = e.toBoolean(sc);
|
||||
if (e.isErrorExp())
|
||||
.error(loc, "pragma(`inline`, `true` or `false`) expected, not `%s`", (*args)[0].toChars());
|
||||
(*args)[0] = e;
|
||||
}
|
||||
|
||||
const opt = e.toBool();
|
||||
if (opt.isEmpty())
|
||||
return PINLINE.default_;
|
||||
else if (opt.get())
|
||||
return PINLINE.always;
|
||||
else
|
||||
return PINLINE.never;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply pragma mangle to FuncDeclarations and VarDeclarations
|
||||
* under `s`, poking through attribute declarations such as
|
||||
* `extern(C)` but not through aggregates or function bodies.
|
||||
*
|
||||
* Params:
|
||||
* s = symbol to apply
|
||||
* sym = overriding symbol name
|
||||
*/
|
||||
private uint setMangleOverride(Dsymbol s, const(char)[] sym)
|
||||
{
|
||||
if (s.isFuncDeclaration() || s.isVarDeclaration())
|
||||
{
|
||||
s.isDeclaration().mangleOverride = sym;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (auto ad = s.isAttribDeclaration())
|
||||
{
|
||||
uint nestedCount = 0;
|
||||
|
||||
ad.include(null).foreachDsymbol( (s) { nestedCount += setMangleOverride(s, sym); } );
|
||||
|
||||
return nestedCount;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********************************************************
|
||||
* Evaluate and print a `pragma(msg, args)`
|
||||
*
|
||||
* Params:
|
||||
* loc = location for error messages
|
||||
* sc = scope for argument interpretation
|
||||
* args = expressions to print
|
||||
* Returns:
|
||||
* `true` on success
|
||||
*/
|
||||
private bool pragmaMsgSemantic(Loc loc, Scope* sc, Expressions* args)
|
||||
{
|
||||
import dmd.tokens;
|
||||
|
||||
if (!args)
|
||||
return true;
|
||||
foreach (arg; *args)
|
||||
{
|
||||
sc = sc.startCTFE();
|
||||
auto e = arg.expressionSemantic(sc);
|
||||
e = resolveProperties(sc, e);
|
||||
sc = sc.endCTFE();
|
||||
|
||||
// pragma(msg) is allowed to contain types as well as expressions
|
||||
e = ctfeInterpretForPragmaMsg(e);
|
||||
if (e.op == EXP.error)
|
||||
{
|
||||
errorSupplemental(loc, "while evaluating `pragma(msg, %s)`", arg.toChars());
|
||||
return false;
|
||||
}
|
||||
if (auto se = e.toStringExp())
|
||||
{
|
||||
const slice = se.toUTF8(sc).peekString();
|
||||
fprintf(stderr, "%.*s", cast(int)slice.length, slice.ptr);
|
||||
}
|
||||
else
|
||||
fprintf(stderr, "%s", e.toChars());
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply pragma printf/scanf to FuncDeclarations under `s`,
|
||||
* poking through attribute declarations such as `extern(C)`
|
||||
* but not through aggregates or function bodies.
|
||||
*
|
||||
* Params:
|
||||
* s = symbol to apply
|
||||
* printf = `true` for printf, `false` for scanf
|
||||
*/
|
||||
private void setPragmaPrintf(Dsymbol s, bool printf)
|
||||
{
|
||||
if (auto fd = s.isFuncDeclaration())
|
||||
{
|
||||
fd.printf = printf;
|
||||
fd.scanf = !printf;
|
||||
}
|
||||
|
||||
if (auto ad = s.isAttribDeclaration())
|
||||
{
|
||||
ad.include(null).foreachDsymbol( (s) { setPragmaPrintf(s, printf); } );
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************
|
||||
* Evaluate `pragma(startAddress, func)` and store the resolved symbol in `args`
|
||||
*
|
||||
* Params:
|
||||
* loc = location for error messages
|
||||
* sc = scope for argument interpretation
|
||||
* args = pragma arguments
|
||||
* Returns:
|
||||
* `true` on success
|
||||
*/
|
||||
private bool pragmaStartAddressSemantic(Loc loc, Scope* sc, Expressions* args)
|
||||
{
|
||||
import dmd.dtemplate;
|
||||
|
||||
if (!args || args.length != 1)
|
||||
{
|
||||
.error(loc, "function name expected for start address");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* https://issues.dlang.org/show_bug.cgi?id=11980
|
||||
* resolveProperties and ctfeInterpret call are not necessary.
|
||||
*/
|
||||
Expression e = (*args)[0];
|
||||
sc = sc.startCTFE();
|
||||
e = e.expressionSemantic(sc);
|
||||
// e = resolveProperties(sc, e);
|
||||
sc = sc.endCTFE();
|
||||
|
||||
// e = e.ctfeInterpret();
|
||||
(*args)[0] = e;
|
||||
Dsymbol sa = getDsymbol(e);
|
||||
if (!sa || !sa.isFuncDeclaration())
|
||||
{
|
||||
.error(loc, "function name expected for start address, not `%s`", e.toChars());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
|
@ -131,5 +131,5 @@ struct Scope
|
|||
AliasDeclaration *aliasAsg; // if set, then aliasAsg is being assigned a new value,
|
||||
// do not set wasRead for it
|
||||
|
||||
Dsymbol *search(const Loc &loc, Identifier *ident, Dsymbol **pscopesym, SearchOptFlags flags = (SearchOptFlags)SearchOpt::all);
|
||||
Dsymbol *search(const Loc &loc, Identifier *ident, Dsymbol *&pscopesym, SearchOptFlags flags = (SearchOptFlags)SearchOpt::all);
|
||||
};
|
||||
|
|
|
@ -829,7 +829,8 @@ private void doGNUABITagSemantic(ref Expression e, ref Expression* lastTag)
|
|||
}
|
||||
|
||||
/**
|
||||
* Try lower a variable's static Associative Array to a newaa struct.
|
||||
* Try lower a variable's Associative Array initializer to a newaa struct
|
||||
* so it can be put in static data.
|
||||
* Params:
|
||||
* vd = Variable to lower
|
||||
* sc = Scope
|
||||
|
@ -839,11 +840,20 @@ void lowerStaticAAs(VarDeclaration vd, Scope* sc)
|
|||
if (vd.storage_class & STC.manifest)
|
||||
return;
|
||||
if (auto ei = vd._init.isExpInitializer())
|
||||
{
|
||||
scope v = new StaticAAVisitor(sc);
|
||||
v.vd = vd;
|
||||
ei.exp.accept(v);
|
||||
}
|
||||
lowerStaticAAs(ei.exp, sc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try lower all Associative Array literals in an expression to a newaa struct
|
||||
* so it can be put in static data.
|
||||
* Params:
|
||||
* e = Expression to traverse
|
||||
* sc = Scope
|
||||
*/
|
||||
void lowerStaticAAs(Expression e, Scope* sc)
|
||||
{
|
||||
scope v = new StaticAAVisitor(sc);
|
||||
e.accept(v);
|
||||
}
|
||||
|
||||
/// Visit Associative Array literals and lower them to structs for static initialization
|
||||
|
@ -851,7 +861,6 @@ private extern(C++) final class StaticAAVisitor : SemanticTimeTransitiveVisitor
|
|||
{
|
||||
alias visit = SemanticTimeTransitiveVisitor.visit;
|
||||
Scope* sc;
|
||||
VarDeclaration vd;
|
||||
|
||||
this(Scope* sc) scope @safe
|
||||
{
|
||||
|
|
|
@ -19,6 +19,7 @@ import dmd.expression;
|
|||
import dmd.expressionsem;
|
||||
import dmd.func;
|
||||
import dmd.globals;
|
||||
import dmd.id;
|
||||
import dmd.identifier;
|
||||
import dmd.init;
|
||||
import dmd.mtype;
|
||||
|
@ -270,6 +271,15 @@ bool discardValue(Expression e)
|
|||
break;
|
||||
}
|
||||
case EXP.call:
|
||||
// https://issues.dlang.org/show_bug.cgi?id=24359
|
||||
auto ce = e.isCallExp();
|
||||
if (const f = ce.f)
|
||||
{
|
||||
if (f.ident == Id.__equals && ce.arguments && ce.arguments.length == 2)
|
||||
{
|
||||
return discardValue(new EqualExp(EXP.equal, e.loc, (*ce.arguments)[0], (*ce.arguments)[1]));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
case EXP.andAnd:
|
||||
case EXP.orOr:
|
||||
|
|
|
@ -16,12 +16,10 @@ module dmd.statementsem;
|
|||
import core.stdc.stdio;
|
||||
|
||||
import dmd.aggregate;
|
||||
import dmd.aliasthis;
|
||||
import dmd.arrayop;
|
||||
import dmd.arraytypes;
|
||||
import dmd.astcodegen;
|
||||
import dmd.astenums;
|
||||
import dmd.attrib;
|
||||
import dmd.blockexit;
|
||||
import dmd.clone;
|
||||
import dmd.cond;
|
||||
|
@ -36,9 +34,7 @@ import dmd.dmodule;
|
|||
import dmd.dscope;
|
||||
import dmd.dsymbol;
|
||||
import dmd.dsymbolsem;
|
||||
import dmd.dtemplate;
|
||||
import dmd.errors;
|
||||
import dmd.errorsink;
|
||||
import dmd.escape;
|
||||
import dmd.expression;
|
||||
import dmd.expressionsem;
|
||||
|
@ -64,7 +60,6 @@ import dmd.root.string;
|
|||
import dmd.semantic2;
|
||||
import dmd.sideeffect;
|
||||
import dmd.statement;
|
||||
import dmd.staticassert;
|
||||
import dmd.target;
|
||||
import dmd.tokens;
|
||||
import dmd.typesem;
|
||||
|
@ -1751,102 +1746,10 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
|
|||
{
|
||||
/* https://dlang.org/spec/statement.html#pragma-statement
|
||||
*/
|
||||
// Should be merged with PragmaDeclaration
|
||||
|
||||
//printf("PragmaStatement::semantic() %s\n", ps.toChars());
|
||||
//printf("body = %p\n", ps._body);
|
||||
if (ps.ident == Id.msg)
|
||||
{
|
||||
if (!pragmaMsgSemantic(ps.loc, sc, ps.args))
|
||||
return setError();
|
||||
}
|
||||
else if (ps.ident == Id.lib)
|
||||
{
|
||||
version (all)
|
||||
{
|
||||
/* Should this be allowed?
|
||||
*/
|
||||
error(ps.loc, "`pragma(lib)` not allowed as statement");
|
||||
return setError();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ps.args || ps.args.length != 1)
|
||||
{
|
||||
error(ps.loc, "`string` expected for library name");
|
||||
return setError();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto se = semanticString(sc, (*ps.args)[0], "library name");
|
||||
if (!se)
|
||||
return setError();
|
||||
|
||||
if (global.params.v.verbose)
|
||||
{
|
||||
message("library %.*s", cast(int)se.len, se.string);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ps.ident == Id.linkerDirective)
|
||||
{
|
||||
/* Should this be allowed?
|
||||
*/
|
||||
error(ps.loc, "`pragma(linkerDirective)` not allowed as statement");
|
||||
import dmd.pragmasem : pragmaStmtSemantic;
|
||||
if (!pragmaStmtSemantic(ps, sc))
|
||||
return setError();
|
||||
}
|
||||
else if (ps.ident == Id.startaddress)
|
||||
{
|
||||
if (!pragmaStartAddressSemantic(ps.loc, sc, ps.args))
|
||||
return setError();
|
||||
}
|
||||
else if (ps.ident == Id.Pinline)
|
||||
{
|
||||
if (auto fd = sc.func)
|
||||
{
|
||||
fd.inlining = evalPragmaInline(ps.loc, sc, ps.args);
|
||||
}
|
||||
else
|
||||
{
|
||||
error(ps.loc, "`pragma(inline)` is not inside a function");
|
||||
return setError();
|
||||
}
|
||||
}
|
||||
else if (ps.ident == Id.mangle)
|
||||
{
|
||||
auto es = ps._body ? ps._body.isExpStatement() : null;
|
||||
auto de = es ? es.exp.isDeclarationExp() : null;
|
||||
if (!de)
|
||||
{
|
||||
error(ps.loc, "`pragma(mangle)` must be attached to a declaration");
|
||||
return setError();
|
||||
}
|
||||
const se = ps.args && (*ps.args).length == 1 ? semanticString(sc, (*ps.args)[0], "pragma mangle argument") : null;
|
||||
if (!se)
|
||||
{
|
||||
error(ps.loc, "`pragma(mangle)` takes a single argument that must be a string literal");
|
||||
return setError();
|
||||
}
|
||||
const cnt = setMangleOverride(de.declaration, cast(const(char)[])se.peekData());
|
||||
if (cnt != 1)
|
||||
assert(0);
|
||||
}
|
||||
else if (!global.params.ignoreUnsupportedPragmas)
|
||||
{
|
||||
error(ps.loc, "unrecognized `pragma(%s)`", ps.ident.toChars());
|
||||
return setError();
|
||||
}
|
||||
|
||||
if (ps._body)
|
||||
{
|
||||
if (ps.ident == Id.msg || ps.ident == Id.startaddress)
|
||||
{
|
||||
error(ps.loc, "`pragma(%s)` is missing a terminating `;`", ps.ident.toChars());
|
||||
return setError();
|
||||
}
|
||||
ps._body = ps._body.statementSemantic(sc);
|
||||
}
|
||||
result = ps._body;
|
||||
}
|
||||
|
||||
|
@ -5010,86 +4913,6 @@ private void debugThrowWalker(Statement s)
|
|||
s.accept(walker);
|
||||
}
|
||||
|
||||
/***********************************************************
|
||||
* Evaluate and print a `pragma(msg, args)`
|
||||
*
|
||||
* Params:
|
||||
* loc = location for error messages
|
||||
* sc = scope for argument interpretation
|
||||
* args = expressions to print
|
||||
* Returns:
|
||||
* `true` on success
|
||||
*/
|
||||
bool pragmaMsgSemantic(Loc loc, Scope* sc, Expressions* args)
|
||||
{
|
||||
if (!args)
|
||||
return true;
|
||||
foreach (arg; *args)
|
||||
{
|
||||
sc = sc.startCTFE();
|
||||
auto e = arg.expressionSemantic(sc);
|
||||
e = resolveProperties(sc, e);
|
||||
sc = sc.endCTFE();
|
||||
|
||||
// pragma(msg) is allowed to contain types as well as expressions
|
||||
e = ctfeInterpretForPragmaMsg(e);
|
||||
if (e.op == EXP.error)
|
||||
{
|
||||
errorSupplemental(loc, "while evaluating `pragma(msg, %s)`", arg.toChars());
|
||||
return false;
|
||||
}
|
||||
if (auto se = e.toStringExp())
|
||||
{
|
||||
const slice = se.toUTF8(sc).peekString();
|
||||
fprintf(stderr, "%.*s", cast(int)slice.length, slice.ptr);
|
||||
}
|
||||
else
|
||||
fprintf(stderr, "%s", e.toChars());
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
/***********************************************************
|
||||
* Evaluate `pragma(startAddress, func)` and store the resolved symbol in `args`
|
||||
*
|
||||
* Params:
|
||||
* loc = location for error messages
|
||||
* sc = scope for argument interpretation
|
||||
* args = pragma arguments
|
||||
* Returns:
|
||||
* `true` on success
|
||||
*/
|
||||
bool pragmaStartAddressSemantic(Loc loc, Scope* sc, Expressions* args)
|
||||
{
|
||||
if (!args || args.length != 1)
|
||||
{
|
||||
.error(loc, "function name expected for start address");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* https://issues.dlang.org/show_bug.cgi?id=11980
|
||||
* resolveProperties and ctfeInterpret call are not necessary.
|
||||
*/
|
||||
Expression e = (*args)[0];
|
||||
sc = sc.startCTFE();
|
||||
e = e.expressionSemantic(sc);
|
||||
// e = resolveProperties(sc, e);
|
||||
sc = sc.endCTFE();
|
||||
|
||||
// e = e.ctfeInterpret();
|
||||
(*args)[0] = e;
|
||||
Dsymbol sa = getDsymbol(e);
|
||||
if (!sa || !sa.isFuncDeclaration())
|
||||
{
|
||||
.error(loc, "function name expected for start address, not `%s`", e.toChars());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/************************************
|
||||
* Check for skipped variable declarations.
|
||||
* Params:
|
||||
|
|
|
@ -51,10 +51,160 @@ import dmd.common.outbuffer;
|
|||
import dmd.rootobject;
|
||||
import dmd.semantic2;
|
||||
import dmd.semantic3;
|
||||
import dmd.templateparamsem;
|
||||
import dmd.tokens;
|
||||
import dmd.typesem;
|
||||
import dmd.visitor;
|
||||
|
||||
/************************************
|
||||
* Perform semantic analysis on template.
|
||||
* Params:
|
||||
* sc = context
|
||||
* tempdecl = template declaration
|
||||
*/
|
||||
void templateDeclarationSemantic(Scope* sc, TemplateDeclaration tempdecl)
|
||||
{
|
||||
enum log = false;
|
||||
static if (log)
|
||||
{
|
||||
printf("TemplateDeclaration.dsymbolSemantic(this = %p, id = '%s')\n", this, tempdecl.ident.toChars());
|
||||
printf("sc.stc = %llx\n", sc.stc);
|
||||
printf("sc.module = %s\n", sc._module.toChars());
|
||||
}
|
||||
if (tempdecl.semanticRun != PASS.initial)
|
||||
return; // semantic() already run
|
||||
|
||||
if (tempdecl._scope)
|
||||
{
|
||||
sc = tempdecl._scope;
|
||||
tempdecl._scope = null;
|
||||
}
|
||||
if (!sc)
|
||||
return;
|
||||
|
||||
// Remember templates defined in module object that we need to know about
|
||||
if (sc._module && sc._module.ident == Id.object)
|
||||
{
|
||||
if (tempdecl.ident == Id.RTInfo)
|
||||
Type.rtinfo = tempdecl;
|
||||
}
|
||||
|
||||
/* Remember Scope for later instantiations, but make
|
||||
* a copy since attributes can change.
|
||||
*/
|
||||
if (!tempdecl._scope)
|
||||
{
|
||||
tempdecl._scope = sc.copy();
|
||||
tempdecl._scope.setNoFree();
|
||||
}
|
||||
|
||||
tempdecl.semanticRun = PASS.semantic;
|
||||
|
||||
tempdecl.parent = sc.parent;
|
||||
tempdecl.visibility = sc.visibility;
|
||||
tempdecl.userAttribDecl = sc.userAttribDecl;
|
||||
tempdecl.cppnamespace = sc.namespace;
|
||||
tempdecl.isstatic = tempdecl.toParent().isModule() || (tempdecl._scope.stc & STC.static_);
|
||||
tempdecl.deprecated_ = !!(sc.stc & STC.deprecated_);
|
||||
|
||||
UserAttributeDeclaration.checkGNUABITag(tempdecl, sc.linkage);
|
||||
|
||||
if (!tempdecl.isstatic)
|
||||
{
|
||||
if (auto ad = tempdecl.parent.pastMixin().isAggregateDeclaration())
|
||||
ad.makeNested();
|
||||
}
|
||||
|
||||
// Set up scope for parameters
|
||||
auto paramsym = new ScopeDsymbol();
|
||||
paramsym.parent = tempdecl.parent;
|
||||
Scope* paramscope = sc.push(paramsym);
|
||||
paramscope.stc = 0;
|
||||
|
||||
if (global.params.ddoc.doOutput)
|
||||
{
|
||||
tempdecl.origParameters = new TemplateParameters(tempdecl.parameters.length);
|
||||
for (size_t i = 0; i < tempdecl.parameters.length; i++)
|
||||
{
|
||||
TemplateParameter tp = (*tempdecl.parameters)[i];
|
||||
(*tempdecl.origParameters)[i] = tp.syntaxCopy();
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < tempdecl.parameters.length; i++)
|
||||
{
|
||||
TemplateParameter tp = (*tempdecl.parameters)[i];
|
||||
if (!tp.declareParameter(paramscope))
|
||||
{
|
||||
error(tp.loc, "parameter `%s` multiply defined", tp.ident.toChars());
|
||||
tempdecl.errors = true;
|
||||
}
|
||||
if (!tp.tpsemantic(paramscope, tempdecl.parameters))
|
||||
{
|
||||
tempdecl.errors = true;
|
||||
}
|
||||
if (i + 1 != tempdecl.parameters.length && tp.isTemplateTupleParameter())
|
||||
{
|
||||
.error(tempdecl.loc, "%s `%s` template sequence parameter must be the last one", tempdecl.kind, tempdecl.toPrettyChars);
|
||||
tempdecl.errors = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate TemplateParameter.dependent
|
||||
*/
|
||||
TemplateParameters tparams = TemplateParameters(1);
|
||||
for (size_t i = 0; i < tempdecl.parameters.length; i++)
|
||||
{
|
||||
TemplateParameter tp = (*tempdecl.parameters)[i];
|
||||
tparams[0] = tp;
|
||||
|
||||
for (size_t j = 0; j < tempdecl.parameters.length; j++)
|
||||
{
|
||||
// Skip cases like: X(T : T)
|
||||
if (i == j)
|
||||
continue;
|
||||
|
||||
if (TemplateTypeParameter ttp = (*tempdecl.parameters)[j].isTemplateTypeParameter())
|
||||
{
|
||||
if (reliesOnTident(ttp.specType, &tparams))
|
||||
tp.dependent = true;
|
||||
}
|
||||
else if (TemplateAliasParameter tap = (*tempdecl.parameters)[j].isTemplateAliasParameter())
|
||||
{
|
||||
if (reliesOnTident(tap.specType, &tparams) ||
|
||||
reliesOnTident(isType(tap.specAlias), &tparams))
|
||||
{
|
||||
tp.dependent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
paramscope.pop();
|
||||
|
||||
// Compute again
|
||||
tempdecl.onemember = null;
|
||||
if (tempdecl.members)
|
||||
{
|
||||
Dsymbol s;
|
||||
if (Dsymbol.oneMembers(tempdecl.members, s, tempdecl.ident) && s)
|
||||
{
|
||||
tempdecl.onemember = s;
|
||||
s.parent = tempdecl;
|
||||
}
|
||||
}
|
||||
|
||||
/* BUG: should check:
|
||||
* 1. template functions must not introduce virtual functions, as they
|
||||
* cannot be accomodated in the vtbl[]
|
||||
* 2. templates cannot introduce non-static data members (i.e. fields)
|
||||
* as they would change the instance size of the aggregate.
|
||||
*/
|
||||
|
||||
tempdecl.semanticRun = PASS.semanticdone;
|
||||
}
|
||||
|
||||
|
||||
/***************************************
|
||||
* Given that ti is an instance of this TemplateDeclaration,
|
||||
* deduce the types of the parameters to this, and store
|
||||
|
@ -1468,7 +1618,7 @@ Lmatch:
|
|||
sc2.minst = sc.minst;
|
||||
sc2.stc |= fd.storage_class & STC.deprecated_;
|
||||
|
||||
fd = td.doHeaderInstantiation(ti, sc2, fd, tthis, argumentList.arguments);
|
||||
fd = doHeaderInstantiation(td, ti, sc2, fd, tthis, argumentList.arguments);
|
||||
sc2 = sc2.pop();
|
||||
sc2 = sc2.pop();
|
||||
|
||||
|
@ -1495,3 +1645,760 @@ Lmatch:
|
|||
//printf("\tmatch %d\n", match);
|
||||
return MATCHpair(matchTiargs, match);
|
||||
}
|
||||
|
||||
/*************************************************
|
||||
* Limited function template instantiation for using fd.leastAsSpecialized()
|
||||
*/
|
||||
private
|
||||
FuncDeclaration doHeaderInstantiation(TemplateDeclaration td, TemplateInstance ti, Scope* sc2, FuncDeclaration fd, Type tthis, Expressions* fargs)
|
||||
{
|
||||
assert(fd);
|
||||
version (none)
|
||||
{
|
||||
printf("doHeaderInstantiation this = %s\n", toChars());
|
||||
}
|
||||
|
||||
// function body and contracts are not need
|
||||
if (fd.isCtorDeclaration())
|
||||
fd = new CtorDeclaration(fd.loc, fd.endloc, fd.storage_class, fd.type.syntaxCopy());
|
||||
else
|
||||
fd = new FuncDeclaration(fd.loc, fd.endloc, fd.ident, fd.storage_class, fd.type.syntaxCopy());
|
||||
fd.parent = ti;
|
||||
|
||||
assert(fd.type.ty == Tfunction);
|
||||
auto tf = fd.type.isTypeFunction();
|
||||
tf.fargs = fargs;
|
||||
|
||||
if (tthis)
|
||||
{
|
||||
// Match 'tthis' to any TemplateThisParameter's
|
||||
bool hasttp = false;
|
||||
foreach (tp; *td.parameters)
|
||||
{
|
||||
TemplateThisParameter ttp = tp.isTemplateThisParameter();
|
||||
if (ttp)
|
||||
hasttp = true;
|
||||
}
|
||||
if (hasttp)
|
||||
{
|
||||
tf = tf.addSTC(ModToStc(tthis.mod)).isTypeFunction();
|
||||
assert(!tf.deco);
|
||||
}
|
||||
}
|
||||
|
||||
Scope* scx = sc2.push();
|
||||
|
||||
// Shouldn't run semantic on default arguments and return type.
|
||||
foreach (ref params; *tf.parameterList.parameters)
|
||||
params.defaultArg = null;
|
||||
tf.incomplete = true;
|
||||
|
||||
if (fd.isCtorDeclaration())
|
||||
{
|
||||
// For constructors, emitting return type is necessary for
|
||||
// isReturnIsolated() in functionResolve.
|
||||
tf.isctor = true;
|
||||
|
||||
Dsymbol parent = td.toParentDecl();
|
||||
Type tret;
|
||||
AggregateDeclaration ad = parent.isAggregateDeclaration();
|
||||
if (!ad || parent.isUnionDeclaration())
|
||||
{
|
||||
tret = Type.tvoid;
|
||||
}
|
||||
else
|
||||
{
|
||||
tret = ad.handleType();
|
||||
assert(tret);
|
||||
tret = tret.addStorageClass(fd.storage_class | scx.stc);
|
||||
tret = tret.addMod(tf.mod);
|
||||
}
|
||||
tf.next = tret;
|
||||
if (ad && ad.isStructDeclaration())
|
||||
tf.isref = 1;
|
||||
//printf("tf = %s\n", tf.toChars());
|
||||
}
|
||||
else
|
||||
tf.next = null;
|
||||
fd.type = tf;
|
||||
fd.type = fd.type.addSTC(scx.stc);
|
||||
fd.type = fd.type.typeSemantic(fd.loc, scx);
|
||||
scx = scx.pop();
|
||||
|
||||
if (fd.type.ty != Tfunction)
|
||||
return null;
|
||||
|
||||
fd.originalType = fd.type; // for mangling
|
||||
//printf("\t[%s] fd.type = %s, mod = %x, ", loc.toChars(), fd.type.toChars(), fd.type.mod);
|
||||
//printf("fd.needThis() = %d\n", fd.needThis());
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* Declare template parameter tp with value o, and install it in the scope sc.
|
||||
*/
|
||||
extern (D) RootObject declareParameter(TemplateDeclaration td, Scope* sc, TemplateParameter tp, RootObject o)
|
||||
{
|
||||
//printf("TemplateDeclaration.declareParameter('%s', o = %p)\n", tp.ident.toChars(), o);
|
||||
Type ta = isType(o);
|
||||
Expression ea = isExpression(o);
|
||||
Dsymbol sa = isDsymbol(o);
|
||||
Tuple va = isTuple(o);
|
||||
|
||||
Declaration d;
|
||||
VarDeclaration v = null;
|
||||
|
||||
if (ea)
|
||||
{
|
||||
if (ea.op == EXP.type)
|
||||
ta = ea.type;
|
||||
else if (auto se = ea.isScopeExp())
|
||||
sa = se.sds;
|
||||
else if (auto te = ea.isThisExp())
|
||||
sa = te.var;
|
||||
else if (auto se = ea.isSuperExp())
|
||||
sa = se.var;
|
||||
else if (auto fe = ea.isFuncExp())
|
||||
{
|
||||
if (fe.td)
|
||||
sa = fe.td;
|
||||
else
|
||||
sa = fe.fd;
|
||||
}
|
||||
}
|
||||
|
||||
if (ta)
|
||||
{
|
||||
//printf("type %s\n", ta.toChars());
|
||||
auto ad = new AliasDeclaration(Loc.initial, tp.ident, ta);
|
||||
ad.storage_class |= STC.templateparameter;
|
||||
d = ad;
|
||||
}
|
||||
else if (sa)
|
||||
{
|
||||
//printf("Alias %s %s;\n", sa.ident.toChars(), tp.ident.toChars());
|
||||
auto ad = new AliasDeclaration(Loc.initial, tp.ident, sa);
|
||||
ad.storage_class |= STC.templateparameter;
|
||||
d = ad;
|
||||
}
|
||||
else if (ea)
|
||||
{
|
||||
// tdtypes.data[i] always matches ea here
|
||||
Initializer _init = new ExpInitializer(td.loc, ea);
|
||||
TemplateValueParameter tvp = tp.isTemplateValueParameter();
|
||||
Type t = tvp ? tvp.valType : null;
|
||||
v = new VarDeclaration(td.loc, t, tp.ident, _init);
|
||||
v.storage_class = STC.manifest | STC.templateparameter;
|
||||
d = v;
|
||||
}
|
||||
else if (va)
|
||||
{
|
||||
//printf("\ttuple\n");
|
||||
d = new TupleDeclaration(td.loc, tp.ident, &va.objects);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
d.storage_class |= STC.templateparameter;
|
||||
|
||||
if (ta)
|
||||
{
|
||||
Type t = ta;
|
||||
// consistent with Type.checkDeprecated()
|
||||
while (t.ty != Tenum)
|
||||
{
|
||||
if (!t.nextOf())
|
||||
break;
|
||||
t = (cast(TypeNext)t).next;
|
||||
}
|
||||
if (Dsymbol s = t.toDsymbol(sc))
|
||||
{
|
||||
if (s.isDeprecated())
|
||||
d.storage_class |= STC.deprecated_;
|
||||
}
|
||||
}
|
||||
else if (sa)
|
||||
{
|
||||
if (sa.isDeprecated())
|
||||
d.storage_class |= STC.deprecated_;
|
||||
}
|
||||
|
||||
if (!sc.insert(d))
|
||||
.error(td.loc, "%s `%s` declaration `%s` is already defined", td.kind, td.toPrettyChars, tp.ident.toChars());
|
||||
d.dsymbolSemantic(sc);
|
||||
/* So the caller's o gets updated with the result of semantic() being run on o
|
||||
*/
|
||||
if (v)
|
||||
o = v._init.initializerToExpression();
|
||||
return o;
|
||||
}
|
||||
|
||||
/*************************************************
|
||||
* Given function arguments, figure out which template function
|
||||
* to expand, and return matching result.
|
||||
* Params:
|
||||
* m = matching result
|
||||
* dstart = the root of overloaded function templates
|
||||
* loc = instantiation location
|
||||
* sc = instantiation scope
|
||||
* tiargs = initial list of template arguments
|
||||
* tthis = if !NULL, the 'this' pointer argument
|
||||
* argumentList= arguments to function
|
||||
* errorHelper = delegate to send error message to if not null
|
||||
*/
|
||||
void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, Objects* tiargs,
|
||||
Type tthis, ArgumentList argumentList, void delegate(const(char)*) scope errorHelper = null)
|
||||
{
|
||||
version (none)
|
||||
{
|
||||
printf("functionResolve() dstart = %s\n", dstart.toChars());
|
||||
printf(" tiargs:\n");
|
||||
if (tiargs)
|
||||
{
|
||||
for (size_t i = 0; i < tiargs.length; i++)
|
||||
{
|
||||
RootObject arg = (*tiargs)[i];
|
||||
printf("\t%s\n", arg.toChars());
|
||||
}
|
||||
}
|
||||
printf(" fargs:\n");
|
||||
for (size_t i = 0; i < (fargs ? fargs.length : 0); i++)
|
||||
{
|
||||
Expression arg = (*fargs)[i];
|
||||
printf("\t%s %s\n", arg.type.toChars(), arg.toChars());
|
||||
//printf("\tty = %d\n", arg.type.ty);
|
||||
}
|
||||
//printf("stc = %llx\n", dstart._scope.stc);
|
||||
//printf("match:t/f = %d/%d\n", ta_last, m.last);
|
||||
}
|
||||
|
||||
// results
|
||||
int property = 0; // 0: uninitialized
|
||||
// 1: seen @property
|
||||
// 2: not @property
|
||||
size_t ov_index = 0;
|
||||
TemplateDeclaration td_best;
|
||||
TemplateInstance ti_best;
|
||||
MATCH ta_last = m.last != MATCH.nomatch ? MATCH.exact : MATCH.nomatch;
|
||||
Type tthis_best;
|
||||
|
||||
int applyFunction(FuncDeclaration fd)
|
||||
{
|
||||
// skip duplicates
|
||||
if (fd == m.lastf)
|
||||
return 0;
|
||||
// explicitly specified tiargs never match to non template function
|
||||
if (tiargs && tiargs.length > 0)
|
||||
return 0;
|
||||
|
||||
// constructors need a valid scope in order to detect semantic errors
|
||||
if (!fd.isCtorDeclaration &&
|
||||
fd.semanticRun < PASS.semanticdone)
|
||||
{
|
||||
fd.ungagSpeculative();
|
||||
fd.dsymbolSemantic(null);
|
||||
}
|
||||
if (fd.semanticRun < PASS.semanticdone)
|
||||
{
|
||||
.error(loc, "forward reference to template `%s`", fd.toChars());
|
||||
return 1;
|
||||
}
|
||||
//printf("fd = %s %s, fargs = %s\n", fd.toChars(), fd.type.toChars(), fargs.toChars());
|
||||
auto tf = fd.type.isTypeFunction();
|
||||
|
||||
int prop = tf.isproperty ? 1 : 2;
|
||||
if (property == 0)
|
||||
property = prop;
|
||||
else if (property != prop)
|
||||
error(fd.loc, "cannot overload both property and non-property functions");
|
||||
|
||||
/* For constructors, qualifier check will be opposite direction.
|
||||
* Qualified constructor always makes qualified object, then will be checked
|
||||
* that it is implicitly convertible to tthis.
|
||||
*/
|
||||
Type tthis_fd = fd.needThis() ? tthis : null;
|
||||
bool isCtorCall = tthis_fd && fd.isCtorDeclaration();
|
||||
if (isCtorCall)
|
||||
{
|
||||
//printf("%s tf.mod = x%x tthis_fd.mod = x%x %d\n", tf.toChars(),
|
||||
// tf.mod, tthis_fd.mod, fd.isReturnIsolated());
|
||||
if (MODimplicitConv(tf.mod, tthis_fd.mod) ||
|
||||
tf.isWild() && tf.isShared() == tthis_fd.isShared() ||
|
||||
fd.isReturnIsolated())
|
||||
{
|
||||
/* && tf.isShared() == tthis_fd.isShared()*/
|
||||
// Uniquely constructed object can ignore shared qualifier.
|
||||
// TODO: Is this appropriate?
|
||||
tthis_fd = null;
|
||||
}
|
||||
else
|
||||
return 0; // MATCH.nomatch
|
||||
}
|
||||
/* Fix Issue 17970:
|
||||
If a struct is declared as shared the dtor is automatically
|
||||
considered to be shared, but when the struct is instantiated
|
||||
the instance is no longer considered to be shared when the
|
||||
function call matching is done. The fix makes it so that if a
|
||||
struct declaration is shared, when the destructor is called,
|
||||
the instantiated struct is also considered shared.
|
||||
*/
|
||||
if (auto dt = fd.isDtorDeclaration())
|
||||
{
|
||||
auto dtmod = dt.type.toTypeFunction();
|
||||
auto shared_dtor = dtmod.mod & MODFlags.shared_;
|
||||
auto shared_this = tthis_fd !is null ?
|
||||
tthis_fd.mod & MODFlags.shared_ : 0;
|
||||
if (shared_dtor && !shared_this)
|
||||
tthis_fd = dtmod;
|
||||
else if (shared_this && !shared_dtor && tthis_fd !is null)
|
||||
tf.mod = tthis_fd.mod;
|
||||
}
|
||||
const(char)* failMessage;
|
||||
MATCH mfa = tf.callMatch(tthis_fd, argumentList, 0, errorHelper, sc);
|
||||
//printf("test1: mfa = %d\n", mfa);
|
||||
if (failMessage)
|
||||
errorHelper(failMessage);
|
||||
if (mfa == MATCH.nomatch)
|
||||
return 0;
|
||||
|
||||
int firstIsBetter()
|
||||
{
|
||||
td_best = null;
|
||||
ti_best = null;
|
||||
ta_last = MATCH.exact;
|
||||
m.last = mfa;
|
||||
m.lastf = fd;
|
||||
tthis_best = tthis_fd;
|
||||
ov_index = 0;
|
||||
m.count = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mfa > m.last) return firstIsBetter();
|
||||
if (mfa < m.last) return 0;
|
||||
|
||||
/* See if one of the matches overrides the other.
|
||||
*/
|
||||
assert(m.lastf);
|
||||
if (m.lastf.overrides(fd)) return 0;
|
||||
if (fd.overrides(m.lastf)) return firstIsBetter();
|
||||
|
||||
/* Try to disambiguate using template-style partial ordering rules.
|
||||
* In essence, if f() and g() are ambiguous, if f() can call g(),
|
||||
* but g() cannot call f(), then pick f().
|
||||
* This is because f() is "more specialized."
|
||||
*/
|
||||
{
|
||||
MATCH c1 = FuncDeclaration.leastAsSpecialized(fd, m.lastf, argumentList.names);
|
||||
MATCH c2 = FuncDeclaration.leastAsSpecialized(m.lastf, fd, argumentList.names);
|
||||
//printf("c1 = %d, c2 = %d\n", c1, c2);
|
||||
if (c1 > c2) return firstIsBetter();
|
||||
if (c1 < c2) return 0;
|
||||
}
|
||||
|
||||
/* The 'overrides' check above does covariant checking only
|
||||
* for virtual member functions. It should do it for all functions,
|
||||
* but in order to not risk breaking code we put it after
|
||||
* the 'leastAsSpecialized' check.
|
||||
* In the future try moving it before.
|
||||
* I.e. a not-the-same-but-covariant match is preferred,
|
||||
* as it is more restrictive.
|
||||
*/
|
||||
if (!m.lastf.type.equals(fd.type))
|
||||
{
|
||||
//printf("cov: %d %d\n", m.lastf.type.covariant(fd.type), fd.type.covariant(m.lastf.type));
|
||||
const lastCovariant = m.lastf.type.covariant(fd.type);
|
||||
const firstCovariant = fd.type.covariant(m.lastf.type);
|
||||
|
||||
if (lastCovariant == Covariant.yes || lastCovariant == Covariant.no)
|
||||
{
|
||||
if (firstCovariant != Covariant.yes && firstCovariant != Covariant.no)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (firstCovariant == Covariant.yes || firstCovariant == Covariant.no)
|
||||
{
|
||||
return firstIsBetter();
|
||||
}
|
||||
}
|
||||
|
||||
/* If the two functions are the same function, like:
|
||||
* int foo(int);
|
||||
* int foo(int x) { ... }
|
||||
* then pick the one with the body.
|
||||
*
|
||||
* If none has a body then don't care because the same
|
||||
* real function would be linked to the decl (e.g from object file)
|
||||
*/
|
||||
if (tf.equals(m.lastf.type) &&
|
||||
fd.storage_class == m.lastf.storage_class &&
|
||||
fd.parent == m.lastf.parent &&
|
||||
fd.visibility == m.lastf.visibility &&
|
||||
fd._linkage == m.lastf._linkage)
|
||||
{
|
||||
if (fd.fbody && !m.lastf.fbody)
|
||||
return firstIsBetter();
|
||||
if (!fd.fbody)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// https://issues.dlang.org/show_bug.cgi?id=14450
|
||||
// Prefer exact qualified constructor for the creating object type
|
||||
if (isCtorCall && tf.mod != m.lastf.type.mod)
|
||||
{
|
||||
if (tthis.mod == tf.mod) return firstIsBetter();
|
||||
if (tthis.mod == m.lastf.type.mod) return 0;
|
||||
}
|
||||
|
||||
m.nextf = fd;
|
||||
m.count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int applyTemplate(TemplateDeclaration td)
|
||||
{
|
||||
//printf("applyTemplate(): td = %s\n", td.toChars());
|
||||
if (td == td_best) // skip duplicates
|
||||
return 0;
|
||||
|
||||
if (!sc)
|
||||
sc = td._scope; // workaround for Type.aliasthisOf
|
||||
|
||||
if (td.semanticRun == PASS.initial && td._scope)
|
||||
{
|
||||
// Try to fix forward reference. Ungag errors while doing so.
|
||||
td.ungagSpeculative();
|
||||
td.dsymbolSemantic(td._scope);
|
||||
}
|
||||
if (td.semanticRun == PASS.initial)
|
||||
{
|
||||
.error(loc, "forward reference to template `%s`", td.toChars());
|
||||
Lerror:
|
||||
m.lastf = null;
|
||||
m.count = 0;
|
||||
m.last = MATCH.nomatch;
|
||||
return 1;
|
||||
}
|
||||
//printf("td = %s\n", td.toChars());
|
||||
|
||||
if (argumentList.hasNames)
|
||||
{
|
||||
.error(loc, "named arguments with Implicit Function Template Instantiation are not supported yet");
|
||||
goto Lerror;
|
||||
}
|
||||
auto f = td.onemember ? td.onemember.isFuncDeclaration() : null;
|
||||
if (!f)
|
||||
{
|
||||
if (!tiargs)
|
||||
tiargs = new Objects();
|
||||
auto ti = new TemplateInstance(loc, td, tiargs);
|
||||
Objects dedtypes = Objects(td.parameters.length);
|
||||
assert(td.semanticRun != PASS.initial);
|
||||
MATCH mta = matchWithInstance(sc, td, ti, dedtypes, argumentList, 0);
|
||||
//printf("matchWithInstance = %d\n", mta);
|
||||
if (mta == MATCH.nomatch || mta < ta_last) // no match or less match
|
||||
return 0;
|
||||
|
||||
ti.templateInstanceSemantic(sc, argumentList);
|
||||
if (!ti.inst) // if template failed to expand
|
||||
return 0;
|
||||
|
||||
Dsymbol s = ti.inst.toAlias();
|
||||
FuncDeclaration fd;
|
||||
if (auto tdx = s.isTemplateDeclaration())
|
||||
{
|
||||
Objects dedtypesX; // empty tiargs
|
||||
|
||||
// https://issues.dlang.org/show_bug.cgi?id=11553
|
||||
// Check for recursive instantiation of tdx.
|
||||
for (TemplatePrevious* p = tdx.previous; p; p = p.prev)
|
||||
{
|
||||
if (arrayObjectMatch(*p.dedargs, dedtypesX))
|
||||
{
|
||||
//printf("recursive, no match p.sc=%p %p %s\n", p.sc, this, this.toChars());
|
||||
/* It must be a subscope of p.sc, other scope chains are not recursive
|
||||
* instantiations.
|
||||
*/
|
||||
for (Scope* scx = sc; scx; scx = scx.enclosing)
|
||||
{
|
||||
if (scx == p.sc)
|
||||
{
|
||||
error(loc, "recursive template expansion while looking for `%s.%s`", ti.toChars(), tdx.toChars());
|
||||
goto Lerror;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* BUG: should also check for ref param differences
|
||||
*/
|
||||
}
|
||||
|
||||
TemplatePrevious pr;
|
||||
pr.prev = tdx.previous;
|
||||
pr.sc = sc;
|
||||
pr.dedargs = &dedtypesX;
|
||||
tdx.previous = ≺ // add this to threaded list
|
||||
|
||||
fd = resolveFuncCall(loc, sc, s, null, tthis, argumentList, FuncResolveFlag.quiet);
|
||||
|
||||
tdx.previous = pr.prev; // unlink from threaded list
|
||||
}
|
||||
else if (s.isFuncDeclaration())
|
||||
{
|
||||
fd = resolveFuncCall(loc, sc, s, null, tthis, argumentList, FuncResolveFlag.quiet);
|
||||
}
|
||||
else
|
||||
goto Lerror;
|
||||
|
||||
if (!fd)
|
||||
return 0;
|
||||
|
||||
if (fd.type.ty != Tfunction)
|
||||
{
|
||||
m.lastf = fd; // to propagate "error match"
|
||||
m.count = 1;
|
||||
m.last = MATCH.nomatch;
|
||||
return 1;
|
||||
}
|
||||
|
||||
Type tthis_fd = fd.needThis() && !fd.isCtorDeclaration() ? tthis : null;
|
||||
|
||||
auto tf = fd.type.isTypeFunction();
|
||||
MATCH mfa = tf.callMatch(tthis_fd, argumentList, 0, null, sc);
|
||||
if (mfa < m.last)
|
||||
return 0;
|
||||
|
||||
if (mta < ta_last) goto Ltd_best2;
|
||||
if (mta > ta_last) goto Ltd2;
|
||||
|
||||
if (mfa < m.last) goto Ltd_best2;
|
||||
if (mfa > m.last) goto Ltd2;
|
||||
|
||||
// td_best and td are ambiguous
|
||||
//printf("Lambig2\n");
|
||||
m.nextf = fd;
|
||||
m.count++;
|
||||
return 0;
|
||||
|
||||
Ltd_best2:
|
||||
return 0;
|
||||
|
||||
Ltd2:
|
||||
// td is the new best match
|
||||
assert(td._scope);
|
||||
td_best = td;
|
||||
ti_best = null;
|
||||
property = 0; // (backward compatibility)
|
||||
ta_last = mta;
|
||||
m.last = mfa;
|
||||
m.lastf = fd;
|
||||
tthis_best = tthis_fd;
|
||||
ov_index = 0;
|
||||
m.nextf = null;
|
||||
m.count = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//printf("td = %s\n", td.toChars());
|
||||
for (size_t ovi = 0; f; f = f.overnext0, ovi++)
|
||||
{
|
||||
if (f.type.ty != Tfunction || f.errors)
|
||||
goto Lerror;
|
||||
|
||||
/* This is a 'dummy' instance to evaluate constraint properly.
|
||||
*/
|
||||
auto ti = new TemplateInstance(loc, td, tiargs);
|
||||
ti.parent = td.parent; // Maybe calculating valid 'enclosing' is unnecessary.
|
||||
|
||||
auto fd = f;
|
||||
MATCHpair x = td.deduceFunctionTemplateMatch(ti, sc, fd, tthis, argumentList);
|
||||
MATCH mta = x.mta;
|
||||
MATCH mfa = x.mfa;
|
||||
//printf("match:t/f = %d/%d\n", mta, mfa);
|
||||
if (!fd || mfa == MATCH.nomatch)
|
||||
continue;
|
||||
|
||||
Type tthis_fd = fd.needThis() ? tthis : null;
|
||||
|
||||
bool isCtorCall = tthis_fd && fd.isCtorDeclaration();
|
||||
if (isCtorCall)
|
||||
{
|
||||
// Constructor call requires additional check.
|
||||
auto tf = fd.type.isTypeFunction();
|
||||
assert(tf.next);
|
||||
if (MODimplicitConv(tf.mod, tthis_fd.mod) ||
|
||||
tf.isWild() && tf.isShared() == tthis_fd.isShared() ||
|
||||
fd.isReturnIsolated())
|
||||
{
|
||||
tthis_fd = null;
|
||||
}
|
||||
else
|
||||
continue; // MATCH.nomatch
|
||||
|
||||
// need to check here whether the constructor is the member of a struct
|
||||
// declaration that defines a copy constructor. This is already checked
|
||||
// in the semantic of CtorDeclaration, however, when matching functions,
|
||||
// the template instance is not expanded.
|
||||
// https://issues.dlang.org/show_bug.cgi?id=21613
|
||||
auto ad = fd.isThis();
|
||||
auto sd = ad.isStructDeclaration();
|
||||
if (checkHasBothRvalueAndCpCtor(sd, fd.isCtorDeclaration(), ti))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mta < ta_last) goto Ltd_best;
|
||||
if (mta > ta_last) goto Ltd;
|
||||
|
||||
if (mfa < m.last) goto Ltd_best;
|
||||
if (mfa > m.last) goto Ltd;
|
||||
|
||||
if (td_best)
|
||||
{
|
||||
// Disambiguate by picking the most specialized TemplateDeclaration
|
||||
MATCH c1 = leastAsSpecialized(sc, td, td_best, argumentList);
|
||||
MATCH c2 = leastAsSpecialized(sc, td_best, td, argumentList);
|
||||
//printf("1: c1 = %d, c2 = %d\n", c1, c2);
|
||||
if (c1 > c2) goto Ltd;
|
||||
if (c1 < c2) goto Ltd_best;
|
||||
}
|
||||
assert(fd && m.lastf);
|
||||
{
|
||||
// Disambiguate by tf.callMatch
|
||||
auto tf1 = fd.type.isTypeFunction();
|
||||
auto tf2 = m.lastf.type.isTypeFunction();
|
||||
MATCH c1 = tf1.callMatch(tthis_fd, argumentList, 0, null, sc);
|
||||
MATCH c2 = tf2.callMatch(tthis_best, argumentList, 0, null, sc);
|
||||
//printf("2: c1 = %d, c2 = %d\n", c1, c2);
|
||||
if (c1 > c2) goto Ltd;
|
||||
if (c1 < c2) goto Ltd_best;
|
||||
}
|
||||
{
|
||||
// Disambiguate by picking the most specialized FunctionDeclaration
|
||||
MATCH c1 = FuncDeclaration.leastAsSpecialized(fd, m.lastf, argumentList.names);
|
||||
MATCH c2 = FuncDeclaration.leastAsSpecialized(m.lastf, fd, argumentList.names);
|
||||
//printf("3: c1 = %d, c2 = %d\n", c1, c2);
|
||||
if (c1 > c2) goto Ltd;
|
||||
if (c1 < c2) goto Ltd_best;
|
||||
}
|
||||
|
||||
// https://issues.dlang.org/show_bug.cgi?id=14450
|
||||
// Prefer exact qualified constructor for the creating object type
|
||||
if (isCtorCall && fd.type.mod != m.lastf.type.mod)
|
||||
{
|
||||
if (tthis.mod == fd.type.mod) goto Ltd;
|
||||
if (tthis.mod == m.lastf.type.mod) goto Ltd_best;
|
||||
}
|
||||
|
||||
m.nextf = fd;
|
||||
m.count++;
|
||||
continue;
|
||||
|
||||
Ltd_best: // td_best is the best match so far
|
||||
//printf("Ltd_best\n");
|
||||
continue;
|
||||
|
||||
Ltd: // td is the new best match
|
||||
//printf("Ltd\n");
|
||||
assert(td._scope);
|
||||
td_best = td;
|
||||
ti_best = ti;
|
||||
property = 0; // (backward compatibility)
|
||||
ta_last = mta;
|
||||
m.last = mfa;
|
||||
m.lastf = fd;
|
||||
tthis_best = tthis_fd;
|
||||
ov_index = ovi;
|
||||
m.nextf = null;
|
||||
m.count = 1;
|
||||
continue;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto td = dstart.isTemplateDeclaration();
|
||||
if (td && td.funcroot)
|
||||
dstart = td.funcroot;
|
||||
overloadApply(dstart, (Dsymbol s)
|
||||
{
|
||||
if (s.errors)
|
||||
return 0;
|
||||
if (auto fd = s.isFuncDeclaration())
|
||||
return applyFunction(fd);
|
||||
if (auto td = s.isTemplateDeclaration())
|
||||
return applyTemplate(td);
|
||||
return 0;
|
||||
}, sc);
|
||||
|
||||
//printf("td_best = %p, m.lastf = %p\n", td_best, m.lastf);
|
||||
if (td_best && ti_best && m.count == 1)
|
||||
{
|
||||
// Matches to template function
|
||||
assert(td_best.onemember && td_best.onemember.isFuncDeclaration());
|
||||
/* The best match is td_best with arguments tdargs.
|
||||
* Now instantiate the template.
|
||||
*/
|
||||
assert(td_best._scope);
|
||||
if (!sc)
|
||||
sc = td_best._scope; // workaround for Type.aliasthisOf
|
||||
|
||||
auto ti = new TemplateInstance(loc, td_best, ti_best.tiargs);
|
||||
ti.templateInstanceSemantic(sc, argumentList);
|
||||
|
||||
m.lastf = ti.toAlias().isFuncDeclaration();
|
||||
if (!m.lastf)
|
||||
goto Lnomatch;
|
||||
if (ti.errors)
|
||||
{
|
||||
Lerror:
|
||||
m.count = 1;
|
||||
assert(m.lastf);
|
||||
m.last = MATCH.nomatch;
|
||||
return;
|
||||
}
|
||||
|
||||
// look forward instantiated overload function
|
||||
// Dsymbol.oneMembers is alredy called in TemplateInstance.semantic.
|
||||
// it has filled overnext0d
|
||||
while (ov_index--)
|
||||
{
|
||||
m.lastf = m.lastf.overnext0;
|
||||
assert(m.lastf);
|
||||
}
|
||||
|
||||
tthis_best = m.lastf.needThis() && !m.lastf.isCtorDeclaration() ? tthis : null;
|
||||
|
||||
if (m.lastf.type.ty == Terror)
|
||||
goto Lerror;
|
||||
auto tf = m.lastf.type.isTypeFunction();
|
||||
if (!tf.callMatch(tthis_best, argumentList, 0, null, sc))
|
||||
goto Lnomatch;
|
||||
|
||||
/* As https://issues.dlang.org/show_bug.cgi?id=3682 shows,
|
||||
* a template instance can be matched while instantiating
|
||||
* that same template. Thus, the function type can be incomplete. Complete it.
|
||||
*
|
||||
* https://issues.dlang.org/show_bug.cgi?id=9208
|
||||
* For auto function, completion should be deferred to the end of
|
||||
* its semantic3. Should not complete it in here.
|
||||
*/
|
||||
if (tf.next && !m.lastf.inferRetType)
|
||||
{
|
||||
m.lastf.type = tf.typeSemantic(loc, sc);
|
||||
}
|
||||
}
|
||||
else if (m.lastf)
|
||||
{
|
||||
// Matches to non template function,
|
||||
// or found matches were ambiguous.
|
||||
assert(m.count >= 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Lnomatch:
|
||||
m.count = 0;
|
||||
m.lastf = null;
|
||||
m.last = MATCH.nomatch;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ import dmd.errorsink;
|
|||
import dmd.expression;
|
||||
import dmd.expressionsem;
|
||||
import dmd.func;
|
||||
import dmd.funcsem;
|
||||
import dmd.globals;
|
||||
import dmd.hdrgen;
|
||||
import dmd.id;
|
||||
|
@ -3742,12 +3743,12 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type
|
|||
// f might be a unittest declaration which is incomplete when compiled
|
||||
// without -unittest. That causes a segfault in checkForwardRef, see
|
||||
// https://issues.dlang.org/show_bug.cgi?id=20626
|
||||
if ((!f.isUnitTestDeclaration() || global.params.useUnitTests) && f.checkForwardRef(loc))
|
||||
if ((!f.isUnitTestDeclaration() || global.params.useUnitTests) && checkForwardRef(f, loc))
|
||||
goto Lerr;
|
||||
}
|
||||
if (auto f = isFuncAddress(mt.exp))
|
||||
{
|
||||
if (f.checkForwardRef(loc))
|
||||
if (checkForwardRef(f, loc))
|
||||
goto Lerr;
|
||||
}
|
||||
|
||||
|
@ -6105,6 +6106,305 @@ extern(C++) bool isBaseOf(Type tthis, Type t, int* poffset)
|
|||
return false;
|
||||
}
|
||||
|
||||
/********************************
|
||||
* Convert to 'const'.
|
||||
*/
|
||||
extern(C++) Type constOf(Type type)
|
||||
{
|
||||
//printf("Type::constOf() %p %s\n", type, type.toChars());
|
||||
if (type.mod == MODFlags.const_)
|
||||
return type;
|
||||
if (type.mcache && type.mcache.cto)
|
||||
{
|
||||
assert(type.mcache.cto.mod == MODFlags.const_);
|
||||
return type.mcache.cto;
|
||||
}
|
||||
Type t = type.makeConst();
|
||||
t = t.merge();
|
||||
t.fixTo(type);
|
||||
//printf("-Type::constOf() %p %s\n", t, t.toChars());
|
||||
return t;
|
||||
}
|
||||
|
||||
/********************************
|
||||
* Convert to 'immutable'.
|
||||
*/
|
||||
extern(C++) Type immutableOf(Type type)
|
||||
{
|
||||
//printf("Type::immutableOf() %p %s\n", this, toChars());
|
||||
if (type.isImmutable())
|
||||
return type;
|
||||
if (type.mcache && type.mcache.ito)
|
||||
{
|
||||
assert(type.mcache.ito.isImmutable());
|
||||
return type.mcache.ito;
|
||||
}
|
||||
Type t = type.makeImmutable();
|
||||
t = t.merge();
|
||||
t.fixTo(type);
|
||||
//printf("\t%p\n", t);
|
||||
return t;
|
||||
}
|
||||
|
||||
/********************************
|
||||
* Make type mutable.
|
||||
*/
|
||||
extern(C++) Type mutableOf(Type type)
|
||||
{
|
||||
//printf("Type::mutableOf() %p, %s\n", type, type.toChars());
|
||||
Type t = type;
|
||||
if (type.isImmutable())
|
||||
{
|
||||
type.getMcache();
|
||||
t = type.mcache.ito; // immutable => naked
|
||||
assert(!t || (t.isMutable() && !t.isShared()));
|
||||
}
|
||||
else if (type.isConst())
|
||||
{
|
||||
type.getMcache();
|
||||
if (type.isShared())
|
||||
{
|
||||
if (type.isWild())
|
||||
t = type.mcache.swcto; // shared wild const -> shared
|
||||
else
|
||||
t = type.mcache.sto; // shared const => shared
|
||||
}
|
||||
else
|
||||
{
|
||||
if (type.isWild())
|
||||
t = type.mcache.wcto; // wild const -> naked
|
||||
else
|
||||
t = type.mcache.cto; // const => naked
|
||||
}
|
||||
assert(!t || t.isMutable());
|
||||
}
|
||||
else if (type.isWild())
|
||||
{
|
||||
type.getMcache();
|
||||
if (type.isShared())
|
||||
t = type.mcache.sto; // shared wild => shared
|
||||
else
|
||||
t = type.mcache.wto; // wild => naked
|
||||
assert(!t || t.isMutable());
|
||||
}
|
||||
if (!t)
|
||||
{
|
||||
t = type.makeMutable();
|
||||
t = t.merge();
|
||||
t.fixTo(type);
|
||||
}
|
||||
else
|
||||
t = t.merge();
|
||||
assert(t.isMutable());
|
||||
return t;
|
||||
}
|
||||
|
||||
extern(C++) Type sharedOf(Type type)
|
||||
{
|
||||
//printf("Type::sharedOf() %p, %s\n", type, type.toChars());
|
||||
if (type.mod == MODFlags.shared_)
|
||||
return type;
|
||||
if (type.mcache && type.mcache.sto)
|
||||
{
|
||||
assert(type.mcache.sto.mod == MODFlags.shared_);
|
||||
return type.mcache.sto;
|
||||
}
|
||||
Type t = type.makeShared();
|
||||
t = t.merge();
|
||||
t.fixTo(type);
|
||||
//printf("\t%p\n", t);
|
||||
return t;
|
||||
}
|
||||
|
||||
extern(C++) Type sharedConstOf(Type type)
|
||||
{
|
||||
//printf("Type::sharedConstOf() %p, %s\n", type, type.toChars());
|
||||
if (type.mod == (MODFlags.shared_ | MODFlags.const_))
|
||||
return type;
|
||||
if (type.mcache && type.mcache.scto)
|
||||
{
|
||||
assert(type.mcache.scto.mod == (MODFlags.shared_ | MODFlags.const_));
|
||||
return type.mcache.scto;
|
||||
}
|
||||
Type t = type.makeSharedConst();
|
||||
t = t.merge();
|
||||
t.fixTo(type);
|
||||
//printf("\t%p\n", t);
|
||||
return t;
|
||||
}
|
||||
|
||||
/********************************
|
||||
* Make type unshared.
|
||||
* 0 => 0
|
||||
* const => const
|
||||
* immutable => immutable
|
||||
* shared => 0
|
||||
* shared const => const
|
||||
* wild => wild
|
||||
* wild const => wild const
|
||||
* shared wild => wild
|
||||
* shared wild const => wild const
|
||||
*/
|
||||
extern(C++) Type unSharedOf(Type type)
|
||||
{
|
||||
//printf("Type::unSharedOf() %p, %s\n", type, type.toChars());
|
||||
Type t = type;
|
||||
|
||||
if (type.isShared())
|
||||
{
|
||||
type.getMcache();
|
||||
if (type.isWild())
|
||||
{
|
||||
if (type.isConst())
|
||||
t = type.mcache.wcto; // shared wild const => wild const
|
||||
else
|
||||
t = type.mcache.wto; // shared wild => wild
|
||||
}
|
||||
else
|
||||
{
|
||||
if (type.isConst())
|
||||
t = type.mcache.cto; // shared const => const
|
||||
else
|
||||
t = type.mcache.sto; // shared => naked
|
||||
}
|
||||
assert(!t || !t.isShared());
|
||||
}
|
||||
|
||||
if (!t)
|
||||
{
|
||||
t = type.nullAttributes();
|
||||
t.mod = type.mod & ~MODFlags.shared_;
|
||||
t.ctype = type.ctype;
|
||||
t = t.merge();
|
||||
t.fixTo(type);
|
||||
}
|
||||
else
|
||||
t = t.merge();
|
||||
assert(!t.isShared());
|
||||
return t;
|
||||
}
|
||||
|
||||
/********************************
|
||||
* Convert to 'wild'.
|
||||
*/
|
||||
extern(C++) Type wildOf(Type type)
|
||||
{
|
||||
//printf("Type::wildOf() %p %s\n", type, type.toChars());
|
||||
if (type.mod == MODFlags.wild)
|
||||
return type;
|
||||
if (type.mcache && type.mcache.wto)
|
||||
{
|
||||
assert(type.mcache.wto.mod == MODFlags.wild);
|
||||
return type.mcache.wto;
|
||||
}
|
||||
Type t = type.makeWild();
|
||||
t = t.merge();
|
||||
t.fixTo(type);
|
||||
//printf("\t%p %s\n", t, t.toChars());
|
||||
return t;
|
||||
}
|
||||
|
||||
extern(C++) Type wildConstOf(Type type)
|
||||
{
|
||||
//printf("Type::wildConstOf() %p %s\n", type, type.toChars());
|
||||
if (type.mod == MODFlags.wildconst)
|
||||
return type;
|
||||
if (type.mcache && type.mcache.wcto)
|
||||
{
|
||||
assert(type.mcache.wcto.mod == MODFlags.wildconst);
|
||||
return type.mcache.wcto;
|
||||
}
|
||||
Type t = type.makeWildConst();
|
||||
t = t.merge();
|
||||
t.fixTo(type);
|
||||
//printf("\t%p %s\n", t, t.toChars());
|
||||
return t;
|
||||
}
|
||||
|
||||
extern(C++) Type sharedWildOf(Type type)
|
||||
{
|
||||
//printf("Type::sharedWildOf() %p, %s\n", type, type.toChars());
|
||||
if (type.mod == (MODFlags.shared_ | MODFlags.wild))
|
||||
return type;
|
||||
if (type.mcache && type.mcache.swto)
|
||||
{
|
||||
assert(type.mcache.swto.mod == (MODFlags.shared_ | MODFlags.wild));
|
||||
return type.mcache.swto;
|
||||
}
|
||||
Type t = type.makeSharedWild();
|
||||
t = t.merge();
|
||||
t.fixTo(type);
|
||||
//printf("\t%p %s\n", t, t.toChars());
|
||||
return t;
|
||||
}
|
||||
|
||||
extern(C++) Type sharedWildConstOf(Type type)
|
||||
{
|
||||
//printf("Type::sharedWildConstOf() %p, %s\n", type, type.toChars());
|
||||
if (type.mod == (MODFlags.shared_ | MODFlags.wildconst))
|
||||
return type;
|
||||
if (type.mcache && type.mcache.swcto)
|
||||
{
|
||||
assert(type.mcache.swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
|
||||
return type.mcache.swcto;
|
||||
}
|
||||
Type t = type.makeSharedWildConst();
|
||||
t = t.merge();
|
||||
t.fixTo(type);
|
||||
//printf("\t%p %s\n", t, t.toChars());
|
||||
return t;
|
||||
}
|
||||
|
||||
/************************************
|
||||
* Apply MODxxxx bits to existing type.
|
||||
*/
|
||||
extern(C++) Type castMod(Type type, MOD mod)
|
||||
{
|
||||
Type t;
|
||||
switch (mod)
|
||||
{
|
||||
case 0:
|
||||
t = type.unSharedOf().mutableOf();
|
||||
break;
|
||||
|
||||
case MODFlags.const_:
|
||||
t = type.unSharedOf().constOf();
|
||||
break;
|
||||
|
||||
case MODFlags.wild:
|
||||
t = type.unSharedOf().wildOf();
|
||||
break;
|
||||
|
||||
case MODFlags.wildconst:
|
||||
t = type.unSharedOf().wildConstOf();
|
||||
break;
|
||||
|
||||
case MODFlags.shared_:
|
||||
t = type.mutableOf().sharedOf();
|
||||
break;
|
||||
|
||||
case MODFlags.shared_ | MODFlags.const_:
|
||||
t = type.sharedConstOf();
|
||||
break;
|
||||
|
||||
case MODFlags.shared_ | MODFlags.wild:
|
||||
t = type.sharedWildOf();
|
||||
break;
|
||||
|
||||
case MODFlags.shared_ | MODFlags.wildconst:
|
||||
t = type.sharedWildConstOf();
|
||||
break;
|
||||
|
||||
case MODFlags.immutable_:
|
||||
t = type.immutableOf();
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
/******************************* Private *****************************************/
|
||||
|
||||
private:
|
||||
|
|
|
@ -299,3 +299,44 @@ bool parseDigits(T)(ref T val, const(char)[] p, const T max = T.max)
|
|||
assert(i.parseDigits("420", 500) && i == 420);
|
||||
assert(!i.parseDigits("420", 400));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast a `ubyte[]` to an array of larger integers as if we are on a big endian architecture
|
||||
* Params:
|
||||
* data = array with big endian data
|
||||
* size = 1 for ubyte[], 2 for ushort[], 4 for uint[], 8 for ulong[]
|
||||
* Returns: copy of `data`, with bytes shuffled if compiled for `version(LittleEndian)`
|
||||
*/
|
||||
ubyte[] arrayCastBigEndian(const ubyte[] data, size_t size)
|
||||
{
|
||||
ubyte[] impl(T)()
|
||||
{
|
||||
auto result = new T[](data.length / T.sizeof);
|
||||
foreach (i; 0 .. result.length)
|
||||
{
|
||||
result[i] = 0;
|
||||
foreach (j; 0 .. T.sizeof)
|
||||
{
|
||||
result[i] |= T(data[i * T.sizeof + j]) << ((T.sizeof - 1 - j) * 8);
|
||||
}
|
||||
}
|
||||
return cast(ubyte[]) result;
|
||||
}
|
||||
switch (size)
|
||||
{
|
||||
case 1: return data.dup;
|
||||
case 2: return impl!ushort;
|
||||
case 4: return impl!uint;
|
||||
case 8: return impl!ulong;
|
||||
default: assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
ubyte[] data = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22];
|
||||
assert(cast(ulong[]) arrayCastBigEndian(data, 8) == [0xAABBCCDDEEFF1122]);
|
||||
assert(cast(uint[]) arrayCastBigEndian(data, 4) == [0xAABBCCDD, 0xEEFF1122]);
|
||||
assert(cast(ushort[]) arrayCastBigEndian(data, 2) == [0xAABB, 0xCCDD, 0xEEFF, 0x1122]);
|
||||
assert(cast(ubyte[]) arrayCastBigEndian(data, 1) == data);
|
||||
}
|
||||
|
|
|
@ -1178,7 +1178,7 @@ public:
|
|||
{
|
||||
libcall = LIBCALL_AAGETY;
|
||||
ptr = build_address (build_expr (e->e1));
|
||||
tinfo = build_typeinfo (e, tb1->unSharedOf ()->mutableOf ());
|
||||
tinfo = build_typeinfo (e, mutableOf (unSharedOf (tb1)));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2170,7 +2170,7 @@ public:
|
|||
{
|
||||
/* Generate a slice for non-zero initialized aggregates,
|
||||
otherwise create an empty array. */
|
||||
gcc_assert (e->type == Type::tvoid->arrayOf ()->constOf ());
|
||||
gcc_assert (e->type == constOf (Type::tvoid->arrayOf ()));
|
||||
|
||||
tree type = build_ctype (e->type);
|
||||
tree length = size_int (sd->dsym->structsize);
|
||||
|
@ -2709,17 +2709,16 @@ public:
|
|||
|
||||
void visit (AssocArrayLiteralExp *e) final override
|
||||
{
|
||||
if (e->lowering != NULL)
|
||||
if (this->constp_ && e->lowering != NULL)
|
||||
{
|
||||
/* When an associative array literal gets lowered, it's converted into a
|
||||
struct literal suitable for static initialization. */
|
||||
gcc_assert (this->constp_);
|
||||
this->result_ = build_expr (e->lowering, this->constp_, true);
|
||||
return ;
|
||||
}
|
||||
|
||||
/* Want the mutable type for typeinfo reference. */
|
||||
Type *tb = e->type->toBasetype ()->mutableOf ();
|
||||
Type *tb = mutableOf (e->type->toBasetype ());
|
||||
|
||||
/* Handle empty assoc array literals. */
|
||||
TypeAArray *ta = tb->isTypeAArray ();
|
||||
|
|
|
@ -150,11 +150,11 @@ get_libcall_type (d_libcall_type type)
|
|||
break;
|
||||
|
||||
case LCT_CONST_TYPEINFO:
|
||||
libcall_types[type] = Type::dtypeinfo->type->constOf ();
|
||||
libcall_types[type] = constOf (Type::dtypeinfo->type);
|
||||
break;
|
||||
|
||||
case LCT_CONST_CLASSINFO:
|
||||
libcall_types[type] = Type::typeinfoclass->type->constOf ();
|
||||
libcall_types[type] = constOf (Type::typeinfoclass->type);
|
||||
break;
|
||||
|
||||
case LCT_ARRAY_VOID:
|
||||
|
@ -202,7 +202,7 @@ get_libcall_type (d_libcall_type type)
|
|||
break;
|
||||
|
||||
case LCT_IMMUTABLE_CHARPTR:
|
||||
libcall_types[type] = Type::tchar->pointerTo ()->immutableOf ();
|
||||
libcall_types[type] = immutableOf (Type::tchar->pointerTo ());
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -577,7 +577,7 @@ public:
|
|||
|
||||
void visit (TypeInfoConstDeclaration *d) final override
|
||||
{
|
||||
Type *tm = d->tinfo->mutableOf ();
|
||||
Type *tm = mutableOf (d->tinfo);
|
||||
tm = merge2 (tm);
|
||||
|
||||
/* The vtable for TypeInfo_Const. */
|
||||
|
@ -594,7 +594,7 @@ public:
|
|||
|
||||
void visit (TypeInfoInvariantDeclaration *d) final override
|
||||
{
|
||||
Type *tm = d->tinfo->mutableOf ();
|
||||
Type *tm = mutableOf (d->tinfo);
|
||||
tm = merge2 (tm);
|
||||
|
||||
/* The vtable for TypeInfo_Invariant. */
|
||||
|
@ -611,7 +611,7 @@ public:
|
|||
|
||||
void visit (TypeInfoSharedDeclaration *d) final override
|
||||
{
|
||||
Type *tm = d->tinfo->unSharedOf ();
|
||||
Type *tm = unSharedOf (d->tinfo);
|
||||
tm = merge2 (tm);
|
||||
|
||||
/* The vtable for TypeInfo_Shared. */
|
||||
|
@ -628,7 +628,7 @@ public:
|
|||
|
||||
void visit (TypeInfoWildDeclaration *d) final override
|
||||
{
|
||||
Type *tm = d->tinfo->mutableOf ();
|
||||
Type *tm = mutableOf (d->tinfo);
|
||||
tm = merge2 (tm);
|
||||
|
||||
/* The vtable for TypeInfo_Inout. */
|
||||
|
|
|
@ -1329,7 +1329,7 @@ build_ctype (Type *t)
|
|||
t->accept (&v);
|
||||
else
|
||||
{
|
||||
Type *tb = t->castMod (0);
|
||||
Type *tb = castMod (t, 0);
|
||||
if (!tb->ctype)
|
||||
tb->accept (&v);
|
||||
t->ctype = insert_type_modifiers (tb->ctype, t->mod);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// PERMUTE_ARGS:
|
||||
// PERMUTE_ARGS:
|
||||
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
|
||||
// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// PERMUTE_ARGS:
|
||||
// PERMUTE_ARGS:
|
||||
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
|
||||
// EXTRA_FILES: ddoc5446a.d ddoc5446b.d
|
||||
// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// PERMUTE_ARGS:
|
||||
// PERMUTE_ARGS:
|
||||
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
|
||||
// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
|
||||
|
||||
|
|
10
gcc/testsuite/gdc.test/compilable/test24338.d
Normal file
10
gcc/testsuite/gdc.test/compilable/test24338.d
Normal file
|
@ -0,0 +1,10 @@
|
|||
// https://issues.dlang.org/show_bug.cgi?id=24338
|
||||
|
||||
enum Foo: char[4]
|
||||
{
|
||||
elem = "test"
|
||||
}
|
||||
|
||||
immutable a = [Foo.elem];
|
||||
immutable b = [Foo.elem];
|
||||
immutable c = a ~ b;
|
34
gcc/testsuite/gdc.test/fail_compilation/discard_value.d
Normal file
34
gcc/testsuite/gdc.test/fail_compilation/discard_value.d
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
TEST_OUTPUT:
|
||||
---
|
||||
fail_compilation/discard_value.d(24): Error: the result of the equality expression `3 is 3` is discarded
|
||||
fail_compilation/discard_value.d(25): Error: the result of the equality expression `null !is null` is discarded
|
||||
fail_compilation/discard_value.d(26): Error: the result of the equality expression `v == 0` is discarded
|
||||
fail_compilation/discard_value.d(27): Error: the result of the equality expression `v == 0` is discarded
|
||||
fail_compilation/discard_value.d(28): Error: `!__equals("", "")` has no effect
|
||||
fail_compilation/discard_value.d(29): Error: the result of the equality expression `"" == ""` is discarded
|
||||
fail_compilation/discard_value.d(30): Error: the result of the equality expression `fun().i == 4` is discarded
|
||||
fail_compilation/discard_value.d(30): note that `fun().i` may have a side effect
|
||||
fail_compilation/discard_value.d(33): Error: the result of the equality expression `slice == slice[0..0]` is discarded
|
||||
---
|
||||
*/
|
||||
|
||||
struct S { int i; }
|
||||
|
||||
S fun() { return S(42); }
|
||||
|
||||
int v;
|
||||
|
||||
void main()
|
||||
{
|
||||
3 is 3;
|
||||
null !is null;
|
||||
true && v == 0;
|
||||
true || v == 0;
|
||||
"" != "";
|
||||
"" == ""; // https://issues.dlang.org/show_bug.cgi?id=24359
|
||||
fun().i == 4; // https://issues.dlang.org/show_bug.cgi?id=12390
|
||||
|
||||
int[] slice = [0, 1];
|
||||
slice == slice[0 .. 0];
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
TEST_OUTPUT:
|
||||
---
|
||||
fail_compilation/fail12390.d(15): Error: the result of the equality expression `fun().i == 4` is discarded
|
||||
fail_compilation/fail12390.d(15): note that `fun().i` may have a side effect
|
||||
---
|
||||
*/
|
||||
|
||||
struct S { int i; }
|
||||
|
||||
S fun() { return S(42); }
|
||||
|
||||
void main()
|
||||
{
|
||||
fun().i == 4;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// REQUIRED_ARGS: -c -o-
|
||||
// REQUIRED_ARGS: -c -o-
|
||||
/*
|
||||
TEST_OUTPUT:
|
||||
---
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// REQUIRED_ARGS: -c -o-
|
||||
// REQUIRED_ARGS: -c -o-
|
||||
/*
|
||||
TEST_OUTPUT:
|
||||
---
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// REQUIRED_ARGS: -c -o-
|
||||
// REQUIRED_ARGS: -c -o-
|
||||
/*
|
||||
TEST_OUTPUT:
|
||||
---
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// REQUIRED_ARGS: -c -o-
|
||||
// REQUIRED_ARGS: -c -o-
|
||||
/*
|
||||
TEST_OUTPUT:
|
||||
---
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// REQUIRED_ARGS: -c -o-
|
||||
// REQUIRED_ARGS: -c -o-
|
||||
/*
|
||||
TEST_OUTPUT:
|
||||
---
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// REQUIRED_ARGS: -c -o-
|
||||
// REQUIRED_ARGS: -c -o-
|
||||
/*
|
||||
TEST_OUTPUT:
|
||||
---
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// REQUIRED_ARGS: -c -o-
|
||||
// REQUIRED_ARGS: -c -o-
|
||||
/*
|
||||
TEST_OUTPUT:
|
||||
---
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
TEST_OUTPUT:
|
||||
---
|
||||
fail_compilation/hexstring.d(29): Error: cannot implicitly convert expression `"123F"` of type `string` to `immutable(ubyte[])`
|
||||
fail_compilation/hexstring.d(30): Error: cannot implicitly convert expression `x"123F"c` of type `string` to `immutable(ubyte[])`
|
||||
fail_compilation/hexstring.d(31): Error: cannot implicitly convert expression `x"123F"` of type `string` to `immutable(ubyte[])`
|
||||
fail_compilation/hexstring.d(33): Error: hex string length 1 must be a multiple of 2 to cast to `immutable(ushort[])`
|
||||
fail_compilation/hexstring.d(34): Error: hex string length 3 must be a multiple of 4 to cast to `immutable(uint[])`
|
||||
fail_compilation/hexstring.d(35): Error: hex string length 5 must be a multiple of 8 to cast to `immutable(ulong[])`
|
||||
|
@ -13,6 +11,8 @@ fail_compilation/hexstring.d(37): Error: array cast from `string` to `immutable(
|
|||
fail_compilation/hexstring.d(38): Error: array cast from `string` to `immutable(ushort[])` is not supported at compile time
|
||||
fail_compilation/hexstring.d(39): Error: array cast from `string` to `immutable(uint[])` is not supported at compile time
|
||||
fail_compilation/hexstring.d(39): perhaps remove postfix `c` from hex string
|
||||
fail_compilation/hexstring.d(40): Error: hex string with `dstring` type needs to be multiple of 4 bytes, not 5
|
||||
fail_compilation/hexstring.d(41): Error: cannot implicitly convert expression `x"44332211"d` of type `dstring` to `immutable(float[])`
|
||||
fail_compilation/hexstring.d(28): Error: cannot implicitly convert expression `x"123F"` of type `string` to `ubyte[]`
|
||||
---
|
||||
*/
|
||||
|
@ -33,7 +33,9 @@ immutable ubyte[] f4 = cast(string) x"123F";
|
|||
immutable ushort[] f5 = cast(immutable ushort[]) x"11";
|
||||
immutable uint[] f6 = cast(immutable uint[]) x"112233";
|
||||
immutable ulong[] f7 = cast(immutable ulong[]) x"1122334455";
|
||||
immutable ulong[] f8 = cast(immutable ulong[]) x"1122334455"w;
|
||||
immutable ulong[] f8 = cast(immutable ulong[]) x"11223344"w;
|
||||
immutable uint[] f9 = cast(immutable uint[]) "ABCD";
|
||||
immutable ushort[] f10 = cast(immutable ushort[]) (x"1122" ~ "");
|
||||
immutable uint[] f11 = cast(immutable uint[]) x"AABBCCDD"c;
|
||||
immutable uint[] f12 = x"1122334455"d;
|
||||
immutable float[] f13 = x"11223344"d;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// https://issues.dlang.org/show_bug.cgi?id=10599
|
||||
// https://issues.dlang.org/show_bug.cgi?id=10599
|
||||
// ICE(interpret.c)
|
||||
/*
|
||||
TEST_OUTPUT:
|
||||
|
|
20
gcc/testsuite/gdc.test/fail_compilation/test24365.d
Normal file
20
gcc/testsuite/gdc.test/fail_compilation/test24365.d
Normal file
|
@ -0,0 +1,20 @@
|
|||
// https://issues.dlang.org/show_bug.cgi?id=243645
|
||||
|
||||
/*
|
||||
TEST_OUTPUT:
|
||||
---
|
||||
fail_compilation/test24365.d(16): Error: `f` cannot be interpreted at compile time, because it has no available source code
|
||||
fail_compilation/test24365.d(14): compile time context created here
|
||||
fail_compilation/test24365.d(19): while evaluating: `static assert(r == 2)`
|
||||
---
|
||||
*/
|
||||
|
||||
void main()
|
||||
{
|
||||
enum r = () {
|
||||
void f();
|
||||
f();
|
||||
return 2;
|
||||
}();
|
||||
static assert(r == 2);
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
PERMUTE_ARGS:
|
||||
RUN_OUTPUT:
|
||||
---
|
||||
hello world
|
||||
---
|
||||
*/
|
||||
|
||||
extern(C) int printf(const char *, ...);
|
||||
|
||||
int main(char[][] args)
|
||||
{
|
||||
printf("hello world\n");
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -260,6 +260,19 @@ void testHexstring()
|
|||
// Test printing StringExp with size 8
|
||||
enum toStr(immutable ulong[] v) = v.stringof;
|
||||
static assert(toStr!y == `x"88776655443322119900FFEEDDCCBBAA"`);
|
||||
|
||||
// Hex string postfixes
|
||||
// https://issues.dlang.org/show_bug.cgi?id=24363
|
||||
wstring wStr = x"AA BB CC DD"w;
|
||||
immutable int[] dStr = x"AA BB CC DD"d;
|
||||
assert(wStr[0] == 0xAABB);
|
||||
assert(wStr[1] == 0xCCDD);
|
||||
assert(dStr[0] == 0xAABBCCDD);
|
||||
|
||||
// Test sliceCmpStringWithArray with size 8
|
||||
static immutable ulong[] z0 = cast(immutable ulong[]) x"1111 1111 1111 1111 0000 000F 0000 0000";
|
||||
static immutable ulong[] z1 = [0x1111_1111_1111_1111, 0x0000_000E_0000_0000];
|
||||
static assert(z0 !is z1);
|
||||
}
|
||||
|
||||
/***************************************************/
|
||||
|
|
|
@ -154,6 +154,17 @@ void testLocalStatic() @trusted
|
|||
|
||||
/////////////////////////////////////////////
|
||||
|
||||
// https://issues.dlang.org/show_bug.cgi?id=24311
|
||||
enum E : int[int] { x = [123: 456] }
|
||||
|
||||
void testEnumInit()
|
||||
{
|
||||
E e = E.init;
|
||||
assert(e[123] == 456);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////
|
||||
|
||||
void main()
|
||||
{
|
||||
testSimple();
|
||||
|
@ -163,4 +174,5 @@ void main()
|
|||
testClassInit();
|
||||
testImmutable();
|
||||
testLocalStatic();
|
||||
testEnumInit();
|
||||
}
|
||||
|
|
|
@ -155,6 +155,19 @@ class C7379
|
|||
}
|
||||
}
|
||||
|
||||
/***********************************/
|
||||
// https://issues.dlang.org/show_bug.cgi?id=23515
|
||||
|
||||
enum Easing : void function()
|
||||
{
|
||||
identity1 = (){},
|
||||
}
|
||||
|
||||
void test23515()
|
||||
{
|
||||
Easing.identity1();
|
||||
}
|
||||
|
||||
/***********************************/
|
||||
|
||||
int main()
|
||||
|
@ -166,6 +179,7 @@ int main()
|
|||
test5();
|
||||
test6();
|
||||
test7();
|
||||
test23515();
|
||||
|
||||
printf("Success\n");
|
||||
return 0;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
e7709452775d374c1e2dfb67566668ada3dec5fc
|
||||
a6f10836997d0b5526c8c363d781b4029c77f09f
|
||||
|
||||
The first line of this file holds the git revision number of the last
|
||||
merge done from the dlang/dmd repository.
|
||||
|
|
|
@ -88,14 +88,7 @@ enum MemoryOrder
|
|||
T atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)(auto ref return scope const T val) pure nothrow @nogc @trusted
|
||||
if (!is(T == shared U, U) && !is(T == shared inout U, U) && !is(T == shared const U, U))
|
||||
{
|
||||
static if (__traits(isFloating, T))
|
||||
{
|
||||
alias IntTy = IntForFloat!T;
|
||||
IntTy r = core.internal.atomic.atomicLoad!ms(cast(IntTy*)&val);
|
||||
return *cast(T*)&r;
|
||||
}
|
||||
else
|
||||
return core.internal.atomic.atomicLoad!ms(cast(T*)&val);
|
||||
return core.internal.atomic.atomicLoad!ms(cast(T*)&val);
|
||||
}
|
||||
|
||||
/// Ditto
|
||||
|
@ -137,13 +130,7 @@ void atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref T val, V newval) pu
|
|||
// resolve implicit conversions
|
||||
T arg = newval;
|
||||
|
||||
static if (__traits(isFloating, T))
|
||||
{
|
||||
alias IntTy = IntForFloat!T;
|
||||
core.internal.atomic.atomicStore!ms(cast(IntTy*)&val, *cast(IntTy*)&arg);
|
||||
}
|
||||
else
|
||||
core.internal.atomic.atomicStore!ms(&val, arg);
|
||||
core.internal.atomic.atomicStore!ms(&val, arg);
|
||||
}
|
||||
|
||||
/// Ditto
|
||||
|
@ -246,14 +233,7 @@ in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned")
|
|||
// resolve implicit conversions
|
||||
T arg = exchangeWith;
|
||||
|
||||
static if (__traits(isFloating, T))
|
||||
{
|
||||
alias IntTy = IntForFloat!T;
|
||||
IntTy r = core.internal.atomic.atomicExchange!ms(cast(IntTy*)here, *cast(IntTy*)&arg);
|
||||
return *cast(shared(T)*)&r;
|
||||
}
|
||||
else
|
||||
return core.internal.atomic.atomicExchange!ms(here, arg);
|
||||
return core.internal.atomic.atomicExchange!ms(here, arg);
|
||||
}
|
||||
|
||||
/// Ditto
|
||||
|
@ -314,14 +294,7 @@ template cas(MemoryOrder succ = MemoryOrder.seq, MemoryOrder fail = MemoryOrder.
|
|||
const T arg1 = ifThis;
|
||||
T arg2 = writeThis;
|
||||
|
||||
static if (__traits(isFloating, T))
|
||||
{
|
||||
alias IntTy = IntForFloat!T;
|
||||
return atomicCompareExchangeStrongNoResult!(succ, fail)(
|
||||
cast(IntTy*)here, *cast(IntTy*)&arg1, *cast(IntTy*)&arg2);
|
||||
}
|
||||
else
|
||||
return atomicCompareExchangeStrongNoResult!(succ, fail)(here, arg1, arg2);
|
||||
return atomicCompareExchangeStrongNoResult!(succ, fail)(here, arg1, arg2);
|
||||
}
|
||||
|
||||
/// Compare-and-set for shared value type
|
||||
|
@ -364,14 +337,7 @@ template cas(MemoryOrder succ = MemoryOrder.seq, MemoryOrder fail = MemoryOrder.
|
|||
// resolve implicit conversions
|
||||
T arg1 = writeThis;
|
||||
|
||||
static if (__traits(isFloating, T))
|
||||
{
|
||||
alias IntTy = IntForFloat!T;
|
||||
return atomicCompareExchangeStrong!(succ, fail)(
|
||||
cast(IntTy*)here, cast(IntTy*)ifThis, *cast(IntTy*)&writeThis);
|
||||
}
|
||||
else
|
||||
return atomicCompareExchangeStrong!(succ, fail)(here, ifThis, writeThis);
|
||||
return atomicCompareExchangeStrong!(succ, fail)(here, ifThis, writeThis);
|
||||
}
|
||||
|
||||
/// Compare and exchange for mixed-`shared`ness types
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -54,7 +54,17 @@ version (DigitalMars)
|
|||
inout(T) atomicLoad(MemoryOrder order = MemoryOrder.seq, T)(inout(T)* src) pure nothrow @nogc @trusted
|
||||
if (CanCAS!T)
|
||||
{
|
||||
static assert(order != MemoryOrder.rel, "invalid MemoryOrder for atomicLoad()");
|
||||
static assert(order != MemoryOrder.rel && order != MemoryOrder.acq_rel,
|
||||
"invalid MemoryOrder for atomicLoad()");
|
||||
|
||||
// We place some storage on the stack,
|
||||
// get a pointer to that (which is also stored on the stack)
|
||||
// and then store the result of the load into the storage.
|
||||
// Finally returning it.
|
||||
// Anything other than this is calling convention specific,
|
||||
// and that is very fail heavy.
|
||||
size_t[2] storage = void;
|
||||
size_t* resultValuePtr = cast(size_t*)&storage[0];
|
||||
|
||||
static if (T.sizeof == size_t.sizeof * 2)
|
||||
{
|
||||
|
@ -62,107 +72,87 @@ version (DigitalMars)
|
|||
{
|
||||
asm pure nothrow @nogc @trusted
|
||||
{
|
||||
push EBX; // call preserved
|
||||
push EDI;
|
||||
push EBX;
|
||||
|
||||
mov EBX, 0;
|
||||
mov ECX, 0;
|
||||
mov EAX, 0;
|
||||
mov EDX, 0;
|
||||
|
||||
mov EDI, src;
|
||||
lock; cmpxchg8b [EDI];
|
||||
pop EBX;
|
||||
|
||||
lea EBX, resultValuePtr;
|
||||
mov EBX, [EBX];
|
||||
mov [EBX], EAX;
|
||||
mov [EBX + size_t.sizeof], EDX;
|
||||
|
||||
pop EDI;
|
||||
pop EBX;
|
||||
}
|
||||
}
|
||||
else version (D_InlineAsm_X86_64)
|
||||
{
|
||||
version (Windows)
|
||||
asm pure nothrow @nogc @trusted
|
||||
{
|
||||
static if (RegisterReturn!T)
|
||||
{
|
||||
enum SrcPtr = SizedReg!CX;
|
||||
enum RetPtr = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
enum SrcPtr = SizedReg!DX;
|
||||
enum RetPtr = SizedReg!CX;
|
||||
}
|
||||
push RBX; // call preserved
|
||||
|
||||
mixin (simpleFormat(q{
|
||||
asm pure nothrow @nogc @trusted
|
||||
{
|
||||
naked;
|
||||
push RBX;
|
||||
mov R8, %0;
|
||||
?1 mov R9, %1;
|
||||
mov RBX, 0;
|
||||
mov RCX, 0;
|
||||
mov RAX, 0;
|
||||
mov RDX, 0;
|
||||
lock; cmpxchg16b [R8];
|
||||
?1 mov [R9], RAX;
|
||||
?1 mov 8[R9], RDX;
|
||||
pop RBX;
|
||||
ret;
|
||||
}
|
||||
}, [SrcPtr, RetPtr]));
|
||||
}
|
||||
else
|
||||
{
|
||||
asm pure nothrow @nogc @trusted
|
||||
{
|
||||
naked;
|
||||
push RBX;
|
||||
mov RBX, 0;
|
||||
mov RCX, 0;
|
||||
mov RAX, 0;
|
||||
mov RDX, 0;
|
||||
lock; cmpxchg16b [RDI];
|
||||
pop RBX;
|
||||
ret;
|
||||
}
|
||||
mov RBX, 0;
|
||||
mov RCX, 0;
|
||||
mov RAX, 0;
|
||||
mov RDX, 0;
|
||||
|
||||
mov R8, src;
|
||||
lock; cmpxchg16b [R8];
|
||||
|
||||
lea RBX, resultValuePtr;
|
||||
mov RBX, [RBX];
|
||||
mov [RBX], RAX;
|
||||
mov [RBX + size_t.sizeof], RDX;
|
||||
|
||||
pop RBX;
|
||||
}
|
||||
}
|
||||
else
|
||||
static assert(0, "Operation not supported");
|
||||
|
||||
return *cast(inout(T)*)resultValuePtr;
|
||||
}
|
||||
else static if (needsLoadBarrier!order)
|
||||
{
|
||||
version (D_InlineAsm_X86)
|
||||
{
|
||||
enum SrcReg = SizedReg!CX;
|
||||
enum ZeroReg = SizedReg!(DX, T);
|
||||
enum ResReg = SizedReg!(AX, T);
|
||||
|
||||
mixin (simpleFormat(q{
|
||||
asm pure nothrow @nogc @trusted
|
||||
{
|
||||
mov %1, 0;
|
||||
mov %2, 0;
|
||||
mov %0, src;
|
||||
lock; cmpxchg [%0], %1;
|
||||
}
|
||||
}, [SrcReg, ZeroReg, ResReg]));
|
||||
}
|
||||
else version (D_InlineAsm_X86_64)
|
||||
{
|
||||
version (Windows)
|
||||
enum SrcReg = SizedReg!CX;
|
||||
else
|
||||
enum SrcReg = SizedReg!DI;
|
||||
enum ZeroReg = SizedReg!(DX, T);
|
||||
enum ResReg = SizedReg!(AX, T);
|
||||
|
||||
mixin (simpleFormat(q{
|
||||
asm pure nothrow @nogc @trusted
|
||||
{
|
||||
naked;
|
||||
mov %1, 0;
|
||||
mov %2, 0;
|
||||
lock; cmpxchg [%0], %1;
|
||||
ret;
|
||||
}
|
||||
}, [SrcReg, ZeroReg, ResReg]));
|
||||
}
|
||||
else
|
||||
static assert(0, "Operation not supported");
|
||||
|
||||
enum SrcReg = SizedReg!CX;
|
||||
enum ZeroReg = SizedReg!(DX, T);
|
||||
enum ResReg = SizedReg!(AX, T);
|
||||
enum TemporaryReg = SizedReg!(BX);
|
||||
|
||||
mixin (simpleFormat(q{
|
||||
asm pure nothrow @nogc @trusted
|
||||
{
|
||||
push %3; // call preserved
|
||||
|
||||
mov %1, 0;
|
||||
mov %2, 0;
|
||||
mov %0, src;
|
||||
lock; cmpxchg [%0], %1;
|
||||
lea %3, resultValuePtr;
|
||||
mov %3, [%3];
|
||||
mov [%3], %2;
|
||||
|
||||
pop %3;
|
||||
}
|
||||
}, [SrcReg, ZeroReg, ResReg, TemporaryReg]));
|
||||
|
||||
return *cast(inout(T)*)resultValuePtr;
|
||||
}
|
||||
else
|
||||
return *src;
|
||||
|
@ -171,7 +161,8 @@ version (DigitalMars)
|
|||
void atomicStore(MemoryOrder order = MemoryOrder.seq, T)(T* dest, T value) pure nothrow @nogc @trusted
|
||||
if (CanCAS!T)
|
||||
{
|
||||
static assert(order != MemoryOrder.acq, "Invalid MemoryOrder for atomicStore()");
|
||||
static assert(order != MemoryOrder.acq && order != MemoryOrder.acq_rel,
|
||||
"Invalid MemoryOrder for atomicStore()");
|
||||
|
||||
static if (T.sizeof == size_t.sizeof * 2)
|
||||
{
|
||||
|
@ -293,48 +284,38 @@ version (DigitalMars)
|
|||
T atomicExchange(MemoryOrder order = MemoryOrder.seq, bool result = true, T)(T* dest, T value) pure nothrow @nogc @trusted
|
||||
if (CanCAS!T)
|
||||
{
|
||||
static assert(order != MemoryOrder.acq, "Invalid MemoryOrder for atomicExchange()");
|
||||
// We place some storage on the stack,
|
||||
// this storage and cast it to appropriete type.
|
||||
// This is calling convention agnostic.
|
||||
size_t storage = void;
|
||||
|
||||
version (D_InlineAsm_X86)
|
||||
{
|
||||
static assert(T.sizeof <= 4, "64bit atomicExchange not supported on 32bit target." );
|
||||
|
||||
enum DestReg = SizedReg!CX;
|
||||
enum ValReg = SizedReg!(AX, T);
|
||||
|
||||
mixin (simpleFormat(q{
|
||||
asm pure nothrow @nogc @trusted
|
||||
{
|
||||
mov %1, value;
|
||||
mov %0, dest;
|
||||
xchg [%0], %1;
|
||||
}
|
||||
}, [DestReg, ValReg]));
|
||||
static assert(T.sizeof <= 4, "64bit atomicExchange not supported on 32bit target.");
|
||||
}
|
||||
else version (D_InlineAsm_X86_64)
|
||||
{
|
||||
version (Windows)
|
||||
{
|
||||
enum DestReg = SizedReg!DX;
|
||||
enum ValReg = SizedReg!(CX, T);
|
||||
}
|
||||
else
|
||||
{
|
||||
enum DestReg = SizedReg!SI;
|
||||
enum ValReg = SizedReg!(DI, T);
|
||||
}
|
||||
enum ResReg = result ? SizedReg!(AX, T) : null;
|
||||
|
||||
mixin (simpleFormat(q{
|
||||
asm pure nothrow @nogc @trusted
|
||||
{
|
||||
naked;
|
||||
xchg [%0], %1;
|
||||
?2 mov %2, %1;
|
||||
ret;
|
||||
}
|
||||
}, [DestReg, ValReg, ResReg]));
|
||||
}
|
||||
else
|
||||
static assert (false, "Unsupported architecture.");
|
||||
static assert(0, "Operation not supported");
|
||||
|
||||
enum DestReg = SizedReg!CX;
|
||||
enum ValReg = SizedReg!(AX, T);
|
||||
|
||||
mixin (simpleFormat(q{
|
||||
asm pure nothrow @nogc @trusted
|
||||
{
|
||||
mov %1, value;
|
||||
mov %0, dest;
|
||||
lock; xchg [%0], %1;
|
||||
|
||||
lea %0, storage;
|
||||
mov [%0], %1;
|
||||
}
|
||||
}, [DestReg, ValReg]));
|
||||
|
||||
return *cast(T*)&storage;
|
||||
}
|
||||
|
||||
alias atomicCompareExchangeWeak = atomicCompareExchangeStrong;
|
||||
|
@ -342,142 +323,102 @@ version (DigitalMars)
|
|||
bool atomicCompareExchangeStrong(MemoryOrder succ = MemoryOrder.seq, MemoryOrder fail = MemoryOrder.seq, T)(T* dest, T* compare, T value) pure nothrow @nogc @trusted
|
||||
if (CanCAS!T)
|
||||
{
|
||||
version (D_InlineAsm_X86)
|
||||
{
|
||||
static if (T.sizeof <= 4)
|
||||
{
|
||||
enum DestAddr = SizedReg!CX;
|
||||
enum CmpAddr = SizedReg!DI;
|
||||
enum Val = SizedReg!(DX, T);
|
||||
enum Cmp = SizedReg!(AX, T);
|
||||
static assert(fail != MemoryOrder.rel && fail != MemoryOrder.acq_rel,
|
||||
"Invalid fail MemoryOrder for atomicCompareExchangeStrong()");
|
||||
static assert (succ >= fail, "The first MemoryOrder argument for atomicCompareExchangeStrong() cannot be weaker than the second argument");
|
||||
bool success;
|
||||
|
||||
mixin (simpleFormat(q{
|
||||
asm pure nothrow @nogc @trusted
|
||||
{
|
||||
push %1;
|
||||
mov %2, value;
|
||||
mov %1, compare;
|
||||
mov %3, [%1];
|
||||
mov %0, dest;
|
||||
lock; cmpxchg [%0], %2;
|
||||
mov [%1], %3;
|
||||
setz AL;
|
||||
pop %1;
|
||||
}
|
||||
}, [DestAddr, CmpAddr, Val, Cmp]));
|
||||
}
|
||||
else static if (T.sizeof == 8)
|
||||
static if (T.sizeof == size_t.sizeof * 2)
|
||||
{
|
||||
// some values simply cannot be loa'd here, so we'll use an intermediary pointer that we can move instead
|
||||
T* valuePointer = &value;
|
||||
|
||||
version (D_InlineAsm_X86)
|
||||
{
|
||||
asm pure nothrow @nogc @trusted
|
||||
{
|
||||
push EBX; // call preserved
|
||||
push EDI;
|
||||
push EBX;
|
||||
lea EDI, value;
|
||||
|
||||
mov EDI, valuePointer; // value
|
||||
mov EBX, [EDI];
|
||||
mov ECX, 4[EDI];
|
||||
mov EDI, compare;
|
||||
mov ECX, [EDI + size_t.sizeof];
|
||||
mov EDI, compare; // [compare]
|
||||
mov EAX, [EDI];
|
||||
mov EDX, 4[EDI];
|
||||
mov EDX, [EDI + size_t.sizeof];
|
||||
|
||||
mov EDI, dest;
|
||||
lock; cmpxchg8b [EDI];
|
||||
|
||||
setz success;
|
||||
mov EDI, compare;
|
||||
mov [EDI], EAX;
|
||||
mov 4[EDI], EDX;
|
||||
setz AL;
|
||||
pop EBX;
|
||||
pop EDI;
|
||||
}
|
||||
}
|
||||
else
|
||||
static assert(T.sizeof <= 8, "128bit atomicCompareExchangeStrong not supported on 32bit target." );
|
||||
}
|
||||
else version (D_InlineAsm_X86_64)
|
||||
{
|
||||
static if (T.sizeof <= 8)
|
||||
{
|
||||
version (Windows)
|
||||
{
|
||||
enum DestAddr = SizedReg!R8;
|
||||
enum CmpAddr = SizedReg!DX;
|
||||
enum Val = SizedReg!(CX, T);
|
||||
}
|
||||
else
|
||||
{
|
||||
enum DestAddr = SizedReg!DX;
|
||||
enum CmpAddr = SizedReg!SI;
|
||||
enum Val = SizedReg!(DI, T);
|
||||
}
|
||||
enum Res = SizedReg!(AX, T);
|
||||
mov [EDI + size_t.sizeof], EDX;
|
||||
|
||||
mixin (simpleFormat(q{
|
||||
asm pure nothrow @nogc @trusted
|
||||
{
|
||||
naked;
|
||||
mov %3, [%1];
|
||||
lock; cmpxchg [%0], %2;
|
||||
jne compare_fail;
|
||||
mov AL, 1;
|
||||
ret;
|
||||
compare_fail:
|
||||
mov [%1], %3;
|
||||
xor AL, AL;
|
||||
ret;
|
||||
}
|
||||
}, [DestAddr, CmpAddr, Val, Res]));
|
||||
pop EDI;
|
||||
pop EBX;
|
||||
}
|
||||
}
|
||||
else version (D_InlineAsm_X86_64)
|
||||
{
|
||||
asm pure nothrow @nogc @trusted
|
||||
{
|
||||
push RBX; // call preserved
|
||||
|
||||
mov R8, valuePointer; // value
|
||||
mov RBX, [R8];
|
||||
mov RCX, [R8 + size_t.sizeof];
|
||||
mov R8, compare; // [compare]
|
||||
mov RAX, [R8];
|
||||
mov RDX, [R8 + size_t.sizeof];
|
||||
|
||||
mov R8, dest;
|
||||
lock; cmpxchg16b [R8];
|
||||
|
||||
setz success;
|
||||
mov R8, compare;
|
||||
mov [R8], RAX;
|
||||
mov [R8 + size_t.sizeof], RDX;
|
||||
|
||||
pop RBX;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
version (Windows)
|
||||
{
|
||||
asm pure nothrow @nogc @trusted
|
||||
{
|
||||
naked;
|
||||
push RBX;
|
||||
mov R9, RDX;
|
||||
mov RAX, [RDX];
|
||||
mov RDX, 8[RDX];
|
||||
mov RBX, [RCX];
|
||||
mov RCX, 8[RCX];
|
||||
lock; cmpxchg16b [R8];
|
||||
pop RBX;
|
||||
jne compare_fail;
|
||||
mov AL, 1;
|
||||
ret;
|
||||
compare_fail:
|
||||
mov [R9], RAX;
|
||||
mov 8[R9], RDX;
|
||||
xor AL, AL;
|
||||
ret;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
asm pure nothrow @nogc @trusted
|
||||
{
|
||||
naked;
|
||||
push RBX;
|
||||
mov R8, RCX;
|
||||
mov R9, RDX;
|
||||
mov RAX, [RDX];
|
||||
mov RDX, 8[RDX];
|
||||
mov RBX, RDI;
|
||||
mov RCX, RSI;
|
||||
lock; cmpxchg16b [R8];
|
||||
pop RBX;
|
||||
jne compare_fail;
|
||||
mov AL, 1;
|
||||
ret;
|
||||
compare_fail:
|
||||
mov [R9], RAX;
|
||||
mov 8[R9], RDX;
|
||||
xor AL, AL;
|
||||
ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
static assert(0, "Operation not supported");
|
||||
}
|
||||
else
|
||||
static assert (false, "Unsupported architecture.");
|
||||
{
|
||||
version (D_InlineAsm_X86)
|
||||
{
|
||||
}
|
||||
else version (D_InlineAsm_X86_64)
|
||||
{
|
||||
}
|
||||
else
|
||||
static assert(0, "Operation not supported");
|
||||
|
||||
enum SrcReg = SizedReg!CX;
|
||||
enum ValueReg = SizedReg!(DX, T);
|
||||
enum CompareReg = SizedReg!(AX, T);
|
||||
|
||||
mixin (simpleFormat(q{
|
||||
asm pure nothrow @nogc @trusted
|
||||
{
|
||||
mov %1, value;
|
||||
mov %0, compare;
|
||||
mov %2, [%0];
|
||||
|
||||
mov %0, dest;
|
||||
lock; cmpxchg [%0], %1;
|
||||
|
||||
setz success;
|
||||
mov %0, compare;
|
||||
mov [%0], %2;
|
||||
}
|
||||
}, [SrcReg, ValueReg, CompareReg]));
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
alias atomicCompareExchangeWeakNoResult = atomicCompareExchangeStrongNoResult;
|
||||
|
@ -485,114 +426,94 @@ version (DigitalMars)
|
|||
bool atomicCompareExchangeStrongNoResult(MemoryOrder succ = MemoryOrder.seq, MemoryOrder fail = MemoryOrder.seq, T)(T* dest, const T compare, T value) pure nothrow @nogc @trusted
|
||||
if (CanCAS!T)
|
||||
{
|
||||
version (D_InlineAsm_X86)
|
||||
{
|
||||
static if (T.sizeof <= 4)
|
||||
{
|
||||
enum DestAddr = SizedReg!CX;
|
||||
enum Cmp = SizedReg!(AX, T);
|
||||
enum Val = SizedReg!(DX, T);
|
||||
static assert(fail != MemoryOrder.rel && fail != MemoryOrder.acq_rel,
|
||||
"Invalid fail MemoryOrder for atomicCompareExchangeStrongNoResult()");
|
||||
static assert (succ >= fail, "The first MemoryOrder argument for atomicCompareExchangeStrongNoResult() cannot be weaker than the second argument");
|
||||
bool success;
|
||||
|
||||
mixin (simpleFormat(q{
|
||||
asm pure nothrow @nogc @trusted
|
||||
{
|
||||
mov %2, value;
|
||||
mov %1, compare;
|
||||
mov %0, dest;
|
||||
lock; cmpxchg [%0], %2;
|
||||
setz AL;
|
||||
}
|
||||
}, [DestAddr, Cmp, Val]));
|
||||
}
|
||||
else static if (T.sizeof == 8)
|
||||
static if (T.sizeof == size_t.sizeof * 2)
|
||||
{
|
||||
// some values simply cannot be loa'd here, so we'll use an intermediary pointer that we can move instead
|
||||
T* valuePointer = &value;
|
||||
const(T)* comparePointer = &compare;
|
||||
|
||||
version (D_InlineAsm_X86)
|
||||
{
|
||||
asm pure nothrow @nogc @trusted
|
||||
{
|
||||
push EBX; // call preserved
|
||||
push EDI;
|
||||
push EBX;
|
||||
lea EDI, value;
|
||||
|
||||
mov EDI, valuePointer; // value
|
||||
mov EBX, [EDI];
|
||||
mov ECX, 4[EDI];
|
||||
lea EDI, compare;
|
||||
mov ECX, [EDI + size_t.sizeof];
|
||||
mov EDI, comparePointer; // compare
|
||||
mov EAX, [EDI];
|
||||
mov EDX, 4[EDI];
|
||||
mov EDX, [EDI + size_t.sizeof];
|
||||
|
||||
mov EDI, dest;
|
||||
lock; cmpxchg8b [EDI];
|
||||
setz AL;
|
||||
pop EBX;
|
||||
pop EDI;
|
||||
}
|
||||
}
|
||||
else
|
||||
static assert(T.sizeof <= 8, "128bit atomicCompareExchangeStrong not supported on 32bit target." );
|
||||
}
|
||||
else version (D_InlineAsm_X86_64)
|
||||
{
|
||||
static if (T.sizeof <= 8)
|
||||
{
|
||||
version (Windows)
|
||||
{
|
||||
enum DestAddr = SizedReg!R8;
|
||||
enum Cmp = SizedReg!(DX, T);
|
||||
enum Val = SizedReg!(CX, T);
|
||||
}
|
||||
else
|
||||
{
|
||||
enum DestAddr = SizedReg!DX;
|
||||
enum Cmp = SizedReg!(SI, T);
|
||||
enum Val = SizedReg!(DI, T);
|
||||
}
|
||||
enum AXReg = SizedReg!(AX, T);
|
||||
|
||||
mixin (simpleFormat(q{
|
||||
asm pure nothrow @nogc @trusted
|
||||
{
|
||||
naked;
|
||||
mov %3, %1;
|
||||
lock; cmpxchg [%0], %2;
|
||||
setz AL;
|
||||
ret;
|
||||
}
|
||||
}, [DestAddr, Cmp, Val, AXReg]));
|
||||
setz success;
|
||||
|
||||
pop EDI;
|
||||
pop EBX;
|
||||
}
|
||||
}
|
||||
else version (D_InlineAsm_X86_64)
|
||||
{
|
||||
asm pure nothrow @nogc @trusted
|
||||
{
|
||||
push RBX; // call preserved
|
||||
|
||||
mov R8, valuePointer; // value
|
||||
mov RBX, [R8];
|
||||
mov RCX, [R8 + size_t.sizeof];
|
||||
mov R8, comparePointer; // compare
|
||||
mov RAX, [R8];
|
||||
mov RDX, [R8 + size_t.sizeof];
|
||||
|
||||
mov R8, dest;
|
||||
lock; cmpxchg16b [R8];
|
||||
|
||||
setz success;
|
||||
|
||||
pop RBX;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
version (Windows)
|
||||
{
|
||||
asm pure nothrow @nogc @trusted
|
||||
{
|
||||
naked;
|
||||
push RBX;
|
||||
mov RAX, [RDX];
|
||||
mov RDX, 8[RDX];
|
||||
mov RBX, [RCX];
|
||||
mov RCX, 8[RCX];
|
||||
lock; cmpxchg16b [R8];
|
||||
setz AL;
|
||||
pop RBX;
|
||||
ret;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
asm pure nothrow @nogc @trusted
|
||||
{
|
||||
naked;
|
||||
push RBX;
|
||||
mov RAX, RDX;
|
||||
mov RDX, RCX;
|
||||
mov RBX, RDI;
|
||||
mov RCX, RSI;
|
||||
lock; cmpxchg16b [R8];
|
||||
setz AL;
|
||||
pop RBX;
|
||||
ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
static assert(0, "Operation not supported");
|
||||
}
|
||||
else
|
||||
static assert (false, "Unsupported architecture.");
|
||||
{
|
||||
version (D_InlineAsm_X86)
|
||||
{
|
||||
}
|
||||
else version (D_InlineAsm_X86_64)
|
||||
{
|
||||
}
|
||||
else
|
||||
static assert(0, "Operation not supported");
|
||||
|
||||
enum SrcReg = SizedReg!CX;
|
||||
enum ValueReg = SizedReg!(DX, T);
|
||||
enum CompareReg = SizedReg!(AX, T);
|
||||
|
||||
mixin (simpleFormat(q{
|
||||
asm pure nothrow @nogc @trusted
|
||||
{
|
||||
mov %1, value;
|
||||
mov %2, compare;
|
||||
|
||||
mov %0, dest;
|
||||
lock; cmpxchg [%0], %1;
|
||||
|
||||
setz success;
|
||||
}
|
||||
}, [SrcReg, ValueReg, CompareReg]));
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void atomicFence(MemoryOrder order = MemoryOrder.seq)() pure nothrow @nogc @trusted
|
||||
|
@ -688,57 +609,40 @@ else version (GNU)
|
|||
import gcc.builtins;
|
||||
import gcc.config;
|
||||
|
||||
// Targets where MemoryOrder.acq_rel is sufficiently cheaper than using
|
||||
// MemoryOrder.seq, used when the MemoryOrder requested is not valid for
|
||||
// a given atomic operation.
|
||||
version (IA64)
|
||||
private enum PreferAcquireRelease = true;
|
||||
else version (PPC)
|
||||
private enum PreferAcquireRelease = true;
|
||||
else version (PPC64)
|
||||
private enum PreferAcquireRelease = true;
|
||||
else
|
||||
private enum PreferAcquireRelease = false;
|
||||
|
||||
enum IsAtomicLockFree(T) = __atomic_is_lock_free(T.sizeof, null);
|
||||
|
||||
inout(T) atomicLoad(MemoryOrder order = MemoryOrder.seq, T)(inout(T)* src) pure nothrow @nogc @trusted
|
||||
if (CanCAS!T)
|
||||
{
|
||||
// MemoryOrder.rel and MemoryOrder.acq_rel are not valid for load.
|
||||
static assert(order != MemoryOrder.rel, "invalid MemoryOrder for atomicLoad()");
|
||||
|
||||
static if (order == MemoryOrder.acq_rel)
|
||||
enum smodel = PreferAcquireRelease ? MemoryOrder.acq : MemoryOrder.seq;
|
||||
else
|
||||
enum smodel = order;
|
||||
static assert(order != MemoryOrder.rel && order != MemoryOrder.acq_rel,
|
||||
"invalid MemoryOrder for atomicLoad()");
|
||||
|
||||
static if (GNU_Have_Atomics || GNU_Have_LibAtomic)
|
||||
{
|
||||
static if (T.sizeof == ubyte.sizeof)
|
||||
{
|
||||
ubyte value = __atomic_load_1(cast(shared)src, smodel);
|
||||
ubyte value = __atomic_load_1(cast(shared)src, order);
|
||||
return *cast(typeof(return)*)&value;
|
||||
}
|
||||
else static if (T.sizeof == ushort.sizeof)
|
||||
{
|
||||
ushort value = __atomic_load_2(cast(shared)src, smodel);
|
||||
ushort value = __atomic_load_2(cast(shared)src, order);
|
||||
return *cast(typeof(return)*)&value;
|
||||
}
|
||||
else static if (T.sizeof == uint.sizeof)
|
||||
{
|
||||
uint value = __atomic_load_4(cast(shared)src, smodel);
|
||||
uint value = __atomic_load_4(cast(shared)src, order);
|
||||
return *cast(typeof(return)*)&value;
|
||||
}
|
||||
else static if (T.sizeof == ulong.sizeof && GNU_Have_64Bit_Atomics)
|
||||
{
|
||||
ulong value = __atomic_load_8(cast(shared)src, smodel);
|
||||
ulong value = __atomic_load_8(cast(shared)src, order);
|
||||
return *cast(typeof(return)*)&value;
|
||||
}
|
||||
else static if (GNU_Have_LibAtomic)
|
||||
{
|
||||
T value;
|
||||
__atomic_load(T.sizeof, cast(shared)src, &value, smodel);
|
||||
__atomic_load(T.sizeof, cast(shared)src, cast(void*)&value, order);
|
||||
return *cast(typeof(return)*)&value;
|
||||
}
|
||||
else
|
||||
|
@ -755,26 +659,21 @@ else version (GNU)
|
|||
void atomicStore(MemoryOrder order = MemoryOrder.seq, T)(T* dest, T value) pure nothrow @nogc @trusted
|
||||
if (CanCAS!T)
|
||||
{
|
||||
// MemoryOrder.acq and MemoryOrder.acq_rel are not valid for store.
|
||||
static assert(order != MemoryOrder.acq, "Invalid MemoryOrder for atomicStore()");
|
||||
|
||||
static if (order == MemoryOrder.acq_rel)
|
||||
enum smodel = PreferAcquireRelease ? MemoryOrder.rel : MemoryOrder.seq;
|
||||
else
|
||||
enum smodel = order;
|
||||
static assert(order != MemoryOrder.acq && order != MemoryOrder.acq_rel,
|
||||
"Invalid MemoryOrder for atomicStore()");
|
||||
|
||||
static if (GNU_Have_Atomics || GNU_Have_LibAtomic)
|
||||
{
|
||||
static if (T.sizeof == ubyte.sizeof)
|
||||
__atomic_store_1(cast(shared)dest, *cast(ubyte*)&value, smodel);
|
||||
__atomic_store_1(cast(shared)dest, *cast(ubyte*)&value, order);
|
||||
else static if (T.sizeof == ushort.sizeof)
|
||||
__atomic_store_2(cast(shared)dest, *cast(ushort*)&value, smodel);
|
||||
__atomic_store_2(cast(shared)dest, *cast(ushort*)&value, order);
|
||||
else static if (T.sizeof == uint.sizeof)
|
||||
__atomic_store_4(cast(shared)dest, *cast(uint*)&value, smodel);
|
||||
__atomic_store_4(cast(shared)dest, *cast(uint*)&value, order);
|
||||
else static if (T.sizeof == ulong.sizeof && GNU_Have_64Bit_Atomics)
|
||||
__atomic_store_8(cast(shared)dest, *cast(ulong*)&value, smodel);
|
||||
__atomic_store_8(cast(shared)dest, *cast(ulong*)&value, order);
|
||||
else static if (GNU_Have_LibAtomic)
|
||||
__atomic_store(T.sizeof, cast(shared)dest, cast(void*)&value, smodel);
|
||||
__atomic_store(T.sizeof, cast(shared)dest, cast(void*)&value, order);
|
||||
else
|
||||
static assert(0, "Invalid template type specified.");
|
||||
}
|
||||
|
@ -843,40 +742,36 @@ else version (GNU)
|
|||
}
|
||||
|
||||
T atomicExchange(MemoryOrder order = MemoryOrder.seq, bool result = true, T)(T* dest, T value) pure nothrow @nogc @trusted
|
||||
if (is(T : ulong) || is(T == class) || is(T == interface) || is(T U : U*))
|
||||
if (CanCAS!T)
|
||||
{
|
||||
static assert(order != MemoryOrder.acq, "Invalid MemoryOrder for atomicExchange()");
|
||||
|
||||
static if (GNU_Have_Atomics || GNU_Have_LibAtomic)
|
||||
{
|
||||
// MemoryOrder.acq is not valid for exchange.
|
||||
static if (order == MemoryOrder.acq)
|
||||
enum smodel = PreferAcquireRelease ? MemoryOrder.acq_rel : MemoryOrder.seq;
|
||||
else
|
||||
enum smodel = order;
|
||||
|
||||
static if (T.sizeof == byte.sizeof)
|
||||
{
|
||||
ubyte res = __atomic_exchange_1(cast(shared)dest, *cast(ubyte*)&value, smodel);
|
||||
ubyte res = __atomic_exchange_1(cast(shared)dest, *cast(ubyte*)&value, order);
|
||||
return *cast(typeof(return)*)&res;
|
||||
}
|
||||
else static if (T.sizeof == short.sizeof)
|
||||
{
|
||||
ushort res = __atomic_exchange_2(cast(shared)dest, *cast(ushort*)&value, smodel);
|
||||
ushort res = __atomic_exchange_2(cast(shared)dest, *cast(ushort*)&value, order);
|
||||
return *cast(typeof(return)*)&res;
|
||||
}
|
||||
else static if (T.sizeof == int.sizeof)
|
||||
{
|
||||
uint res = __atomic_exchange_4(cast(shared)dest, *cast(uint*)&value, smodel);
|
||||
uint res = __atomic_exchange_4(cast(shared)dest, *cast(uint*)&value, order);
|
||||
return *cast(typeof(return)*)&res;
|
||||
}
|
||||
else static if (T.sizeof == long.sizeof && GNU_Have_64Bit_Atomics)
|
||||
{
|
||||
ulong res = __atomic_exchange_8(cast(shared)dest, *cast(ulong*)&value, smodel);
|
||||
ulong res = __atomic_exchange_8(cast(shared)dest, *cast(ulong*)&value, order);
|
||||
return *cast(typeof(return)*)&res;
|
||||
}
|
||||
else static if (GNU_Have_LibAtomic)
|
||||
{
|
||||
T res = void;
|
||||
__atomic_exchange(T.sizeof, cast(shared)dest, cast(void*)&value, &res, smodel);
|
||||
__atomic_exchange(T.sizeof, cast(shared)dest, cast(void*)&value, &res, order);
|
||||
return res;
|
||||
}
|
||||
else
|
||||
|
@ -920,46 +815,29 @@ else version (GNU)
|
|||
private bool atomicCompareExchangeImpl(MemoryOrder succ = MemoryOrder.seq, MemoryOrder fail = MemoryOrder.seq, bool weak, T)(T* dest, T* compare, T value) pure nothrow @nogc @trusted
|
||||
if (CanCAS!T)
|
||||
{
|
||||
static assert(fail != MemoryOrder.rel && fail != MemoryOrder.acq_rel,
|
||||
"Invalid fail MemoryOrder for atomicCompareExchange()");
|
||||
static assert (succ >= fail, "The first MemoryOrder argument for atomicCompareExchange() cannot be weaker than the second argument");
|
||||
|
||||
bool res = void;
|
||||
|
||||
static if (GNU_Have_Atomics || GNU_Have_LibAtomic)
|
||||
{
|
||||
static if (fail == MemoryOrder.rel || fail == MemoryOrder.acq_rel)
|
||||
{
|
||||
// MemoryOrder.rel and MemoryOrder.acq_rel are not valid failure models.
|
||||
enum smodel = (succ != MemoryOrder.seq && PreferAcquireRelease)
|
||||
? MemoryOrder.acq_rel : MemoryOrder.seq;
|
||||
enum fmodel = (succ != MemoryOrder.seq && PreferAcquireRelease)
|
||||
? MemoryOrder.raw : MemoryOrder.seq;
|
||||
}
|
||||
else static if (fail > succ)
|
||||
{
|
||||
// Failure memory model cannot be stronger than success.
|
||||
enum smodel = (fail != MemoryOrder.seq && PreferAcquireRelease)
|
||||
? MemoryOrder.acq_rel : MemoryOrder.seq;
|
||||
enum fmodel = fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
enum smodel = succ;
|
||||
enum fmodel = fail;
|
||||
}
|
||||
|
||||
static if (T.sizeof == byte.sizeof)
|
||||
res = __atomic_compare_exchange_1(cast(shared)dest, compare, *cast(ubyte*)&value,
|
||||
weak, smodel, fmodel);
|
||||
weak, succ, fail);
|
||||
else static if (T.sizeof == short.sizeof)
|
||||
res = __atomic_compare_exchange_2(cast(shared)dest, compare, *cast(ushort*)&value,
|
||||
weak, smodel, fmodel);
|
||||
weak, succ, fail);
|
||||
else static if (T.sizeof == int.sizeof)
|
||||
res = __atomic_compare_exchange_4(cast(shared)dest, compare, *cast(uint*)&value,
|
||||
weak, smodel, fmodel);
|
||||
weak, succ, fail);
|
||||
else static if (T.sizeof == long.sizeof && GNU_Have_64Bit_Atomics)
|
||||
res = __atomic_compare_exchange_8(cast(shared)dest, compare, *cast(ulong*)&value,
|
||||
weak, smodel, fmodel);
|
||||
weak, succ, fail);
|
||||
else static if (GNU_Have_LibAtomic)
|
||||
res = __atomic_compare_exchange(T.sizeof, cast(shared)dest, compare, cast(void*)&value,
|
||||
smodel, fmodel);
|
||||
succ, fail);
|
||||
else
|
||||
static assert(0, "Invalid template type specified.");
|
||||
}
|
||||
|
@ -1122,10 +1000,10 @@ version (Windows)
|
|||
enum RegisterReturn(T) = is(T : U[], U) || is(T : R delegate(A), R, A...);
|
||||
}
|
||||
|
||||
enum CanCAS(T) = is(T : ulong) ||
|
||||
enum CanCAS(T) = (__traits(isScalar, T) && // check to see if it is some kind of basic type like an integer/float/pointer
|
||||
T.sizeof <= size_t.sizeof * 2) || // make sure if it is, that it is no more than 2 words
|
||||
is(T == class) ||
|
||||
is(T == interface) ||
|
||||
is(T : U*, U) ||
|
||||
is(T : U[], U) ||
|
||||
is(T : R delegate(A), R, A...) ||
|
||||
(is(T == struct) && __traits(isPOD, T) &&
|
||||
|
|
|
@ -38,7 +38,7 @@ template Unqual(T : const U, U)
|
|||
|
||||
template BaseElemOf(T)
|
||||
{
|
||||
static if (is(T == E[N], E, size_t N))
|
||||
static if (is(OriginalType!T == E[N], E, size_t N))
|
||||
alias BaseElemOf = BaseElemOf!E;
|
||||
else
|
||||
alias BaseElemOf = T;
|
||||
|
@ -51,6 +51,8 @@ unittest
|
|||
static assert(is(BaseElemOf!(int[1][2]) == int));
|
||||
static assert(is(BaseElemOf!(int[1][]) == int[1][]));
|
||||
static assert(is(BaseElemOf!(int[][1]) == int[]));
|
||||
static enum E : int[2]{ test = [0, 1] }
|
||||
static assert(is(BaseElemOf!(E) == int));
|
||||
}
|
||||
|
||||
// [For internal use]
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
*
|
||||
* $(NOTE The D shared type qualifier is the closest to the _Atomic type qualifier from C. It may be changed from shared in the future.)
|
||||
*
|
||||
* $(NOTE Fail memory order is currently ignored due to limitations with internal implementation of atomics.)
|
||||
*
|
||||
* Copyright: Copyright Richard (Rikki) Andrew Cattermole 2023.
|
||||
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
|
||||
* Authors: Richard (Rikki) Andrew cattermole
|
||||
|
@ -101,7 +103,7 @@ version (DigitalMars)
|
|||
alias atomic_fetch_and_explicit = atomic_fetch_and_explicit_impl; ///
|
||||
}
|
||||
}
|
||||
else version(GNU)
|
||||
else
|
||||
{
|
||||
alias atomic_flag_clear = atomic_flag_clear_impl; ///
|
||||
alias atomic_flag_clear_explicit = atomic_flag_clear_explicit_impl; ///
|
||||
|
@ -169,6 +171,7 @@ void atomic_flag_clear_explicit_impl()(atomic_flag* obj, memory_order order)
|
|||
break;
|
||||
|
||||
case memory_order.memory_order_acquire:
|
||||
case memory_order.memory_order_acq_rel:
|
||||
// Ideally this would error at compile time but alas it is not an intrinsic.
|
||||
// Note: this is not a valid memory order for this operation.
|
||||
atomicStore!(memory_order.memory_order_seq_cst)(&obj.b, false);
|
||||
|
@ -178,10 +181,6 @@ void atomic_flag_clear_explicit_impl()(atomic_flag* obj, memory_order order)
|
|||
atomicStore!(memory_order.memory_order_release)(&obj.b, false);
|
||||
break;
|
||||
|
||||
case memory_order.memory_order_acq_rel:
|
||||
atomicStore!(memory_order.memory_order_acq_rel)(&obj.b, false);
|
||||
break;
|
||||
|
||||
case memory_order.memory_order_seq_cst:
|
||||
atomicStore(&obj.b, false);
|
||||
break;
|
||||
|
@ -197,7 +196,7 @@ bool atomic_flag_test_and_set_impl()(atomic_flag* obj)
|
|||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@trusted unittest
|
||||
{
|
||||
atomic_flag flag;
|
||||
assert(!atomic_flag_test_and_set_impl(&flag));
|
||||
|
@ -216,7 +215,9 @@ bool atomic_flag_test_and_set_explicit_impl()(atomic_flag* obj, memory_order ord
|
|||
return atomicExchange!(memory_order.memory_order_relaxed)(&obj.b, true);
|
||||
|
||||
case memory_order.memory_order_acquire:
|
||||
return atomicExchange!(memory_order.memory_order_acquire)(&obj.b, true);
|
||||
// Ideally this would error at compile time but alas it is not an intrinsic.
|
||||
// Note: this is not a valid memory order for this operation.
|
||||
return atomicExchange!(memory_order.memory_order_seq_cst)(&obj.b, true);
|
||||
|
||||
case memory_order.memory_order_release:
|
||||
return atomicExchange!(memory_order.memory_order_release)(&obj.b, true);
|
||||
|
@ -230,7 +231,7 @@ bool atomic_flag_test_and_set_explicit_impl()(atomic_flag* obj, memory_order ord
|
|||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@trusted unittest
|
||||
{
|
||||
atomic_flag flag;
|
||||
assert(!atomic_flag_test_and_set_explicit_impl(&flag, memory_order.memory_order_seq_cst));
|
||||
|
@ -253,6 +254,9 @@ unittest
|
|||
{
|
||||
shared int val;
|
||||
atomic_init(val, 2);
|
||||
|
||||
shared float valF;
|
||||
atomic_init(valF, 3.2);
|
||||
}
|
||||
|
||||
/// No-op function, doesn't apply to D
|
||||
|
@ -269,7 +273,7 @@ void atomic_signal_fence_impl()(memory_order order)
|
|||
final switch (order)
|
||||
{
|
||||
case memory_order.memory_order_relaxed:
|
||||
atomicSignalFence!(memory_order.memory_order_relaxed);
|
||||
// This is a no-op operation for relaxed memory orders.
|
||||
break;
|
||||
|
||||
case memory_order.memory_order_acquire:
|
||||
|
@ -303,7 +307,7 @@ void atomic_thread_fence_impl()(memory_order order)
|
|||
final switch (order)
|
||||
{
|
||||
case memory_order.memory_order_relaxed:
|
||||
atomicFence!(memory_order.memory_order_relaxed);
|
||||
// This is a no-op operation for relaxed memory orders.
|
||||
break;
|
||||
|
||||
case memory_order.memory_order_acquire:
|
||||
|
@ -413,14 +417,17 @@ pragma(inline, true)
|
|||
void atomic_store_impl(A, C)(shared(A)* obj, C desired) @trusted
|
||||
{
|
||||
assert(obj !is null);
|
||||
atomicStore(obj, cast(A)desired);
|
||||
atomicStore(cast(A*)obj, cast(A)desired);
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@trusted unittest
|
||||
{
|
||||
shared(int) obj;
|
||||
atomic_store_impl(&obj, 3);
|
||||
|
||||
shared(float) objF;
|
||||
atomic_store_impl(&objF, 3.21);
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -432,34 +439,34 @@ void atomic_store_explicit_impl(A, C)(shared(A)* obj, C desired, memory_order or
|
|||
final switch (order)
|
||||
{
|
||||
case memory_order.memory_order_relaxed:
|
||||
atomicStore!(memory_order.memory_order_relaxed)(obj, cast(A)desired);
|
||||
atomicStore!(memory_order.memory_order_relaxed)(cast(A*)obj, cast(A)desired);
|
||||
break;
|
||||
|
||||
case memory_order.memory_order_acquire:
|
||||
case memory_order.memory_order_acq_rel:
|
||||
// Ideally this would error at compile time but alas it is not an intrinsic.
|
||||
// Note: this is not a valid memory order for this operation.
|
||||
atomicStore!(memory_order.memory_order_release)(obj, cast(A)desired);
|
||||
atomicStore!(memory_order.memory_order_release)(cast(A*)obj, cast(A)desired);
|
||||
break;
|
||||
|
||||
case memory_order.memory_order_release:
|
||||
atomicStore!(memory_order.memory_order_release)(obj, cast(A)desired);
|
||||
break;
|
||||
|
||||
case memory_order.memory_order_acq_rel:
|
||||
atomicStore!(memory_order.memory_order_acq_rel)(obj, cast(A)desired);
|
||||
atomicStore!(memory_order.memory_order_release)(cast(A*)obj, cast(A)desired);
|
||||
break;
|
||||
|
||||
case memory_order.memory_order_seq_cst:
|
||||
atomicStore!(memory_order.memory_order_seq_cst)(obj, cast(A)desired);
|
||||
atomicStore!(memory_order.memory_order_seq_cst)(cast(A*)obj, cast(A)desired);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@trusted unittest
|
||||
{
|
||||
shared(int) obj;
|
||||
atomic_store_explicit_impl(&obj, 3, memory_order.memory_order_seq_cst);
|
||||
|
||||
shared(float) objF;
|
||||
atomic_store_explicit_impl(&objF, 3.21, memory_order.memory_order_seq_cst);
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -467,14 +474,24 @@ pragma(inline, true)
|
|||
A atomic_load_impl(A)(const shared(A)* obj) @trusted
|
||||
{
|
||||
assert(obj !is null);
|
||||
return atomicLoad(cast(shared(A)*)obj);
|
||||
return atomicLoad(cast(A*)obj);
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@trusted unittest
|
||||
{
|
||||
shared(int) obj = 3;
|
||||
assert(atomic_load_impl(&obj) == 3);
|
||||
|
||||
shared(float) objF = 3.5;
|
||||
assert(atomic_load_impl(&objF) > 3);
|
||||
|
||||
static struct S2
|
||||
{
|
||||
size_t[2] values;
|
||||
}
|
||||
align(S2.sizeof) shared(S2) objS2 = {[1, 2]};
|
||||
assert(atomic_load_impl(&objS2).values == [1, 2]);
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -486,29 +503,30 @@ A atomic_load_explicit_impl(A)(const shared(A)* obj, memory_order order) @truste
|
|||
final switch (order)
|
||||
{
|
||||
case memory_order.memory_order_relaxed:
|
||||
return atomicLoad!(memory_order.memory_order_relaxed)(obj);
|
||||
return atomicLoad!(memory_order.memory_order_relaxed)(cast(A*)obj);
|
||||
|
||||
case memory_order.memory_order_acquire:
|
||||
return atomicLoad!(memory_order.memory_order_acquire)(obj);
|
||||
return atomicLoad!(memory_order.memory_order_acquire)(cast(A*)obj);
|
||||
|
||||
case memory_order.memory_order_release:
|
||||
case memory_order.memory_order_acq_rel:
|
||||
// Ideally this would error at compile time but alas it is not an intrinsic.
|
||||
// Note: this is not a valid memory order for this operation.
|
||||
return atomicLoad!(memory_order.memory_order_acquire)(obj);
|
||||
|
||||
case memory_order.memory_order_acq_rel:
|
||||
return atomicLoad!(memory_order.memory_order_acq_rel)(obj);
|
||||
return atomicLoad!(memory_order.memory_order_acquire)(cast(A*)obj);
|
||||
|
||||
case memory_order.memory_order_seq_cst:
|
||||
return atomicLoad!(memory_order.memory_order_seq_cst)(obj);
|
||||
return atomicLoad!(memory_order.memory_order_seq_cst)(cast(A*)obj);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@trusted unittest
|
||||
{
|
||||
shared(int) obj = 3;
|
||||
assert(atomic_load_explicit_impl(&obj, memory_order.memory_order_seq_cst) == 3);
|
||||
|
||||
shared(float) objF = 3.5;
|
||||
assert(atomic_load_explicit_impl(&objF, memory_order.memory_order_seq_cst) > 3);
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -516,14 +534,17 @@ pragma(inline, true)
|
|||
A atomic_exchange_impl(A, C)(shared(A)* obj, C desired) @trusted
|
||||
{
|
||||
assert(obj !is null);
|
||||
return atomicExchange(cast(shared(A)*)obj, cast(A)desired);
|
||||
return atomicExchange(cast(A*)obj, cast(A)desired);
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@trusted unittest
|
||||
{
|
||||
shared(int) obj = 3;
|
||||
assert(atomic_exchange_impl(&obj, 2) == 3);
|
||||
|
||||
shared(float) objF = 3;
|
||||
assert(atomic_exchange_impl(&objF, 2.1) > 2.5);
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -535,38 +556,44 @@ A atomic_exchange_explicit_impl(A, C)(shared(A)* obj, C desired, memory_order or
|
|||
final switch (order)
|
||||
{
|
||||
case memory_order.memory_order_relaxed:
|
||||
return atomicExchange!(memory_order.memory_order_relaxed)(obj, cast(A)desired);
|
||||
return atomicExchange!(memory_order.memory_order_relaxed)(cast(A*)obj, cast(A)desired);
|
||||
|
||||
case memory_order.memory_order_acquire:
|
||||
return atomicExchange!(memory_order.memory_order_acquire)(obj, cast(A)desired);
|
||||
// Ideally this would error at compile time but alas it is not an intrinsic.
|
||||
// Note: this is not a valid memory order for this operation.
|
||||
return atomicExchange!(memory_order.memory_order_seq_cst)(cast(A*)obj, cast(A)desired);
|
||||
|
||||
case memory_order.memory_order_release:
|
||||
return atomicExchange!(memory_order.memory_order_release)(obj, cast(A)desired);
|
||||
return atomicExchange!(memory_order.memory_order_release)(cast(A*)obj, cast(A)desired);
|
||||
|
||||
case memory_order.memory_order_acq_rel:
|
||||
return atomicExchange!(memory_order.memory_order_acq_rel)(obj, cast(A)desired);
|
||||
return atomicExchange!(memory_order.memory_order_acq_rel)(cast(A*)obj, cast(A)desired);
|
||||
|
||||
case memory_order.memory_order_seq_cst:
|
||||
return atomicExchange!(memory_order.memory_order_seq_cst)(obj, cast(A)desired);
|
||||
return atomicExchange!(memory_order.memory_order_seq_cst)(cast(A*)obj, cast(A)desired);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@trusted unittest
|
||||
{
|
||||
shared(int) obj = 3;
|
||||
assert(atomic_exchange_explicit_impl(&obj, 2, memory_order.memory_order_seq_cst) == 3);
|
||||
|
||||
shared(float) objF = 1.5;
|
||||
assert(atomic_exchange_explicit_impl(&objF, 2.1, memory_order.memory_order_seq_cst) < 2);
|
||||
}
|
||||
|
||||
///
|
||||
pragma(inline, true)
|
||||
bool atomic_compare_exchange_strong_impl(A, C)(shared(A)* obj, A* expected, C desired) @trusted
|
||||
bool atomic_compare_exchange_strong_impl(A, B, C)(shared(A)* obj, B* expected, C desired) @trusted
|
||||
{
|
||||
return atomicCompareExchangeStrong(cast(A*)obj, expected, cast(A)desired);
|
||||
static assert(is(shared(B) : A), "Both expected and object must be the same type");
|
||||
return atomicCompareExchangeStrong(cast(B*)obj, expected, cast(B)desired);
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@trusted unittest
|
||||
{
|
||||
shared(int) obj = 3;
|
||||
int expected = 3;
|
||||
|
@ -574,14 +601,23 @@ unittest
|
|||
}
|
||||
|
||||
///
|
||||
pragma(inline, true)
|
||||
bool atomic_compare_exchange_weak_impl(A, C)(shared(A)* obj, A* expected, C desired) @trusted
|
||||
@trusted unittest
|
||||
{
|
||||
return atomicCompareExchangeStrong(cast(A*)obj, expected, cast(A)desired);
|
||||
shared(float) obj = 3;
|
||||
float expected = 3;
|
||||
assert(atomic_compare_exchange_strong_impl(&obj, &expected, 2.1));
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
pragma(inline, true)
|
||||
bool atomic_compare_exchange_weak_impl(A, B, C)(shared(A)* obj, B* expected, C desired) @trusted
|
||||
{
|
||||
static assert(is(shared(B) : A), "Both expected and object must be the same type");
|
||||
return atomicCompareExchangeWeak(cast(B*)obj, expected, cast(B)desired);
|
||||
}
|
||||
|
||||
///
|
||||
@trusted unittest
|
||||
{
|
||||
shared(int) obj = 3;
|
||||
int expected = 3;
|
||||
|
@ -589,90 +625,38 @@ unittest
|
|||
}
|
||||
|
||||
///
|
||||
pragma(inline, true)
|
||||
bool atomic_compare_exchange_strong_explicit_impl(A, C)(shared(A)* obj, A* expected, C desired, memory_order succ, memory_order fail) @trusted
|
||||
@trusted unittest
|
||||
{
|
||||
shared(float) obj = 3;
|
||||
float expected = 3;
|
||||
static assert(__traits(compiles, {atomic_compare_exchange_weak_impl(&obj, &expected, 2.1);}));
|
||||
}
|
||||
|
||||
///
|
||||
pragma(inline, true)
|
||||
bool atomic_compare_exchange_strong_explicit_impl(A, B, C)(shared(A)* obj, B* expected, C desired, memory_order succ, memory_order fail) @trusted
|
||||
{
|
||||
static assert(is(shared(B) : A), "Both expected and object must be the same type");
|
||||
assert(obj !is null);
|
||||
// We use these giant switch inside switch statements
|
||||
// because as of 2023 they are capable of being for the most part inlined by gdc & ldc when using literal arguments for memory_order.
|
||||
// NOTE: To not have to deal with all invalid cases, the failure model is ignored for now.
|
||||
|
||||
final switch(succ)
|
||||
{
|
||||
case memory_order.memory_order_relaxed:
|
||||
final switch(fail)
|
||||
{
|
||||
case memory_order.memory_order_relaxed:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_acquire:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_relaxed, memory_order.memory_order_acquire)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_release:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_relaxed, memory_order.memory_order_release)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_acq_rel:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_relaxed, memory_order.memory_order_acq_rel)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_seq_cst:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_relaxed, memory_order.memory_order_seq_cst)(cast(A*)obj, expected, cast(A)desired);
|
||||
}
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
|
||||
case memory_order.memory_order_acquire:
|
||||
final switch(fail)
|
||||
{
|
||||
case memory_order.memory_order_relaxed:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_acquire, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_acquire:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_acquire, memory_order.memory_order_acquire)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_release:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_acquire, memory_order.memory_order_release)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_acq_rel:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_acquire, memory_order.memory_order_acq_rel)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_seq_cst:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_acquire, memory_order.memory_order_seq_cst)(cast(A*)obj, expected, cast(A)desired);
|
||||
}
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_acquire, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
|
||||
case memory_order.memory_order_release:
|
||||
final switch(fail)
|
||||
{
|
||||
case memory_order.memory_order_relaxed:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_release, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_acquire:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_release, memory_order.memory_order_acquire)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_release:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_release, memory_order.memory_order_release)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_acq_rel:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_release, memory_order.memory_order_acq_rel)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_seq_cst:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_release, memory_order.memory_order_seq_cst)(cast(A*)obj, expected, cast(A)desired);
|
||||
}
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_release, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
|
||||
case memory_order.memory_order_acq_rel:
|
||||
final switch(fail)
|
||||
{
|
||||
case memory_order.memory_order_relaxed:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_acq_rel, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_acquire:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_acq_rel, memory_order.memory_order_acquire)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_release:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_acq_rel, memory_order.memory_order_release)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_acq_rel:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_acq_rel, memory_order.memory_order_acq_rel)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_seq_cst:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_acq_rel, memory_order.memory_order_seq_cst)(cast(A*)obj, expected, cast(A)desired);
|
||||
}
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_acq_rel, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
|
||||
case memory_order.memory_order_seq_cst:
|
||||
final switch(fail)
|
||||
{
|
||||
case memory_order.memory_order_relaxed:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_seq_cst, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_acquire:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_seq_cst, memory_order.memory_order_acquire)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_release:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_seq_cst, memory_order.memory_order_release)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_acq_rel:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_seq_cst, memory_order.memory_order_acq_rel)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_seq_cst:
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_seq_cst, memory_order.memory_order_seq_cst)(cast(A*)obj, expected, cast(A)desired);
|
||||
}
|
||||
return atomicCompareExchangeStrong!(memory_order.memory_order_seq_cst, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@trusted unittest
|
||||
{
|
||||
shared(int) obj = 3;
|
||||
int expected = 3;
|
||||
|
@ -680,125 +664,107 @@ unittest
|
|||
}
|
||||
|
||||
///
|
||||
pragma(inline, true)
|
||||
bool atomic_compare_exchange_weak_explicit_impl(A, C)(shared(A)* obj, A* expected, C desired, memory_order succ, memory_order fail) @trusted
|
||||
@trusted unittest
|
||||
{
|
||||
align(size_t[2].sizeof) shared(size_t[2]) obj = [3, 4];
|
||||
size_t[2] expected = [3, 4];
|
||||
size_t[2] toSwap = [1, 2];
|
||||
assert(atomic_compare_exchange_strong_explicit_impl(&obj, &expected, toSwap, memory_order.memory_order_seq_cst, memory_order.memory_order_seq_cst));
|
||||
}
|
||||
|
||||
///
|
||||
@trusted unittest
|
||||
{
|
||||
shared(float) obj = 3;
|
||||
float expected = 3;
|
||||
assert(atomic_compare_exchange_strong_explicit_impl(&obj, &expected, 2.1, memory_order.memory_order_seq_cst, memory_order.memory_order_seq_cst));
|
||||
}
|
||||
|
||||
///
|
||||
pragma(inline, true)
|
||||
bool atomic_compare_exchange_weak_explicit_impl(A, B, C)(shared(A)* obj, B* expected, C desired, memory_order succ, memory_order fail) @trusted
|
||||
{
|
||||
static assert(is(shared(B) : A), "Both expected and object must be the same type");
|
||||
assert(obj !is null);
|
||||
// We use these giant switch inside switch statements
|
||||
// because as of 2023 they are capable of being for the most part inlined by gdc & ldc when using literal arguments for memory_order.
|
||||
// NOTE: To not have to deal with all invalid cases, the failure model is ignored for now.
|
||||
|
||||
final switch(succ)
|
||||
{
|
||||
case memory_order.memory_order_relaxed:
|
||||
final switch(fail)
|
||||
{
|
||||
case memory_order.memory_order_relaxed:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_acquire:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_release:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_acq_rel:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_seq_cst:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired);
|
||||
}
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
|
||||
case memory_order.memory_order_acquire:
|
||||
final switch(fail)
|
||||
{
|
||||
case memory_order.memory_order_relaxed:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_acquire, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_acquire:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_acquire, memory_order.memory_order_acquire)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_release:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_acquire, memory_order.memory_order_release)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_acq_rel:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_acquire, memory_order.memory_order_acq_rel)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_seq_cst:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_acquire, memory_order.memory_order_seq_cst)(cast(A*)obj, expected, cast(A)desired);
|
||||
}
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_acquire, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
|
||||
case memory_order.memory_order_release:
|
||||
final switch(fail)
|
||||
{
|
||||
case memory_order.memory_order_relaxed:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_release, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_acquire:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_release, memory_order.memory_order_acquire)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_release:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_release, memory_order.memory_order_release)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_acq_rel:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_release, memory_order.memory_order_acq_rel)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_seq_cst:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_release, memory_order.memory_order_seq_cst)(cast(A*)obj, expected, cast(A)desired);
|
||||
}
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_release, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
|
||||
case memory_order.memory_order_acq_rel:
|
||||
final switch(fail)
|
||||
{
|
||||
case memory_order.memory_order_relaxed:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_acq_rel, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_acquire:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_acq_rel, memory_order.memory_order_acquire)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_release:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_acq_rel, memory_order.memory_order_release)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_acq_rel:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_acq_rel, memory_order.memory_order_acq_rel)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_seq_cst:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_acq_rel, memory_order.memory_order_seq_cst)(cast(A*)obj, expected, cast(A)desired);
|
||||
}
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_acq_rel, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
|
||||
case memory_order.memory_order_seq_cst:
|
||||
final switch(fail)
|
||||
{
|
||||
case memory_order.memory_order_relaxed:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_relaxed)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_acquire:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_acquire)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_release:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_release)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_acq_rel:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_acq_rel)(cast(A*)obj, expected, cast(A)desired);
|
||||
case memory_order.memory_order_seq_cst:
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_seq_cst)(cast(A*)obj, expected, cast(A)desired);
|
||||
}
|
||||
return atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@trusted unittest
|
||||
{
|
||||
shared(int) obj = 3;
|
||||
int expected = 3;
|
||||
atomic_compare_exchange_weak_explicit_impl(&obj, &expected, 2, memory_order.memory_order_seq_cst, memory_order.memory_order_seq_cst);
|
||||
}
|
||||
|
||||
///
|
||||
@trusted unittest
|
||||
{
|
||||
align(size_t[2].sizeof) shared(size_t[2]) obj = [3, 4];
|
||||
size_t[2] expected = [3, 4];
|
||||
size_t[2] toSwap = [1, 2];
|
||||
assert(atomic_compare_exchange_weak_explicit_impl(&obj, &expected, toSwap, memory_order.memory_order_seq_cst, memory_order.memory_order_seq_cst));
|
||||
}
|
||||
|
||||
///
|
||||
@trusted unittest
|
||||
{
|
||||
shared(float) obj = 3;
|
||||
float expected = 3;
|
||||
atomic_compare_exchange_weak_explicit_impl(&obj, &expected, 2, memory_order.memory_order_seq_cst, memory_order.memory_order_seq_cst);
|
||||
}
|
||||
|
||||
///
|
||||
pragma(inline, true)
|
||||
A atomic_fetch_add_impl(A, M)(shared(A)* obj, M arg) @trusted
|
||||
{
|
||||
assert(obj !is null);
|
||||
return atomicFetchAdd(cast(A*)obj, arg);
|
||||
return atomic_fetch_op!(memory_order.memory_order_seq_cst, "+=")(cast(A*)obj, arg);
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@trusted unittest
|
||||
{
|
||||
shared(int) val;
|
||||
atomic_fetch_add_impl(&val, 3);
|
||||
assert(atomic_load_impl(&val) == 3);
|
||||
|
||||
shared(float) valF = 0.5;
|
||||
atomic_fetch_add_impl(&valF, 3);
|
||||
assert(atomic_load_impl(&valF) > 3);
|
||||
}
|
||||
|
||||
pragma(inline, true)
|
||||
A atomic_fetch_sub_impl(A, M)(shared(A)* obj, M arg) @trusted
|
||||
{
|
||||
assert(obj !is null);
|
||||
return atomicFetchSub(cast(A*)obj, arg);
|
||||
return atomic_fetch_op!(memory_order.memory_order_seq_cst, "-=")(cast(A*)obj, arg);
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@trusted unittest
|
||||
{
|
||||
shared(int) val = 3;
|
||||
atomic_fetch_sub_impl(&val, 3);
|
||||
assert(atomic_load_impl(&val) == 0);
|
||||
|
||||
shared(float) valF = 3;
|
||||
atomic_fetch_sub_impl(&valF, 1);
|
||||
assert(atomic_load_impl(&valF) < 3);
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -810,24 +776,28 @@ A atomic_fetch_add_explicit_impl(A, M)(shared(A)* obj, M arg, memory_order order
|
|||
final switch(order)
|
||||
{
|
||||
case memory_order.memory_order_relaxed:
|
||||
return atomicFetchAdd!(memory_order.memory_order_relaxed)(cast(A*)obj, arg);
|
||||
return atomic_fetch_op!(memory_order.memory_order_relaxed, "+=")(cast(A*)obj, arg);
|
||||
case memory_order.memory_order_acquire:
|
||||
return atomicFetchAdd!(memory_order.memory_order_acquire)(cast(A*)obj, arg);
|
||||
return atomic_fetch_op!(memory_order.memory_order_acquire, "+=")(cast(A*)obj, arg);
|
||||
case memory_order.memory_order_release:
|
||||
return atomicFetchAdd!(memory_order.memory_order_release)(cast(A*)obj, arg);
|
||||
return atomic_fetch_op!(memory_order.memory_order_release, "+=")(cast(A*)obj, arg);
|
||||
case memory_order.memory_order_acq_rel:
|
||||
return atomicFetchAdd!(memory_order.memory_order_acq_rel)(cast(A*)obj, arg);
|
||||
return atomic_fetch_op!(memory_order.memory_order_acq_rel, "+=")(cast(A*)obj, arg);
|
||||
case memory_order.memory_order_seq_cst:
|
||||
return atomicFetchAdd!(memory_order.memory_order_seq_cst)(cast(A*)obj, arg);
|
||||
return atomic_fetch_op!(memory_order.memory_order_seq_cst, "+=")(cast(A*)obj, arg);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@trusted unittest
|
||||
{
|
||||
shared(int) val;
|
||||
atomic_fetch_add_explicit_impl(&val, 3, memory_order.memory_order_seq_cst);
|
||||
assert(atomic_load_impl(&val) == 3);
|
||||
|
||||
shared(float) valF = 3;
|
||||
atomic_fetch_add_explicit_impl(&valF, 3, memory_order.memory_order_seq_cst);
|
||||
assert(atomic_load_impl(&valF) > 3);
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -839,24 +809,28 @@ A atomic_fetch_sub_explicit_impl(A, M)(shared(A)* obj, M arg, memory_order order
|
|||
final switch(order)
|
||||
{
|
||||
case memory_order.memory_order_relaxed:
|
||||
return atomicFetchSub!(memory_order.memory_order_relaxed)(cast(A*)obj, arg);
|
||||
return atomic_fetch_op!(memory_order.memory_order_relaxed, "-=")(cast(A*)obj, arg);
|
||||
case memory_order.memory_order_acquire:
|
||||
return atomicFetchSub!(memory_order.memory_order_acquire)(cast(A*)obj, arg);
|
||||
return atomic_fetch_op!(memory_order.memory_order_acquire, "-=")(cast(A*)obj, arg);
|
||||
case memory_order.memory_order_release:
|
||||
return atomicFetchSub!(memory_order.memory_order_release)(cast(A*)obj, arg);
|
||||
return atomic_fetch_op!(memory_order.memory_order_release, "-=")(cast(A*)obj, arg);
|
||||
case memory_order.memory_order_acq_rel:
|
||||
return atomicFetchSub!(memory_order.memory_order_acq_rel)(cast(A*)obj, arg);
|
||||
return atomic_fetch_op!(memory_order.memory_order_acq_rel, "-=")(cast(A*)obj, arg);
|
||||
case memory_order.memory_order_seq_cst:
|
||||
return atomicFetchSub!(memory_order.memory_order_seq_cst)(cast(A*)obj, arg);
|
||||
return atomic_fetch_op!(memory_order.memory_order_seq_cst, "-=")(cast(A*)obj, arg);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@trusted unittest
|
||||
{
|
||||
shared(int) val = 3;
|
||||
atomic_fetch_sub_explicit_impl(&val, 3, memory_order.memory_order_seq_cst);
|
||||
assert(atomic_load_impl(&val) == 0);
|
||||
|
||||
shared(float) valF = 4;
|
||||
atomic_fetch_sub_explicit_impl(&valF, 3, memory_order.memory_order_seq_cst);
|
||||
assert(atomic_load_impl(&valF) < 4);
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -864,21 +838,11 @@ pragma(inline, true)
|
|||
A atomic_fetch_or_impl(A, M)(shared(A)* obj, M arg) @trusted
|
||||
{
|
||||
assert(obj !is null);
|
||||
|
||||
// copied from atomicOp
|
||||
|
||||
A set, get = atomicLoad(cast(A*)obj);
|
||||
|
||||
do
|
||||
{
|
||||
set = get | arg;
|
||||
} while (!atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_seq_cst)(cast(A*)obj, &get, cast(A)set));
|
||||
|
||||
return get;
|
||||
return atomic_fetch_op!(memory_order.memory_order_seq_cst, "|=")(cast(A*)obj, arg);
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@trusted unittest
|
||||
{
|
||||
shared(int) val = 5;
|
||||
atomic_fetch_or_impl(&val, 3);
|
||||
|
@ -891,56 +855,23 @@ A atomic_fetch_or_explicit_impl(A, M)(shared(A)* obj, M arg, memory_order order)
|
|||
{
|
||||
assert(obj !is null);
|
||||
|
||||
A set, get;
|
||||
|
||||
final switch(order)
|
||||
{
|
||||
case memory_order.memory_order_relaxed:
|
||||
get = atomicLoad!(memory_order.memory_order_relaxed)(cast(A*)obj);
|
||||
do
|
||||
{
|
||||
set = get | arg;
|
||||
} while (!atomicCompareExchangeWeak!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(A*)obj, &get, cast(A)set));
|
||||
break;
|
||||
|
||||
return atomic_fetch_op!(memory_order.memory_order_relaxed, "|=")(cast(A*)obj, arg);
|
||||
case memory_order.memory_order_acquire:
|
||||
get = atomicLoad!(memory_order.memory_order_acquire)(cast(A*)obj);
|
||||
do
|
||||
{
|
||||
set = get | arg;
|
||||
} while (!atomicCompareExchangeWeak!(memory_order.memory_order_acquire, memory_order.memory_order_acquire)(cast(A*)obj, &get, cast(A)set));
|
||||
break;
|
||||
|
||||
return atomic_fetch_op!(memory_order.memory_order_acquire, "|=")(cast(A*)obj, arg);
|
||||
case memory_order.memory_order_release:
|
||||
get = atomicLoad!(memory_order.memory_order_relaxed)(cast(A*)obj);
|
||||
do
|
||||
{
|
||||
set = get | arg;
|
||||
} while (!atomicCompareExchangeWeak!(memory_order.memory_order_release, memory_order.memory_order_release)(cast(A*)obj, &get, cast(A)set));
|
||||
break;
|
||||
|
||||
return atomic_fetch_op!(memory_order.memory_order_release, "|=")(cast(A*)obj, arg);
|
||||
case memory_order.memory_order_acq_rel:
|
||||
get = atomicLoad!(memory_order.memory_order_acq_rel)(cast(A*)obj);
|
||||
do
|
||||
{
|
||||
set = get | arg;
|
||||
} while (!atomicCompareExchangeWeak!(memory_order.memory_order_acq_rel, memory_order.memory_order_acq_rel)(cast(A*)obj, &get, cast(A)set));
|
||||
break;
|
||||
|
||||
return atomic_fetch_op!(memory_order.memory_order_acq_rel, "|=")(cast(A*)obj, arg);
|
||||
case memory_order.memory_order_seq_cst:
|
||||
get = atomicLoad!(memory_order.memory_order_relaxed)(cast(A*)obj);
|
||||
do
|
||||
{
|
||||
set = get | arg;
|
||||
} while (!atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_seq_cst)(cast(A*)obj, &get, cast(A)set));
|
||||
break;
|
||||
return atomic_fetch_op!(memory_order.memory_order_seq_cst, "|=")(cast(A*)obj, arg);
|
||||
}
|
||||
|
||||
return get;
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@trusted unittest
|
||||
{
|
||||
shared(int) val = 5;
|
||||
atomic_fetch_or_explicit_impl(&val, 3, memory_order.memory_order_seq_cst);
|
||||
|
@ -952,21 +883,11 @@ pragma(inline, true)
|
|||
A atomic_fetch_xor_impl(A, M)(shared(A)* obj, M arg) @trusted
|
||||
{
|
||||
assert(obj !is null);
|
||||
|
||||
// copied from atomicOp
|
||||
|
||||
A set, get = atomicLoad(cast(A*)obj);
|
||||
|
||||
do
|
||||
{
|
||||
set = get ^ arg;
|
||||
} while (!atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_seq_cst)(cast(A*)obj, &get, cast(A)set));
|
||||
|
||||
return get;
|
||||
return atomic_fetch_op!(memory_order.memory_order_seq_cst, "^=")(cast(A*)obj, arg);
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@trusted unittest
|
||||
{
|
||||
shared(int) val = 5;
|
||||
atomic_fetch_xor_impl(&val, 3);
|
||||
|
@ -979,56 +900,23 @@ A atomic_fetch_xor_explicit_impl(A, M)(shared(A)* obj, M arg, memory_order order
|
|||
{
|
||||
assert(obj !is null);
|
||||
|
||||
A set, get;
|
||||
|
||||
final switch(order)
|
||||
{
|
||||
case memory_order.memory_order_relaxed:
|
||||
get = atomicLoad!(memory_order.memory_order_relaxed)(cast(A*)obj);
|
||||
do
|
||||
{
|
||||
set = get ^ arg;
|
||||
} while (!atomicCompareExchangeWeak!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(A*)obj, &get, cast(A)set));
|
||||
break;
|
||||
|
||||
return atomic_fetch_op!(memory_order.memory_order_relaxed, "^=")(cast(A*)obj, arg);
|
||||
case memory_order.memory_order_acquire:
|
||||
get = atomicLoad!(memory_order.memory_order_acquire)(cast(A*)obj);
|
||||
do
|
||||
{
|
||||
set = get ^ arg;
|
||||
} while (!atomicCompareExchangeWeak!(memory_order.memory_order_acquire, memory_order.memory_order_acquire)(cast(A*)obj, &get, cast(A)set));
|
||||
break;
|
||||
|
||||
return atomic_fetch_op!(memory_order.memory_order_acquire, "^=")(cast(A*)obj, arg);
|
||||
case memory_order.memory_order_release:
|
||||
get = atomicLoad!(memory_order.memory_order_relaxed)(cast(A*)obj);
|
||||
do
|
||||
{
|
||||
set = get ^ arg;
|
||||
} while (!atomicCompareExchangeWeak!(memory_order.memory_order_release, memory_order.memory_order_release)(cast(A*)obj, &get, cast(A)set));
|
||||
break;
|
||||
|
||||
return atomic_fetch_op!(memory_order.memory_order_release, "^=")(cast(A*)obj, arg);
|
||||
case memory_order.memory_order_acq_rel:
|
||||
get = atomicLoad!(memory_order.memory_order_acq_rel)(cast(A*)obj);
|
||||
do
|
||||
{
|
||||
set = get ^ arg;
|
||||
} while (!atomicCompareExchangeWeak!(memory_order.memory_order_acq_rel, memory_order.memory_order_acq_rel)(cast(A*)obj, &get, cast(A)set));
|
||||
break;
|
||||
|
||||
return atomic_fetch_op!(memory_order.memory_order_acq_rel, "^=")(cast(A*)obj, arg);
|
||||
case memory_order.memory_order_seq_cst:
|
||||
get = atomicLoad!(memory_order.memory_order_relaxed)(cast(A*)obj);
|
||||
do
|
||||
{
|
||||
set = get ^ arg;
|
||||
} while (!atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_seq_cst)(cast(A*)obj, &get, cast(A)set));
|
||||
break;
|
||||
return atomic_fetch_op!(memory_order.memory_order_seq_cst, "^=")(cast(A*)obj, arg);
|
||||
}
|
||||
|
||||
return get;
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@trusted unittest
|
||||
{
|
||||
shared(int) val = 5;
|
||||
atomic_fetch_xor_explicit_impl(&val, 3, memory_order.memory_order_seq_cst);
|
||||
|
@ -1040,21 +928,11 @@ pragma(inline, true)
|
|||
A atomic_fetch_and_impl(A, M)(shared(A)* obj, M arg) @trusted
|
||||
{
|
||||
assert(obj !is null);
|
||||
|
||||
// copied from atomicOp
|
||||
|
||||
A set, get = atomicLoad(cast(A*)obj);
|
||||
|
||||
do
|
||||
{
|
||||
set = get & arg;
|
||||
} while (!atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_seq_cst)(cast(A*)obj, &get, cast(A)set));
|
||||
|
||||
return get;
|
||||
return atomic_fetch_op!(memory_order.memory_order_seq_cst, "&=")(cast(A*)obj, arg);
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
@trusted unittest
|
||||
{
|
||||
shared(int) val = 5;
|
||||
atomic_fetch_and_impl(&val, 3);
|
||||
|
@ -1067,52 +945,19 @@ A atomic_fetch_and_explicit_impl(A, M)(shared(A)* obj, M arg, memory_order order
|
|||
{
|
||||
assert(obj !is null);
|
||||
|
||||
A set, get;
|
||||
|
||||
final switch(order)
|
||||
{
|
||||
case memory_order.memory_order_relaxed:
|
||||
get = atomicLoad!(memory_order.memory_order_relaxed)(cast(A*)obj);
|
||||
do
|
||||
{
|
||||
set = get & arg;
|
||||
} while (!atomicCompareExchangeWeak!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(A*)obj, &get, cast(A)set));
|
||||
break;
|
||||
|
||||
return atomic_fetch_op!(memory_order.memory_order_relaxed, "&=")(cast(A*)obj, arg);
|
||||
case memory_order.memory_order_acquire:
|
||||
get = atomicLoad!(memory_order.memory_order_acquire)(cast(A*)obj);
|
||||
do
|
||||
{
|
||||
set = get & arg;
|
||||
} while (!atomicCompareExchangeWeak!(memory_order.memory_order_acquire, memory_order.memory_order_acquire)(cast(A*)obj, &get, cast(A)set));
|
||||
break;
|
||||
|
||||
return atomic_fetch_op!(memory_order.memory_order_acquire, "&=")(cast(A*)obj, arg);
|
||||
case memory_order.memory_order_release:
|
||||
get = atomicLoad!(memory_order.memory_order_relaxed)(cast(A*)obj);
|
||||
do
|
||||
{
|
||||
set = get & arg;
|
||||
} while (!atomicCompareExchangeWeak!(memory_order.memory_order_release, memory_order.memory_order_release)(cast(A*)obj, &get, cast(A)set));
|
||||
break;
|
||||
|
||||
return atomic_fetch_op!(memory_order.memory_order_release, "&=")(cast(A*)obj, arg);
|
||||
case memory_order.memory_order_acq_rel:
|
||||
get = atomicLoad!(memory_order.memory_order_acq_rel)(cast(A*)obj);
|
||||
do
|
||||
{
|
||||
set = get & arg;
|
||||
} while (!atomicCompareExchangeWeak!(memory_order.memory_order_acq_rel, memory_order.memory_order_acq_rel)(cast(A*)obj, &get, cast(A)set));
|
||||
break;
|
||||
|
||||
return atomic_fetch_op!(memory_order.memory_order_acq_rel, "&=")(cast(A*)obj, arg);
|
||||
case memory_order.memory_order_seq_cst:
|
||||
get = atomicLoad!(memory_order.memory_order_relaxed)(cast(A*)obj);
|
||||
do
|
||||
{
|
||||
set = get & arg;
|
||||
} while (!atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_seq_cst)(cast(A*)obj, &get, cast(A)set));
|
||||
break;
|
||||
return atomic_fetch_op!(memory_order.memory_order_seq_cst, "&=")(cast(A*)obj, arg);
|
||||
}
|
||||
|
||||
return get;
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -1122,3 +967,33 @@ unittest
|
|||
atomic_fetch_and_explicit_impl(&val, 3, memory_order.memory_order_seq_cst);
|
||||
assert(atomic_load_impl(&val) == 1);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
pragma(inline, true)
|
||||
A atomic_fetch_op(memory_order order, string op, A, M)(A* obj, M arg) @trusted
|
||||
{
|
||||
static if (is(A : ulong) && (op == "+=" || op == "-="))
|
||||
{
|
||||
// these cannot handle floats
|
||||
static if (op == "+=")
|
||||
{
|
||||
return atomicFetchAdd!order(obj, arg);
|
||||
}
|
||||
else static if (op == "-=")
|
||||
{
|
||||
return atomicFetchSub!order(obj, arg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// copied from core.atomic
|
||||
A set, get = atomicLoad!(MemoryOrder.raw, A)(obj);
|
||||
do
|
||||
{
|
||||
set = get;
|
||||
mixin("set " ~ op ~ " arg;"); // will error if op (which is not exposed to user) is invalid
|
||||
} while (!atomicCompareExchangeWeak!(order, MemoryOrder.raw)(obj, &get, set));
|
||||
return get; // unlike core.atomic we return the prior value, not the new one.
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1066,13 +1066,11 @@ private:
|
|||
{
|
||||
m_pmem = valloc( sz );
|
||||
}
|
||||
else static if ( __traits( compiles, malloc ) )
|
||||
{
|
||||
m_pmem = malloc( sz );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pmem = null;
|
||||
import core.stdc.stdlib : malloc;
|
||||
|
||||
m_pmem = malloc( sz );
|
||||
}
|
||||
|
||||
if ( !m_pmem )
|
||||
|
@ -1116,11 +1114,8 @@ private:
|
|||
// Free this fiber's stack.
|
||||
//
|
||||
final void freeStack() nothrow @nogc
|
||||
in
|
||||
{
|
||||
assert( m_pmem && m_ctxt );
|
||||
}
|
||||
do
|
||||
in(m_pmem)
|
||||
in(m_ctxt)
|
||||
{
|
||||
// NOTE: m_ctxt is guaranteed to be alive because it is held in the
|
||||
// global context list.
|
||||
|
@ -1140,11 +1135,7 @@ private:
|
|||
{
|
||||
munmap( m_pmem, m_size );
|
||||
}
|
||||
else static if ( __traits( compiles, valloc ) )
|
||||
{
|
||||
free( m_pmem );
|
||||
}
|
||||
else static if ( __traits( compiles, malloc ) )
|
||||
else
|
||||
{
|
||||
free( m_pmem );
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
6d6e0b9b9fb5e882c7296f719baf829feb4939a3
|
||||
31dedd7daa38004229cb4e49a03ac7a6e0f723dd
|
||||
|
||||
The first line of this file holds the git revision number of the last
|
||||
merge done from the dlang/phobos repository.
|
||||
|
|
|
@ -5053,7 +5053,8 @@ if (isInputRange!Range)
|
|||
_input = input;
|
||||
_sentinel = sentinel;
|
||||
_openRight = openRight;
|
||||
static if (isInputRange!Sentinel)
|
||||
static if (isInputRange!Sentinel
|
||||
&& is(immutable ElementEncodingType!Sentinel == immutable ElementEncodingType!Range))
|
||||
{
|
||||
_matchStarted = predSatisfied();
|
||||
_done = _input.empty || _sentinel.empty || openRight && _matchStarted;
|
||||
|
@ -5120,7 +5121,8 @@ if (isInputRange!Range)
|
|||
assert(!empty, "Can not popFront of an empty Until");
|
||||
if (!_openRight)
|
||||
{
|
||||
static if (isInputRange!Sentinel)
|
||||
static if (isInputRange!Sentinel
|
||||
&& is(immutable ElementEncodingType!Sentinel == immutable ElementEncodingType!Range))
|
||||
{
|
||||
_input.popFront();
|
||||
_done = _input.empty || _sentinel.empty;
|
||||
|
@ -5237,6 +5239,7 @@ pure @safe unittest
|
|||
assert(equal(r.save, "foo"));
|
||||
}
|
||||
}
|
||||
|
||||
// https://issues.dlang.org/show_bug.cgi?id=14543
|
||||
pure @safe unittest
|
||||
{
|
||||
|
@ -5267,3 +5270,10 @@ pure @safe unittest
|
|||
assert("one two three".until!((a,b)=>a.toUpper == b)("TWO", No.openRight).equal("one two"));
|
||||
}
|
||||
|
||||
// https://issues.dlang.org/show_bug.cgi?id=24342
|
||||
pure @safe unittest
|
||||
{
|
||||
import std.algorithm.comparison : equal;
|
||||
assert(["A", "BC", "D"].until("BC", No.openRight).equal(["A", "BC"]));
|
||||
assert([[1], [2, 3], [4]].until([2, 3], No.openRight).equal([[1], [2, 3]]));
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue