blob: bcdbec5fe2d5b5a3fd61b46016afefa37c4c2178 [file] [log] [blame]
/**
* Semantic analysis for D types.
*
* Copyright: Copyright (C) 1999-2022 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/typesem.d, _typesem.d)
* Documentation: https://dlang.org/phobos/dmd_typesem.html
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/typesem.d
*/
module dmd.typesem;
import core.checkedint;
import core.stdc.string;
import core.stdc.stdio;
import dmd.access;
import dmd.aggregate;
import dmd.aliasthis;
import dmd.arrayop;
import dmd.arraytypes;
import dmd.astcodegen;
import dmd.astenums;
import dmd.dcast;
import dmd.dclass;
import dmd.declaration;
import dmd.denum;
import dmd.dimport;
import dmd.dmangle;
import dmd.dmodule;
import dmd.dscope;
import dmd.dstruct;
import dmd.dsymbol;
import dmd.dsymbolsem;
import dmd.dtemplate;
import dmd.errors;
import dmd.expression;
import dmd.expressionsem;
import dmd.func;
import dmd.globals;
import dmd.hdrgen;
import dmd.id;
import dmd.identifier;
import dmd.imphint;
import dmd.importc;
import dmd.init;
import dmd.initsem;
import dmd.visitor;
import dmd.mtype;
import dmd.objc;
import dmd.opover;
import dmd.parse;
import dmd.root.complex;
import dmd.root.ctfloat;
import dmd.root.rmem;
import dmd.common.outbuffer;
import dmd.root.rootobject;
import dmd.root.string;
import dmd.root.stringtable;
import dmd.safe;
import dmd.semantic3;
import dmd.sideeffect;
import dmd.target;
import dmd.tokens;
/**************************
* This evaluates exp while setting length to be the number
* of elements in the tuple t.
*/
private Expression semanticLength(Scope* sc, Type t, Expression exp)
{
if (auto tt = t.isTypeTuple())
{
ScopeDsymbol sym = new ArrayScopeSymbol(sc, tt);
sym.parent = sc.scopesym;
sc = sc.push(sym);
sc = sc.startCTFE();
exp = exp.expressionSemantic(sc);
exp = resolveProperties(sc, exp);
sc = sc.endCTFE();
sc.pop();
}
else
{
sc = sc.startCTFE();
exp = exp.expressionSemantic(sc);
exp = resolveProperties(sc, exp);
sc = sc.endCTFE();
}
return exp;
}
private Expression semanticLength(Scope* sc, TupleDeclaration tup, Expression exp)
{
ScopeDsymbol sym = new ArrayScopeSymbol(sc, tup);
sym.parent = sc.scopesym;
sc = sc.push(sym);
sc = sc.startCTFE();
exp = exp.expressionSemantic(sc);
exp = resolveProperties(sc, exp);
sc = sc.endCTFE();
sc.pop();
return exp;
}
/*************************************
* Resolve a tuple index, `s[oindex]`, by figuring out what `s[oindex]` represents.
* Setting one of pe/pt/ps.
* Params:
* loc = location for error messages
* sc = context
* s = symbol being indexed - could be a tuple, could be an expression
* pe = set if s[oindex] is an Expression, otherwise null
* pt = set if s[oindex] is a Type, otherwise null
* ps = set if s[oindex] is a Dsymbol, otherwise null
* oindex = index into s
*/
private void resolveTupleIndex(const ref Loc loc, Scope* sc, Dsymbol s, out Expression pe, out Type pt, out Dsymbol ps, RootObject oindex)
{
auto tup = s.isTupleDeclaration();
auto eindex = isExpression(oindex);
auto tindex = isType(oindex);
auto sindex = isDsymbol(oindex);
if (!tup)
{
// It's really an index expression
if (tindex)
eindex = new TypeExp(loc, tindex);
else if (sindex)
eindex = symbolToExp(sindex, loc, sc, false);
Expression e = new IndexExp(loc, symbolToExp(s, loc, sc, false), eindex);
e = e.expressionSemantic(sc);
resolveExp(e, pt, pe, ps);
return;
}
// Convert oindex to Expression, then try to resolve to constant.
if (tindex)
tindex.resolve(loc, sc, eindex, tindex, sindex);
if (sindex)
eindex = symbolToExp(sindex, loc, sc, false);
if (!eindex)
{
.error(loc, "index `%s` is not an expression", oindex.toChars());
pt = Type.terror;
return;
}
eindex = semanticLength(sc, tup, eindex);
eindex = eindex.ctfeInterpret();
if (eindex.op == EXP.error)
{
pt = Type.terror;
return;
}
const(uinteger_t) d = eindex.toUInteger();
if (d >= tup.objects.dim)
{
.error(loc, "tuple index `%llu` exceeds length %llu", d, cast(ulong)tup.objects.dim);
pt = Type.terror;
return;
}
RootObject o = (*tup.objects)[cast(size_t)d];
ps = isDsymbol(o);
if (auto t = isType(o))
pt = t.typeSemantic(loc, sc);
if (auto e = isExpression(o))
resolveExp(e, pt, pe, ps);
}
/*************************************
* Takes an array of Identifiers and figures out if
* it represents a Type, Expression, or Dsymbol.
* Params:
* mt = array of identifiers
* loc = location for error messages
* sc = context
* s = symbol to start search at
* scopesym = unused
* pe = set if expression otherwise null
* pt = set if type otherwise null
* ps = set if symbol otherwise null
* typeid = set if in TypeidExpression https://dlang.org/spec/expression.html#TypeidExpression
*/
private void resolveHelper(TypeQualified mt, const ref Loc loc, Scope* sc, Dsymbol s, Dsymbol scopesym,
out Expression pe, out Type pt, out Dsymbol ps, bool intypeid = false)
{
version (none)
{
printf("TypeQualified::resolveHelper(sc = %p, idents = '%s')\n", sc, mt.toChars());
if (scopesym)
printf("\tscopesym = '%s'\n", scopesym.toChars());
}
if (!s)
{
/* Look for what user might have intended
*/
const p = mt.mutableOf().unSharedOf().toChars();
auto id = Identifier.idPool(p, cast(uint)strlen(p));
if (const n = importHint(id.toString()))
error(loc, "`%s` is not defined, perhaps `import %.*s;` ?", p, cast(int)n.length, n.ptr);
else if (auto s2 = sc.search_correct(id))
error(loc, "undefined identifier `%s`, did you mean %s `%s`?", p, s2.kind(), s2.toChars());
else if (const q = Scope.search_correct_C(id))
error(loc, "undefined identifier `%s`, did you mean `%s`?", p, q);
else if ((id == Id.This && sc.getStructClassScope()) ||
(id == Id._super && sc.getClassScope()))
error(loc, "undefined identifier `%s`, did you mean `typeof(%s)`?", p, p);
else
error(loc, "undefined identifier `%s`", p);
pt = Type.terror;
return;
}
//printf("\t1: s = '%s' %p, kind = '%s'\n",s.toChars(), s, s.kind());
Declaration d = s.isDeclaration();
if (d && (d.storage_class & STC.templateparameter))
s = s.toAlias();
else
{
// check for deprecated or disabled aliases
// functions are checked after overloading
// templates are checked after matching constraints
if (!s.isFuncDeclaration() && !s.isTemplateDeclaration())
s.checkDeprecated(loc, sc);
if (d)
d.checkDisabled(loc, sc, true);
}
s = s.toAlias();
//printf("\t2: s = '%s' %p, kind = '%s'\n",s.toChars(), s, s.kind());
for (size_t i = 0; i < mt.idents.dim; i++)
{
RootObject id = mt.idents[i];
if (id.dyncast() == DYNCAST.expression ||
id.dyncast() == DYNCAST.type)
{
Type tx;
Expression ex;
Dsymbol sx;
resolveTupleIndex(loc, sc, s, ex, tx, sx, id);
if (sx)
{
s = sx.toAlias();
continue;
}
if (tx)
ex = new TypeExp(loc, tx);
assert(ex);
ex = typeToExpressionHelper(mt, ex, i + 1);
ex = ex.expressionSemantic(sc);
resolveExp(ex, pt, pe, ps);
return;
}
Type t = s.getType(); // type symbol, type alias, or type tuple?
uint errorsave = global.errors;
int flags = t is null ? SearchLocalsOnly : IgnorePrivateImports;
Dsymbol sm = s.searchX(loc, sc, id, flags);
if (sm)
{
if (!(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc, sm))
{
.error(loc, "`%s` is not visible from module `%s`", sm.toPrettyChars(), sc._module.toChars());
sm = null;
}
// Same check as in Expression.semanticY(DotIdExp)
else if (sm.isPackage() && checkAccess(sc, sm.isPackage()))
{
// @@@DEPRECATED_2.106@@@
// Should be an error in 2.106. Just remove the deprecation call
// and uncomment the null assignment
deprecation(loc, "%s %s is not accessible here, perhaps add 'static import %s;'", sm.kind(), sm.toPrettyChars(), sm.toPrettyChars());
//sm = null;
}
}
if (global.errors != errorsave)
{
pt = Type.terror;
return;
}
void helper3()
{
Expression e;
VarDeclaration v = s.isVarDeclaration();
FuncDeclaration f = s.isFuncDeclaration();
if (intypeid || !v && !f)
e = symbolToExp(s, loc, sc, true);
else
e = new VarExp(loc, s.isDeclaration(), true);
e = typeToExpressionHelper(mt, e, i);
e = e.expressionSemantic(sc);
resolveExp(e, pt, pe, ps);
}
//printf("\t3: s = %p %s %s, sm = %p\n", s, s.kind(), s.toChars(), sm);
if (intypeid && !t && sm && sm.needThis())
return helper3();
if (VarDeclaration v = s.isVarDeclaration())
{
// https://issues.dlang.org/show_bug.cgi?id=19913
// v.type would be null if it is a forward referenced member.
if (v.type is null)
v.dsymbolSemantic(sc);
if (v.storage_class & (STC.const_ | STC.immutable_ | STC.manifest) ||
v.type.isConst() || v.type.isImmutable())
{
// https://issues.dlang.org/show_bug.cgi?id=13087
// this.field is not constant always
if (!v.isThisDeclaration())
return helper3();
}
}
if (!sm)
{
if (!t)
{
if (s.isDeclaration()) // var, func, or tuple declaration?
{
t = s.isDeclaration().type;
if (!t && s.isTupleDeclaration()) // expression tuple?
return helper3();
}
else if (s.isTemplateInstance() ||
s.isImport() || s.isPackage() || s.isModule())
{
return helper3();
}
}
if (t)
{
sm = t.toDsymbol(sc);
if (sm && id.dyncast() == DYNCAST.identifier)
{
sm = sm.search(loc, cast(Identifier)id, IgnorePrivateImports);
if (!sm)
return helper3();
}
else
return helper3();
}
else
{
if (id.dyncast() == DYNCAST.dsymbol)
{
// searchX already handles errors for template instances
assert(global.errors);
}
else
{
assert(id.dyncast() == DYNCAST.identifier);
sm = s.search_correct(cast(Identifier)id);
if (sm)
error(loc, "identifier `%s` of `%s` is not defined, did you mean %s `%s`?", id.toChars(), mt.toChars(), sm.kind(), sm.toChars());
else
error(loc, "identifier `%s` of `%s` is not defined", id.toChars(), mt.toChars());
}
pe = ErrorExp.get();
return;
}
}
s = sm.toAlias();
}
if (auto em = s.isEnumMember())
{
// It's not a type, it's an expression
pe = em.getVarExp(loc, sc);
return;
}
if (auto v = s.isVarDeclaration())
{
/* This is mostly same with DsymbolExp::semantic(), but we cannot use it
* because some variables used in type context need to prevent lowering
* to a literal or contextful expression. For example:
*
* enum a = 1; alias b = a;
* template X(alias e){ alias v = e; } alias x = X!(1);
* struct S { int v; alias w = v; }
* // TypeIdentifier 'a', 'e', and 'v' should be EXP.variable,
* // because getDsymbol() need to work in AliasDeclaration::semantic().
*/
if (!v.type ||
!v.type.deco && v.inuse)
{
if (v.inuse) // https://issues.dlang.org/show_bug.cgi?id=9494
error(loc, "circular reference to %s `%s`", v.kind(), v.toPrettyChars());
else
error(loc, "forward reference to %s `%s`", v.kind(), v.toPrettyChars());
pt = Type.terror;
return;
}
if (v.type.ty == Terror)
pt = Type.terror;
else
pe = new VarExp(loc, v);
return;
}
if (auto fld = s.isFuncLiteralDeclaration())
{
//printf("'%s' is a function literal\n", fld.toChars());
auto e = new FuncExp(loc, fld);
pe = e.expressionSemantic(sc);
return;
}
version (none)
{
if (FuncDeclaration fd = s.isFuncDeclaration())
{
pe = new DsymbolExp(loc, fd);
return;
}
}
Type t;
while (1)
{
t = s.getType();
if (t)
break;
ps = s;
return;
}
if (auto ti = t.isTypeInstance())
if (ti != mt && !ti.deco)
{
if (!ti.tempinst.errors)
error(loc, "forward reference to `%s`", ti.toChars());
pt = Type.terror;
return;
}
if (t.ty == Ttuple)
pt = t;
else
pt = t.merge();
}
/************************************
* Transitively search a type for all function types.
* If any function types with parameters are found that have parameter identifiers
* or default arguments, remove those and create a new type stripped of those.
* This is used to determine the "canonical" version of a type which is useful for
* comparisons.
* Params:
* t = type to scan
* Returns:
* `t` if no parameter identifiers or default arguments found, otherwise a new type that is
* the same as t but with no parameter identifiers or default arguments.
*/
private Type stripDefaultArgs(Type t)
{
static Parameters* stripParams(Parameters* parameters)
{
static Parameter stripParameter(Parameter p)
{
Type t = stripDefaultArgs(p.type);
return (t != p.type || p.defaultArg || p.ident || p.userAttribDecl)
? new Parameter(p.storageClass, t, null, null, null)
: null;
}
if (parameters)
{
foreach (i, p; *parameters)
{
Parameter ps = stripParameter(p);
if (ps)
{
// Replace params with a copy we can modify
Parameters* nparams = new Parameters(parameters.dim);
foreach (j, ref np; *nparams)
{
Parameter pj = (*parameters)[j];
if (j < i)
np = pj;
else if (j == i)
np = ps;
else
{
Parameter nps = stripParameter(pj);
np = nps ? nps : pj;
}
}
return nparams;
}
}
}
return parameters;
}
if (t is null)
return t;
if (auto tf = t.isTypeFunction())
{
Type tret = stripDefaultArgs(tf.next);
Parameters* params = stripParams(tf.parameterList.parameters);
if (tret == tf.next && params == tf.parameterList.parameters)
return t;
TypeFunction tr = tf.copy().isTypeFunction();
tr.parameterList.parameters = params;
tr.next = tret;
//printf("strip %s\n <- %s\n", tr.toChars(), t.toChars());
return tr;
}
else if (auto tt = t.isTypeTuple())
{
Parameters* args = stripParams(tt.arguments);
if (args == tt.arguments)
return t;
TypeTuple tr = t.copy().isTypeTuple();
tr.arguments = args;
return tr;
}
else if (t.ty == Tenum)
{
// TypeEnum::nextOf() may be != NULL, but it's not necessary here.
return t;
}
else
{
Type tn = t.nextOf();
Type n = stripDefaultArgs(tn);
if (n == tn)
return t;
TypeNext tr = cast(TypeNext)t.copy();
tr.next = n;
return tr;
}
}
/******************************************
* We've mistakenly parsed `t` as a type.
* Redo `t` as an Expression only if there are no type modifiers.
* Params:
* t = mistaken type
* Returns:
* t redone as Expression, null if cannot
*/
Expression typeToExpression(Type t)
{
static Expression visitSArray(TypeSArray t)
{
if (auto e = t.next.typeToExpression())
return new ArrayExp(t.dim.loc, e, t.dim);
return null;
}
static Expression visitAArray(TypeAArray t)
{
if (auto e = t.next.typeToExpression())
{
if (auto ei = t.index.typeToExpression())
return new ArrayExp(t.loc, e, ei);
}
return null;
}
static Expression visitIdentifier(TypeIdentifier t)
{
return typeToExpressionHelper(t, new IdentifierExp(t.loc, t.ident));
}
static Expression visitInstance(TypeInstance t)
{
return typeToExpressionHelper(t, new ScopeExp(t.loc, t.tempinst));
}
// easy way to enable 'auto v = new int[mixin("exp")];' in 2.088+
static Expression visitMixin(TypeMixin t)
{
return new TypeExp(t.loc, t);
}
if (t.mod)
return null;
switch (t.ty)
{
case Tsarray: return visitSArray(t.isTypeSArray());
case Taarray: return visitAArray(t.isTypeAArray());
case Tident: return visitIdentifier(t.isTypeIdentifier());
case Tinstance: return visitInstance(t.isTypeInstance());
case Tmixin: return visitMixin(t.isTypeMixin());
default: return null;
}
}
/* Helper function for `typeToExpression`. Contains common code
* for TypeQualified derived classes.
*/
Expression typeToExpressionHelper(TypeQualified t, Expression e, size_t i = 0)
{
//printf("toExpressionHelper(e = %s %s)\n", EXPtoString(e.op).ptr, e.toChars());
foreach (id; t.idents[i .. t.idents.dim])
{
//printf("\t[%d] e: '%s', id: '%s'\n", i, e.toChars(), id.toChars());
final switch (id.dyncast())
{
// ... '. ident'
case DYNCAST.identifier:
e = new DotIdExp(e.loc, e, cast(Identifier)id);
break;
// ... '. name!(tiargs)'
case DYNCAST.dsymbol:
auto ti = (cast(Dsymbol)id).isTemplateInstance();
assert(ti);
e = new DotTemplateInstanceExp(e.loc, e, ti.name, ti.tiargs);
break;
// ... '[type]'
case DYNCAST.type: // https://issues.dlang.org/show_bug.cgi?id=1215
e = new ArrayExp(t.loc, e, new TypeExp(t.loc, cast(Type)id));
break;
// ... '[expr]'
case DYNCAST.expression: // https://issues.dlang.org/show_bug.cgi?id=1215
e = new ArrayExp(t.loc, e, cast(Expression)id);
break;
case DYNCAST.object:
case DYNCAST.tuple:
case DYNCAST.parameter:
case DYNCAST.statement:
case DYNCAST.condition:
case DYNCAST.templateparameter:
case DYNCAST.initializer:
assert(0);
}
}
return e;
}
/******************************************
* Perform semantic analysis on a type.
* Params:
* type = Type AST node
* loc = the location of the type
* sc = context
* Returns:
* `Type` with completed semantic analysis, `Terror` if errors
* were encountered
*/
extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
{
static Type error()
{
return Type.terror;
}
Type visitType(Type t)
{
// @@@DEPRECATED_2.110@@@
// Use of `cent` and `ucent` has always been an error.
// Starting from 2.100, recommend core.int128 as a replace for the
// lack of compiler support.
if (t.ty == Tint128 || t.ty == Tuns128)
{
.error(loc, "`cent` and `ucent` types are obsolete, use `core.int128.Cent` instead");
return error();
}
return t.merge();
}
Type visitVector(TypeVector mtype)
{
const errors = global.errors;
mtype.basetype = mtype.basetype.typeSemantic(loc, sc);
if (errors != global.errors)
return error();
mtype.basetype = mtype.basetype.toBasetype().mutableOf();
if (mtype.basetype.ty != Tsarray)
{
.error(loc, "T in __vector(T) must be a static array, not `%s`", mtype.basetype.toChars());
return error();
}
TypeSArray t = mtype.basetype.isTypeSArray();
const sz = cast(int)t.size(loc);
final switch (target.isVectorTypeSupported(sz, t.nextOf()))
{
case 0:
// valid
break;
case 1:
// no support at all
.error(loc, "SIMD vector types not supported on this platform");
return error();
case 2:
// invalid base type
.error(loc, "vector type `%s` is not supported on this platform", mtype.toChars());
return error();
case 3:
// invalid size
.error(loc, "%d byte vector type `%s` is not supported on this platform", sz, mtype.toChars());
return error();
}
return merge(mtype);
}
Type visitSArray(TypeSArray mtype)
{
//printf("TypeSArray::semantic() %s\n", toChars());
Type t;
Expression e;
Dsymbol s;
mtype.next.resolve(loc, sc, e, t, s);
if (auto tup = s ? s.isTupleDeclaration() : null)
{
mtype.dim = semanticLength(sc, tup, mtype.dim);
mtype.dim = mtype.dim.ctfeInterpret();
if (mtype.dim.op == EXP.error)
return error();
uinteger_t d = mtype.dim.toUInteger();
if (d >= tup.objects.dim)
{
.error(loc, "tuple index %llu exceeds %llu", cast(ulong)d, cast(ulong)tup.objects.dim);
return error();
}
RootObject o = (*tup.objects)[cast(size_t)d];
if (o.dyncast() != DYNCAST.type)
{
.error(loc, "`%s` is not a type", mtype.toChars());
return error();
}
return (cast(Type)o).addMod(mtype.mod);
}
if (t && t.ty == Terror)
return error();
Type tn = mtype.next.typeSemantic(loc, sc);
if (tn.ty == Terror)
return error();
Type tbn = tn.toBasetype();
if (mtype.dim)
{
auto errors = global.errors;
mtype.dim = semanticLength(sc, tbn, mtype.dim);
if (errors != global.errors)
return error();
mtype.dim = mtype.dim.optimize(WANTvalue);
mtype.dim = mtype.dim.ctfeInterpret();
if (mtype.dim.op == EXP.error)
return error();
errors = global.errors;
dinteger_t d1 = mtype.dim.toInteger();
if (errors != global.errors)
return error();
mtype.dim = mtype.dim.implicitCastTo(sc, Type.tsize_t);
mtype.dim = mtype.dim.optimize(WANTvalue);
if (mtype.dim.op == EXP.error)
return error();
errors = global.errors;
dinteger_t d2 = mtype.dim.toInteger();
if (errors != global.errors)
return error();
if (mtype.dim.op == EXP.error)
return error();
Type overflowError()
{
.error(loc, "`%s` size %llu * %llu exceeds 0x%llx size limit for static array",
mtype.toChars(), cast(ulong)tbn.size(loc), cast(ulong)d1, target.maxStaticDataSize);
return error();
}
if (d1 != d2)
return overflowError();
Type tbx = tbn.baseElemOf();
if (tbx.ty == Tstruct && !tbx.isTypeStruct().sym.members ||
tbx.ty == Tenum && !tbx.isTypeEnum().sym.members)
{
/* To avoid meaningless error message, skip the total size limit check
* when the bottom of element type is opaque.
*/
}
else if (tbn.isTypeBasic() ||
tbn.ty == Tpointer ||
tbn.ty == Tarray ||
tbn.ty == Tsarray ||
tbn.ty == Taarray ||
(tbn.ty == Tstruct && tbn.isTypeStruct().sym.sizeok == Sizeok.done) ||
tbn.ty == Tclass)
{
/* Only do this for types that don't need to have semantic()
* run on them for the size, since they may be forward referenced.
*/
bool overflow = false;
if (mulu(tbn.size(loc), d2, overflow) >= target.maxStaticDataSize || overflow)
return overflowError();
}
}
switch (tbn.ty)
{
case Ttuple:
{
// Index the tuple to get the type
assert(mtype.dim);
TypeTuple tt = tbn.isTypeTuple();
uinteger_t d = mtype.dim.toUInteger();
if (d >= tt.arguments.dim)
{
.error(loc, "tuple index %llu exceeds %llu", cast(ulong)d, cast(ulong)tt.arguments.dim);
return error();
}
Type telem = (*tt.arguments)[cast(size_t)d].type;
return telem.addMod(mtype.mod);
}
case Tfunction:
case Tnone:
.error(loc, "cannot have array of `%s`", tbn.toChars());
return error();
default:
break;
}
if (tbn.isscope())
{
.error(loc, "cannot have array of scope `%s`", tbn.toChars());
return error();
}
/* Ensure things like const(immutable(T)[3]) become immutable(T[3])
* and const(T)[3] become const(T[3])
*/
mtype.next = tn;
mtype.transitive();
return mtype.addMod(tn.mod).merge();
}
Type visitDArray(TypeDArray mtype)
{
Type tn = mtype.next.typeSemantic(loc, sc);
Type tbn = tn.toBasetype();
switch (tbn.ty)
{
case Ttuple:
return tbn;
case Tfunction:
case Tnone:
.error(loc, "cannot have array of `%s`", tbn.toChars());
return error();
case Terror:
return error();
default:
break;
}
if (tn.isscope())
{
.error(loc, "cannot have array of scope `%s`", tn.toChars());
return error();
}
mtype.next = tn;
mtype.transitive();
return merge(mtype);
}
Type visitAArray(TypeAArray mtype)
{
//printf("TypeAArray::semantic() %s index.ty = %d\n", mtype.toChars(), mtype.index.ty);
if (mtype.deco)
{
return mtype;
}
mtype.loc = loc;
if (sc)
sc.setNoFree();
// Deal with the case where we thought the index was a type, but
// in reality it was an expression.
if (mtype.index.ty == Tident || mtype.index.ty == Tinstance || mtype.index.ty == Tsarray || mtype.index.ty == Ttypeof || mtype.index.ty == Treturn || mtype.index.ty == Tmixin)
{
Expression e;
Type t;
Dsymbol s;
mtype.index.resolve(loc, sc, e, t, s);
// https://issues.dlang.org/show_bug.cgi?id=15478
if (s)
e = symbolToExp(s, loc, sc, false);
if (e)
{
// It was an expression -
// Rewrite as a static array
auto tsa = new TypeSArray(mtype.next, e);
return tsa.typeSemantic(loc, sc);
}
else if (t)
mtype.index = t.typeSemantic(loc, sc);
else
{
.error(loc, "index is not a type or an expression");
return error();
}
}
else
mtype.index = mtype.index.typeSemantic(loc, sc);
mtype.index = mtype.index.merge2();
if (mtype.index.nextOf() && !mtype.index.nextOf().isImmutable())
{
mtype.index = mtype.index.constOf().mutableOf();
version (none)
{
printf("index is %p %s\n", mtype.index, mtype.index.toChars());
mtype.index.check();
printf("index.mod = x%x\n", mtype.index.mod);
printf("index.ito = x%p\n", mtype.index.getMcache().ito);
if (mtype.index.getMcache().ito)
{
printf("index.ito.mod = x%x\n", mtype.index.getMcache().ito.mod);
printf("index.ito.ito = x%p\n", mtype.index.getMcache().ito.getMcache().ito);
}
}
}
switch (mtype.index.toBasetype().ty)
{
case Tfunction:
case Tvoid:
case Tnone:
case Ttuple:
.error(loc, "cannot have associative array key of `%s`", mtype.index.toBasetype().toChars());
goto case Terror;
case Terror:
return error();
default:
break;
}
Type tbase = mtype.index.baseElemOf();
while (tbase.ty == Tarray)
tbase = tbase.nextOf().baseElemOf();
if (auto ts = tbase.isTypeStruct())
{
/* AA's need typeid(index).equals() and getHash(). Issue error if not correctly set up.
*/
StructDeclaration sd = ts.sym;
if (sd.semanticRun < PASS.semanticdone)
sd.dsymbolSemantic(null);
// duplicate a part of StructDeclaration::semanticTypeInfoMembers
//printf("AA = %s, key: xeq = %p, xerreq = %p xhash = %p\n", toChars(), sd.xeq, sd.xerreq, sd.xhash);
if (sd.xeq && sd.xeq.isGenerated() && sd.xeq._scope && sd.xeq.semanticRun < PASS.semantic3done)
{
uint errors = global.startGagging();
sd.xeq.semantic3(sd.xeq._scope);
if (global.endGagging(errors))
sd.xeq = sd.xerreq;
}
//printf("AA = %s, key: xeq = %p, xhash = %p\n", toChars(), sd.xeq, sd.xhash);
const(char)* s = (mtype.index.toBasetype().ty != Tstruct) ? "bottom of " : "";
if (!sd.xeq)
{
// If sd.xhash != NULL:
// sd or its fields have user-defined toHash.
// AA assumes that its result is consistent with bitwise equality.
// else:
// bitwise equality & hashing
}
else if (sd.xeq == sd.xerreq)
{
if (search_function(sd, Id.eq))
{
.error(loc, "%sAA key type `%s` does not have `bool opEquals(ref const %s) const`", s, sd.toChars(), sd.toChars());
}
else
{
.error(loc, "%sAA key type `%s` does not support const equality", s, sd.toChars());
}
return error();
}
else if (!sd.xhash)
{
if (search_function(sd, Id.eq))
{
.error(loc, "%sAA key type `%s` should have `extern (D) size_t toHash() const nothrow @safe` if `opEquals` defined", s, sd.toChars());
}
else
{
.error(loc, "%sAA key type `%s` supports const equality but doesn't support const hashing", s, sd.toChars());
}
return error();
}
else
{
// defined equality & hashing
assert(sd.xeq && sd.xhash);
/* xeq and xhash may be implicitly defined by compiler. For example:
* struct S { int[] arr; }
* With 'arr' field equality and hashing, compiler will implicitly
* generate functions for xopEquals and xtoHash in TypeInfo_Struct.
*/
}
}
else if (tbase.ty == Tclass && !tbase.isTypeClass().sym.isInterfaceDeclaration())
{
ClassDeclaration cd = tbase.isTypeClass().sym;
if (cd.semanticRun < PASS.semanticdone)
cd.dsymbolSemantic(null);
if (!ClassDeclaration.object)
{
.error(Loc.initial, "missing or corrupt object.d");
fatal();
}
__gshared FuncDeclaration feq = null;
__gshared FuncDeclaration fcmp = null;
__gshared FuncDeclaration fhash = null;
if (!feq)
feq = search_function(ClassDeclaration.object, Id.eq).isFuncDeclaration();
if (!fcmp)
fcmp = search_function(ClassDeclaration.object, Id.cmp).isFuncDeclaration();
if (!fhash)
fhash = search_function(ClassDeclaration.object, Id.tohash).isFuncDeclaration();
assert(fcmp && feq && fhash);
if (feq.vtblIndex < cd.vtbl.dim && cd.vtbl[feq.vtblIndex] == feq)
{
version (all)
{
if (fcmp.vtblIndex < cd.vtbl.dim && cd.vtbl[fcmp.vtblIndex] != fcmp)
{
const(char)* s = (mtype.index.toBasetype().ty != Tclass) ? "bottom of " : "";
.error(loc, "%sAA key type `%s` now requires equality rather than comparison", s, cd.toChars());
errorSupplemental(loc, "Please override `Object.opEquals` and `Object.toHash`.");
}
}
}
}
mtype.next = mtype.next.typeSemantic(loc, sc).merge2();
mtype.transitive();
switch (mtype.next.toBasetype().ty)
{
case Tfunction:
case Tvoid:
case Tnone:
case Ttuple:
.error(loc, "cannot have associative array of `%s`", mtype.next.toChars());
goto case Terror;
case Terror:
return error();
default:
break;
}
if (mtype.next.isscope())
{
.error(loc, "cannot have array of scope `%s`", mtype.next.toChars());
return error();
}
return merge(mtype);
}
Type visitPointer(TypePointer mtype)
{
//printf("TypePointer::semantic() %s\n", toChars());
if (mtype.deco)
{
return mtype;
}
Type n = mtype.next.typeSemantic(loc, sc);
switch (n.toBasetype().ty)
{
case Ttuple:
.error(loc, "cannot have pointer to `%s`", n.toChars());
goto case Terror;
case Terror:
return error();
default:
break;
}
if (n != mtype.next)
{
mtype.deco = null;
}
mtype.next = n;
if (mtype.next.ty != Tfunction)
{
mtype.transitive();
return merge(mtype);
}
version (none)
{
return merge(mtype);
}
else
{
mtype.deco = merge(mtype).deco;
/* Don't return merge(), because arg identifiers and default args
* can be different
* even though the types match
*/
return mtype;
}
}
Type visitReference(TypeReference mtype)
{
//printf("TypeReference::semantic()\n");
Type n = mtype.next.typeSemantic(loc, sc);
if (n != mtype.next)
mtype.deco = null;
mtype.next = n;
mtype.transitive();
return merge(mtype);
}
Type visitFunction(TypeFunction mtype)
{
if (mtype.deco) // if semantic() already run
{
//printf("already done\n");
return mtype;
}
//printf("TypeFunction::semantic() this = %p\n", this);
//printf("TypeFunction::semantic() %s, sc.stc = %llx\n", mtype.toChars(), sc.stc);
bool errors = false;
if (mtype.inuse > global.recursionLimit)
{
mtype.inuse = 0;
.error(loc, "recursive type");
return error();
}
/* Copy in order to not mess up original.
* This can produce redundant copies if inferring return type,
* as semantic() will get called again on this.
*/
TypeFunction tf = mtype.copy().toTypeFunction();
if (mtype.parameterList.parameters)
{
tf.parameterList.parameters = mtype.parameterList.parameters.copy();
for (size_t i = 0; i < mtype.parameterList.parameters.dim; i++)
{
Parameter p = cast(Parameter)mem.xmalloc(__traits(classInstanceSize, Parameter));
memcpy(cast(void*)p, cast(void*)(*mtype.parameterList.parameters)[i], __traits(classInstanceSize, Parameter));
(*tf.parameterList.parameters)[i] = p;
}
}
if (sc.stc & STC.pure_)
tf.purity = PURE.fwdref;
if (sc.stc & STC.nothrow_)
tf.isnothrow = true;
if (sc.stc & STC.nogc)
tf.isnogc = true;
if (sc.stc & STC.ref_)
tf.isref = true;
if (sc.stc & STC.return_)
tf.isreturn = true;
if (sc.stc & STC.returnScope)
tf.isreturnscope = true;
if (sc.stc & STC.returninferred)
tf.isreturninferred = true;
if (sc.stc & STC.scope_)
tf.isScopeQual = true;
if (sc.stc & STC.scopeinferred)
tf.isscopeinferred = true;
// if (tf.isreturn && !tf.isref)
// tf.isScopeQual = true; // return by itself means 'return scope'
if (tf.trust == TRUST.default_)
{
if (sc.stc & STC.safe)
tf.trust = TRUST.safe;
else if (sc.stc & STC.system)
tf.trust = TRUST.system;
else if (sc.stc & STC.trusted)
tf.trust = TRUST.trusted;
}
if (sc.stc & STC.property)
tf.isproperty = true;
if (sc.stc & STC.live)
tf.islive = true;
tf.linkage = sc.linkage;
version (none)
{
/* If the parent is @safe, then this function defaults to safe
* too.
* If the parent's @safe-ty is inferred, then this function's @safe-ty needs
* to be inferred first.
*/
if (tf.trust == TRUST.default_)
for (Dsymbol p = sc.func; p; p = p.toParent2())
{
FuncDeclaration fd = p.isFuncDeclaration();
if (fd)
{
if (fd.isSafeBypassingInference())
tf.trust = TRUST.safe; // default to @safe
break;
}
}
}
bool wildreturn = false;
if (tf.next)
{
sc = sc.push();
sc.stc &= ~(STC.TYPECTOR | STC.FUNCATTR);
tf.next = tf.next.typeSemantic(loc, sc);
sc = sc.pop();
errors |= tf.checkRetType(loc);
if (tf.next.isscope() && !tf.isctor)
{
.error(loc, "functions cannot return `scope %s`", tf.next.toChars());
errors = true;
}
if (tf.next.hasWild())
wildreturn = true;
if (tf.isreturn && !tf.isref && !tf.next.hasPointers())
{
tf.isreturn = false;
}
}
/// Perform semantic on the default argument to a parameter
/// Modify the `defaultArg` field of `fparam`, which must not be `null`
/// Returns `false` whether an error was encountered.
static bool defaultArgSemantic (ref Parameter fparam, Scope* sc)
{
Expression e = fparam.defaultArg;
const isRefOrOut = fparam.isReference();
const isAuto = fparam.storageClass & (STC.auto_ | STC.autoref);
if (isRefOrOut && !isAuto)
{
e = e.expressionSemantic(sc);
e = resolveProperties(sc, e);
}
else
{
e = inferType(e, fparam.type);
Initializer iz = new ExpInitializer(e.loc, e);
iz = iz.initializerSemantic(sc, fparam.type, INITnointerpret);
e = iz.initializerToExpression();
}
if (e.op == EXP.function_) // https://issues.dlang.org/show_bug.cgi?id=4820
{
FuncExp fe = e.isFuncExp();
// Replace function literal with a function symbol,
// since default arg expression must be copied when used
// and copying the literal itself is wrong.
e = new VarExp(e.loc, fe.fd, false);
e = new AddrExp(e.loc, e);
e = e.expressionSemantic(sc);
}
if (isRefOrOut && (!isAuto || e.isLvalue())
&& !MODimplicitConv(e.type.mod, fparam.type.mod))
{
const(char)* errTxt = fparam.storageClass & STC.ref_ ? "ref" : "out";
.error(e.loc, "expression `%s` of type `%s` is not implicitly convertible to type `%s %s` of parameter `%s`",
e.toChars(), e.type.toChars(), errTxt, fparam.type.toChars(), fparam.toChars());
}
e = e.implicitCastTo(sc, fparam.type);
// default arg must be an lvalue
if (isRefOrOut && !isAuto &&
!(global.params.previewIn && (fparam.storageClass & STC.in_)) &&
global.params.rvalueRefParam != FeatureState.enabled)
e = e.toLvalue(sc, e);
fparam.defaultArg = e;
return (e.op != EXP.error);
}
ubyte wildparams = 0;
if (tf.parameterList.parameters)
{
/* Create a scope for evaluating the default arguments for the parameters
*/
Scope* argsc = sc.push();
argsc.stc = 0; // don't inherit storage class
argsc.visibility = Visibility(Visibility.Kind.public_);
argsc.func = null;
size_t dim = tf.parameterList.length;
for (size_t i = 0; i < dim; i++)
{
Parameter fparam = tf.parameterList[i];
fparam.storageClass |= STC.parameter;
mtype.inuse++;
fparam.type = fparam.type.typeSemantic(loc, argsc);
mtype.inuse--;
if (fparam.type.ty == Terror)
{
errors = true;
continue;
}
fparam.type = fparam.type.addStorageClass(fparam.storageClass);
if (fparam.storageClass & (STC.auto_ | STC.alias_ | STC.static_))
{
if (!fparam.type)
continue;
}
fparam.type = fparam.type.cAdjustParamType(sc); // adjust C array and function parameter types
Type t = fparam.type.toBasetype();
/* If fparam after semantic() turns out to be a tuple, the number of parameters may
* change.
*/
if (auto tt = t.isTypeTuple())
{
/* TypeFunction::parameter also is used as the storage of
* Parameter objects for FuncDeclaration. So we should copy
* the elements of TypeTuple::arguments to avoid unintended
* sharing of Parameter object among other functions.
*/
if (tt.arguments && tt.arguments.dim)
{
/* Propagate additional storage class from tuple parameters to their
* element-parameters.
* Make a copy, as original may be referenced elsewhere.
*/
size_t tdim = tt.arguments.dim;
auto newparams = new Parameters(tdim);
for (size_t j = 0; j < tdim; j++)
{
Parameter narg = (*tt.arguments)[j];
// https://issues.dlang.org/show_bug.cgi?id=12744
// If the storage classes of narg
// conflict with the ones in fparam, it's ignored.
StorageClass stc = fparam.storageClass | narg.storageClass;
StorageClass stc1 = fparam.storageClass & (STC.ref_ | STC.out_ | STC.lazy_);
StorageClass stc2 = narg.storageClass & (STC.ref_ | STC.out_ | STC.lazy_);
if (stc1 && stc2 && stc1 != stc2)
{
OutBuffer buf1; stcToBuffer(&buf1, stc1 | ((stc1 & STC.ref_) ? (fparam.storageClass & STC.auto_) : 0));
OutBuffer buf2; stcToBuffer(&buf2, stc2);
.error(loc, "incompatible parameter storage classes `%s` and `%s`",
buf1.peekChars(), buf2.peekChars());
errors = true;
stc = stc1 | (stc & ~(STC.ref_ | STC.out_ | STC.lazy_));
}
(*newparams)[j] = new Parameter(
stc, narg.type, narg.ident, narg.defaultArg, narg.userAttribDecl);
}
fparam.type = new TypeTuple(newparams);
fparam.type = fparam.type.typeSemantic(loc, argsc);
}
fparam.storageClass = STC.parameter;
/* Reset number of parameters, and back up one to do this fparam again,
* now that it is a tuple
*/
dim = tf.parameterList.length;
i--;
continue;
}
if (t.ty == Tfunction)
{
.error(loc, "cannot have parameter of function type `%s`", fparam.type.toChars());
errors = true;
}
else if (!fparam.isReference() &&
(t.ty == Tstruct || t.ty == Tsarray || t.ty == Tenum))
{
Type tb2 = t.baseElemOf();
if (tb2.ty == Tstruct && !tb2.isTypeStruct().sym.members ||
tb2.ty == Tenum && !tb2.isTypeEnum().sym.memtype)
{
if (global.params.previewIn && (fparam.storageClass & STC.in_))
{
.error(loc, "cannot infer `ref` for `in` parameter `%s` of opaque type `%s`",
fparam.toChars(), fparam.type.toChars());
}
else
.error(loc, "cannot have parameter of opaque type `%s` by value",
fparam.type.toChars());
errors = true;
}
}
else if (!(fparam.storageClass & STC.lazy_) && t.ty == Tvoid)
{
.error(loc, "cannot have parameter of type `%s`", fparam.type.toChars());
errors = true;
}
if (fparam.storageClass & STC.return_)
{
if (fparam.isReference())
{
// Disabled for the moment awaiting improvement to allow return by ref
// to be transformed into return by scope.
if (0 && !tf.isref)
{
auto stc = fparam.storageClass & (STC.ref_ | STC.out_);
.error(loc, "parameter `%s` is `return %s` but function does not return by `ref`",
fparam.ident ? fparam.ident.toChars() : "",
stcToString(stc).ptr);
errors = true;
}
}
else
{
if (!(fparam.storageClass & STC.scope_))
fparam.storageClass |= STC.scope_ | STC.scopeinferred; // 'return' implies 'scope'
if (tf.isref)
{
}
else if (tf.next && !tf.next.hasPointers() && tf.next.toBasetype().ty != Tvoid)
{
fparam.storageClass &= ~STC.return_; // https://issues.dlang.org/show_bug.cgi?id=18963
}
}
if (i + 1 == dim && tf.parameterList.varargs == VarArg.typesafe &&
(t.isTypeDArray() || t.isTypeClass()))
{
/* This is because they can be constructed on the stack
* https://dlang.org/spec/function.html#typesafe_variadic_functions
*/
.error(loc, "typesafe variadic function parameter `%s` of type `%s` cannot be marked `return`",
fparam.ident ? fparam.ident.toChars() : "", t.toChars());
errors = true;
}
}
if (fparam.storageClass & STC.out_)
{
if (ubyte m = fparam.type.mod & (MODFlags.immutable_ | MODFlags.const_ | MODFlags.wild))
{
.error(loc, "cannot have `%s out` parameter of type `%s`", MODtoChars(m), t.toChars());
errors = true;
}
else
{
Type tv = t.baseElemOf();
if (tv.ty == Tstruct && tv.isTypeStruct().sym.noDefaultCtor)
{
.error(loc, "cannot have `out` parameter of type `%s` because the default construction is disabled", fparam.type.toChars());
errors = true;
}
}
}
if (t.hasWild())
{
wildparams |= 1;
//if (tf.next && !wildreturn)
// error(loc, "inout on parameter means inout must be on return type as well (if from D1 code, replace with `ref`)");
}
/* Scope attribute is not necessary if the parameter type does not have pointers
*/
const sr = buildScopeRef(fparam.storageClass);
switch (sr)
{
case ScopeRef.Scope:
case ScopeRef.RefScope:
case ScopeRef.ReturnRef_Scope:
if (!fparam.type.hasPointers())
fparam.storageClass &= ~STC.scope_;
break;
case ScopeRef.ReturnScope:
case ScopeRef.Ref_ReturnScope:
if (!fparam.type.hasPointers())
fparam.storageClass &= ~(STC.return_ | STC.scope_ | STC.returnScope);
break;
default:
break;
}
// Remove redundant storage classes for type, they are already applied
fparam.storageClass &= ~(STC.TYPECTOR);
// -preview=in: add `ref` storage class to suited `in` params
if (global.params.previewIn && (fparam.storageClass & (STC.in_ | STC.ref_)) == STC.in_)
{
auto ts = t.baseElemOf().isTypeStruct();
const isPOD = !ts || ts.sym.isPOD();
if (!isPOD || target.preferPassByRef(t))
fparam.storageClass |= STC.ref_;
}
}
// Now that we completed semantic for the argument types,
// run semantic on their default values,
// bearing in mind tuples have been expanded.
// We need to keep a pair of [oidx, eidx] (original index,
// extended index), as we need to run semantic when `oidx` changes.
size_t tupleOrigIdx = size_t.max;
size_t tupleExtIdx = size_t.max;
foreach (oidx, oparam, eidx, eparam; tf.parameterList)
{
// oparam (original param) will always have the default arg
// if there's one, but `eparam` will not if it's an expanded
// tuple. When we see an expanded tuple, we need to save its
// position to get the offset in it later on.
if (oparam.defaultArg)
{
// Get the obvious case out of the way
if (oparam is eparam)
errors |= !defaultArgSemantic(eparam, argsc);
// We're seeing a new tuple
else if (tupleOrigIdx == size_t.max || tupleOrigIdx < oidx)
{
/* https://issues.dlang.org/show_bug.cgi?id=18572
*
* If a tuple parameter has a default argument, when expanding the parameter
* tuple the default argument tuple must also be expanded.
*/
tupleOrigIdx = oidx;
tupleExtIdx = eidx;
errors |= !defaultArgSemantic(oparam, argsc);
TupleExp te = oparam.defaultArg.isTupleExp();
if (te && te.exps && te.exps.length)
eparam.defaultArg = (*te.exps)[0];
}
// Processing an already-seen tuple
else
{
TupleExp te = oparam.defaultArg.isTupleExp();
if (te && te.exps && te.exps.length)
eparam.defaultArg = (*te.exps)[eidx - tupleExtIdx];
}
}
// We need to know the default argument to resolve `auto ref`,
// hence why this has to take place as the very last step.
/* Resolve "auto ref" storage class to be either ref or value,
* based on the argument matching the parameter
*/
if (eparam.storageClass & STC.auto_)
{
Expression farg = mtype.fargs && eidx < mtype.fargs.dim ?
(*mtype.fargs)[eidx] : eparam.defaultArg;
if (farg && (eparam.storageClass & STC.ref_))
{
if (!farg.isLvalue())
eparam.storageClass &= ~STC.ref_; // value parameter
eparam.storageClass &= ~STC.auto_; // https://issues.dlang.org/show_bug.cgi?id=14656
eparam.storageClass |= STC.autoref;
}
else if (mtype.incomplete && (eparam.storageClass & STC.ref_))
{
// the default argument may have been temporarily removed,
// see usage of `TypeFunction.incomplete`.
// https://issues.dlang.org/show_bug.cgi?id=19891
eparam.storageClass &= ~STC.auto_;
eparam.storageClass |= STC.autoref;
}
else
{
.error(loc, "`auto` can only be used as part of `auto ref` for template function parameters");
errors = true;
}
}
}
argsc.pop();
}
if (tf.isWild())
wildparams |= 2;
if (wildreturn && !wildparams)
{
.error(loc, "`inout` on `return` means `inout` must be on a parameter as well for `%s`", mtype.toChars());
errors = true;
}
tf.isInOutParam = (wildparams & 1) != 0;
tf.isInOutQual = (wildparams & 2) != 0;
if (tf.isproperty && (tf.parameterList.varargs != VarArg.none || tf.parameterList.length > 2))
{
.error(loc, "properties can only have zero, one, or two parameter");
errors = true;
}
if (tf.parameterList.varargs == VarArg.variadic && tf.linkage != LINK.d && tf.parameterList.length == 0 &&
!(sc.flags & SCOPE.Cfile))
{
.error(loc, "variadic functions with non-D linkage must have at least one parameter");
errors = true;
}
if (errors)
return error();
if (tf.next)
tf.deco = tf.merge().deco;
/* Don't return merge(), because arg identifiers and default args
* can be different
* even though the types match
*/
return tf;
}
Type visitDelegate(TypeDelegate mtype)
{
//printf("TypeDelegate::semantic() %s\n", mtype.toChars());
if (mtype.deco) // if semantic() already run
{
//printf("already done\n");
return mtype;
}
mtype.next = mtype.next.typeSemantic(loc, sc);
if (mtype.next.ty != Tfunction)
return error();
/* In order to deal with https://issues.dlang.org/show_bug.cgi?id=4028
* perhaps default arguments should
* be removed from next before the merge.
*/
version (none)
{
return mtype.merge();
}
else
{
/* Don't return merge(), because arg identifiers and default args
* can be different
* even though the types match
*/
mtype.deco = mtype.merge().deco;
return mtype;
}
}
Type visitIdentifier(TypeIdentifier mtype)
{
Type t;
Expression e;
Dsymbol s;
//printf("TypeIdentifier::semantic(%s)\n", mtype.toChars());
mtype.resolve(loc, sc, e, t, s);
if (t)
{
//printf("\tit's a type %d, %s, %s\n", t.ty, t.toChars(), t.deco);
return t.addMod(mtype.mod);
}
else
{
if (s)
{
auto td = s.isTemplateDeclaration;
if (td && td.onemember && td.onemember.isAggregateDeclaration)
.error(loc, "template %s `%s` is used as a type without instantiation"
~ "; to instantiate it use `%s!(arguments)`",
s.kind, s.toPrettyChars, s.ident.toChars);
else
.error(loc, "%s `%s` is used as a type", s.kind, s.toPrettyChars);
//assert(0);
}
else if (e.op == EXP.variable) // special case: variable is used as a type
{
/*
N.B. This branch currently triggers for the following code
template test(x* x)
{
}
i.e. the compiler prints "variable x is used as a type"
which isn't a particularly good error message (x is a variable?).
*/
Dsymbol varDecl = mtype.toDsymbol(sc);
const(Loc) varDeclLoc = varDecl.getLoc();
Module varDeclModule = varDecl.getModule(); //This can be null
.error(loc, "variable `%s` is used as a type", mtype.toChars());
//Check for null to avoid https://issues.dlang.org/show_bug.cgi?id=22574
if ((varDeclModule !is null) && varDeclModule != sc._module) // variable is imported
{
const(Loc) varDeclModuleImportLoc = varDeclModule.getLoc();
.errorSupplemental(
varDeclModuleImportLoc,
"variable `%s` is imported here from: `%s`",
varDecl.toChars,
varDeclModule.toPrettyChars,
);
}
.errorSupplemental(varDeclLoc, "variable `%s` is declared here", varDecl.toChars);
}
else
.error(loc, "`%s` is used as a type", mtype.toChars());
return error();
}
}
Type visitInstance(TypeInstance mtype)
{
Type t;
Expression e;
Dsymbol s;
//printf("TypeInstance::semantic(%p, %s)\n", this, toChars());
{
const errors = global.errors;
mtype.resolve(loc, sc, e, t, s);
// if we had an error evaluating the symbol, suppress further errors
if (!t && errors != global.errors)
return error();
}
if (!t)
{
if (!e && s && s.errors)
{
// if there was an error evaluating the symbol, it might actually
// be a type. Avoid misleading error messages.
.error(loc, "`%s` had previous errors", mtype.toChars());
}
else
.error(loc, "`%s` is used as a type", mtype.toChars());
return error();
}
return t;
}
Type visitTypeof(TypeTypeof mtype)
{
//printf("TypeTypeof::semantic() %s\n", mtype.toChars());
Expression e;
Type t;
Dsymbol s;
mtype.resolve(loc, sc, e, t, s);
if (s && (t = s.getType()) !is null)
t = t.addMod(mtype.mod);
if (!t)
{
.error(loc, "`%s` is used as a type", mtype.toChars());
return error();
}
return t;
}
Type visitTraits(TypeTraits mtype)
{
if (mtype.ty == Terror)
return mtype;
const inAlias = (sc.flags & SCOPE.alias_) != 0;
if (mtype.exp.ident != Id.allMembers &&
mtype.exp.ident != Id.derivedMembers &&
mtype.exp.ident != Id.getMember &&
mtype.exp.ident != Id.parent &&
mtype.exp.ident != Id.parameters &&
mtype.exp.ident != Id.child &&
mtype.exp.ident != Id.toType &&
mtype.exp.ident != Id.getOverloads &&
mtype.exp.ident != Id.getVirtualFunctions &&
mtype.exp.ident != Id.getVirtualMethods &&
mtype.exp.ident != Id.getAttributes &&
mtype.exp.ident != Id.getUnitTests &&
mtype.exp.ident != Id.getAliasThis)
{
static immutable (const(char)*)[2] ctxt = ["as type", "in alias"];
.error(mtype.loc, "trait `%s` is either invalid or not supported %s",
mtype.exp.ident.toChars, ctxt[inAlias]);
mtype.ty = Terror;
return mtype;
}
import dmd.traits : semanticTraits;
Type result;
if (Expression e = semanticTraits(mtype.exp, sc))
{
switch (e.op)
{
case EXP.dotVariable:
mtype.sym = e.isDotVarExp().var;
break;
case EXP.variable:
mtype.sym = e.isVarExp().var;
break;
case EXP.function_:
auto fe = e.isFuncExp();
mtype.sym = fe.td ? fe.td : fe.fd;
break;
case EXP.dotTemplateDeclaration:
mtype.sym = e.isDotTemplateExp().td;
break;
case EXP.dSymbol:
mtype.sym = e.isDsymbolExp().s;
break;
case EXP.template_:
mtype.sym = e.isTemplateExp().td;
break;
case EXP.scope_:
mtype.sym = e.isScopeExp().sds;
break;
case EXP.tuple:
TupleExp te = e.toTupleExp();
Objects* elems = new Objects(te.exps.dim);
foreach (i; 0 .. elems.dim)
{
auto src = (*te.exps)[i];
switch (src.op)
{
case EXP.type:
(*elems)[i] = src.isTypeExp().type;
break;
case EXP.dotType:
(*elems)[i] = src.isDotTypeExp().sym.isType();
break;
case EXP.overloadSet:
(*elems)[i] = src.isOverExp().type;
break;
default:
if (auto sym = isDsymbol(src))
(*elems)[i] = sym;
else
(*elems)[i] = src;
}
}
TupleDeclaration td = new TupleDeclaration(e.loc, Identifier.generateId("__aliastup"), elems);
mtype.sym = td;
break;
case EXP.dotType:
result = e.isDotTypeExp().sym.isType();
break;
case EXP.type:
result = e.isTypeExp().type;
break;
case EXP.overloadSet:
result = e.isOverExp().type;
break;
default:
break;
}
}
if (result)
result = result.addMod(mtype.mod);
if (!inAlias && !result)
{
if (!global.errors)
.error(mtype.loc, "`%s` does not give a valid type", mtype.toChars);
return error();
}
return result;
}
Type visitReturn(TypeReturn mtype)
{
//printf("TypeReturn::semantic() %s\n", toChars());
Expression e;
Type t;
Dsymbol s;
mtype.resolve(loc, sc, e, t, s);
if (s && (t = s.getType()) !is null)
t = t.addMod(mtype.mod);
if (!t)
{
.error(loc, "`%s` is used as a type", mtype.toChars());
return error();
}
return t;
}
Type visitStruct(TypeStruct mtype)
{
//printf("TypeStruct::semantic('%s')\n", mtype.toChars());
if (mtype.deco)
return mtype;
/* Don't semantic for sym because it should be deferred until
* sizeof needed or its members accessed.
*/
// instead, parent should be set correctly
assert(mtype.sym.parent);
if (mtype.sym.type.ty == Terror)
return error();
return merge(mtype);
}
Type visitEnum(TypeEnum mtype)
{
//printf("TypeEnum::semantic() %s\n", toChars());
return mtype.deco ? mtype : merge(mtype);
}
Type visitClass(TypeClass mtype)
{
//printf("TypeClass::semantic(%s)\n", mtype.toChars());
if (mtype.deco)
return mtype;
/* Don't semantic for sym because it should be deferred until
* sizeof needed or its members accessed.
*/
// instead, parent should be set correctly
assert(mtype.sym.parent);
if (mtype.sym.type.ty == Terror)
return error();
return merge(mtype);
}
Type visitTuple(TypeTuple mtype)
{
//printf("TypeTuple::semantic(this = %p)\n", this);
//printf("TypeTuple::semantic() %p, %s\n", this, toChars());
if (!mtype.deco)
mtype.deco = merge(mtype).deco;
/* Don't return merge(), because a tuple with one type has the
* same deco as that type.
*/
return mtype;
}
Type visitSlice(TypeSlice mtype)
{
//printf("TypeSlice::semantic() %s\n", toChars());
Type tn = mtype.next.typeSemantic(loc, sc);
//printf("next: %s\n", tn.toChars());
Type tbn = tn.toBasetype();
if (tbn.ty != Ttuple)
{
.error(loc, "can only slice tuple types, not `%s`", tbn.toChars());
return error();
}
TypeTuple tt = cast(TypeTuple)tbn;
mtype.lwr = semanticLength(sc, tbn, mtype.lwr);
mtype.upr = semanticLength(sc, tbn, mtype.upr);
mtype.lwr = mtype.lwr.ctfeInterpret();
mtype.upr = mtype.upr.ctfeInterpret();
if (mtype.lwr.op == EXP.error || mtype.upr.op == EXP.error)
return error();
uinteger_t i1 = mtype.lwr.toUInteger();
uinteger_t i2 = mtype.upr.toUInteger();
if (!(i1 <= i2 && i2 <= tt.arguments.dim))
{
.error(loc, "slice `[%llu..%llu]` is out of range of `[0..%llu]`",
cast(ulong)i1, cast(ulong)i2, cast(ulong)tt.arguments.dim);
return error();
}
mtype.next = tn;
mtype.transitive();
auto args = new Parameters();
args.reserve(cast(size_t)(i2 - i1));
foreach (arg; (*tt.arguments)[cast(size_t)i1 .. cast(size_t)i2])
{
args.push(arg);
}
Type t = new TypeTuple(args);
return t.typeSemantic(loc, sc);
}
Type visitMixin(TypeMixin mtype)
{
//printf("TypeMixin::semantic() %s\n", toChars());
Expression e;
Type t;
Dsymbol s;
mtype.resolve(loc, sc, e, t, s);
if (t && t.ty != Terror)
return t;
.error(mtype.loc, "`mixin(%s)` does not give a valid type", mtype.obj.toChars);
return error();
}
Type visitTag(TypeTag mtype)
{
//printf("TypeTag.semantic() %s\n", mtype.toChars());
if (mtype.resolved)
{
/* struct S s, *p;
*/
//printf("already resolved\n");
return mtype.resolved;
}
/* Find the current scope by skipping tag scopes.
* In C, tag scopes aren't considered scopes.
*/
Scope* sc2 = sc;
while (1)
{
sc2 = sc2.inner();
auto scopesym = sc2.scopesym;
if (scopesym.isStructDeclaration())
{
sc2 = sc2.enclosing;
continue;
}
break;
}
/* Declare mtype as a struct/union/enum declaration
*/
void declareTag()
{
void declare(ScopeDsymbol sd)
{
sd.members = mtype.members;
auto scopesym = sc2.inner().scopesym;
if (scopesym.members)
scopesym.members.push(sd);
if (scopesym.symtab && !scopesym.symtabInsert(sd))
{
Dsymbol s2 = scopesym.symtabLookup(sd, mtype.id);
handleTagSymbols(*sc2, sd, s2, scopesym);
}
sd.parent = sc2.parent;
sd.dsymbolSemantic(sc2);
}
switch (mtype.tok)
{
case TOK.enum_:
auto ed = new EnumDeclaration(mtype.loc, mtype.id, mtype.base);
declare(ed);
mtype.resolved = visitEnum(new TypeEnum(ed));
break;
case TOK.struct_:
auto sd = new StructDeclaration(mtype.loc, mtype.id, false);
declare(sd);
mtype.resolved = visitStruct(new TypeStruct(sd));
break;
case TOK.union_:
auto ud = new UnionDeclaration(mtype.loc, mtype.id);
declare(ud);
mtype.resolved = visitStruct(new TypeStruct(ud));
break;
default:
assert(0);
}
}
/* If it doesn't have a tag by now, supply one.
* It'll be unique, and therefore introducing.
* Declare it, and done.
*/
if (!mtype.id)
{
mtype.id = Identifier.generateId("__tag"[]);
declareTag();
return mtype.resolved;
}
/* look for pre-existing declaration
*/
Dsymbol scopesym;
auto s = sc2.search(mtype.loc, mtype.id, &scopesym, IgnoreErrors | TagNameSpace);
if (!s || s.isModule())
{
// no pre-existing declaration, so declare it
if (mtype.tok == TOK.enum_ && !mtype.members)
.error(mtype.loc, "`enum %s` is incomplete without members", mtype.id.toChars()); // C11 6.7.2.3-3
declareTag();
return mtype.resolved;
}
/* A redeclaration only happens if both declarations are in
* the same scope
*/
const bool redeclar = (scopesym == sc2.inner().scopesym);
if (redeclar)
{
if (mtype.tok == TOK.enum_ && s.isEnumDeclaration())
{
auto ed = s.isEnumDeclaration();
if (mtype.members && ed.members)
.error(mtype.loc, "`%s` already has members", mtype.id.toChars());
else if (!ed.members)
{
ed.members = mtype.members;
}
else
{
}
mtype.resolved = ed.type;
}
else if (mtype.tok == TOK.union_ && s.isUnionDeclaration() ||
mtype.tok == TOK.struct_ && s.isStructDeclaration())
{
// Add members to original declaration
auto sd = s.isStructDeclaration();
if (mtype.members && sd.members)
{
/* struct S { int b; };
* struct S { int a; } *s;
*/
.error(mtype.loc, "`%s` already has members", mtype.id.toChars());
}
else if (!sd.members)
{
/* struct S;
* struct S { int a; } *s;
*/
sd.members = mtype.members;
if (sd.semanticRun == PASS.semanticdone)
{
/* The first semantic pass marked `sd` as an opaque struct.
* Re-run semantic so that all newly assigned members are
* picked up and added to the symtab.
*/
sd.semanticRun = PASS.semantic;
sd.dsymbolSemantic(sc2);
}
}
else
{
/* struct S { int a; };
* struct S *s;
*/
}
mtype.resolved = sd.type;
}
else
{
/* int S;
* struct S { int a; } *s;
*/
.error(mtype.loc, "redeclaration of `%s`", mtype.id.toChars());
mtype.resolved = error();
}
}
else if (mtype.members)
{
/* struct S;
* { struct S { int a; } *s; }
*/
declareTag();
}
else
{
if (mtype.tok == TOK.enum_ && s.isEnumDeclaration())
{
mtype.resolved = s.isEnumDeclaration().type;
}
else if (mtype.tok == TOK.union_ && s.isUnionDeclaration() ||
mtype.tok == TOK.struct_ && s.isStructDeclaration())
{
/* struct S;
* { struct S *s; }
*/
mtype.resolved = s.isStructDeclaration().type;
}
else
{
/* union S;
* { struct S *s; }
*/
.error(mtype.loc, "redeclaring `%s %s` as `%s %s`",
s.kind(), s.toChars(), Token.toChars(mtype.tok), mtype.id.toChars());
declareTag();
}
}
return mtype.resolved;
}
switch (type.ty)
{
default: return visitType(type);
case Tvector: return visitVector(type.isTypeVector());
case Tsarray: return visitSArray(type.isTypeSArray());
case Tarray: return visitDArray(type.isTypeDArray());
case Taarray: return visitAArray(type.isTypeAArray());
case Tpointer: return visitPointer(type.isTypePointer());
case Treference: return visitReference(type.isTypeReference());
case Tfunction: return visitFunction(type.isTypeFunction());
case Tdelegate: return visitDelegate(type.isTypeDelegate());
case Tident: return visitIdentifier(type.isTypeIdentifier());
case Tinstance: return visitInstance(type.isTypeInstance());
case Ttypeof: return visitTypeof(type.isTypeTypeof());
case Ttraits: return visitTraits(type.isTypeTraits());
case Treturn: return visitReturn(type.isTypeReturn());
case Tstruct: return visitStruct(type.isTypeStruct());
case Tenum: return visitEnum(type.isTypeEnum());
case Tclass: return visitClass(type.isTypeClass());
case Ttuple: return visitTuple(type.isTypeTuple());
case Tslice: return visitSlice(type.isTypeSlice());
case Tmixin: return visitMixin(type.isTypeMixin());
case Ttag: return visitTag(type.isTypeTag());
}
}
/******************************************
* Compile the MixinType, returning the type or expression AST.
*
* Doesn't run semantic() on the returned object.
* Params:
* tm = mixin to compile as a type or expression
* loc = location for error messages
* sc = context
* Return:
* null if error, else RootObject AST as parsed
*/
RootObject compileTypeMixin(TypeMixin tm, Loc loc, Scope* sc)
{
OutBuffer buf;
if (expressionsToString(buf, sc, tm.exps))
return null;
const errors = global.errors;
const len = buf.length;
buf.writeByte(0);
const str = buf.extractSlice()[0 .. len];
scope p = new Parser!ASTCodegen(loc, sc._module, str, false);
p.nextToken();
//printf("p.loc.linnum = %d\n", p.loc.linnum);
auto o = p.parseTypeOrAssignExp(TOK.endOfFile);
if (errors != global.errors)
{
assert(global.errors != errors); // should have caught all these cases
return null;
}
if (p.token.value != TOK.endOfFile)
{
.error(loc, "incomplete mixin type `%s`", str.ptr);
return null;
}
Type t = o.isType();
Expression e = t ? t.typeToExpression() : o.isExpression();
return (!e && t) ? t : e;
}
/************************************
* If an identical type to `type` is in `type.stringtable`, return
* the latter one. Otherwise, add it to `type.stringtable`.
* Some types don't get merged and are returned as-is.
* Params:
* type = Type to check against existing types
* Returns:
* the type that was merged
*/
extern (C++) Type merge(Type type)
{
switch (type.ty)
{
case Terror:
case Ttypeof:
case Tident:
case Tinstance:
case Tmixin:
return type;
case Tsarray:
// prevents generating the mangle if the array dim is not yet known
if (!type.isTypeSArray().dim.isIntegerExp())
return type;
goto default;
case Tenum:
break;
case Taarray:
if (!type.isTypeAArray().index.merge().deco)
return type;
goto default;
default:
if (type.nextOf() && !type.nextOf().deco)
return type;
break;
}
//printf("merge(%s)\n", toChars());
if (!type.deco)
{
OutBuffer buf;
buf.reserve(32);
mangleToBuffer(type, &buf);
auto sv = type.stringtable.update(buf[]);
if (sv.value)
{
Type t = sv.value;
debug
{
import core.stdc.stdio;
if (!t.deco)
printf("t = %s\n", t.toChars());
}
assert(t.deco);
//printf("old value, deco = '%s' %p\n", t.deco, t.deco);
return t;
}
else
{
Type t = stripDefaultArgs(type);
sv.value = t;
type.deco = t.deco = cast(char*)sv.toDchars();
//printf("new value, deco = '%s' %p\n", t.deco, t.deco);
return t;
}
}
return type;
}
/***************************************
* Calculate built-in properties which just the type is necessary.
*
* Params:
* t = the type for which the property is calculated
* scope_ = the scope from which the property is being accessed. Used for visibility checks only.
* loc = the location where the property is encountered
* ident = the identifier of the property
* flag = if flag & 1, don't report "not a property" error and just return NULL.
* Returns:
* expression representing the property, or null if not a property and (flag & 1)
*/
Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier ident, int flag)
{
Expression visitType(Type mt)
{
Expression e;
static if (LOGDOTEXP)
{
printf("Type::getProperty(type = '%s', ident = '%s')\n", mt.toChars(), ident.toChars());
}
if (ident == Id.__sizeof)
{
const sz = mt.size(loc);
if (sz == SIZE_INVALID)
return ErrorExp.get();
e = new IntegerExp(loc, sz, Type.tsize_t);
}
else if (ident == Id.__xalignof)
{
const explicitAlignment = mt.alignment();
const naturalAlignment = mt.alignsize();
const actualAlignment = (explicitAlignment.isDefault() ? naturalAlignment : explicitAlignment.get());
e = new IntegerExp(loc, actualAlignment, Type.tsize_t);
}
else if (ident == Id._init)
{
Type tb = mt.toBasetype();
e = mt.defaultInitLiteral(loc);
if (tb.ty == Tstruct && tb.needsNested())
{
e.isStructLiteralExp().useStaticInit = true;
}
}
else if (ident == Id._mangleof)
{
if (!mt.deco)
{
error(loc, "forward reference of type `%s.mangleof`", mt.toChars());
e = ErrorExp.get();
}
else
{
e = new StringExp(loc, mt.deco.toDString());
Scope sc;
e = e.expressionSemantic(&sc);
}
}
else if (ident == Id.stringof)
{
const s = mt.toChars();
e = new StringExp(loc, s.toDString());
Scope sc;
e = e.expressionSemantic(&sc);
}
else if (flag && mt != Type.terror)
{
return null;
}
else
{
Dsymbol s = null;
if (mt.ty == Tstruct || mt.ty == Tclass || mt.ty == Tenum)
s = mt.toDsymbol(null);
if (s)
s = s.search_correct(ident);
if (s && !symbolIsVisible(scope_, s))
s = null;
if (mt != Type.terror)
{
if (s)
error(loc, "no property `%s` for type `%s`, did you mean `%s`?", ident.toChars(), mt.toChars(), s.toPrettyChars());
else if (ident == Id.call && mt.ty == Tclass)
error(loc, "no property `%s` for type `%s`, did you mean `new %s`?", ident.toChars(), mt.toChars(), mt.toPrettyChars());
else if (const n = importHint(ident.toString()))
error(loc, "no property `%s` for type `%s`, perhaps `import %.*s;` is needed?", ident.toChars(), mt.toChars(), cast(int)n.length, n.ptr);
else
{
error(loc, "no property `%s` for type `%s`", ident.toChars(), mt.toPrettyChars(true));
if (auto dsym = mt.toDsymbol(scope_))
if (auto sym = dsym.isAggregateDeclaration())
{
if (auto fd = search_function(sym, Id.opDispatch))
errorSupplemental(loc, "potentially malformed `opDispatch`. Use an explicit instantiation to get a better error message");
else if (!sym.members)
errorSupplemental(sym.loc, "`%s %s` is opaque and has no members.", sym.kind, mt.toPrettyChars(true));
}
}
}
e = ErrorExp.get();
}
return e;
}
Expression visitError(TypeError)
{
return ErrorExp.get();
}
Expression visitBasic(TypeBasic mt)
{
Expression integerValue(dinteger_t i)
{
return new IntegerExp(loc, i, mt);
}
Expression intValue(dinteger_t i)
{
return new IntegerExp(loc, i, Type.tint32);
}
Expression floatValue(real_t r)
{
if (mt.isreal() || mt.isimaginary())
return new RealExp(loc, r, mt);
else
{
return new ComplexExp(loc, complex_t(r, r), mt);
}
}
//printf("TypeBasic::getProperty('%s')\n", ident.toChars());
if (ident == Id.max)
{
switch (mt.ty)
{
case Tint8: return integerValue(byte.max);
case Tuns8: return integerValue(ubyte.max);
case Tint16: return integerValue(short.max);
case Tuns16: return integerValue(ushort.max);
case Tint32: return integerValue(int.max);
case Tuns32: return integerValue(uint.max);
case Tint64: return integerValue(long.max);
case Tuns64: return integerValue(ulong.max);
case Tbool: return integerValue(bool.max);
case Tchar: return integerValue(char.max);
case Twchar: return integerValue(wchar.max);
case Tdchar: return integerValue(dchar.max);
case Tcomplex32:
case Timaginary32:
case Tfloat32: return floatValue(target.FloatProperties.max);
case Tcomplex64:
case Timaginary64:
case Tfloat64: return floatValue(target.DoubleProperties.max);
case Tcomplex80:
case Timaginary80:
case Tfloat80: return floatValue(target.RealProperties.max);
default: break;
}
}
else if (ident == Id.min)
{
switch (mt.ty)
{
case Tint8: return integerValue(byte.min);
case Tuns8:
case Tuns16:
case Tuns32:
case Tuns64:
case Tbool:
case Tchar:
case Twchar:
case Tdchar: return integerValue(0);
case Tint16: return integerValue(short.min);
case Tint32: return integerValue(int.min);
case Tint64: return integerValue(long.min);
default: break;
}
}
else if (ident == Id.min_normal)
{
switch (mt.ty)
{
case Tcomplex32:
case Timaginary32:
case Tfloat32: return floatValue(target.FloatProperties.min_normal);
case Tcomplex64:
case Timaginary64:
case Tfloat64: return floatValue(target.DoubleProperties.min_normal);
case Tcomplex80:
case Timaginary80:
case Tfloat80: return floatValue(target.RealProperties.min_normal);
default: break;
}
}
else if (ident == Id.nan)
{
switch (mt.ty)
{
case Tcomplex32:
case Tcomplex64:
case Tcomplex80:
case Timaginary32:
case Timaginary64:
case Timaginary80:
case Tfloat32:
case Tfloat64:
case Tfloat80: return floatValue(target.RealProperties.nan);
default: break;
}
}
else if (ident == Id.infinity)
{
switch (mt.ty)
{
case Tcomplex32:
case Tcomplex64:
case Tcomplex80:
case Timaginary32:
case Timaginary64:
case Timaginary80:
case Tfloat32:
case Tfloat64:
case Tfloat80: return floatValue(target.RealProperties.infinity);
default: break;
}
}
else if (ident == Id.dig)
{
switch (mt.ty)
{
case Tcomplex32:
case Timaginary32:
case Tfloat32: return intValue(target.FloatProperties.dig);
case Tcomplex64:
case Timaginary64:
case Tfloat64: return intValue(target.DoubleProperties.dig);
case Tcomplex80:
case Timaginary80:
case Tfloat80: return intValue(target.RealProperties.dig);
default: break;
}
}
else if (ident == Id.epsilon)
{
switch (mt.ty)
{
case Tcomplex32:
case Timaginary32:
case Tfloat32: return floatValue(target.FloatProperties.epsilon);
case Tcomplex64:
case Timaginary64:
case Tfloat64: return floatValue(target.DoubleProperties.epsilon);
case Tcomplex80:
case Timaginary80:
case Tfloat80: return floatValue(target.RealProperties.epsilon);
default: break;
}
}
else if (ident == Id.mant_dig)
{
switch (mt.ty)
{
case Tcomplex32:
case Timaginary32:
case Tfloat32: return intValue(target.FloatProperties.mant_dig);
case Tcomplex64:
case Timaginary64:
case Tfloat64: return intValue(target.DoubleProperties.mant_dig);
case Tcomplex80:
case Timaginary80:
case Tfloat80: return intValue(target.RealProperties.mant_dig);
default: break;
}
}
else if (ident == Id.max_10_exp)
{
switch (mt.ty)
{
case Tcomplex32:
case Timaginary32:
case Tfloat32: return intValue(target.FloatProperties.max_10_exp);
case Tcomplex64:
case Timaginary64:
case Tfloat64: return intValue(target.DoubleProperties.max_10_exp);
case Tcomplex80:
case Timaginary80:
case Tfloat80: return intValue(target.RealProperties.max_10_exp);
default: break;
}
}
else if (ident == Id.max_exp)
{
switch (mt.ty)
{
case Tcomplex32:
case Timaginary32:
case Tfloat32: return intValue(target.FloatProperties.max_exp);
case Tcomplex64:
case Timaginary64:
case Tfloat64: return intValue(target.DoubleProperties.max_exp);
case Tcomplex80:
case Timaginary80:
case Tfloat80: return intValue(target.RealProperties.max_exp);
default: break;
}
}
else if (ident == Id.min_10_exp)
{
switch (mt.ty)
{
case Tcomplex32:
case Timaginary32:
case Tfloat32: return intValue(target.FloatProperties.min_10_exp);
case Tcomplex64:
case Timaginary64:
case Tfloat64: return intValue(target.DoubleProperties.min_10_exp);
case Tcomplex80:
case Timaginary80:
case Tfloat80: return intValue(target.RealProperties.min_10_exp);
default: break;
}
}
else if (ident == Id.min_exp)
{
switch (mt.ty)
{
case Tcomplex32:
case Timaginary32:
case Tfloat32: return intValue(target.FloatProperties.min_exp);
case Tcomplex64:
case Timaginary64:
case Tfloat64: return intValue(target.DoubleProperties.min_exp);
case Tcomplex80:
case Timaginary80:
case Tfloat80: return intValue(target.RealProperties.min_exp);
default: break;
}
}
return visitType(mt);
}
Expression visitVector(TypeVector mt)
{
return visitType(mt);
}
Expression visitEnum(TypeEnum mt)
{
Expression e;
if (ident == Id.max || ident == Id.min)
{
return mt.sym.getMaxMinValue(loc, ident);
}
else if (ident == Id._init)
{
e = mt.defaultInitLiteral(loc);
}
else if (ident == Id.stringof)
{
e = new StringExp(loc, mt.toString());
Scope sc;
e = e.expressionSemantic(&sc);
}
else if (ident == Id._mangleof)
{
e = visitType(mt);
}
else
{
e = mt.toBasetype().getProperty(scope_, loc, ident, flag);
}
return e;
}
Expression visitTuple(TypeTuple mt)
{
Expression e;
static if (LOGDOTEXP)
{
printf("TypeTuple::getProperty(type = '%s', ident = '%s')\n", mt.toChars(), ident.toChars());
}
if (ident == Id.length)
{
e = new IntegerExp(loc, mt.arguments.dim, Type.tsize_t);
}
else if (ident == Id._init)
{
e = mt.defaultInitLiteral(loc);
}
else if (flag)
{
e = null;
}
else
{
error(loc, "no property `%s` for tuple `%s`", ident.toChars(), mt.toChars());
e = ErrorExp.get();
}
return e;
}
switch (t.ty)
{
default: return t.isTypeBasic() ?
visitBasic(cast(TypeBasic)t) :
visitType(t);
case Terror: return visitError (t.isTypeError());
case Tvector: return visitVector(t.isTypeVector());
case Tenum: return visitEnum (t.isTypeEnum());
case Ttuple: return visitTuple (t.isTypeTuple());
}
}
/***************************************
* Determine if Expression `exp` should instead be a Type, a Dsymbol, or remain an Expression.
* Params:
* exp = Expression to look at
* t = if exp should be a Type, set t to that Type else null
* s = if exp should be a Dsymbol, set s to that Dsymbol else null
* e = if exp should remain an Expression, set e to that Expression else null
*
*/
private void resolveExp(Expression exp, out Type t, out Expression e, out Dsymbol s)
{
if (exp.isTypeExp())
t = exp.type;
else if (auto ve = exp.isVarExp())
{
if (auto v = ve.var.isVarDeclaration())
e = exp;
else
s = ve.var;
}
else if (auto te = exp.isTemplateExp())
s = te.td;
else if (auto se = exp.isScopeExp())
s = se.sds;
else if (exp.isFuncExp())
s = getDsymbol(exp);
else if (auto dte = exp.isDotTemplateExp())
s = dte.td;
else if (exp.isErrorExp())
t = Type.terror;
else
e = exp;
}
/************************************
* Resolve type 'mt' to either type, symbol, or expression.
* If errors happened, resolved to Type.terror.
*
* Params:
* mt = type to be resolved
* loc = the location where the type is encountered
* sc = the scope of the type
* pe = is set if t is an expression
* pt = is set if t is a type
* ps = is set if t is a symbol
* intypeid = true if in type id
*/
void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type pt, out Dsymbol ps, bool intypeid = false)
{
void returnExp(Expression e)
{
pe = e;
pt = null;
ps = null;
}
void returnType(Type t)
{
pe = null;
pt = t;
ps = null;
}
void returnSymbol(Dsymbol s)
{
pe = null;
pt = null;
ps = s;
}
void returnError()
{
returnType(Type.terror);
}
void visitType(Type mt)
{
//printf("Type::resolve() %s, %d\n", mt.toChars(), mt.ty);
Type t = typeSemantic(mt, loc, sc);
assert(t);
returnType(t);
}
void visitSArray(TypeSArray mt)
{
//printf("TypeSArray::resolve() %s\n", mt.toChars());
mt.next.resolve(loc, sc, pe, pt, ps, intypeid);
//printf("s = %p, e = %p, t = %p\n", ps, pe, pt);
if (pe)
{
// It's really an index expression
if (Dsymbol s = getDsymbol(pe))
pe = new DsymbolExp(loc, s);
returnExp(new ArrayExp(loc, pe, mt.dim));
}
else if (ps)
{
Dsymbol s = ps;
if (auto tup = s.isTupleDeclaration())
{
mt.dim = semanticLength(sc, tup, mt.dim);
mt.dim = mt.dim.ctfeInterpret();
if (mt.dim.op == EXP.error)
return returnError();
const d = mt.dim.toUInteger();
if (d >= tup.objects.dim)
{
error(loc, "tuple index `%llu` exceeds length %llu", d, cast(ulong) tup.objects.dim);
return returnError();
}
RootObject o = (*tup.objects)[cast(size_t)d];
if (o.dyncast() == DYNCAST.dsymbol)
{
return returnSymbol(cast(Dsymbol)o);
}
if (o.dyncast() == DYNCAST.expression)
{
Expression e = cast(Expression)o;
if (e.op == EXP.dSymbol)
return returnSymbol(e.isDsymbolExp().s);
else
return returnExp(e);
}
if (o.dyncast() == DYNCAST.type)
{
return returnType((cast(Type)o).addMod(mt.mod));
}
/* Create a new TupleDeclaration which
* is a slice [d..d+1] out of the old one.
* Do it this way because TemplateInstance::semanticTiargs()
* can handle unresolved Objects this way.
*/
auto objects = new Objects(1);
(*objects)[0] = o;
return returnSymbol(new TupleDeclaration(loc, tup.ident, objects));
}
else
return visitType(mt);
}
else
{
if (pt.ty != Terror)
mt.next = pt; // prevent re-running semantic() on 'next'
visitType(mt);
}
}
void visitDArray(TypeDArray mt)
{
//printf("TypeDArray::resolve() %s\n", mt.toChars());
mt.next.resolve(loc, sc, pe, pt, ps, intypeid);
//printf("s = %p, e = %p, t = %p\n", ps, pe, pt);
if (pe)
{
// It's really a slice expression
if (Dsymbol s = getDsymbol(pe))
pe = new DsymbolExp(loc, s);
returnExp(new ArrayExp(loc, pe));
}
else if (ps)
{
if (auto tup = ps.isTupleDeclaration())
{
// keep ps
}
else
visitType(mt);
}
else
{
if (pt.ty != Terror)
mt.next = pt; // prevent re-running semantic() on 'next'
visitType(mt);
}
}
void visitAArray(TypeAArray mt)
{
//printf("TypeAArray::resolve() %s\n", mt.toChars());
// Deal with the case where we thought the index was a type, but
// in reality it was an expression.
if (mt.index.ty == Tident || mt.index.ty == Tinstance || mt.index.ty == Tsarray)
{
Expression e;
Type t;
Dsymbol s;
mt.index.resolve(loc, sc, e, t, s, intypeid);
if (e)
{
// It was an expression -
// Rewrite as a static array
auto tsa = new TypeSArray(mt.next, e);
tsa.mod = mt.mod; // just copy mod field so tsa's semantic is not yet done
return tsa.resolve(loc, sc, pe, pt, ps, intypeid);
}
else if (t)
mt.index = t;
else
.error(loc, "index is not a type or an expression");
}
visitType(mt);
}
/*************************************
* Takes an array of Identifiers and figures out if
* it represents a Type or an Expression.
* Output:
* if expression, pe is set
* if type, pt is set
*/
void visitIdentifier(TypeIdentifier mt)
{
//printf("TypeIdentifier::resolve(sc = %p, idents = '%s')\n", sc, mt.toChars());
if (mt.ident == Id.ctfe)
{
error(loc, "variable `__ctfe` cannot be read at compile time");
return returnError();
}
if (mt.ident == Id.builtin_va_list) // gcc has __builtin_va_xxxx for stdarg.h
{
/* Since we don't support __builtin_va_start, -arg, -end, we don't
* have to actually care what -list is. A void* will do.
* If we ever do care, import core.stdc.stdarg and pull
* the definition out of that, similarly to how std.math is handled for PowExp
*/
pt = target.va_listType(loc, sc);
return;
}
Dsymbol scopesym;
Dsymbol s = sc.search(loc, mt.ident, &scopesym);
/*
* https://issues.dlang.org/show_bug.cgi?id=1170
* https://issues.dlang.org/show_bug.cgi?id=10739
*
* If a symbol is not found, it might be declared in
* a mixin-ed string or a mixin-ed template, so before
* issuing an error semantically analyze all string/template
* mixins that are members of the current ScopeDsymbol.
*/
if (!s && sc.enclosing)
{
ScopeDsymbol sds = sc.enclosing.scopesym;
if (sds && sds.members)
{
void semanticOnMixin(Dsymbol member)
{
if (auto compileDecl = member.isCompileDeclaration())
compileDecl.dsymbolSemantic(sc);
else if (auto mixinTempl = member.isTemplateMixin())
mixinTempl.dsymbolSemantic(sc);
}
sds.members.foreachDsymbol( s => semanticOnMixin(s) );
s = sc.search(loc, mt.ident, &scopesym);
}
}
if (s)
{
// https://issues.dlang.org/show_bug.cgi?id=16042
// If `f` is really a function template, then replace `f`
// with the function template declaration.
if (auto f = s.isFuncDeclaration())
{
if (auto td = getFuncTemplateDecl(f))
{
// If not at the beginning of the overloaded list of
// `TemplateDeclaration`s, then get the beginning
if (td.overroot)
td = td.overroot;
s = td;
}
}
}
mt.resolveHelper(loc, sc, s, scopesym, pe, pt, ps, intypeid);
if (pt)
pt = pt.addMod(mt.mod);
}
void visitInstance(TypeInstance mt)
{
// Note close similarity to TypeIdentifier::resolve()
//printf("TypeInstance::resolve(sc = %p, tempinst = '%s')\n", sc, mt.tempinst.toChars());
mt.tempinst.dsymbolSemantic(sc);
if (!global.gag && mt.tempinst.errors)
return returnError();
mt.resolveHelper(loc, sc, mt.tempinst, null, pe, pt, ps, intypeid);
if (pt)
pt = pt.addMod(mt.mod);
//if (pt) printf("pt = %d '%s'\n", pt.ty, pt.toChars());
}
void visitTypeof(TypeTypeof mt)
{
//printf("TypeTypeof::resolve(this = %p, sc = %p, idents = '%s')\n", mt, sc, mt.toChars());
//static int nest; if (++nest == 50) *(char*)0=0;
if (sc is null)
{
error(loc, "invalid scope");
return returnError();
}
if (mt.inuse)
{
mt.inuse = 2;
error(loc, "circular `typeof` definition");
Lerr:
mt.inuse--;
return returnError();
}
mt.inuse++;
/* Currently we cannot evaluate 'exp' in speculative context, because
* the type implementation may leak to the final execution. Consider:
*
* struct S(T) {
* string toString() const { return "x"; }
* }
* void main() {
* alias X = typeof(S!int());
* assert(typeid(X).toString() == "x");
* }
*/
Scope* sc2 = sc.push();
if (!mt.exp.isTypeidExp())
/* Treat typeof(typeid(exp)) as needing
* the full semantic analysis of the typeid.
* https://issues.dlang.org/show_bug.cgi?id=20958
*/
sc2.intypeof = 1;
auto exp2 = mt.exp.expressionSemantic(sc2);
exp2 = resolvePropertiesOnly(sc2, exp2);
sc2.pop();
if (exp2.op == EXP.error)
{
if (!global.gag)
mt.exp = exp2;
goto Lerr;
}
mt.exp = exp2;
if (mt.exp.op == EXP.type ||
mt.exp.op == EXP.scope_)
{
if (mt.exp.checkType())
goto Lerr;
/* Today, 'typeof(func)' returns void if func is a
* function template (TemplateExp), or
* template lambda (FuncExp).
* It's actually used in Phobos as an idiom, to branch code for
* template functions.
*/
}
if (auto f = mt.exp.op == EXP.variable ? mt.exp.isVarExp().var.isFuncDeclaration()
: mt.exp.op == EXP.dotVariable ? mt.exp.isDotVarExp().var.isFuncDeclaration() : null)
{
// 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))
goto Lerr;
}
if (auto f = isFuncAddress(mt.exp))
{
if (f.checkForwardRef(loc))
goto Lerr;
}
Type t = mt.exp.type;
if (!t)
{
error(loc, "expression `%s` has no type", mt.exp.toChars());
goto Lerr;
}
if (t.ty == Ttypeof)
{
error(loc, "forward reference to `%s`", mt.toChars());
goto Lerr;
}
if (mt.idents.dim == 0)
{
returnType(t.addMod(mt.mod));
}
else
{
if (Dsymbol s = t.toDsymbol(sc))
mt.resolveHelper(loc, sc, s, null, pe, pt, ps, intypeid);
else
{
auto e = typeToExpressionHelper(mt, new TypeExp(loc, t));
e = e.expressionSemantic(sc);
resolveExp(e, pt, pe, ps);
}
if (pt)
pt = pt.addMod(mt.mod);
}
mt.inuse--;
}
void visitReturn(TypeReturn mt)
{
//printf("TypeReturn::resolve(sc = %p, idents = '%s')\n", sc, mt.toChars());
Type t;
{
FuncDeclaration func = sc.func;
if (!func)
{
error(loc, "`typeof(return)` must be inside function");
return returnError();
}
if (func.fes)
func = func.fes.func;
t = func.type.nextOf();
if (!t)
{
error(loc, "cannot use `typeof(return)` inside function `%s` with inferred return type", sc.func.toChars());
return returnError();
}
}
if (mt.idents.dim == 0)
{
return returnType(t.addMod(mt.mod));
}
else
{
if (Dsymbol s = t.toDsymbol(sc))
mt.resolveHelper(loc, sc, s, null, pe, pt, ps, intypeid);
else
{
auto e = typeToExpressionHelper(mt, new TypeExp(loc, t));
e = e.expressionSemantic(sc);
resolveExp(e, pt, pe, ps);
}
if (pt)
pt = pt.addMod(mt.mod);
}
}
void visitSlice(TypeSlice mt)
{
mt.next.resolve(loc, sc, pe, pt, ps, intypeid);
if (pe)
{
// It's really a slice expression
if (Dsymbol s = getDsymbol(pe))
pe = new DsymbolExp(loc, s);
return returnExp(new ArrayExp(loc, pe, new IntervalExp(loc, mt.lwr, mt.upr)));
}
else if (ps)
{
Dsymbol s = ps;
TupleDeclaration td = s.isTupleDeclaration();
if (td)
{
/* It's a slice of a TupleDeclaration
*/
ScopeDsymbol sym = new ArrayScopeSymbol(sc, td);
sym.parent = sc.scopesym;
sc = sc.push(sym);
sc = sc.startCTFE();
mt.lwr = mt.lwr.expressionSemantic(sc);
mt.upr = mt.upr.expressionSemantic(sc);
sc = sc.endCTFE();
sc = sc.pop();
mt.lwr = mt.lwr.ctfeInterpret();
mt.upr = mt.upr.ctfeInterpret();
const i1 = mt.lwr.toUInteger();
const i2 = mt.upr.toUInteger();
if (!(i1 <= i2 && i2 <= td.objects.dim))
{
error(loc, "slice `[%llu..%llu]` is out of range of [0..%llu]", i1, i2, cast(ulong) td.objects.dim);
return returnError();
}
if (i1 == 0 && i2 == td.objects.dim)
{
return returnSymbol(td);
}
/* Create a new TupleDeclaration which
* is a slice [i1..i2] out of the old one.
*/
auto objects = new Objects(cast(size_t)(i2 - i1));
for (size_t i = 0; i < objects.dim; i++)
{
(*objects)[i] = (*td.objects)[cast(size_t)i1 + i];
}
return returnSymbol(new TupleDeclaration(loc, td.ident, objects));
}
else
visitType(mt);
}
else
{
if (pt.ty != Terror)
mt.next = pt; // prevent re-running semantic() on 'next'
visitType(mt);
}
}
void visitMixin(TypeMixin mt)
{
RootObject o = mt.obj;
// if already resolved just set pe/pt/ps and return.
if (o)
{
pe = o.isExpression();
pt = o.isType();
ps = o.isDsymbol();
return;
}
o = mt.compileTypeMixin(loc, sc);
if (auto t = o.isType())
{
resolve(t, loc, sc, pe, pt, ps, intypeid);
if (pt)
pt = pt.addMod(mt.mod);
}
else if (auto e = o.isExpression())
{
e = e.expressionSemantic(sc);
if (auto et = e.isTypeExp())
returnType(et.type.addMod(mt.mod));
else
returnExp(e);
}
else
returnError();
// save the result
mt.obj = pe ? pe : (pt ? pt : ps);
}
void visitTraits(TypeTraits tt)
{
if (Type t = typeSemantic(tt, loc, sc))
returnType(t);
else if (tt.sym)
returnSymbol(tt.sym);
else
return returnError();
}
switch (mt.ty)
{
default: visitType (mt); break;
case Tsarray: visitSArray (mt.isTypeSArray()); break;
case Tarray: visitDArray (mt.isTypeDArray()); break;
case Taarray: visitAArray (mt.isTypeAArray()); break;
case Tident: visitIdentifier(mt.isTypeIdentifier()); break;
case Tinstance: visitInstance (mt.isTypeInstance()); break;
case Ttypeof: visitTypeof (mt.isTypeTypeof()); break;
case Treturn: visitReturn (mt.isTypeReturn()); break;
case Tslice: visitSlice (mt.isTypeSlice()); break;
case Tmixin: visitMixin (mt.isTypeMixin()); break;
case Ttraits: visitTraits (mt.isTypeTraits()); break;
}
}
/************************
* Access the members of the object e. This type is same as e.type.
* Params:
* mt = type for which the dot expression is used
* sc = instantiating scope
* e = expression to convert
* ident = identifier being used
* flag = DotExpFlag bit flags
*
* Returns:
* resulting expression with e.ident resolved
*/
Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag)
{
Expression visitType(Type mt)
{
VarDeclaration v = null;
static if (LOGDOTEXP)
{
printf("Type::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
}
Expression ex = e.lastComma();
if (ex.op == EXP.dotVariable)
{
DotVarExp dv = cast(DotVarExp)ex;
v = dv.var.isVarDeclaration();
}
else if (ex.op == EXP.variable)
{
VarExp ve = cast(VarExp)ex;
v = ve.var.isVarDeclaration();
}
if (v)
{
if (ident == Id.offsetof)
{
v.dsymbolSemantic(null);
if (v.isField())
{
auto ad = v.toParent().isAggregateDeclaration();
objc.checkOffsetof(e, ad);
ad.size(e.loc);
if (ad.sizeok != Sizeok.done)
return ErrorExp.get();
return new IntegerExp(e.loc, v.offset, Type.tsize_t);
}
}
else if (ident == Id._init)
{
Type tb = mt.toBasetype();
e = mt.defaultInitLiteral(e.loc);
if (tb.ty == Tstruct && tb.needsNested())
{
e.isStructLiteralExp().useStaticInit = true;
}
goto Lreturn;
}
}
if (ident == Id.stringof)
{
/* https://issues.dlang.org/show_bug.cgi?id=3796
* this should demangle e.type.deco rather than
* pretty-printing the type.
*/
e = new StringExp(e.loc, e.toString());
}
else
e = mt.getProperty(sc, e.loc, ident, flag & DotExpFlag.gag);
Lreturn:
if (e)
e = e.expressionSemantic(sc);
return e;
}
Expression visitError(TypeError)
{
return ErrorExp.get();
}
Expression visitBasic(TypeBasic mt)
{
static if (LOGDOTEXP)
{
printf("TypeBasic::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
}
Type t;
if (ident == Id.re)
{
switch (mt.ty)
{
case Tcomplex32:
t = mt.tfloat32;
goto L1;
case Tcomplex64:
t = mt.tfloat64;
goto L1;
case Tcomplex80:
t = mt.tfloat80;
goto L1;
L1:
e = e.castTo(sc, t);
break;
case Tfloat32:
case Tfloat64:
case Tfloat80:
break;
case Timaginary32:
t = mt.tfloat32;
goto L2;
case Timaginary64:
t = mt.tfloat64;
goto L2;
case Timaginary80:
t = mt.tfloat80;
goto L2;
L2:
e = new RealExp(e.loc, CTFloat.zero, t);
break;
default:
e = mt.Type.getProperty(sc, e.loc, ident, flag);
break;
}
}
else if (ident == Id.im)
{
Type t2;
switch (mt.ty)
{
case Tcomplex32:
t = mt.timaginary32;
t2 = mt.tfloat32;
goto L3;
case Tcomplex64:
t = mt.timaginary64;
t2 = mt.tfloat64;
goto L3;
case Tcomplex80:
t = mt.timaginary80;
t2 = mt.tfloat80;
goto L3;
L3:
e = e.castTo(sc, t);
e.type = t2;
break;
case Timaginary32:
t = mt.tfloat32;
goto L4;
case Timaginary64:
t = mt.tfloat64;
goto L4;
case Timaginary80:
t = mt.tfloat80;
goto L4;
L4:
e = e.copy();
e.type = t;
break;
case Tfloat32:
case Tfloat64:
case Tfloat80:
e = new RealExp(e.loc, CTFloat.zero, mt);
break;
default:
e = mt.Type.getProperty(sc, e.loc, ident, flag);
break;
}
}
else
{
return visitType(mt);
}
if (!(flag & 1) || e)
e = e.expressionSemantic(sc);
return e;
}
Expression visitVector(TypeVector mt)
{
static if (LOGDOTEXP)
{
printf("TypeVector::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
}
if (ident == Id.ptr && e.op == EXP.call)
{
/* The trouble with EXP.call is the return ABI for float[4] is different from
* __vector(float[4]), and a type paint won't do.
*/
e = new AddrExp(e.loc, e);
e = e.expressionSemantic(sc);
return e.castTo(sc, mt.basetype.nextOf().pointerTo());
}
if (ident == Id.array)
{
//e = e.castTo(sc, basetype);
// Keep lvalue-ness
e = new VectorArrayExp(e.loc, e);
e = e.expressionSemantic(sc);
return e;
}
if (ident == Id._init || ident == Id.offsetof || ident == Id.stringof || ident == Id.__xalignof)
{
// init should return a new VectorExp
// https://issues.dlang.org/show_bug.cgi?id=12776
// offsetof does not work on a cast expression, so use e directly
// stringof should not add a cast to the output
return visitType(mt);
}
// Properties based on the vector element type and are values of the element type
if (ident == Id.max || ident == Id.min || ident == Id.min_normal ||
ident == Id.nan || ident == Id.infinity || ident == Id.epsilon)
{
auto vet = mt.basetype.isTypeSArray().next; // vector element type
if (auto ev = getProperty(vet, sc, e.loc, ident, DotExpFlag.gag))
return ev.castTo(sc, mt); // 'broadcast' ev to the vector elements
}
return mt.basetype.dotExp(sc, e.castTo(sc, mt.basetype), ident, flag);
}
Expression visitArray(TypeArray mt)
{
static if (LOGDOTEXP)
{
printf("TypeArray::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
}
e = visitType(mt);
if (!(flag & 1) || e)
e = e.expressionSemantic(sc);
return e;
}
Expression visitSArray(TypeSArray mt)
{
static if (LOGDOTEXP)
{
printf("TypeSArray::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
}
if (ident == Id.length)
{
Loc oldLoc = e.loc;
e = mt.dim.copy();
e.loc = oldLoc;
}
else if (ident == Id.ptr)
{
if (e.op == EXP.type)
{
e.error("`%s` is not an expression", e.toChars());
return ErrorExp.get();
}
else if (checkUnsafeDotExp(sc, e, ident, flag))
{
return ErrorExp.get();
}
e = e.castTo(sc, e.type.nextOf().pointerTo());
}
else
{
e = visitArray(mt);
}
if (!(flag & 1) || e)
e = e.expressionSemantic(sc);
return e;
}
Expression visitDArray(TypeDArray mt)
{
static if (LOGDOTEXP)
{
printf("TypeDArray::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
}
if (e.op == EXP.type && (ident == Id.length || ident == Id.ptr))
{
e.error("`%s` is not an expression", e.toChars());
return ErrorExp.get();
}
if (ident == Id.length)
{
if (e.op == EXP.string_)
{
StringExp se = cast(StringExp)e;
return new IntegerExp(se.loc, se.len, Type.tsize_t);
}
if (e.op == EXP.null_)
{
return new IntegerExp(e.loc, 0, Type.tsize_t);
}
if (checkNonAssignmentArrayOp(e))
{
return ErrorExp.get();
}
e = new ArrayLengthExp(e.loc, e);
e.type = Type.tsize_t;
return e;
}
else if (ident == Id.ptr)
{
if (checkUnsafeDotExp(sc, e, ident, flag))
return ErrorExp.get();
return e.castTo(sc, mt.next.pointerTo());
}
else
{
return visitArray(mt);
}
}
Expression visitAArray(TypeAArray mt)
{
static if (LOGDOTEXP)
{
printf("TypeAArray::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
}
if (ident == Id.length)
{
__gshared FuncDeclaration fd_aaLen = null;
if (fd_aaLen is null)
{
auto fparams = new Parameters();
fparams.push(new Parameter(STC.const_ | STC.scope_, mt, null, null, null));
fd_aaLen = FuncDeclaration.genCfunc(fparams, Type.tsize_t, Id.aaLen);
TypeFunction tf = fd_aaLen.type.toTypeFunction();
tf.purity = PURE.const_;
tf.isnothrow = true;
tf.isnogc = false;
}
Expression ev = new VarExp(e.loc, fd_aaLen, false);
e = new CallExp(e.loc, ev, e);
e.type = fd_aaLen.type.toTypeFunction().next;
return e;
}
else
{
return visitType(mt);
}
}
Expression visitReference(TypeReference mt)
{
static if (LOGDOTEXP)
{
printf("TypeReference::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
}
// References just forward things along
return mt.next.dotExp(sc, e, ident, flag);
}
Expression visitDelegate(TypeDelegate mt)
{
static if (LOGDOTEXP)
{
printf("TypeDelegate::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
}
if (ident == Id.ptr)
{
e = new DelegatePtrExp(e.loc, e);
e = e.expressionSemantic(sc);
}
else if (ident == Id.funcptr)
{
if (checkUnsafeDotExp(sc, e, ident, flag))
{
return ErrorExp.get();
}
e = new DelegateFuncptrExp(e.loc, e);
e = e.expressionSemantic(sc);
}
else
{
return visitType(mt);
}
return e;
}
/***************************************
* `ident` was not found as a member of `mt`.
* Attempt to use overloaded opDot(), overloaded opDispatch(), or `alias this`.
* If that fails, forward to visitType().
* Params:
* mt = class or struct
* sc = context
* e = `this` for `ident`
* ident = name of member
* flag = flag & 1, don't report "not a property" error and just return NULL.
* flag & DotExpFlag.noAliasThis, don't do 'alias this' resolution.
* Returns:
* resolved expression if found, otherwise null
*/
Expression noMember(Type mt, Scope* sc, Expression e, Identifier ident, int flag)
{
//printf("Type.noMember(e: %s ident: %s flag: %d)\n", e.toChars(), ident.toChars(), flag);
bool gagError = flag & 1;
__gshared int nest; // https://issues.dlang.org/show_bug.cgi?id=17380
static Expression returnExp(Expression e)
{
--nest;
return e;
}
if (++nest > global.recursionLimit)
{
.error(e.loc, "cannot resolve identifier `%s`", ident.toChars());
return returnExp(gagError ? null : ErrorExp.get());
}
assert(mt.ty == Tstruct || mt.ty == Tclass);
auto sym = mt.toDsymbol(sc).isAggregateDeclaration();
assert(sym);
if (// https://issues.dlang.org/show_bug.cgi?id=22054
// if a class or struct does not have a body
// there is no point in searching for its members
sym.members &&
ident != Id.__sizeof &&
ident != Id.__xalignof &&
ident != Id._init &&
ident != Id._mangleof &&
ident != Id.stringof &&
ident != Id.offsetof &&
// https://issues.dlang.org/show_bug.cgi?id=15045
// Don't forward special built-in member functions.
ident != Id.ctor &&
ident != Id.dtor &&
ident != Id.__xdtor &&
ident != Id.postblit &&
ident != Id.__xpostblit)
{
/* Look for overloaded opDot() to see if we should forward request
* to it.
*/
if (auto fd = search_function(sym, Id.opDot))
{
/* Rewrite e.ident as:
* e.opDot().ident
*/
e = build_overload(e.loc, sc, e, null, fd);
// @@@DEPRECATED_2.110@@@.
// Deprecated in 2.082, made an error in 2.100.
e.error("`opDot` is obsolete. Use `alias this`");
return ErrorExp.get();
}
/* Look for overloaded opDispatch to see if we should forward request
* to it.
*/
if (auto fd = search_function(sym, Id.opDispatch))
{
/* Rewrite e.ident as:
* e.opDispatch!("ident")
*/
TemplateDeclaration td = fd.isTemplateDeclaration();
if (!td)
{
fd.error("must be a template `opDispatch(string s)`, not a %s", fd.kind());
return returnExp(ErrorExp.get());
}
auto se = new StringExp(e.loc, ident.toString());
auto tiargs = new Objects();
tiargs.push(se);
auto dti = new DotTemplateInstanceExp(e.loc, e, Id.opDispatch, tiargs);
dti.ti.tempdecl = td;
/* opDispatch, which doesn't need IFTI, may occur instantiate error.
* e.g.
* template opDispatch(name) if (isValid!name) { ... }
*/
uint errors = gagError ? global.startGagging() : 0;
e = dti.semanticY(sc, 0);
if (gagError && global.endGagging(errors))
e = null;
return returnExp(e);
}
/* See if we should forward to the alias this.
*/
auto alias_e = flag & DotExpFlag.noAliasThis ? null
: resolveAliasThis(sc, e, gagError);
if (alias_e && alias_e != e)
{
/* Rewrite e.ident as:
* e.aliasthis.ident
*/
auto die = new DotIdExp(e.loc, alias_e, ident);
auto errors = gagError ? 0 : global.startGagging();
auto exp = die.semanticY(sc, gagError);
if (!gagError)
{
global.endGagging(errors);
if (exp && exp.op == EXP.error)
exp = null;
}
if (exp && gagError)
// now that we know that the alias this leads somewhere useful,
// go back and print deprecations/warnings that we skipped earlier due to the gag
resolveAliasThis(sc, e, false);
return returnExp(exp);
}
}
return returnExp(visitType(mt));
}
Expression visitStruct(TypeStruct mt)
{
Dsymbol s;
static if (LOGDOTEXP)
{
printf("TypeStruct::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
}
assert(e.op != EXP.dot);
// https://issues.dlang.org/show_bug.cgi?id=14010
if (ident == Id._mangleof)
{
return mt.getProperty(sc, e.loc, ident, flag & 1);
}
/* If e.tupleof
*/
if (ident == Id._tupleof)
{
/* Create a TupleExp out of the fields of the struct e:
* (e.field0, e.field1, e.field2, ...)
*/
e = e.expressionSemantic(sc); // do this before turning on noaccesscheck
if (!mt.sym.determineFields())
{
error(e.loc, "unable to determine fields of `%s` because of forward references", mt.toChars());
}
Expression e0;
Expression ev = e.op == EXP.type ? null : e;
if (ev)
ev = extractSideEffect(sc, "__tup", e0, ev);
auto exps = new Expressions();
exps.reserve(mt.sym.fields.dim);
for (size_t i = 0; i < mt.sym.fields.dim; i++)
{
VarDeclaration v = mt.sym.fields[i];
Expression ex;
if (ev)
ex = new DotVarExp(e.loc, ev, v);
else
{
ex = new VarExp(e.loc, v);
ex.type = ex.type.addMod(e.type.mod);
}
exps.push(ex);
}
e = new TupleExp(e.loc, e0, exps);
Scope* sc2 = sc.push();
sc2.flags |= SCOPE.noaccesscheck;
e = e.expressionSemantic(sc2);
sc2.pop();
return e;
}
immutable flags = sc.flags & SCOPE.ignoresymbolvisibility ? IgnoreSymbolVisibility : 0;
s = mt.sym.search(e.loc, ident, flags | IgnorePrivateImports);
L1:
if (!s)
{
return noMember(mt, sc, e, ident, flag);
}
if (!(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc, s))
{
return noMember(mt, sc, e, ident, flag);
}
s = s.toAlias();
if (auto em = s.isEnumMember())
{
return em.getVarExp(e.loc, sc);
}
if (auto v = s.isVarDeclaration())
{
v.checkDeprecated(e.loc, sc);
v.checkDisabled(e.loc, sc);
if (!v.type ||
!v.type.deco && v.inuse)
{
if (v.inuse) // https://issues.dlang.org/show_bug.cgi?id=9494
e.error("circular reference to %s `%s`", v.kind(), v.toPrettyChars());
else
e.error("forward reference to %s `%s`", v.kind(), v.toPrettyChars());
return ErrorExp.get();
}
if (v.type.ty == Terror)
{
return ErrorExp.get();
}
if ((v.storage_class & STC.manifest) && v._init)
{
if (v.inuse)
{
e.error("circular initialization of %s `%s`", v.kind(), v.toPrettyChars());
return ErrorExp.get();
}
checkAccess(e.loc, sc, null, v);
Expression ve = new VarExp(e.loc, v);
if (!isTrivialExp(e))
{
ve = new CommaExp(e.loc, e, ve);
}
return ve.expressionSemantic(sc);
}
}
if (auto t = s.getType())
{
return (new TypeExp(e.loc, t)).expressionSemantic(sc);
}
TemplateMixin tm = s.isTemplateMixin();
if (tm)
{
return new DotExp(e.loc, e, new ScopeExp(e.loc, tm)).expressionSemantic(sc);
}
TemplateDeclaration td = s.isTemplateDeclaration();
if (td)
{
if (e.op == EXP.type)
e = new TemplateExp(e.loc, td);
else
e = new DotTemplateExp(e.loc, e, td);
return e.expressionSemantic(sc);
}
TemplateInstance ti = s.isTemplateInstance();
if (ti)
{
if (!ti.semanticRun)
{
ti.dsymbolSemantic(sc);
if (!ti.inst || ti.errors) // if template failed to expand
{
return ErrorExp.get();
}
}
s = ti.inst.toAlias();
if (!s.isTemplateInstance())
goto L1;
if (e.op == EXP.type)
e = new ScopeExp(e.loc, ti);
else
e = new DotExp(e.loc, e, new ScopeExp(e.loc, ti));
return e.expressionSemantic(sc);
}
if (s.isImport() || s.isModule() || s.isPackage())
{
return symbolToExp(s, e.loc, sc, false);
}
OverloadSet o = s.isOverloadSet();
if (o)
{
auto oe = new OverExp(e.loc, o);
if (e.op == EXP.type)
{
return oe;
}
return new DotExp(e.loc, e, oe);
}
Declaration d = s.isDeclaration();
if (!d)
{
e.error("`%s.%s` is not a declaration", e.toChars(), ident.toChars());
return ErrorExp.get();
}
if (e.op == EXP.type)
{
/* It's:
* Struct.d
*/
if (TupleDeclaration tup = d.isTupleDeclaration())
{
e = new TupleExp(e.loc, tup);
return e.expressionSemantic(sc);
}
if (d.needThis() && sc.intypeof != 1)
{
/* Rewrite as:
* this.d
*/
if (hasThis(sc))
{
e = new DotVarExp(e.loc, new ThisExp(e.loc), d);
return e.expressionSemantic(sc);
}
}
if (d.semanticRun == PASS.initial)
d.dsymbolSemantic(null);
checkAccess(e.loc, sc, e, d);
auto ve = new VarExp(e.loc, d);
if (d.isVarDeclaration() && d.needThis())
ve.type = d.type.addMod(e.type.mod);
return ve;
}
bool unreal = e.op == EXP.variable && (cast(VarExp)e).var.isField();
if (d.isDataseg() || unreal && d.isField())
{
// (e, d)
checkAccess(e.loc, sc, e, d);
Expression ve = new VarExp(e.loc, d);
e = unreal ? ve : new CommaExp(e.loc, e, ve);
return e.expressionSemantic(sc);
}
e = new DotVarExp(e.loc, e, d);
return e.expressionSemantic(sc);
}
Expression visitEnum(TypeEnum mt)
{
static if (LOGDOTEXP)
{
printf("TypeEnum::dotExp(e = '%s', ident = '%s') '%s'\n", e.toChars(), ident.toChars(), mt.toChars());
}
// https://issues.dlang.org/show_bug.cgi?id=14010
if (ident == Id._mangleof)
{
return mt.getProperty(sc, e.loc, ident, flag & 1);
}
if (mt.sym.semanticRun < PASS.semanticdone)
mt.sym.dsymbolSemantic(null);
Dsymbol s = mt.sym.search(e.loc, ident);
if (!s)
{
if (ident == Id.max || ident == Id.min || ident == Id._init)
{
return mt.getProperty(sc, e.loc, ident, flag & 1);
}
Expression res = mt.sym.getMemtype(Loc.initial).dotExp(sc, e, ident, 1);
if (!(flag & 1) && !res)
{
if (auto ns = mt.sym.search_correct(ident))
e.error("no property `%s` for type `%s`. Did you mean `%s.%s` ?", ident.toChars(), mt.toChars(), mt.toChars(),
ns.toChars());
else
e.error("no property `%s` for type `%s`", ident.toChars(),
mt.toChars());
return ErrorExp.get();
}
return res;
}
EnumMember m = s.isEnumMember();
return m.getVarExp(e.loc, sc);
}
Expression visitClass(TypeClass mt)
{
Dsymbol s;
static if (LOGDOTEXP)
{
printf("TypeClass::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
}
assert(e.op != EXP.dot);
// https://issues.dlang.org/show_bug.cgi?id=12543
if (ident == Id.__sizeof || ident == Id.__xalignof || ident == Id._mangleof)
{
return mt.Type.getProperty(sc, e.loc, ident, 0);
}
/* If e.tupleof
*/
if (ident == Id._tupleof)
{
objc.checkTupleof(e, mt);
/* Create a TupleExp
*/
e = e.expressionSemantic(sc); // do this before turning on noaccesscheck
mt.sym.size(e.loc); // do semantic of type
Expression e0;
Expression ev = e.op == EXP.type ? null : e;
if (ev)
ev = extractSideEffect(sc, "__tup", e0, ev);
auto exps = new Expressions();
exps.reserve(mt.sym.fields.dim);
for (size_t i = 0; i < mt.sym.fields.dim; i++)
{
VarDeclaration v = mt.sym.fields[i];
// Don't include hidden 'this' pointer
if (v.isThisDeclaration())
continue;
Expression ex;
if (ev)
ex = new DotVarExp(e.loc, ev, v);
else
{
ex = new VarExp(e.loc, v);
ex.type = ex.type.addMod(e.type.mod);
}
exps.push(ex);
}
e = new TupleExp(e.loc, e0, exps);
Scope* sc2 = sc.push();
sc2.flags |= SCOPE.noaccesscheck;
e = e.expressionSemantic(sc2);
sc2.pop();
return e;
}
int flags = sc.flags & SCOPE.ignoresymbolvisibility ? IgnoreSymbolVisibility : 0;
s = mt.sym.search(e.loc, ident, flags | IgnorePrivateImports);
L1:
if (!s)
{
// See if it's a 'this' class or a base class
if (mt.sym.ident == ident)
{
if (e.op == EXP.type)
{
return mt.Type.getProperty(sc, e.loc, ident, 0);
}
e = new DotTypeExp(e.loc, e, mt.sym);
e = e.expressionSemantic(sc);
return e;
}
if (auto cbase = mt.sym.searchBase(ident))
{
if (e.op == EXP.type)
{
return mt.Type.getProperty(sc, e.loc, ident, 0);
}
if (auto ifbase = cbase.isInterfaceDeclaration())
e = new CastExp(e.loc, e, ifbase.type);
else
e = new DotTypeExp(e.loc, e, cbase);
e = e.expressionSemantic(sc);
return e;
}
if (ident == Id.classinfo)
{
if (!Type.typeinfoclass)
{
error(e.loc, "`object.TypeInfo_Class` could not be found, but is implicitly used");
return ErrorExp.get();
}
Type t = Type.typeinfoclass.type;
if (e.op == EXP.type || e.op == EXP.dotType)
{
/* For type.classinfo, we know the classinfo
* at compile time.
*/
if (!mt.sym.vclassinfo)
mt.sym.vclassinfo = new TypeInfoClassDeclaration(mt.sym.type);
e = new VarExp(e.loc, mt.sym.vclassinfo);
e = e.addressOf();
e.type = t; // do this so we don't get redundant dereference
}
else
{
/* For class objects, the classinfo reference is the first
* entry in the vtbl[]
*/
e = new PtrExp(e.loc, e);
e.type = t.pointerTo();
if (mt.sym.isInterfaceDeclaration())
{
if (mt.sym.isCPPinterface())
{
/* C++ interface vtbl[]s are different in that the
* first entry is always pointer to the first virtual
* function, not classinfo.
* We can't get a .classinfo for it.
*/
error(e.loc, "no `.classinfo` for C++ interface objects");
}
/* For an interface, the first entry in the vtbl[]
* is actually a pointer to an instance of struct Interface.
* The first member of Interface is the .classinfo,
* so add an extra pointer indirection.
*/
e.type = e.type.pointerTo();
e = new PtrExp(e.loc, e);
e.type = t.pointerTo();
}
e = new PtrExp(e.loc, e, t);
}
return e;
}
if (ident == Id.__vptr)
{
/* The pointer to the vtbl[]
* *cast(immutable(void*)**)e
*/
e = e.castTo(sc, mt.tvoidptr.immutableOf().pointerTo().pointerTo());
e = new PtrExp(e.loc, e);
e = e.expressionSemantic(sc);
return e;
}
if (ident == Id.__monitor && mt.sym.hasMonitor())
{
/* The handle to the monitor (call it a void*)
* *(cast(void**)e + 1)
*/
e = e.castTo(sc, mt.tvoidptr.pointerTo());
e = new AddExp(e.loc, e, IntegerExp.literal!1);
e = new PtrExp(e.loc, e);
e = e.expressionSemantic(sc);
return e;
}
if (ident == Id.outer && mt.sym.vthis)
{
if (mt.sym.vthis.semanticRun == PASS.initial)
mt.sym.vthis.dsymbolSemantic(null);
if (auto cdp = mt.sym.toParentLocal().isClassDeclaration())
{
auto dve = new DotVarExp(e.loc, e, mt.sym.vthis);
dve.type = cdp.type.addMod(e.type.mod);
return dve;
}
/* https://issues.dlang.org/show_bug.cgi?id=15839
* Find closest parent class through nested functions.
*/
for (auto p = mt.sym.toParentLocal(); p; p = p.toParentLocal())
{
auto fd = p.isFuncDeclaration();
if (!fd)
break;
auto ad = fd.isThis();
if (!ad && fd.isNested())
continue;
if (!ad)
break;
if (auto cdp = ad.isClassDeclaration())
{
auto ve = new ThisExp(e.loc);
ve.var = fd.vthis;
const nestedError = fd.vthis.checkNestedReference(sc, e.loc);
assert(!nestedError);
ve.type = cdp.type.addMod(fd.vthis.type.mod).addMod(e.type.mod);
return ve;
}
break;
}
// Continue to show enclosing function's frame (stack or closure).
auto dve = new DotVarExp(e.loc, e, mt.sym.vthis);
dve.type = mt.sym.vthis.type.addMod(e.type.mod);
return dve;
}
return noMember(mt, sc, e, ident, flag & 1);
}
if (!(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc, s))
{
return noMember(mt, sc, e, ident, flag);
}
if (!s.isFuncDeclaration()) // because of overloading
{
s.checkDeprecated(e.loc, sc);
if (auto d = s.isDeclaration())
d.checkDisabled(e.loc, sc);
}
s = s.toAlias();
if (auto em = s.isEnumMember())
{
return em.getVarExp(e.loc, sc);
}
if (auto v = s.isVarDeclaration())
{
if (!v.type ||
!v.type.deco && v.inuse)
{
if (v.inuse) // https://issues.dlang.org/show_bug.cgi?id=9494
e.error("circular reference to %s `%s`", v.kind(), v.toPrettyChars());
else
e.error("forward reference to %s `%s`", v.kind(), v.toPrettyChars());
return ErrorExp.get();
}
if (v.type.ty == Terror)
{
return ErrorExp.get();
}
if ((v.storage_class & STC.manifest) && v._init)
{
if (v.inuse)
{
e.error("circular initialization of %s `%s`", v.kind(), v.toPrettyChars());
return ErrorExp.get();
}
checkAccess(e.loc, sc, null, v);
Expression ve = new VarExp(e.loc, v);
ve = ve.expressionSemantic(sc);
return ve;
}
}
if (auto t = s.getType())
{
return (new TypeExp(e.loc, t)).expressionSemantic(sc);
}
TemplateMixin tm = s.isTemplateMixin();
if (tm)
{
return new DotExp(e.loc, e, new ScopeExp(e.loc, tm)).expressionSemantic(sc);
}
TemplateDeclaration td = s.isTemplateDeclaration();
Expression toTemplateExp(TemplateDeclaration td)
{
if (e.op == EXP.type)
e = new TemplateExp(e.loc, td);
else
e = new DotTemplateExp(e.loc, e, td);
e = e.expressionSemantic(sc);
return e;
}
if (td)
{
return toTemplateExp(td);
}
TemplateInstance ti = s.isTemplateInstance();
if (ti)
{
if (!ti.semanticRun)
{
ti.dsymbolSemantic(sc);
if (!ti.inst || ti.errors) // if template failed to expand
{
return ErrorExp.get();
}
}
s = ti.inst.toAlias();
if (!s.isTemplateInstance())
goto L1;
if (e.op == EXP.type)
e = new ScopeExp(e.loc, ti);
else
e = new DotExp(e.loc, e, new ScopeExp(e.loc, ti));
return e.expressionSemantic(sc);
}
if (s.isImport() || s.isModule() || s.isPackage())
{
e = symbolToExp(s, e.loc, sc, false);
return e;
}
OverloadSet o = s.isOverloadSet();
if (o)
{
auto oe = new OverExp(e.loc, o);
if (e.op == EXP.type)
{
return oe;
}
return new DotExp(e.loc, e, oe);
}
Declaration d = s.isDeclaration();
if (!d)
{
e.error("`%s.%s` is not a declaration", e.toChars(), ident.toChars());
return ErrorExp.get();
}
if (e.op == EXP.type)
{
/* It's:
* Class.d
*/
if (TupleDeclaration tup = d.isTupleDeclaration())
{
e = new TupleExp(e.loc, tup);
e = e.expressionSemantic(sc);
return e;
}
if (mt.sym.classKind == ClassKind.objc
&& d.isFuncDeclaration()
&& d.isFuncDeclaration().isStatic
&& d.isFuncDeclaration().objc.selector)
{
auto classRef = new ObjcClassReferenceExp(e.loc, mt.sym);
return new DotVarExp(e.loc, classRef, d).expressionSemantic(sc);
}
else if (d.needThis() && sc.intypeof != 1)
{
/* Rewrite as:
* this.d
*/
AggregateDeclaration ad = d.isMemberLocal();
if (auto f = hasThis(sc))
{
// This is almost same as getRightThis() in expressionsem.d
Expression e1;
Type t;
/* returns: true to continue, false to return */
if (f.hasDualContext())
{
if (f.followInstantiationContext(ad))
{
e1 = new VarExp(e.loc, f.vthis);
e1 = new PtrExp(e1.loc, e1);
e1 = new IndexExp(e1.loc, e1, IntegerExp.literal!1);
auto pd = f.toParent2().isDeclaration();
assert(pd);
t = pd.type.toBasetype();
e1 = getThisSkipNestedFuncs(e1.loc, sc, f.toParent2(), ad, e1, t, d, true);
if (!e1)
{
e = new VarExp(e.loc, d);
return e;
}
goto L2;
}
}
e1 = new ThisExp(e.loc);
e1 = e1.expressionSemantic(sc);
L2:
t = e1.type.toBasetype();
ClassDeclaration cd = e.type.isClassHandle();
ClassDeclaration tcd = t.isClassHandle();
if (cd && tcd && (tcd == cd || cd.isBaseOf(tcd, null)))
{
e = new DotTypeExp(e1.loc, e1, cd);
e = new DotVarExp(e.loc, e, d);
e = e.expressionSemantic(sc);
return e;
}
if (tcd && tcd.isNested())
{
/* e1 is the 'this' pointer for an inner class: tcd.
* Rewrite it as the 'this' pointer for the outer class.
*/
auto vthis = tcd.followInstantiationContext(ad) ? tcd.vthis2 : tcd.vthis;
e1 = new DotVarExp(e.loc, e1, vthis);
e1.type = vthis.type;
e1.type = e1.type.addMod(t.mod);
// Do not call ensureStaticLinkTo()
//e1 = e1.expressionSemantic(sc);
// Skip up over nested functions, and get the enclosing
// class type.
e1 = getThisSkipNestedFuncs(e1.loc, sc, tcd.toParentP(ad), ad, e1, t, d, true);
if (!e1)
{
e = new VarExp(e.loc, d);
return e;
}
goto L2;
}
}
}
//printf("e = %s, d = %s\n", e.toChars(), d.toChars());
if (d.semanticRun == PASS.initial)
d.dsymbolSemantic(null);
// If static function, get the most visible overload.
// Later on the call is checked for correctness.
// https://issues.dlang.org/show_bug.cgi?id=12511
Dsymbol d2 = d;
if (auto fd = d.isFuncDeclaration())
{
import dmd.access : mostVisibleOverload;
d2 = mostVisibleOverload(fd, sc._module);
}
checkAccess(e.loc, sc, e, d2);
if (d2.isDeclaration())
{
d = cast(Declaration)d2;
auto ve = new VarExp(e.loc, d);
if (d.isVarDeclaration() && d.needThis())
ve.type = d.type.addMod(e.type.mod);
return ve;
}
else if (d2.isTemplateDeclaration())
{
return toTemplateExp(cast(TemplateDeclaration)d2);
}
else
assert(0);
}
bool unreal = e.op == EXP.variable && (cast(VarExp)e).var.isField();
if (d.isDataseg() || unreal && d.isField())
{
// (e, d)
checkAccess(e.loc, sc, e, d);
Expression ve = new VarExp(e.loc, d);
e = unreal ? ve : new CommaExp(e.loc, e, ve);
e = e.expressionSemantic(sc);
return e;
}
e = new DotVarExp(e.loc, e, d);
e = e.expressionSemantic(sc);
return e;
}
switch (mt.ty)
{
case Tvector: return visitVector (mt.isTypeVector());
case Tsarray: return visitSArray (mt.isTypeSArray());
case Tstruct: return visitStruct (mt.isTypeStruct());
case Tenum: return visitEnum (mt.isTypeEnum());
case Terror: return visitError (mt.isTypeError());
case Tarray: return visitDArray (mt.isTypeDArray());
case Taarray: return visitAArray (mt.isTypeAArray());
case Treference: return visitReference(mt.isTypeReference());
case Tdelegate: return visitDelegate (mt.isTypeDelegate());
case Tclass: return visitClass (mt.isTypeClass());
default: return mt.isTypeBasic()
? visitBasic(cast(TypeBasic)mt)
: visitType(mt);
}
}
/************************
* Get the the default initialization expression for a type.
* Params:
* mt = the type for which the init expression is returned
* loc = the location where the expression needs to be evaluated
* isCfile = default initializers are different with C
*
* Returns:
* The initialization expression for the type.
*/
extern (C++) Expression defaultInit(Type mt, const ref Loc loc, const bool isCfile = false)
{
Expression visitBasic(TypeBasic mt)
{
static if (LOGDEFAULTINIT)
{
printf("TypeBasic::defaultInit() '%s'\n", mt.toChars());
}
dinteger_t value = 0;
switch (mt.ty)
{
case Tchar:
value = isCfile ? 0 : 0xFF;
break;
case Twchar:
case Tdchar:
value = isCfile ? 0 : 0xFFFF;
break;
case Timaginary32:
case Timaginary64:
case Timaginary80:
case Tfloat32:
case Tfloat64:
case Tfloat80:
return new RealExp(loc, isCfile ? CTFloat.zero : target.RealProperties.nan, mt);
case Tcomplex32:
case Tcomplex64:
case Tcomplex80:
{
// Can't use fvalue + I*fvalue (the im part becomes a quiet NaN).
const cvalue = isCfile ? complex_t(CTFloat.zero, CTFloat.zero)
: complex_t(target.RealProperties.nan, target.RealProperties.nan);
return new ComplexExp(loc, cvalue, mt);
}
case Tvoid:
error(loc, "`void` does not have a default initializer");
return ErrorExp.get();
default:
break;
}
return new IntegerExp(loc, value, mt);
}
Expression visitVector(TypeVector mt)
{
//printf("TypeVector::defaultInit()\n");
assert(mt.basetype.ty == Tsarray);
Expression e = mt.basetype.defaultInit(loc, isCfile);
auto ve = new VectorExp(loc, e, mt);
ve.type = mt;
ve.dim = cast(int)(mt.basetype.size(loc) / mt.elementType().size(loc));
return ve;
}
Expression visitSArray(TypeSArray mt)
{
static if (LOGDEFAULTINIT)
{
printf("TypeSArray::defaultInit() '%s'\n", mt.toChars());
}
if (mt.next.ty == Tvoid)
return mt.tuns8.defaultInit(loc, isCfile);
else
return mt.next.defaultInit(loc, isCfile);
}
Expression visitFunction(TypeFunction mt)
{
error(loc, "`function` does not have a default initializer");
return ErrorExp.get();
}
Expression visitStruct(TypeStruct mt)
{
static if (LOGDEFAULTINIT)
{
printf("TypeStruct::defaultInit() '%s'\n", mt.toChars());
}
Declaration d = new SymbolDeclaration(mt.sym.loc, mt.sym);
assert(d);
d.type = mt;
d.storage_class |= STC.rvalue; // https://issues.dlang.org/show_bug.cgi?id=14398
return new VarExp(mt.sym.loc, d);
}
Expression visitEnum(TypeEnum mt)
{
static if (LOGDEFAULTINIT)
{
printf("TypeEnum::defaultInit() '%s'\n", mt.toChars());
}
// Initialize to first member of enum
Expression e = mt.sym.getDefaultValue(loc);
e = e.copy();
e.loc = loc;
e.type = mt; // to deal with const, immutable, etc., variants
return e;
}
Expression visitTuple(TypeTuple mt)
{
static if (LOGDEFAULTINIT)
{
printf("TypeTuple::defaultInit() '%s'\n", mt.toChars());
}
auto exps = new Expressions(mt.arguments.dim);
for (size_t i = 0; i < mt.arguments.dim; i++)
{
Parameter p = (*mt.arguments)[i];
assert(p.type);
Expression e = p.type.defaultInitLiteral(loc);
if (e.op == EXP.error)
{
return e;
}
(*exps)[i] = e;
}
return new TupleExp(loc, exps);
}
Expression visitNoreturn(TypeNoreturn mt)
{
static if (LOGDEFAULTINIT)
{
printf("TypeNoreturn::defaultInit() '%s'\n", mt.toChars());
}
auto cond = IntegerExp.createBool(false);
auto msg = new StringExp(loc, "Accessed expression of type `noreturn`");
msg.type = Type.tstring;
auto ae = new AssertExp(loc, cond, msg);
ae.type = mt;
return ae;
}
switch (mt.ty)
{
case Tvector: return visitVector (mt.isTypeVector());
case Tsarray: return visitSArray (mt.isTypeSArray());
case Tfunction: return visitFunction(mt.isTypeFunction());
case Tstruct: return visitStruct (mt.isTypeStruct());
case Tenum: return visitEnum (mt.isTypeEnum());
case Ttuple: return visitTuple (mt.isTypeTuple());
case Tnull: return new NullExp(Loc.initial, Type.tnull);
case Terror: return ErrorExp.get();
case Tarray:
case Taarray:
case Tpointer:
case Treference:
case Tdelegate:
case Tclass: return new NullExp(loc, mt);
case Tnoreturn: return visitNoreturn(mt.isTypeNoreturn());
default: return mt.isTypeBasic() ?
visitBasic(cast(TypeBasic)mt) :
null;
}
}
/******************************
* Get the value of the .max/.min property of `ed` as an Expression.
* Lazily computes the value and caches it in maxval/minval.
* Reports any errors.
* Params:
* ed = the EnumDeclaration being examined
* loc = location to use for error messages
* id = Id::max or Id::min
* Returns:
* corresponding value of .max/.min
*/
private Expression getMaxMinValue(EnumDeclaration ed, const ref Loc loc, Identifier id)
{
//printf("EnumDeclaration::getMaxValue()\n");
static Expression pvalToResult(Expression e, const ref Loc loc)
{
if (e.op != EXP.error)
{
e = e.copy();
e.loc = loc;
}
return e;
}
Expression* pval = (id == Id.max) ? &ed.maxval : &ed.minval;
Expression errorReturn()
{
*pval = ErrorExp.get();
return *pval;
}
if (ed.inuse)
{
ed.error(loc, "recursive definition of `.%s` property", id.toChars());
return errorReturn();
}
if (*pval)
return pvalToResult(*pval, loc);
if (ed._scope)
dsymbolSemantic(ed, ed._scope);
if (ed.errors)
return errorReturn();
if (!ed.members)
{
if (ed.isSpecial())
{
/* Allow these special enums to not need a member list
*/
return ed.memtype.getProperty(ed._scope, loc, id, 0);
}
ed.error(loc, "is opaque and has no `.%s`", id.toChars());
return errorReturn();
}
if (!(ed.memtype && ed.memtype.isintegral()))
{
ed.error(loc, "has no `.%s` property because base type `%s` is not an integral type",
id.toChars(), ed.memtype ? ed.memtype.toChars() : "");
return errorReturn();
}
bool first = true;
for (size_t i = 0; i < ed.members.dim; i++)
{
EnumMember em = (*ed.members)[i].isEnumMember();
if (!em)
continue;
if (em.errors)
{
ed.errors = true;
continue;
}
if (em.semanticRun < PASS.semanticdone)
{
em.error("is forward referenced looking for `.%s`", id.toChars());
ed.errors = true;
continue;
}
if (first)
{
*pval = em.value;
first = false;
}
else
{
/* In order to work successfully with UDTs,
* build expressions to do the comparisons,
* and let the semantic analyzer and constant
* folder give us the result.
*/
/* Compute:
* if (e > maxval)
* maxval = e;
*/
Expression e = em.value;
Expression ec = new CmpExp(id == Id.max ? EXP.greaterThan : EXP.lessThan, em.loc, e, *pval);
ed.inuse++;
ec = ec.expressionSemantic(em._scope);
ed.inuse--;
ec = ec.ctfeInterpret();
if (ec.op == EXP.error)
{
ed.errors = true;
continue;
}
if (ec.toInteger())
*pval = e;
}
}
return ed.errors ? errorReturn() : pvalToResult(*pval, loc);
}