mirror of
https://github.com/dlang/dmd.git
synced 2025-04-28 06:00:13 +03:00
Optimize AliasAssign tuple building
This commit is contained in:
parent
cac3f6b299
commit
10d62fc382
8 changed files with 329 additions and 12 deletions
|
@ -563,8 +563,9 @@ extern (C++) abstract class Declaration : Dsymbol
|
|||
extern (C++) final class TupleDeclaration : Declaration
|
||||
{
|
||||
Objects* objects;
|
||||
bool isexp; // true: expression tuple
|
||||
TypeTuple tupletype; // !=null if this is a type tuple
|
||||
bool isexp; // true: expression tuple
|
||||
bool building; // it's growing in AliasAssign semantic
|
||||
|
||||
extern (D) this(const ref Loc loc, Identifier ident, Objects* objects)
|
||||
{
|
||||
|
@ -588,7 +589,7 @@ extern (C++) final class TupleDeclaration : Declaration
|
|||
*/
|
||||
|
||||
//printf("TupleDeclaration::getType() %s\n", toChars());
|
||||
if (isexp)
|
||||
if (isexp || building)
|
||||
return null;
|
||||
if (!tupletype)
|
||||
{
|
||||
|
@ -931,6 +932,19 @@ extern (C++) final class AliasDeclaration : Declaration
|
|||
}
|
||||
else
|
||||
{
|
||||
// stop AliasAssign tuple building
|
||||
if (aliassym)
|
||||
{
|
||||
if (auto td = aliassym.isTupleDeclaration())
|
||||
{
|
||||
if (td.building)
|
||||
{
|
||||
td.building = false;
|
||||
semanticRun = PASS.semanticdone;
|
||||
return td;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_import && _import._scope)
|
||||
{
|
||||
/* If this is an internal alias for selective/renamed import,
|
||||
|
|
|
@ -166,9 +166,9 @@ class TupleDeclaration final : public Declaration
|
|||
{
|
||||
public:
|
||||
Objects *objects;
|
||||
bool isexp; // true: expression tuple
|
||||
|
||||
TypeTuple *tupletype; // !=NULL if this is a type tuple
|
||||
bool isexp; // true: expression tuple
|
||||
bool building; // it's growing in AliasAssign semantic
|
||||
|
||||
TupleDeclaration *syntaxCopy(Dsymbol *) override;
|
||||
const char *kind() const override;
|
||||
|
|
|
@ -6332,6 +6332,10 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, Expressions*
|
|||
* resolve the alias of eponymous member.
|
||||
*/
|
||||
tempinst.aliasdecl = tempinst.aliasdecl.toAlias2();
|
||||
|
||||
// stop AliasAssign tuple building
|
||||
if (auto td = tempinst.aliasdecl.isTupleDeclaration())
|
||||
td.building = false;
|
||||
}
|
||||
|
||||
Laftersemantic:
|
||||
|
@ -6726,13 +6730,28 @@ private void aliasAssignSemantic(AliasAssign ds, Scope* sc)
|
|||
*/
|
||||
|
||||
const errors = global.errors;
|
||||
Dsymbol s;
|
||||
|
||||
// Try AliasSeq optimization
|
||||
if (auto ti = ds.type.isTypeInstance())
|
||||
{
|
||||
if (!ti.tempinst.findTempDecl(sc, null))
|
||||
return errorRet();
|
||||
if (auto tempinst = isAliasSeq(sc, ti))
|
||||
{
|
||||
s = aliasAssignInPlace(sc, tempinst, aliassym);
|
||||
if (!s)
|
||||
return errorRet();
|
||||
goto Lsymdone;
|
||||
}
|
||||
}
|
||||
|
||||
/* This section is needed because Type.resolve() will:
|
||||
* const x = 3;
|
||||
* alias y = x;
|
||||
* try to convert identifier x to 3.
|
||||
*/
|
||||
auto s = ds.type.toDsymbol(sc);
|
||||
s = ds.type.toDsymbol(sc);
|
||||
if (errors != global.errors)
|
||||
return errorRet();
|
||||
if (s == aliassym)
|
||||
|
@ -6784,6 +6803,7 @@ private void aliasAssignSemantic(AliasAssign ds, Scope* sc)
|
|||
|
||||
if (s) // it's a symbolic alias
|
||||
{
|
||||
Lsymdone:
|
||||
//printf("alias %s resolved to %s %s\n", toChars(), s.kind(), s.toChars());
|
||||
aliassym.type = null;
|
||||
aliassym.aliassym = s;
|
||||
|
@ -6812,6 +6832,135 @@ private void aliasAssignSemantic(AliasAssign ds, Scope* sc)
|
|||
ds.semanticRun = PASS.semanticdone;
|
||||
}
|
||||
|
||||
/***************************************
|
||||
* Expands template instance arguments inside 'alias assign' target declaration (aliassym),
|
||||
* instead of inside 'tempinst.tiargs' every time.
|
||||
* Params:
|
||||
* tempinst = AliasSeq instance
|
||||
* aliassym = the AliasDeclaration corresponding to AliasAssign
|
||||
* Returns:
|
||||
* null.
|
||||
*/
|
||||
private TupleDeclaration aliasAssignInPlace(Scope* sc, TemplateInstance tempinst,
|
||||
AliasDeclaration aliassym)
|
||||
{
|
||||
// Mark instance with semantic done, not needed but just in case.
|
||||
tempinst.inst = tempinst;
|
||||
tempinst.semanticRun = PASS.semanticdone;
|
||||
TupleDeclaration td;
|
||||
if (aliassym.type)
|
||||
{
|
||||
// Convert TypeTuple to TupleDeclaration to avoid back and forth allocations
|
||||
// in the assignment process
|
||||
if (auto tt = aliassym.type.isTypeTuple())
|
||||
{
|
||||
auto objs = new Objects(tt.arguments.length);
|
||||
foreach (i, p; *tt.arguments)
|
||||
(*objs)[i] = p.type;
|
||||
td = new TupleDeclaration(tempinst.loc, aliassym.ident, objs);
|
||||
td.storage_class |= STC.templateparameter;
|
||||
td.building = true;
|
||||
aliassym.type = null;
|
||||
}
|
||||
else if (aliassym.type.isTypeError())
|
||||
return null;
|
||||
|
||||
}
|
||||
else if (auto otd = aliassym.aliassym.isTupleDeclaration())
|
||||
{
|
||||
if (otd.building)
|
||||
td = otd;
|
||||
else
|
||||
{
|
||||
td = new TupleDeclaration(tempinst.loc, aliassym.ident, otd.objects.copy());
|
||||
td.storage_class |= STC.templateparameter;
|
||||
td.building = true;
|
||||
}
|
||||
}
|
||||
// If starting from single element in aliassym (td == null) we need to build the tuple
|
||||
// after semanticTiargs to keep same semantics (for example a FuncLiteraldeclaration
|
||||
// template argument is converted to FuncExp)
|
||||
if (td)
|
||||
aliassym.aliassym = td;
|
||||
aliassym.semanticRun = PASS.semanticdone;
|
||||
if (!TemplateInstance.semanticTiargs(tempinst.loc, sc, tempinst.tiargs, 0, td))
|
||||
{
|
||||
tempinst.errors = true;
|
||||
return null;
|
||||
}
|
||||
// The alias will stop tuple 'building' mode when used (in AliasDeclaration.toAlias(),
|
||||
// then TupleDeclaration.getType() will work again)
|
||||
aliassym.semanticRun = PASS.initial;
|
||||
if (!td)
|
||||
{
|
||||
td = new TupleDeclaration(tempinst.loc, aliassym.ident, tempinst.tiargs);
|
||||
td.storage_class |= STC.templateparameter;
|
||||
td.building = true;
|
||||
return td;
|
||||
}
|
||||
|
||||
auto tiargs = tempinst.tiargs;
|
||||
size_t oldlen = td.objects.length;
|
||||
size_t origstart;
|
||||
size_t insertidx;
|
||||
size_t insertlen;
|
||||
foreach (i, o; *tiargs)
|
||||
{
|
||||
if (o !is td)
|
||||
{
|
||||
++insertlen;
|
||||
continue;
|
||||
}
|
||||
// tuple contains itself (tuple = AliasSeq!(..., tuple, ...))
|
||||
if (insertlen) // insert any left element before
|
||||
{
|
||||
td.objects.insert(insertidx, (*tiargs)[i - insertlen .. i]);
|
||||
if (insertidx == 0) // reset original tuple start point
|
||||
origstart = insertlen;
|
||||
insertlen = 0;
|
||||
}
|
||||
if (insertidx) // insert tuple if found more than one time
|
||||
{
|
||||
td.objects.reserve(oldlen); // reserve first to assert a valid slice
|
||||
td.objects.pushSlice((*td.objects)[origstart .. origstart + oldlen]);
|
||||
}
|
||||
insertidx = td.objects.length;
|
||||
}
|
||||
if (insertlen)
|
||||
{
|
||||
if (insertlen != tiargs.length) // insert any left element
|
||||
td.objects.pushSlice((*tiargs)[$ - insertlen .. $]);
|
||||
else
|
||||
// just assign tiargs if tuple = AliasSeq!(nottuple, nottuple...)
|
||||
td.objects = tempinst.tiargs;
|
||||
}
|
||||
return td;
|
||||
}
|
||||
|
||||
/***************************************
|
||||
* Check if a template instance is a trivial AliasSeq but without other overloads.
|
||||
* We can only be 100% sure of being AliasSeq after running semanticTiargs()
|
||||
* and findBestMatch() but this optimization must happen before that.
|
||||
*/
|
||||
private TemplateInstance isAliasSeq(Scope* sc, TypeInstance ti)
|
||||
{
|
||||
auto tovers = ti.tempinst.tempdecl.isOverloadSet();
|
||||
foreach (size_t oi; 0 .. tovers ? tovers.a.dim : 1)
|
||||
{
|
||||
Dsymbol dstart = tovers ? tovers.a[oi] : ti.tempinst.tempdecl;
|
||||
int r = overloadApply(dstart, (Dsymbol s)
|
||||
{
|
||||
auto td = s.isTemplateDeclaration();
|
||||
if (!td || !td.isTrivialAliasSeq)
|
||||
return 1;
|
||||
return 0;
|
||||
});
|
||||
if (r)
|
||||
return null;
|
||||
}
|
||||
return ti.tempinst;
|
||||
}
|
||||
|
||||
/***************************************
|
||||
* Find all instance fields in `ad`, then push them into `fields`.
|
||||
*
|
||||
|
|
|
@ -6564,10 +6564,12 @@ extern (C++) class TemplateInstance : ScopeDsymbol
|
|||
* tiargs array of template arguments
|
||||
* flags 1: replace const variables with their initializers
|
||||
* 2: don't devolve Parameter to Type
|
||||
* atd tuple being optimized. If found, it's not expanded here
|
||||
* but in AliasAssign semantic.
|
||||
* Returns:
|
||||
* false if one or more arguments have errors.
|
||||
*/
|
||||
extern (D) static bool semanticTiargs(const ref Loc loc, Scope* sc, Objects* tiargs, int flags)
|
||||
extern (D) static bool semanticTiargs(const ref Loc loc, Scope* sc, Objects* tiargs, int flags, TupleDeclaration atd = null)
|
||||
{
|
||||
// Run semantic on each argument, place results in tiargs[]
|
||||
//printf("+TemplateInstance.semanticTiargs()\n");
|
||||
|
@ -6767,6 +6769,11 @@ extern (C++) class TemplateInstance : ScopeDsymbol
|
|||
TupleDeclaration d = sa.toAlias().isTupleDeclaration();
|
||||
if (d)
|
||||
{
|
||||
if (d is atd)
|
||||
{
|
||||
(*tiargs)[j] = d;
|
||||
continue;
|
||||
}
|
||||
// Expand tuple
|
||||
tiargs.remove(j);
|
||||
tiargs.insert(j, d.objects);
|
||||
|
|
|
@ -5787,8 +5787,9 @@ class TupleDeclaration final : public Declaration
|
|||
{
|
||||
public:
|
||||
Array<RootObject* >* objects;
|
||||
bool isexp;
|
||||
TypeTuple* tupletype;
|
||||
bool isexp;
|
||||
bool building;
|
||||
TupleDeclaration* syntaxCopy(Dsymbol* s) override;
|
||||
const char* kind() const override;
|
||||
Type* getType() override;
|
||||
|
|
|
@ -222,6 +222,16 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
extern (D) void insert(size_t index, T[] a) pure nothrow
|
||||
{
|
||||
size_t d = a.length;
|
||||
reserve(d);
|
||||
if (length != index)
|
||||
memmove(data.ptr + index + d, data.ptr + index, (length - index) * T.sizeof);
|
||||
memcpy(data.ptr + index, a.ptr, d * T.sizeof);
|
||||
length += d;
|
||||
}
|
||||
|
||||
void insert(size_t index, T ptr) pure nothrow
|
||||
{
|
||||
reserve(1);
|
||||
|
@ -414,6 +424,14 @@ unittest
|
|||
arrayA.zero();
|
||||
foreach(e; arrayA)
|
||||
assert(e == 0);
|
||||
|
||||
arrayA.setDim(0);
|
||||
arrayA.pushSlice([5, 6]);
|
||||
arrayA.insert(1, [1, 2]);
|
||||
assert(arrayA[] == [5, 1, 2, 6]);
|
||||
arrayA.insert(0, [7, 8]);
|
||||
arrayA.insert(arrayA.length, [0, 9]);
|
||||
assert(arrayA[] == [7, 8, 5, 1, 2, 6, 0, 9]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -38,3 +38,98 @@ alias TK2 = reverse!(int, const uint, X2);
|
|||
static assert(TK2[0] == 3);
|
||||
static assert(is(TK2[1] == const(uint)));
|
||||
static assert(is(TK2[2] == int));
|
||||
|
||||
/**************************************************/
|
||||
|
||||
template Tp(Args...)
|
||||
{
|
||||
alias Tp = AliasSeq!(int, 1, "asd", Args);
|
||||
static foreach (arg; Args)
|
||||
{
|
||||
Tp = AliasSeq!(4, Tp, "zxc", arg, Tp, 5, 4, int, Tp[0..2]);
|
||||
}
|
||||
}
|
||||
|
||||
void fun(){}
|
||||
|
||||
alias a1 = Tp!(char[], fun, x => x);
|
||||
static assert(
|
||||
__traits(isSame, a1, AliasSeq!(4, 4, 4, int, 1, "asd", char[], fun,
|
||||
x => x, "zxc", char[], int, 1, "asd", char[], fun, x => x,
|
||||
5, 4, int, int, 1, "zxc", fun, 4, int, 1, "asd", char[],
|
||||
fun, x => x, "zxc", char[], int, 1, "asd", char[], fun,
|
||||
x => x, 5, 4, int, int, 1, 5, 4, int, 4, int, "zxc", x => x,
|
||||
4, 4, int, 1, "asd", char[], fun, x => x, "zxc", char[],
|
||||
int, 1, "asd", char[], fun, x => x, 5, 4, int, int, 1,
|
||||
"zxc", fun, 4, int, 1, "asd", char[], fun, x => x, "zxc",
|
||||
char[], int, 1, "asd", char[], fun, x => x, 5, 4, int, int,
|
||||
1, 5, 4, int, 4, int, 5, 4, int, 4, 4)));
|
||||
|
||||
template Tp2(Args...)
|
||||
{
|
||||
alias Tp2 = () => 1;
|
||||
static foreach (i; 0..Args.length)
|
||||
Tp2 = AliasSeq!(Tp2, Args[i]);
|
||||
}
|
||||
|
||||
const x = 8;
|
||||
static assert(
|
||||
__traits(isSame, Tp2!(2, float, x), AliasSeq!(() => 1, 2, float, x)));
|
||||
|
||||
|
||||
enum F(int i) = i * i;
|
||||
|
||||
template staticMap2(alias fun, args...)
|
||||
{
|
||||
alias staticMap2 = AliasSeq!();
|
||||
static foreach (i; 0 .. args.length)
|
||||
staticMap2 = AliasSeq!(fun!(args[i]), staticMap2, fun!(args[i]));
|
||||
}
|
||||
|
||||
enum a2 = staticMap2!(F, 0, 1, 2, 3, 4);
|
||||
|
||||
struct Cmp(T...){}
|
||||
// isSame sucks
|
||||
static assert(is(Cmp!a2 == Cmp!(16, 9, 4, 1, 0, 0, 1, 4, 9, 16)));
|
||||
|
||||
template Tp3()
|
||||
{
|
||||
alias aa1 = int;
|
||||
static foreach (t; AliasSeq!(float, char[]))
|
||||
aa1 = AliasSeq!(aa1, t);
|
||||
static assert(is(aa1 == AliasSeq!(int, float, char[])));
|
||||
|
||||
alias aa2 = AliasSeq!int;
|
||||
static foreach (t; AliasSeq!(float, char[]))
|
||||
aa2 = AliasSeq!(aa2, t);
|
||||
static assert(is(aa2 == AliasSeq!(int, float, char[])));
|
||||
|
||||
alias aa3 = AliasSeq!int;
|
||||
aa3 = AliasSeq!(float, char);
|
||||
static assert(is(aa3 == AliasSeq!(float, char)));
|
||||
}
|
||||
alias a3 = Tp3!();
|
||||
|
||||
template Tp4() // Uses slow path because overload
|
||||
{
|
||||
alias AliasSeq(T...) = T;
|
||||
alias AliasSeq(alias f, T...) = T;
|
||||
|
||||
alias aa4 = int;
|
||||
aa4 = AliasSeq!(aa4, float);
|
||||
static assert(is(aa4 == AliasSeq!(int, float)));
|
||||
|
||||
}
|
||||
alias a4 = Tp4!();
|
||||
|
||||
template Tp5() // same tp overloaded, still uses fast path
|
||||
{
|
||||
alias AliasSeq2(T...) = T;
|
||||
alias AliasSeq = AliasSeq2;
|
||||
alias AliasSeq = AliasSeq2;
|
||||
|
||||
alias aa5 = int;
|
||||
aa5 = AliasSeq!(aa5, float);
|
||||
static assert(is(aa5 == AliasSeq!(int, float)));
|
||||
}
|
||||
alias a5 = Tp5!();
|
||||
|
|
33
compiler/test/fail_compilation/aliasassign2.d
Normal file
33
compiler/test/fail_compilation/aliasassign2.d
Normal file
|
@ -0,0 +1,33 @@
|
|||
/* TEST_OUTPUT:
|
||||
---
|
||||
fail_compilation/aliasassign2.d(16): Error: `alias aa1 = aa1;` cannot alias itself, use a qualified name to create an overload set
|
||||
fail_compilation/aliasassign2.d(19): Error: template instance `aliasassign2.Tp1!()` error instantiating
|
||||
fail_compilation/aliasassign2.d(24): Error: undefined identifier `unknown`
|
||||
fail_compilation/aliasassign2.d(26): Error: template instance `aliasassign2.Tp2!()` error instantiating
|
||||
fail_compilation/aliasassign2.d(31): Error: template instance `AliasSeqX!(aa3, 1)` template `AliasSeqX` is not defined, did you mean AliasSeq(T...)?
|
||||
fail_compilation/aliasassign2.d(33): Error: template instance `aliasassign2.Tp3!()` error instantiating
|
||||
---
|
||||
*/
|
||||
|
||||
alias AliasSeq(T...) = T;
|
||||
|
||||
template Tp1()
|
||||
{
|
||||
alias aa1 = aa1;
|
||||
aa1 = AliasSeq!(aa1, float);
|
||||
}
|
||||
alias a1 = Tp1!();
|
||||
|
||||
template Tp2()
|
||||
{
|
||||
alias aa2 = AliasSeq!();
|
||||
aa2 = AliasSeq!(aa2, unknown);
|
||||
}
|
||||
alias a2 = Tp2!();
|
||||
|
||||
template Tp3()
|
||||
{
|
||||
alias aa3 = AliasSeq!();
|
||||
aa3 = AliasSeqX!(aa3, 1);
|
||||
}
|
||||
alias a3 = Tp3!();
|
Loading…
Add table
Add a link
Reference in a new issue