233 changes: 230 additions & 3 deletions src/dmd/dinterpret.d
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import dmd.statement;
import dmd.tokens;
import dmd.utf;
import dmd.visitor;

//debug = LOG;
/*************************************
* Entry point for CTFE.
* A compile-time result is required. Give an error if not possible.
Expand Down Expand Up @@ -2197,6 +2197,13 @@ public:
return;
}
result = getVarExp(e.loc, istate, e.var, goal);

if (result.op == TOK.type)
{
// we leave TypeExp alone
return ;
}

if (exceptionOrCant(result))
return;
if ((e.var.storage_class & (STC.ref_ | STC.out_)) == 0 && e.type.baseElemOf().ty != Tstruct)
Expand Down Expand Up @@ -2291,6 +2298,13 @@ public:
result = CTFEExp.cantexp;
}
}
else if (v.type.ty == Ttype)
{
// Hack
// We have to set values for Ttype.
result = v.type.defaultInitLiteral(e.loc);
setValue(v, result);
}
else if (v.type.size() == 0)
{
// Zero-length arrays don't need an initializer
Expand Down Expand Up @@ -2478,6 +2492,7 @@ public:
expandTuples(expsx);
if (expsx.dim != dim)
{
// printf("expsx: %s, e.elements %s\n", expsx.toChars(), e.elements.toChars());
e.error("CTFE internal error: invalid array literal");
result = CTFEExp.cantexp;
return;
Expand Down Expand Up @@ -2884,6 +2899,33 @@ public:
result = CTFEExp.cantexp;
}

override void visit(DotIdExp die)
{
debug (LOG)
{
printf("%s DotIdExp::interpret() %s\n", die.loc.toChars(), die.toChars());
}

if (die.ident == Id.stringof)
{
auto e1 = interpretRegion(die.e1, istate);
result = new StringExp(die.loc, e1.toString());
result.type = Type.tstring;
}
else if (die.ident == Id.__sizeof)
{
auto e1 = interpretRegion(die.e1, istate);
result = new IntegerExp(die.loc, e1.type.size(), Type.tsize_t);
result.type = Type.tsize_t;
}
else
{
die.error("identifier: %s could not be resolved for an alias variable", die.ident.toChars());
result = ErrorExp.get();
result.type = Type.terror;
}
}

override void visit(UnaExp e)
{
debug (LOG)
Expand Down Expand Up @@ -2934,6 +2976,176 @@ public:
}
}

override void visit(IsExp e)
{
auto targe = e.targ.isTypeExpression();
if(!targe)
{
e.error("is expressions within type functions may only use type expressions");
result = ErrorExp.get();
return ;
}
auto targ = ctfeInterpret(targe.exp);
auto te = targ.isTypeExp();
/// tet is short for typeExp.type;
auto tet = te ? te.type : null;
result = IntegerExp.createBool(tet && tet.ty != Terror && tet.ty != Tempty);

auto tspece = e.tspec ? e.tspec.isTypeExpression() : null;
auto tspec = tspece ? ctfeInterpret(tspece.exp) : null;
auto te_spec = tspec ? tspec.isTypeExp() : null;
auto ts = te_spec ? te_spec.type : e.tspec;

// handling of == and &&
// See IsExp handling in expressionsem.d
if (e.tspec && !e.id && !(e.parameters && e.parameters.dim))
{
assert(ts);
if (e.tok == TOK.colon)
{
result = IntegerExp.createBool(tet.implicitConvTo(ts) != MATCH.nomatch);
}
else if(e.tok == TOK.equal)
{
result = IntegerExp.createBool(tet.equals(ts));
}
}
else if (e.tok2 != TOK.reserved)
{
e.error("Complex pattern matching forms of is expressions are not supported in TypeFunctions yet");
result = IntegerExp.createBool(false);
}
}


override void visit(TraitsExp e)
{
TypeExp getTypeExp(RootObject o)
{
TypeExp te_result = null;

if (o.dyncast() != DYNCAST.expression || !(cast(Expression)o).isVarExp())
{
e.error("VarExp expected!");
}
else
{
auto e1 = interpretRegion(cast(VarExp)o, istate);
import dmd.asttypename;
te_result = e1.isTypeExp();
if (!te_result)
{
e.error("Type expected");
result = ErrorExp.get();
}
}

return te_result;
}

Dsymbol getDsymbolWithoutExpCtx(RootObject oarg)
{
if (auto e = isExpression(oarg))
{
if (e.op == TOK.dotVariable)
return (cast(DotVarExp)e).var;
if (e.op == TOK.dotTemplateDeclaration)
return (cast(DotTemplateExp)e).td;
if (e.op == TOK.type)
{
auto type = (cast(TypeExp)e).type;
return type.toDsymbol(null);
}
}
return getDsymbol(oarg);
}
debug (LOG)
{
printf("%s TraitsExp::interpret() %s\n", e.loc.toChars(), e.toChars());
}
// some checking has already been done by semantic
/+ DISABLED UNTIL WE GET TALIAS
if (e.ident == Id.getAttributes)
{
auto o = (*e.args)[0];
if (o.dyncast() != DYNCAST.expression || !(cast(Expression)o).isVarExp())
{
e.error("VarExp expected!");
result = ErrorExp.get();
return ;
}
// we've got a var exp;
auto e1 = interpretRegion(cast(VarExp)o, istate);
auto po = isParameter(o);
auto s = getDsymbolWithoutExpCtx(e1);
UserAttributeDeclaration udad = null;
if (po)
{
udad = po.userAttribDecl;
}
else if (s)
{
if (s.isImport())
{
s = s.isImport().mod;
}
//printf("getAttributes %s, attrs = %p, scope = %p\n", s.toChars(), s.userAttribDecl, s.scope);
udad = s.userAttribDecl;
}
else
{
e.error("first argument is not a symbol");
result = ErrorExp.get();
return ;
}
auto exps = udad ? udad.getAttributes() : new Expressions();
expandTuples(exps);
result = new ArrayLiteralExp(e.loc, Type.talias.arrayOf(), exps);
}
+/

if (e.ident == Id.identifier)
{
auto o = (*e.args)[0];

auto te = getTypeExp(o);
result = new StringExp(e.loc, te.type.toString());
result.type = Type.tstring;
return;
}

if (e.ident == Id.getSuper)
{
auto o = (*e.args)[0];

auto te = getTypeExp(o);

if (te.type.ty != Tclass)
{
warning(e.loc, "__traits(getSuper), used on type `%s` which is not a class", te.toChars());
result = new TypeExp(e.loc, Type.tempty);
}
else
{
auto tc = cast(TypeClass) te.type;
auto cd = tc.sym;
if (cd.semanticRun < PASS.semanticdone)
cd.dsymbolSemantic(null);
auto bases = cd.baseclasses;

auto baseType = bases.length ? (*bases)[0].type : Type.tempty;

result = new TypeExp(e.loc, baseType);
}

return;
}
}

extern (D) private void interpretCommon(BinExp e, fp_t fp)
{
debug (LOG)
Expand Down Expand Up @@ -3503,7 +3715,8 @@ public:
if (newval.type.ty != Tarray)
{
newval = copyLiteral(newval).copy();
newval.type = e.e2.type; // repaint type
if (newval.op != TOK.type) // don't repaint type expressions that's kindof destrucive
newval.type = e.e2.type; // repaint type
}
else
{
Expand Down Expand Up @@ -3786,7 +3999,13 @@ public:
Type t1b = e1.type.toBasetype();
bool wantCopy = t1b.baseElemOf().ty == Tstruct;

if (newval.op == TOK.structLiteral && oldval)
// Super super hacky!
if (e1.type.ty == Ttype)
{
// we are doing an alias assign ... so just assign!
oldval = newval;
}
else if (newval.op == TOK.structLiteral && oldval)
{
assert(oldval.op == TOK.structLiteral || oldval.op == TOK.arrayLiteral || oldval.op == TOK.string_);
newval = copyLiteral(newval).copy();
Expand Down Expand Up @@ -5736,6 +5955,14 @@ public:
result = CTFEExp.voidexp;
return;
}

// casts to alias just pass trough to the exp
if (e.to.ty == Ttype)
{
result = ctfeInterpret(e1);
//result.type = Type.Ttype;
return ;
}
if (e.to.ty == Tpointer && e1.op != TOK.null_)
{
Type pointee = (cast(TypePointer)e.type).next;
Expand Down
3 changes: 3 additions & 0 deletions src/dmd/dmangle.d
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ private immutable char[TMAX] mangleChar =
// X // variadic T t...)
// Y // variadic T t,...)
// Z // not variadic, end of parameters
Tempty : 'E', // doesn't matter has to be something

// '@' shouldn't appear anywhere in the deco'd names
Tident : '@',
Expand All @@ -108,6 +109,8 @@ private immutable char[TMAX] mangleChar =
Tvector : '@',
Ttraits : '@',
Tmixin : '@',
Ttype : '@',
Texp : '@',
];

unittest
Expand Down
3 changes: 2 additions & 1 deletion src/dmd/dstruct.d
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,8 @@ extern (C++) class StructDeclaration : AggregateDeclaration
}
}

argTypes = target.toArgTypes(type);
if (!type.isTypeType())
argTypes = target.toArgTypes(type);
}

/***************************************
Expand Down
16 changes: 16 additions & 0 deletions src/dmd/expression.d
Original file line number Diff line number Diff line change
Expand Up @@ -3391,12 +3391,20 @@ extern (C++) final class TypeExp : Expression

override bool checkType()
{
if (type.ty == Tempty)
{
return false;
}
error("type `%s` is not an expression", toChars());
return true;
}

override bool checkValue()
{
if (type.ty == Tempty)
{
return false;
}
error("type `%s` has no value", toChars());
return true;
}
Expand Down Expand Up @@ -4324,11 +4332,19 @@ extern (C++) abstract class BinExp : Expression

// CondExp uses 'a ? b : c' but we're comparing 'b : c'
TOK thisOp = (op == TOK.question) ? TOK.colon : op;
bool failedAlready = false;

if (!global.params.sk_typefunctions)
{
if (e1.op == TOK.type || e2.op == TOK.type)
{
error("incompatible types for `(%s) %s (%s)`: cannot use `%s` with types",
e1.toChars(), Token.toChars(thisOp), e2.toChars(), Token.toChars(op));
failedAlready = true;
}
}

if (failedAlready) {}
else if (e1.type.equals(e2.type))
{
error("incompatible types for `(%s) %s (%s)`: both operands are of type `%s`",
Expand Down
176 changes: 167 additions & 9 deletions src/dmd/expressionsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -1124,6 +1124,27 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 =
Dsymbol s;
Objects* tiargs;
Type tthis;

// This got added for UFCS on type function which can't do the usual way
{
if (e1.op == TOK.dotIdentifier)
{
DotIdExp die = cast(DotIdExp) e1;
auto p = searchUFCS(sc, die, die.ident);
import dmd.asttypename;
if (auto ds = p.isDsymbolExp())
{
auto ss = ds.s;
auto fd = ss.isFuncDeclaration();
if (fd)
{
e1 = new CallExp(e1.loc, fd, die.e1);
e1 = expressionSemantic(e1, sc);
}
}
}
}

if (e1.op == TOK.dot)
{
DotExp de = cast(DotExp)e1;
Expand Down Expand Up @@ -1326,8 +1347,11 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 =
{
VarExp ve = cast(VarExp)e1;
VarDeclaration v = ve.var.isVarDeclaration();
if (v && ve.checkPurity(sc, v))
return ErrorExp.get();
if (!v || !(v.type && (v.type.ty == Ttype && (!v.type.nextOf() || v.type.nextOf().ty == Ttype))))
{
if (v && ve.checkPurity(sc, v))
return ErrorExp.get();
}
}
if (e2)
return null;
Expand Down Expand Up @@ -1601,7 +1625,7 @@ private Expression rewriteOpAssign(BinExp exp)
* Returns:
* true a semantic error occurred
*/
private bool preFunctionParameters(Scope* sc, Expressions* exps, const bool reportErrors = true)
private bool preFunctionParameters(Scope* sc, Expressions* exps, const bool reportErrors = true, FuncDeclaration fd = null)
{
bool err = false;
if (exps)
Expand All @@ -1614,6 +1638,10 @@ private bool preFunctionParameters(Scope* sc, Expressions* exps, const bool repo
arg = resolveProperties(sc, arg);
if (arg.op == TOK.type)
{
if (fd && fd.isTFunction())
{
continue;
}
// for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
arg = resolveAliasThis(sc, arg);

Expand Down Expand Up @@ -3473,7 +3501,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
Type tb = exp.type.toBasetype();
//printf("tb: %s, deco = %s\n", tb.toChars(), tb.deco);
if (arrayExpressionSemantic(exp.newargs, sc) ||
preFunctionParameters(sc, exp.newargs))
preFunctionParameters(sc, exp.newargs, true))
{
return setError();
}
Expand All @@ -3492,7 +3520,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
return setError();
}
}
else if (preFunctionParameters(sc, exp.arguments))
else if (preFunctionParameters(sc, exp.arguments, true))
{
return setError();
}
Expand Down Expand Up @@ -4201,6 +4229,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
Expression ethis = null;
Type tthis = null;
Expression e1org = exp.e1;
FuncDeclaration fd = null;

if (exp.e1.op == TOK.comma)
{
Expand All @@ -4216,16 +4245,22 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
DelegateExp de = cast(DelegateExp)exp.e1;
exp.e1 = new DotVarExp(de.loc, de.e1, de.func, de.hasOverloads);
fd = de.func;
visit(exp);
return;
}
if (exp.e1.op == TOK.function_)
{
if (arrayExpressionSemantic(exp.arguments, sc) || preFunctionParameters(sc, exp.arguments))
FuncExp fe = cast(FuncExp)exp.e1;
fd = fe.fd;
bool argument_semantic_errored = arrayExpressionSemantic(exp.arguments, sc);
if ((!global.params.sk_typefunctions) && (!argument_semantic_errored))
argument_semantic_errored |= preFunctionParameters(sc, exp.arguments, true, fd);

if (argument_semantic_errored)
return setError();

// Run e1 semantic even if arguments have any errors
FuncExp fe = cast(FuncExp)exp.e1;
exp.e1 = callExpSemantic(fe, sc, exp.arguments);
if (exp.e1.op == TOK.error)
{
Expand Down Expand Up @@ -4420,7 +4455,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
result = exp.e1;
return;
}
if (arrayExpressionSemantic(exp.arguments, sc) || preFunctionParameters(sc, exp.arguments))
bool argument_semantic_errored = arrayExpressionSemantic(exp.arguments, sc);

if ((!global.params.sk_typefunctions) && (!argument_semantic_errored))
argument_semantic_errored |= preFunctionParameters(sc, exp.arguments, true, fd);

if (argument_semantic_errored)
return setError();

// Check for call operator overload
Expand Down Expand Up @@ -5359,6 +5399,58 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
result = e;
}

static struct ResolveResult
{
enum ResolveKind
{
Undefined,

Expression,
Type,
Symbol,
}

Type type;
Expression exp;
Dsymbol sym;

ResolveKind kind;
}

private ResolveResult resolve_(Type t, const ref Loc loc, Scope* sc)
{
ResolveResult r;
resolve(t, loc, sc, r.exp, r.type, r.sym);

if (r.exp)
{
assert(r.type is null && r.sym is null);
r.kind = ResolveResult.ResolveKind.Expression;
}

if (r.type)
{
assert(r.exp is null && r.sym is null);
r.kind = ResolveResult.ResolveKind.Type;
}


if (r.sym)
{
assert(r.exp is null && r.type is null);
r.kind = ResolveResult.ResolveKind.Symbol;
}


if (!global.gag)
{
error(loc, "Resolving `%s` failed.", t.toChars());
}


return r;
}

override void visit(IsExp e)
{
/* is(targ id tok tspec)
Expand Down Expand Up @@ -5411,8 +5503,28 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
}
if (e.id && !(sc.flags & SCOPE.condition))
{
if (!global.params.sk_typefunctions && sc.flags & SCOPE.ctfe)
{
e.error("can only declare type aliases within `static if` conditionals or `static assert`s");
return setError();
}
else
{
auto vd = new VarDeclaration(e.loc, Type.basic[Ttype], e.id, new ExpInitializer(e.loc,
new TypeExp(e.loc, new TypeError())));
if (e.tok2 == TOK.super_)
{
// vd.type = Type.basic[Ttype].arrayOf();
}
else
vd.type = Type.basic[Ttype];

//vd.setScope(sc);
vd.storage_class |= STCStorageClass.temp;
sc.insert(vd);

e.id = cast(Identifier) vd;
}
}

if (e.tok2 == TOK.package_ || e.tok2 == TOK.module_) // These is() expressions are special because they can work on modules, not just types.
Expand Down Expand Up @@ -5441,11 +5553,18 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
sc2.flags |= SCOPE.fullinst;
Type t = e.targ.trySemantic(e.loc, sc2);
sc2.pop();
if (!t) // errors, so condition is false
if (!t || t.ty == Tempty) // errors or empty type, so condition is false
return no();
e.targ = t;
}

if (!e.tspec && e.targ.isTypeExpression())
{
e.type = Type.tbool;
result = e;
return ;
}

if (e.tok2 != TOK.reserved)
{
switch (e.tok2)
Expand Down Expand Up @@ -5623,6 +5742,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
* is(targ : tspec)
*/
e.tspec = e.tspec.typeSemantic(e.loc, sc);
if (e.targ.isTypeExpression() || e.tspec.isTypeExpression())
{
e.type = Type.tbool;
result = e;
return ;
}
//printf("targ = %s, %s\n", e.targ.toChars(), e.targ.deco);
//printf("tspec = %s, %s\n", e.tspec.toChars(), e.tspec.deco);

Expand Down Expand Up @@ -9130,6 +9255,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
return setResult(ale1x);
ale.e1 = ale1x;

if (isTypeType(ale.e1.type))
{
exp.type = Type.tsize_t;
return setResult(exp);
}

Type tn = ale.e1.type.toBasetype().nextOf();
checkDefCtor(ale.loc, tn);

Expand Down Expand Up @@ -11714,6 +11845,33 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag)
if (e != exp)
return e;

if (exp.e1.type && exp.e1.type.ty == Ttype)
{
if (exp.ident == Id._init)
{
return new TypeExp(exp.loc, Type.basic[Tempty]);
}
// printf("resloving %s on a type-variable '%s'.type(%s)\n", exp.ident.toChars(), exp.e1.toChars(), exp.e1.type.toChars()); //debugline
if (exp.ident == Id.stringof)
goto Lskip;

if (exp.ident == Id.__sizeof
|| exp.ident == Id.length)
{
exp.type = Type.tsize_t;
}
/+ DISABLED UNTIL WE HAVE TALIAS
else if (exp.ident == Id._tupleof)
{
exp.type = Type.talias.arrayOf;
// yes this acutally is talias because it has more than just types
}
+/
return exp;
}
Lskip:


Expression eleft;
Expression eright;
if (exp.e1.op == TOK.dot)
Expand Down
26 changes: 26 additions & 0 deletions src/dmd/func.d
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,32 @@ extern (C++) class FuncDeclaration : Declaration
return f;
}

/// Determine if we are a type function.
final bool isTFunction()
{
// TODO: this function will not return the correct result for
// auto returning functions if the function does return
// a tuple with but just takes regular value arguments

bool hasAliasInDecl = false;

auto tf = type.toTypeFunction();

if (tf.nextOf && (tf.nextOf.ty == Ttype || isTypeType(tf.nextOf)))
hasAliasInDecl = true;

if (!hasAliasInDecl && tf.parameterList.parameters) foreach(p;*tf.parameterList.parameters)
{
if (p.type.ty == Ttype || isTypeType(p.type))
{
hasAliasInDecl = true;
break;
}
}

return hasAliasInDecl;
}

/****************************************************
* Resolve forward reference of function signature -
* parameter types, return type, and attributes.
Expand Down
1 change: 1 addition & 0 deletions src/dmd/globals.d
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ extern (C++) struct Param
bool mscoff = false; // for Win32: write MsCoff object files instead of OMF
DiagnosticReporting useDeprecated = DiagnosticReporting.inform; // how use of deprecated features are handled
bool stackstomp; // add stack stomping code
bool sk_typefunctions; /// enable type functions
bool useUnitTests; // generate unittest code
bool useInline = false; // inline expand functions
bool useDIP25; // implement http://wiki.dlang.org/DIP25
Expand Down
8 changes: 8 additions & 0 deletions src/dmd/glue.d
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,10 @@ void FuncDeclaration_toObjFile(FuncDeclaration fd, bool multiobj)
if (!fd.fbody)
return;

// Don't generate code for compile time only functions
if ((fd.flags & FUNCFLAG.compileTimeOnly) || fd.isTFunction())
return;

UnitTestDeclaration ud = fd.isUnitTestDeclaration();
if (ud && !global.params.useUnitTests)
return;
Expand Down Expand Up @@ -1291,6 +1295,10 @@ tym_t totym(Type tx)
t = TYint;
break;

case Ttype:
error(Loc.initial, "__type escaped into the backend");
break;

case Tnull:
t = TYnptr;
break;
Expand Down
6 changes: 6 additions & 0 deletions src/dmd/hdrgen.d
Original file line number Diff line number Diff line change
Expand Up @@ -3784,6 +3784,11 @@ private void typeToBufferx(Type t, OutBuffer* buf, HdrGenState* hgs)
buf.writeByte(')');
}

void visitExpression(TypeExpression t)
{
toCBuffer(t.exp, buf, hgs);
}

switch (t.ty)
{
default: return t.isTypeBasic() ?
Expand Down Expand Up @@ -3811,5 +3816,6 @@ private void typeToBufferx(Type t, OutBuffer* buf, HdrGenState* hgs)
case Tslice: return visitSlice(cast(TypeSlice)t);
case Tnull: return visitNull(cast(TypeNull)t);
case Tmixin: return visitMixin(cast(TypeMixin)t);
case Texp: return visitExpression(cast(TypeExpression)t);
}
}
1 change: 1 addition & 0 deletions src/dmd/id.d
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@ immutable Msgtable[] msgtable =
{ "getFunctionVariadicStyle" },
{ "getParameterStorageClasses" },
{ "getLinkage" },
{ "getSuper" },
{ "getUnitTests" },
{ "getVirtualIndex" },
{ "getPointerBitmap" },
Expand Down
2 changes: 2 additions & 0 deletions src/dmd/mars.d
Original file line number Diff line number Diff line change
Expand Up @@ -1866,6 +1866,8 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param
params.verbose = true;
else if (arg == "-vcg-ast")
params.vcg_ast = true;
else if (arg == "-sktf")
params.sk_typefunctions = true;
else if (arg == "-vtls") // https://dlang.org/dmd.html#switch-vtls
params.vtls = true;
else if (startsWith(p + 1, "vtemplates")) // https://dlang.org/dmd.html#switch-vtemplates
Expand Down
142 changes: 136 additions & 6 deletions src/dmd/mtype.d
Original file line number Diff line number Diff line change
Expand Up @@ -248,16 +248,21 @@ enum ENUMTY : int

Tdelegate,
Tnone,
Tempty, /// the empty type ∅

Ttype, /// Type type

Tvoid,

Tint8,
Tuns8,
Tint16,
Tuns16,
Tint32,
Tuns32,
Tint64,

Tuns64,

Tfloat32,
Tfloat64,
Tfloat80,
Expand Down Expand Up @@ -285,6 +290,7 @@ enum ENUMTY : int
Tuns128,
Ttraits,
Tmixin,
Texp,
TMAX,
}

Expand All @@ -300,6 +306,8 @@ alias Tstruct = ENUMTY.Tstruct;
alias Tenum = ENUMTY.Tenum;
alias Tdelegate = ENUMTY.Tdelegate;
alias Tnone = ENUMTY.Tnone;
alias Tempty = ENUMTY.Tempty;
alias Ttype = ENUMTY.Ttype;
alias Tvoid = ENUMTY.Tvoid;
alias Tint8 = ENUMTY.Tint8;
alias Tuns8 = ENUMTY.Tuns8;
Expand Down Expand Up @@ -334,6 +342,7 @@ alias Tint128 = ENUMTY.Tint128;
alias Tuns128 = ENUMTY.Tuns128;
alias Ttraits = ENUMTY.Ttraits;
alias Tmixin = ENUMTY.Tmixin;
alias Texp = ENUMTY.Texp;
alias TMAX = ENUMTY.TMAX;

alias TY = ubyte;
Expand Down Expand Up @@ -416,6 +425,7 @@ extern (C++) abstract class Type : ASTNode

type* ctype; // for back end

extern (C++) __gshared Type ttype;
extern (C++) __gshared Type tvoid;
extern (C++) __gshared Type tint8;
extern (C++) __gshared Type tuns8;
Expand Down Expand Up @@ -449,6 +459,8 @@ extern (C++) abstract class Type : ASTNode
extern (C++) __gshared Type tdstring; // immutable(dchar)[]
extern (C++) __gshared Type terror; // for error recovery
extern (C++) __gshared Type tnull; // for null type
extern (C++) __gshared Type tempty; // only used for __type.init


extern (C++) __gshared Type tsize_t; // matches size_t alias
extern (C++) __gshared Type tptrdiff_t; // matches ptrdiff_t alias
Expand Down Expand Up @@ -501,6 +513,7 @@ extern (C++) abstract class Type : ASTNode
sizeTy[Tvector] = __traits(classInstanceSize, TypeVector);
sizeTy[Ttraits] = __traits(classInstanceSize, TypeTraits);
sizeTy[Tmixin] = __traits(classInstanceSize, TypeMixin);
sizeTy[Texp] = __traits(classInstanceSize, TypeExpression);
return sizeTy;
}();

Expand Down Expand Up @@ -825,8 +838,10 @@ extern (C++) abstract class Type : ASTNode
stringtable._init(14_000);

// Set basic types
__gshared TY* basetab =
__gshared TY[] basetab =
[
Ttype,
Tempty,
Tvoid,
Tint8,
Tuns8,
Expand All @@ -851,17 +866,19 @@ extern (C++) abstract class Type : ASTNode
Tchar,
Twchar,
Tdchar,
Terror
];

for (size_t i = 0; basetab[i] != Terror; i++)
foreach(ty;basetab)
{
Type t = new TypeBasic(basetab[i]);
Type t = new TypeBasic(ty);
t = t.merge();
basic[basetab[i]] = t;
basic[ty] = t;
}
basic[Terror] = new TypeError();

ttype = basic[Ttype];
tempty = basic[Tempty];

tvoid = basic[Tvoid];
tint8 = basic[Tint8];
tuns8 = basic[Tuns8];
Expand Down Expand Up @@ -2234,6 +2251,8 @@ extern (C++) abstract class Type : ASTNode
//printf("to : %s\n", to.toChars());
if (this.equals(to))
return MATCH.exact;
if (to.ty == Ttype)
return MATCH.convert;
return MATCH.nomatch;
}

Expand Down Expand Up @@ -2699,6 +2718,7 @@ extern (C++) abstract class Type : ASTNode
inout(TypeNull) isTypeNull() { return ty == Tnull ? cast(typeof(return))this : null; }
inout(TypeMixin) isTypeMixin() { return ty == Tmixin ? cast(typeof(return))this : null; }
inout(TypeTraits) isTypeTraits() { return ty == Ttraits ? cast(typeof(return))this : null; }
inout(TypeExpression) isTypeExpression() { return ty == Texp ? cast(typeof(return))this : null; }
}

override void accept(Visitor v)
Expand Down Expand Up @@ -3063,6 +3083,15 @@ extern (C++) final class TypeBasic : Type
uint flags = 0;
switch (ty)
{

case Tempty:
d = Token.toChars(TOK.emptyType_);
break;

case Ttype:
d = Token.toChars(TOK._type__);
break;

case Tvoid:
d = Token.toChars(TOK.void_);
break;
Expand Down Expand Up @@ -3207,6 +3236,10 @@ extern (C++) final class TypeBasic : Type
//printf("TypeBasic::size()\n");
switch (ty)
{
case Ttype:
size = 0;
break;

case Tint8:
case Tuns8:
size = 1;
Expand Down Expand Up @@ -3325,6 +3358,12 @@ extern (C++) final class TypeBasic : Type
if (this == to)
return MATCH.exact;

// any type implicitly converts to __type;
if (to.ty == Ttype)
{
return MATCH.convert;
}

if (ty == to.ty)
{
if (mod == to.mod)
Expand Down Expand Up @@ -5410,6 +5449,48 @@ extern (C++) final class TypeTraits : Type
}
}

/******
* Type Expressions.
*
* Those are Wrapper types which are required to represnt
* A reference to an alias variable when it is used in place of a type
*
* We should only see this one in type functions.
*/
extern (C++) final class TypeExpression : Type
{
Loc loc;
Expression exp;

extern (D) this(const ref Loc loc, Expression exp)
{
super(Texp);
this.loc = loc;
this.exp = exp;
}

override const(char)* kind() const
{
return "type-expression";
}

override Type syntaxCopy()
{
return new TypeExpression(loc, exp.syntaxCopy());
}

override Dsymbol toDsymbol(Scope* sc)
{
return getDsymbol(exp);
}

override void accept(Visitor v)
{
v.visit(this);
}
}


/******
* Implements mixin types.
*
Expand Down Expand Up @@ -7204,3 +7285,52 @@ bool isCopyable(Type t)
}
return true;
}

/***************************************************
* Determine if type t is a Ttype on some level.
* Params:
* t = type to check
* Returns:
* true if it's a Ttype (a type type)
*/
static bool isTypeType(Type t)
{
// printf("isTypeType: %s\n", t.toChars());
static Type[128] prevTypes;
static size_t n = 0;
prevTypes[n++] = t;
scope(exit) prevTypes[--n] = null;

if (t.ty == Tenum) // hack! this is to avoid asking nextof of enums
return false;
if (t.ty == Ttype)
return true;
if (t.ty == Tstruct)
{
auto sym = (cast(TypeStruct)t).sym;
if (sym) foreach(f;sym.fields)
{
if (f.type && f.type.isTypeType())
{
return true;
}
}
}

Type tnext = null;
tnext = t.nextOf();
//printf("tnext: %s\n", tnext.toChars());


foreach(idx;0 .. n)
{
// crappy protection against infinite recursion
if (tnext is prevTypes[idx])
{
// printf("exiting because we're seeing %s again\n", t.toChars());
return false;
}
}

return tnext ? isTypeType(tnext) : false;
}
24 changes: 24 additions & 0 deletions src/dmd/parse.d
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,7 @@ final class Parser(AST) : Lexer
case TOK.complex64:
case TOK.complex80:
case TOK.void_:
case TOK._type__:
case TOK.alias_:
case TOK.identifier:
case TOK.super_:
Expand Down Expand Up @@ -3631,6 +3632,14 @@ final class Parser(AST) : Lexer
//printf("parseBasicType()\n");
switch (token.value)
{
case TOK._type__ :
t = AST.Type.ttype;
goto LabelX;

case TOK.emptyType_ :
t = AST.Type.tempty;
goto LabelX;

case TOK.void_:
t = AST.Type.tvoid;
goto LabelX;
Expand Down Expand Up @@ -4460,6 +4469,7 @@ final class Parser(AST) : Lexer
return a;
}
}

/* Look for:
* alias identifier = type;
* alias identifier(...) = type;
Expand Down Expand Up @@ -5678,6 +5688,7 @@ final class Parser(AST) : Lexer
case TOK.complex64:
case TOK.complex80:
case TOK.void_:
case TOK._type__:
// bug 7773: int.max is always a part of expression
if (peekNext() == TOK.dot)
goto Lexp;
Expand Down Expand Up @@ -6953,6 +6964,7 @@ final class Parser(AST) : Lexer
case TOK.complex64:
case TOK.complex80:
case TOK.void_:
case TOK._type__:
t = peek(t);
break;

Expand Down Expand Up @@ -7018,6 +7030,7 @@ final class Parser(AST) : Lexer
case TOK.complex64:
case TOK.complex80:
case TOK.void_:
case TOK._type__:
case TOK.int32Literal:
case TOK.uns32Literal:
case TOK.int64Literal:
Expand Down Expand Up @@ -7964,6 +7977,10 @@ final class Parser(AST) : Lexer
t = AST.Type.tvoid;
goto LabelX;

case TOK._type__:
t = AST.Type.ttype;
goto LabelX;

case TOK.int8:
t = AST.Type.tint8;
goto LabelX;
Expand Down Expand Up @@ -8063,6 +8080,12 @@ final class Parser(AST) : Lexer
e = new AST.CallExp(loc, e, parseArguments());
break;
}
// if the type is not followed by a dot then the type is a type expression
if (token.value != TOK.dot)
{
e = new AST.TypeExp(loc, t);
break;
}
check(TOK.dot, t.toChars());
if (token.value != TOK.identifier)
{
Expand Down Expand Up @@ -8567,6 +8590,7 @@ final class Parser(AST) : Lexer
case TOK.complex64:
case TOK.complex80:
case TOK.void_:
case TOK._type__:
{
// (type) una_exp
nextToken();
Expand Down
1 change: 1 addition & 0 deletions src/dmd/parsetimevisitor.d
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ public:
void visit(AST.TypeQualified t) { visit(cast(AST.Type)t); }
void visit(AST.TypeTraits t) { visit(cast(AST.Type)t); }
void visit(AST.TypeMixin t) { visit(cast(AST.Type)t); }
void visit(AST.TypeExpression t) { visit(cast(AST.Type)t); }

// TypeNext
void visit(AST.TypeReference t) { visit(cast(AST.TypeNext)t); }
Expand Down
2 changes: 2 additions & 0 deletions src/dmd/root/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ enum DYNCAST
class RootObject
{
public:
size_t serial;

RootObject() { }

virtual bool equals(const RootObject *o) const;
Expand Down
12 changes: 11 additions & 1 deletion src/dmd/root/rootobject.d
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,18 @@ enum DYNCAST : int

extern (C++) class RootObject
{
this() nothrow pure @nogc @safe
/*should be static shared*/ __gshared size_t nextSerial;

size_t serial;

this() nothrow @nogc
{
if (!__ctfe)
{
serial = ++nextSerial;
// this.serial = atomicFetchAdd(nextSerial, 1);
// assert(serial != 459715);
}
}

bool equals(const RootObject o) const
Expand Down
4 changes: 4 additions & 0 deletions src/dmd/semantic3.d
Original file line number Diff line number Diff line change
Expand Up @@ -475,9 +475,13 @@ private extern(C++) final class Semantic3Visitor : Visitor
if (f.parameterList.parameters)
foreach (fparam; *f.parameterList.parameters)
{
if (isTypeType(fparam.type))
funcdecl.flags |= FUNCFLAG.compileTimeOnly;

if (!fparam.ident)
continue; // never used, so ignore
// expand any tuples

if (fparam.type.ty != Ttuple)
continue;

Expand Down
16 changes: 11 additions & 5 deletions src/dmd/tokens.d
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,11 @@ enum TOK : ubyte
wchar_,
dchar_,
bool_,
emptyType_,
_type__,

// Aggregates
struct_ = 151,
struct_ = 153,
class_,
interface_,
union_,
Expand Down Expand Up @@ -223,7 +225,7 @@ enum TOK : ubyte
immutable_,

// Statements
if_ = 181,
if_ = 183,
else_,
while_,
for_,
Expand All @@ -249,7 +251,7 @@ enum TOK : ubyte
onScopeSuccess,

// Contracts
invariant_ = 205,
invariant_ = 207,

// Testing
unittest_,
Expand All @@ -259,7 +261,7 @@ enum TOK : ubyte
ref_,
macro_,

parameters = 210,
parameters = 212,
traits,
overloadSet,
pure_,
Expand All @@ -279,7 +281,7 @@ enum TOK : ubyte
vector,
pound,

interval = 229,
interval = 231,
voidExpression,
cantExpression,
showCtfeContext,
Expand Down Expand Up @@ -338,6 +340,8 @@ private immutable TOK[] keywords =
TOK.float64,
TOK.float80,
TOK.bool_,
TOK._type__,
TOK.emptyType_,
TOK.char_,
TOK.wchar_,
TOK.dchar_,
Expand Down Expand Up @@ -494,6 +498,8 @@ extern (C++) struct Token
TOK.float64: "double",
TOK.float80: "real",
TOK.bool_: "bool",
TOK._type__ : "__type__",
TOK.emptyType_ : "__emptyType",
TOK.char_: "char",
TOK.wchar_: "wchar",
TOK.dchar_: "dchar",
Expand Down
19 changes: 19 additions & 0 deletions src/dmd/toobj.d
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,25 @@ void toObjFile(Dsymbol ds, bool multiobj)
obj_append(sd);
return;
}
bool hasAliasMembers = false;
if (sd.members) foreach(Dsymbol m;*sd.members)
{
Type type = m.getType();

if (auto vd = m.isVarDeclaration())
{
type = vd.type;
}
if (type && (type.ty == Ttype || isTypeType(type)))
{
hasAliasMembers = true;
break;
}
}


if (hasAliasMembers)
return; // cannot generate a struct with alias members

// Anonymous structs/unions only exist as part of others,
// do not output forward referenced structs's
Expand Down
46 changes: 46 additions & 0 deletions src/dmd/traits.d
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,19 @@ private Expression pointerBitmap(TraitsExp e)
return ale;
}

private TraitsExp interceptTypeType(return ref TraitsExp e, RootObject o, Type resultType)
{
if (Type t = getType(o))
{
if (t.ty == Ttype)
{
e.type = resultType;
return e;
}
}
return null;
}

Expression semanticTraits(TraitsExp e, Scope* sc)
{
static if (LOGSEMANTIC)
Expand Down Expand Up @@ -797,6 +810,12 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
return dimError(1);

auto o = (*e.args)[0];

if (auto exp = interceptTypeType(e, o, Type.tstring))
{
return exp;
}

Identifier id;
if (auto po = isParameter(o))
{
Expand Down Expand Up @@ -1246,6 +1265,17 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
return dimError(1);

auto o = (*e.args)[0];
auto type = o.getType();
/+ reenable when we have talias
if (type && type.ty == Ttype)
{
// TODO: :PERFORMANCE: commonly used type; we should cache it
auto t = Type.talias.arrayOf();
// this to is actually talias since the attribs can be anything
e.type = t;
return e;
}
+/
auto po = isParameter(o);
auto s = getDsymbolWithoutExpCtx(o);
UserAttributeDeclaration udad = null;
Expand Down Expand Up @@ -1981,6 +2011,22 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
auto tup = new TupleExp(e.loc, exps);
return tup.expressionSemantic(sc);
}
else if (e.ident == Id.getSuper)
{
if (dim != 1)
return dimError(1);

auto o = (*e.args)[0];

if (auto exp = interceptTypeType(e, o, Type.ttype))
{
return exp;
}
else
{
e.error("trait getSuper is only suppoerted for type functions as for now");
}
}

static const(char)[] trait_search_fp(const(char)[] seed, ref int cost)
{
Expand Down
83 changes: 79 additions & 4 deletions src/dmd/typesem.d
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,12 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
return Type.terror;
}

Type visitExpression(TypeExpression te)
{
// printf("visitExpression\n");
return te;
}

Type visitType(Type t)
{
if (t.ty == Tint128 || t.ty == Tuns128)
Expand Down Expand Up @@ -699,7 +705,7 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)

Type visitSArray(TypeSArray mtype)
{
//printf("TypeSArray::semantic() %s\n", toChars());
//printf("TypeSArray::semantic() %s\n",mtype.toChars());
Type t;
Expression e;
Dsymbol s;
Expand Down Expand Up @@ -733,6 +739,24 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
return error();

Type tbn = tn.toBasetype();

if (tn.ty == Texp)
{
auto te = cast(TypeExpression) tn;
// - if type next is a TypeExpression we are most likely in a type function context -
// if the type of that expression is __type__* or __type__[] or an associative array with value type __type__
// the elem type (type.nextOf) should be __type__
// we can return the type __type__ as type of this.
// this has to be done now, since the value of the .dim expression might not be avilable at this time
// import dmd.asttypename;
// printf("Exp: %s(astTypeName: %s) exp.type: %s\n", te.exp.toChars(), te.exp.astTypeName().ptr, te.exp.type.toChars());

auto tnexp = te.exp.type.nextOf();

if (tnexp && tnexp.ty == Ttype)
return tnexp.addMod(mtype.mod);
}

if (mtype.dim)
{
//https://issues.dlang.org/show_bug.cgi?id=15478
Expand Down Expand Up @@ -1671,6 +1695,11 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
Dsymbol varDecl = mtype.toDsymbol(sc);
const(Loc) varDeclLoc = varDecl.getLoc();
Module varDeclModule = varDecl.getModule();
// special special case when the variable is a type variable
if (e.type.isTypeType())
{
return new TypeExpression(e.loc, e).addMod(mtype.mod);
}

.error(loc, "variable `%s` is used as a type", mtype.toChars());

Expand Down Expand Up @@ -2006,6 +2035,7 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
case Ttuple: return visitTuple (cast(TypeTuple)type);
case Tslice: return visitSlice(cast(TypeSlice)type);
case Tmixin: return visitMixin(cast(TypeMixin)type);
case Texp: return visitExpression(cast(TypeExpression)type);
}
}

Expand Down Expand Up @@ -2911,8 +2941,25 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type
}
mt.exp = exp2;

if (mt.exp.op == TOK.type ||
mt.exp.op == TOK.scope_)

Type t = mt.exp.type;
if (global.params.sk_typefunctions)
{
if (mt.exp.op == TOK.type)
{
auto te = mt.exp.isTypeExp();

if (te.type.ty == Terror || !global.params.sk_typefunctions)
goto Lerr;
else
{
pt = Type.basic[Ttype].addMod(mt.mod);
goto Lret;
}
}
}
if (mt.exp.op == TOK.scope_ ||
mt.exp.op == TOK.type)
{
if (mt.exp.checkType())
goto Lerr;
Expand All @@ -2939,7 +2986,6 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type
goto Lerr;
}

Type t = mt.exp.type;
if (!t)
{
error(loc, "expression `%s` has no type", mt.exp.toChars());
Expand Down Expand Up @@ -2967,6 +3013,7 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type
if (pt)
pt = pt.addMod(mt.mod);
}
Lret:
mt.inuse--;
}

Expand Down Expand Up @@ -3187,6 +3234,31 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag)
*/
e = new StringExp(e.loc, e.toString());
}
else if (ident == Id._tupleof)
{
if (e.type.ty == Tarray && e.type.nextOf().ty == Ttype)
{
import dmd.dinterpret;
import dmd.ctfeexpr;
auto res = ctfeInterpret(e);
if (!res || exceptionOrCantInterpret(res))
{
e.error("Cannot create tuple from alias[]: %s", e.toChars());
e = ErrorExp.get();
goto Lreturn;
}
auto ae = res.isArrayLiteralExp();
assert(ae);
e = new TupleExp(e.loc, ae.elements);
goto Lreturn;
}
else
{
e.error("You may only use .tupleof on alias[] in this context");
e = ErrorExp.get();
goto Lreturn;
}
}
else
e = mt.getProperty(sc, e.loc, ident, flag & DotExpFlag.gag);

Expand Down Expand Up @@ -4432,6 +4504,9 @@ Expression defaultInit(Type mt, const ref Loc loc)
error(loc, "`void` does not have a default initializer");
return ErrorExp.get();

case Ttype:
return new TypeExp(loc, Type.basic[Tempty]);

default:
break;
}
Expand Down
4 changes: 4 additions & 0 deletions test/compilable/test_alias_function_parameter.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/*
REQUIRED_ARGS: -sktf
*/
bool isSameSymbol(__type__ X,__type__ Y);
31 changes: 31 additions & 0 deletions test/compilable/test_alias_simple.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
REQUIRED_ARGS: -sktf
*/
alias type = __type__;

bool isType(type t)
{
return is(t);
}

static assert(isType(S1));

static assert(!isType("hello"));

struct S1 { double[2] x; }
struct S2 { int[16] i16; }

size_t sizeOf(type t)
{
return t.sizeof;
}
static assert(sizeOf(S1) == S1.sizeof);
static assert(sizeOf(S2) == S2.sizeof);

string stringOf(type y)
{
return __traits(identifier, y);
}
static assert(stringOf(S1) == S1.stringof);
static assert(stringOf(S2) == S2.stringof);