blob: f899bd79d20b177ea8121ed292b4551adb0fd41a [file] [log] [blame]
/**
* Semantic analysis of expressions.
*
* Specification: ($LINK2 https://dlang.org/spec/expression.html, Expressions)
*
* 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/expressionsem.d, _expressionsem.d)
* Documentation: https://dlang.org/phobos/dmd_expressionsem.html
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/expressionsem.d
*/
module dmd.expressionsem;
import core.stdc.stdio;
import dmd.access;
import dmd.aggregate;
import dmd.aliasthis;
import dmd.arrayop;
import dmd.arraytypes;
import dmd.attrib;
import dmd.astcodegen;
import dmd.astenums;
import dmd.canthrow;
import dmd.chkformat;
import dmd.ctorflow;
import dmd.dscope;
import dmd.dsymbol;
import dmd.declaration;
import dmd.dclass;
import dmd.dcast;
import dmd.delegatize;
import dmd.denum;
import dmd.dimport;
import dmd.dinterpret;
import dmd.dmangle;
import dmd.dmodule;
import dmd.dstruct;
import dmd.dsymbolsem;
import dmd.dtemplate;
import dmd.errors;
import dmd.escape;
import dmd.expression;
import dmd.file_manager;
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.inline;
import dmd.intrange;
import dmd.mtype;
import dmd.mustuse;
import dmd.nspace;
import dmd.opover;
import dmd.optimize;
import dmd.parse;
import dmd.printast;
import dmd.root.array;
import dmd.root.ctfloat;
import dmd.root.file;
import dmd.root.filename;
import dmd.common.outbuffer;
import dmd.root.rootobject;
import dmd.root.string;
import dmd.root.utf;
import dmd.semantic2;
import dmd.semantic3;
import dmd.sideeffect;
import dmd.safe;
import dmd.target;
import dmd.tokens;
import dmd.traits;
import dmd.typesem;
import dmd.typinf;
import dmd.utils;
import dmd.visitor;
enum LOGSEMANTIC = false;
/********************************************************
* Perform semantic analysis and CTFE on expressions to produce
* a string.
* Params:
* buf = append generated string to buffer
* sc = context
* exps = array of Expressions
* Returns:
* true on error
*/
bool expressionsToString(ref OutBuffer buf, Scope* sc, Expressions* exps)
{
if (!exps)
return false;
foreach (ex; *exps)
{
if (!ex)
continue;
auto sc2 = sc.startCTFE();
auto e2 = ex.expressionSemantic(sc2);
auto e3 = resolveProperties(sc2, e2);
sc2.endCTFE();
// allowed to contain types as well as expressions
auto e4 = ctfeInterpretForPragmaMsg(e3);
if (!e4 || e4.op == EXP.error)
return true;
// expand tuple
if (auto te = e4.isTupleExp())
{
if (expressionsToString(buf, sc, te.exps))
return true;
continue;
}
// char literals exp `.toStringExp` return `null` but we cant override it
// because in most contexts we don't want the conversion to succeed.
IntegerExp ie = e4.isIntegerExp();
const ty = (ie && ie.type) ? ie.type.ty : Terror;
if (ty.isSomeChar)
{
auto tsa = new TypeSArray(ie.type, IntegerExp.literal!1);
e4 = new ArrayLiteralExp(ex.loc, tsa, ie);
}
if (StringExp se = e4.toStringExp())
buf.writestring(se.toUTF8(sc).peekString());
else
buf.writestring(e4.toString());
}
return false;
}
/***********************************************************
* Resolve `exp` as a compile-time known string.
* Params:
* sc = scope
* exp = Expression which expected as a string
* s = What the string is expected for, will be used in error diagnostic.
* Returns:
* String literal, or `null` if error happens.
*/
StringExp semanticString(Scope *sc, Expression exp, const char* s)
{
sc = sc.startCTFE();
exp = exp.expressionSemantic(sc);
exp = resolveProperties(sc, exp);
sc = sc.endCTFE();
if (exp.op == EXP.error)
return null;
auto e = exp;
if (exp.type.isString())
{
e = e.ctfeInterpret();
if (e.op == EXP.error)
return null;
}
auto se = e.toStringExp();
if (!se)
{
exp.error("`string` expected for %s, not `(%s)` of type `%s`",
s, exp.toChars(), exp.type.toChars());
return null;
}
return se;
}
private Expression extractOpDollarSideEffect(Scope* sc, UnaExp ue)
{
Expression e0;
Expression e1 = Expression.extractLast(ue.e1, e0);
// https://issues.dlang.org/show_bug.cgi?id=12585
// Extract the side effect part if ue.e1 is comma.
if ((sc.flags & SCOPE.ctfe) ? hasSideEffect(e1) : !isTrivialExp(e1)) // match logic in extractSideEffect()
{
/* Even if opDollar is needed, 'e1' should be evaluate only once. So
* Rewrite:
* e1.opIndex( ... use of $ ... )
* e1.opSlice( ... use of $ ... )
* as:
* (ref __dop = e1, __dop).opIndex( ... __dop.opDollar ...)
* (ref __dop = e1, __dop).opSlice( ... __dop.opDollar ...)
*/
e1 = extractSideEffect(sc, "__dop", e0, e1, false);
assert(e1.isVarExp());
e1.isVarExp().var.storage_class |= STC.exptemp; // lifetime limited to expression
}
ue.e1 = e1;
return e0;
}
/**************************************
* Runs semantic on ae.arguments. Declares temporary variables
* if '$' was used.
*/
Expression resolveOpDollar(Scope* sc, ArrayExp ae, Expression* pe0)
{
assert(!ae.lengthVar);
*pe0 = null;
AggregateDeclaration ad = isAggregate(ae.e1.type);
Dsymbol slice = search_function(ad, Id.slice);
//printf("slice = %s %s\n", slice.kind(), slice.toChars());
foreach (i, e; *ae.arguments)
{
if (i == 0)
*pe0 = extractOpDollarSideEffect(sc, ae);
if (e.op == EXP.interval && !(slice && slice.isTemplateDeclaration()))
{
Lfallback:
if (ae.arguments.dim == 1)
return null;
ae.error("multi-dimensional slicing requires template `opSlice`");
return ErrorExp.get();
}
//printf("[%d] e = %s\n", i, e.toChars());
// Create scope for '$' variable for this dimension
auto sym = new ArrayScopeSymbol(sc, ae);
sym.parent = sc.scopesym;
sc = sc.push(sym);
ae.lengthVar = null; // Create it only if required
ae.currentDimension = i; // Dimension for $, if required
e = e.expressionSemantic(sc);
e = resolveProperties(sc, e);
if (ae.lengthVar && sc.func)
{
// If $ was used, declare it now
Expression de = new DeclarationExp(ae.loc, ae.lengthVar);
de = de.expressionSemantic(sc);
*pe0 = Expression.combine(*pe0, de);
}
sc = sc.pop();
if (auto ie = e.isIntervalExp())
{
auto tiargs = new Objects();
Expression edim = new IntegerExp(ae.loc, i, Type.tsize_t);
edim = edim.expressionSemantic(sc);
tiargs.push(edim);
auto fargs = new Expressions(2);
(*fargs)[0] = ie.lwr;
(*fargs)[1] = ie.upr;
uint xerrors = global.startGagging();
sc = sc.push();
FuncDeclaration fslice = resolveFuncCall(ae.loc, sc, slice, tiargs, ae.e1.type, fargs, FuncResolveFlag.quiet);
sc = sc.pop();
global.endGagging(xerrors);
if (!fslice)
goto Lfallback;
e = new DotTemplateInstanceExp(ae.loc, ae.e1, slice.ident, tiargs);
e = new CallExp(ae.loc, e, fargs);
e = e.expressionSemantic(sc);
}
if (!e.type)
{
ae.error("`%s` has no value", e.toChars());
e = ErrorExp.get();
}
if (e.op == EXP.error)
return e;
(*ae.arguments)[i] = e;
}
return ae;
}
/**************************************
* Runs semantic on se.lwr and se.upr. Declares a temporary variable
* if '$' was used.
* Returns:
* ae, or ErrorExp if errors occurred
*/
Expression resolveOpDollar(Scope* sc, ArrayExp ae, IntervalExp ie, Expression* pe0)
{
//assert(!ae.lengthVar);
if (!ie)
return ae;
VarDeclaration lengthVar = ae.lengthVar;
bool errors = false;
// create scope for '$'
auto sym = new ArrayScopeSymbol(sc, ae);
sym.parent = sc.scopesym;
sc = sc.push(sym);
Expression sem(Expression e)
{
e = e.expressionSemantic(sc);
e = resolveProperties(sc, e);
if (!e.type)
{
ae.error("`%s` has no value", e.toChars());
errors = true;
}
return e;
}
ie.lwr = sem(ie.lwr);
ie.upr = sem(ie.upr);
if (ie.lwr.isErrorExp() || ie.upr.isErrorExp())
errors = true;
if (lengthVar != ae.lengthVar && sc.func)
{
// If $ was used, declare it now
Expression de = new DeclarationExp(ae.loc, ae.lengthVar);
de = de.expressionSemantic(sc);
*pe0 = Expression.combine(*pe0, de);
}
sc = sc.pop();
return errors ? ErrorExp.get() : ae;
}
/******************************
* Perform semantic() on an array of Expressions.
*/
extern(D) bool arrayExpressionSemantic(
Expression[] exps, Scope* sc, bool preserveErrors = false)
{
bool err = false;
foreach (ref e; exps)
{
if (e is null) continue;
auto e2 = e.expressionSemantic(sc);
if (e2.op == EXP.error)
err = true;
if (preserveErrors || e2.op != EXP.error)
e = e2;
}
return err;
}
/*
Checks if `exp` contains a direct access to a `noreturn`
variable. If that is the case, an `assert(0)` expression
is generated and returned. This function should be called
only after semantic analysis has been performed on `exp`.
Params:
exp = expression that is checked
Returns:
An `assert(0)` expression if `exp` contains a `noreturn`
variable access, `exp` otherwise.
*/
Expression checkNoreturnVarAccess(Expression exp)
{
assert(exp.type);
Expression result = exp;
if (exp.type.isTypeNoreturn() && !exp.isAssertExp() &&
!exp.isThrowExp() && !exp.isCallExp())
{
auto msg = new StringExp(exp.loc, "Accessed expression of type `noreturn`");
msg.type = Type.tstring;
result = new AssertExp(exp.loc, IntegerExp.literal!0, msg);
result.type = exp.type;
}
return result;
}
/******************************
* Check the tail CallExp is really property function call.
* Bugs:
* This doesn't appear to do anything.
*/
private bool checkPropertyCall(Expression e)
{
e = lastComma(e);
if (auto ce = e.isCallExp())
{
if (ce.f)
{
auto tf = ce.f.type.isTypeFunction();
/* If a forward reference to ce.f, try to resolve it
*/
if (!tf.deco && ce.f.semanticRun < PASS.semanticdone)
{
ce.f.dsymbolSemantic(null);
tf = ce.f.type.isTypeFunction();
}
}
else if (!ce.e1.type.isFunction_Delegate_PtrToFunction())
assert(0);
}
return false;
}
/******************************
* Find symbol in accordance with the UFCS name look up rule
*/
private Expression searchUFCS(Scope* sc, UnaExp ue, Identifier ident)
{
//printf("searchUFCS(ident = %s)\n", ident.toChars());
Loc loc = ue.loc;
// TODO: merge with Scope.search.searchScopes()
Dsymbol searchScopes(int flags)
{
Dsymbol s = null;
for (Scope* scx = sc; scx; scx = scx.enclosing)
{
if (!scx.scopesym)
continue;
if (scx.scopesym.isModule())
flags |= SearchUnqualifiedModule; // tell Module.search() that SearchLocalsOnly is to be obeyed
s = scx.scopesym.search(loc, ident, flags);
if (s)
{
// overload set contains only module scope symbols.
if (s.isOverloadSet())
break;
// selective/renamed imports also be picked up
if (AliasDeclaration ad = s.isAliasDeclaration())
{
if (ad._import)
break;
}
// See only module scope symbols for UFCS target.
Dsymbol p = s.toParent2();
if (p && p.isModule())
break;
}
s = null;
// Stop when we hit a module, but keep going if that is not just under the global scope
if (scx.scopesym.isModule() && !(scx.enclosing && !scx.enclosing.enclosing))
break;
}
return s;
}
int flags = 0;
Dsymbol s;
if (sc.flags & SCOPE.ignoresymbolvisibility)
flags |= IgnoreSymbolVisibility;
// First look in local scopes
s = searchScopes(flags | SearchLocalsOnly);
if (!s)
{
// Second look in imported modules
s = searchScopes(flags | SearchImportsOnly);
}
if (!s)
return ue.e1.type.getProperty(sc, loc, ident, 0, ue.e1);
FuncDeclaration f = s.isFuncDeclaration();
if (f)
{
TemplateDeclaration td = getFuncTemplateDecl(f);
if (td)
{
if (td.overroot)
td = td.overroot;
s = td;
}
}
if (auto dti = ue.isDotTemplateInstanceExp())
{
auto ti = new TemplateInstance(loc, s.ident, dti.ti.tiargs);
if (!ti.updateTempDecl(sc, s))
return ErrorExp.get();
return new ScopeExp(loc, ti);
}
else
{
//printf("-searchUFCS() %s\n", s.toChars());
return new DsymbolExp(loc, s);
}
}
/******************************
* Pull out callable entity with UFCS.
*/
private Expression resolveUFCS(Scope* sc, CallExp ce)
{
Loc loc = ce.loc;
Expression eleft;
Expression e;
if (auto die = ce.e1.isDotIdExp())
{
Identifier ident = die.ident;
Expression ex = die.semanticX(sc);
if (ex != die)
{
ce.e1 = ex;
return null;
}
eleft = die.e1;
Type t = eleft.type.toBasetype();
if (t.ty == Tarray || t.ty == Tsarray || t.ty == Tnull || (t.isTypeBasic() && t.ty != Tvoid))
{
/* Built-in types and arrays have no callable properties, so do shortcut.
* It is necessary in: e.init()
*/
}
else if (t.ty == Taarray)
{
if (ident == Id.remove)
{
/* Transform:
* aa.remove(arg) into delete aa[arg]
*/
if (!ce.arguments || ce.arguments.dim != 1)
{
ce.error("expected key as argument to `aa.remove()`");
return ErrorExp.get();
}
if (!eleft.type.isMutable())
{
ce.error("cannot remove key from `%s` associative array `%s`", MODtoChars(t.mod), eleft.toChars());
return ErrorExp.get();
}
Expression key = (*ce.arguments)[0];
key = key.expressionSemantic(sc);
key = resolveProperties(sc, key);
TypeAArray taa = t.isTypeAArray();
key = key.implicitCastTo(sc, taa.index);
if (key.checkValue() || key.checkSharedAccess(sc))
return ErrorExp.get();
semanticTypeInfo(sc, taa.index);
return new RemoveExp(loc, eleft, key);
}
}
else
{
if (Expression ey = die.semanticY(sc, 1))
{
if (ey.op == EXP.error)
return ey;
ce.e1 = ey;
if (isDotOpDispatch(ey))
{
// even opDispatch and UFCS must have valid arguments,
// so now that we've seen indication of a problem,
// check them for issues.
Expressions* originalArguments = Expression.arraySyntaxCopy(ce.arguments);
uint errors = global.startGagging();
e = ce.expressionSemantic(sc);
if (!global.endGagging(errors))
return e;
if (arrayExpressionSemantic(originalArguments.peekSlice(), sc))
return ErrorExp.get();
/* fall down to UFCS */
}
else
return null;
}
}
/* https://issues.dlang.org/show_bug.cgi?id=13953
*
* If a struct has an alias this to an associative array
* and remove is used on a struct instance, we have to
* check first if there is a remove function that can be called
* on the struct. If not we must check the alias this.
*
* struct A
* {
* string[string] a;
* alias a this;
* }
*
* void fun()
* {
* A s;
* s.remove("foo");
* }
*/
const errors = global.startGagging();
e = searchUFCS(sc, die, ident);
// if there were any errors and the identifier was remove
if (global.endGagging(errors))
{
if (ident == Id.remove)
{
// check alias this
Expression alias_e = resolveAliasThis(sc, die.e1, 1);
if (alias_e && alias_e != die.e1)
{
die.e1 = alias_e;
CallExp ce2 = ce.syntaxCopy();
ce2.e1 = die;
e = ce2.isCallExp().trySemantic(sc);
if (e)
return e;
}
}
// if alias this did not work out, print the initial errors
searchUFCS(sc, die, ident);
}
}
else if (auto dti = ce.e1.isDotTemplateInstanceExp())
{
if (Expression ey = dti.semanticY(sc, 1))
{
ce.e1 = ey;
return null;
}
eleft = dti.e1;
e = searchUFCS(sc, dti, dti.ti.name);
}
else
return null;
// Rewrite
ce.e1 = e;
if (!ce.arguments)
ce.arguments = new Expressions();
ce.arguments.shift(eleft);
return null;
}
/******************************
* Pull out property with UFCS.
*/
private Expression resolveUFCSProperties(Scope* sc, Expression e1, Expression e2 = null)
{
Loc loc = e1.loc;
Expression eleft;
Expression e;
if (auto die = e1.isDotIdExp())
{
eleft = die.e1;
e = searchUFCS(sc, die, die.ident);
}
else if (auto dti = e1.isDotTemplateInstanceExp())
{
eleft = dti.e1;
e = searchUFCS(sc, dti, dti.ti.name);
}
else
return null;
if (e is null)
return null;
// Rewrite
if (e2)
{
// run semantic without gagging
e2 = e2.expressionSemantic(sc);
/* f(e1) = e2
*/
Expression ex = e.copy();
auto a1 = new Expressions(1);
(*a1)[0] = eleft;
ex = new CallExp(loc, ex, a1);
auto e1PassSemantic = ex.trySemantic(sc);
/* f(e1, e2)
*/
auto a2 = new Expressions(2);
(*a2)[0] = eleft;
(*a2)[1] = e2;
e = new CallExp(loc, e, a2);
e = e.trySemantic(sc);
if (!e1PassSemantic && !e)
{
/* https://issues.dlang.org/show_bug.cgi?id=20448
*
* If both versions have failed to pass semantic,
* f(e1) = e2 gets priority in error printing
* because f might be a templated function that
* failed to instantiate and we have to print
* the instantiation errors.
*/
return e1.expressionSemantic(sc);
}
else if (ex && !e)
{
checkPropertyCall(ex);
ex = new AssignExp(loc, ex, e2);
return ex.expressionSemantic(sc);
}
else
{
// strict setter prints errors if fails
e = e.expressionSemantic(sc);
}
checkPropertyCall(e);
return e;
}
else
{
/* f(e1)
*/
auto arguments = new Expressions(1);
(*arguments)[0] = eleft;
e = new CallExp(loc, e, arguments);
e = e.expressionSemantic(sc);
checkPropertyCall(e);
return e.expressionSemantic(sc);
}
}
/******************************
* If e1 is a property function (template), resolve it.
*/
Expression resolvePropertiesOnly(Scope* sc, Expression e1)
{
//printf("e1 = %s %s\n", Token.toChars(e1.op), e1.toChars());
Expression handleOverloadSet(OverloadSet os)
{
assert(os);
foreach (s; os.a)
{
auto fd = s.isFuncDeclaration();
auto td = s.isTemplateDeclaration();
if (fd)
{
if (fd.type.isTypeFunction().isproperty)
return resolveProperties(sc, e1);
}
else if (td && td.onemember && (fd = td.onemember.isFuncDeclaration()) !is null)
{
if (fd.type.isTypeFunction().isproperty ||
(fd.storage_class2 & STC.property) ||
(td._scope.stc & STC.property))
return resolveProperties(sc, e1);
}
}
return e1;
}
Expression handleTemplateDecl(TemplateDeclaration td)
{
assert(td);
if (td.onemember)
{
if (auto fd = td.onemember.isFuncDeclaration())
{
if (fd.type.isTypeFunction().isproperty ||
(fd.storage_class2 & STC.property) ||
(td._scope.stc & STC.property))
return resolveProperties(sc, e1);
}
}
return e1;
}
Expression handleFuncDecl(FuncDeclaration fd)
{
assert(fd);
if (fd.type.isTypeFunction().isproperty)
return resolveProperties(sc, e1);
return e1;
}
if (auto de = e1.isDotExp())
{
if (auto os = de.e2.isOverExp())
return handleOverloadSet(os.vars);
}
else if (auto oe = e1.isOverExp())
return handleOverloadSet(oe.vars);
else if (auto dti = e1.isDotTemplateInstanceExp())
{
if (dti.ti.tempdecl)
if (auto td = dti.ti.tempdecl.isTemplateDeclaration())
return handleTemplateDecl(td);
}
else if (auto dte = e1.isDotTemplateExp())
return handleTemplateDecl(dte.td);
else if (auto se = e1.isScopeExp())
{
Dsymbol s = se.sds;
TemplateInstance ti = s.isTemplateInstance();
if (ti && !ti.semanticRun && ti.tempdecl)
if (auto td = ti.tempdecl.isTemplateDeclaration())
return handleTemplateDecl(td);
}
else if (auto et = e1.isTemplateExp())
return handleTemplateDecl(et.td);
else if (e1.isDotVarExp() && e1.type.isTypeFunction())
{
DotVarExp dve = e1.isDotVarExp();
return handleFuncDecl(dve.var.isFuncDeclaration());
}
else if (e1.isVarExp() && e1.type && e1.type.isTypeFunction() && (sc.intypeof || !e1.isVarExp().var.needThis()))
return handleFuncDecl(e1.isVarExp().var.isFuncDeclaration());
return e1;
}
/****************************************
* Turn symbol `s` into the expression it represents.
*
* Params:
* s = symbol to resolve
* loc = location of use of `s`
* sc = context
* hasOverloads = applies if `s` represents a function.
* true means it's overloaded and will be resolved later,
* false means it's the exact function symbol.
* Returns:
* `s` turned into an expression, `ErrorExp` if an error occurred
*/
Expression symbolToExp(Dsymbol s, const ref Loc loc, Scope *sc, bool hasOverloads)
{
static if (LOGSEMANTIC)
{
printf("DsymbolExp::resolve(%s %s)\n", s.kind(), s.toChars());
}
Lagain:
Expression e;
//printf("DsymbolExp:: %p '%s' is a symbol\n", this, toChars());
//printf("s = '%s', s.kind = '%s'\n", s.toChars(), s.kind());
Dsymbol olds = s;
Declaration d = s.isDeclaration();
if (d && (d.storage_class & STC.templateparameter))
{
s = s.toAlias();
}
else
{
// 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);
}
// https://issues.dlang.org/show_bug.cgi?id=12023
// if 's' is a tuple variable, the tuple is returned.
s = s.toAlias();
//printf("s = '%s', s.kind = '%s', s.needThis() = %p\n", s.toChars(), s.kind(), s.needThis());
if (s != olds && !s.isFuncDeclaration() && !s.isTemplateDeclaration())
{
s.checkDeprecated(loc, sc);
if (d)
d.checkDisabled(loc, sc);
}
if (auto sd = s.isDeclaration())
{
if (sd.isSystem())
{
if (sc.setUnsafePreview(global.params.systemVariables, false, loc,
"cannot access `@system` variable `%s` in @safe code", sd))
{
return ErrorExp.get();
}
}
}
}
if (auto em = s.isEnumMember())
{
return em.getVarExp(loc, sc);
}
if (auto v = s.isVarDeclaration())
{
//printf("Identifier '%s' is a variable, type '%s'\n", s.toChars(), v.type.toChars());
if (sc.intypeof == 1 && !v.inuse)
v.dsymbolSemantic(sc);
if (!v.type || // during variable type inference
!v.type.deco && v.inuse) // during variable type semantic
{
if (v.inuse) // variable type depends on the variable itself
error(loc, "circular reference to %s `%s`", v.kind(), v.toPrettyChars());
else // variable type cannot be determined
error(loc, "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)
{
error(loc, "circular initialization of %s `%s`", v.kind(), v.toPrettyChars());
return ErrorExp.get();
}
e = v.expandInitializer(loc);
v.inuse++;
e = e.expressionSemantic(sc);
v.inuse--;
return e;
}
// We need to run semantics to correctly set 'STC.field' if it is a member variable
// that could be forward referenced. This is needed for 'v.needThis()' to work
if (v.isThis())
v.dsymbolSemantic(sc);
// Change the ancestor lambdas to delegate before hasThis(sc) call.
if (v.checkNestedReference(sc, loc))
return ErrorExp.get();
if (v.needThis() && hasThis(sc))
e = new DotVarExp(loc, new ThisExp(loc), v);
else
e = new VarExp(loc, v);
e = e.expressionSemantic(sc);
return e;
}
if (auto fld = s.isFuncLiteralDeclaration())
{
//printf("'%s' is a function literal\n", fld.toChars());
e = new FuncExp(loc, fld);
return e.expressionSemantic(sc);
}
if (auto f = s.isFuncDeclaration())
{
f = f.toAliasFunc();
if (!f.functionSemantic())
return ErrorExp.get();
if (!hasOverloads && f.checkForwardRef(loc))
return ErrorExp.get();
auto fd = s.isFuncDeclaration();
fd.type = f.type;
return new VarExp(loc, fd, hasOverloads);
}
if (OverDeclaration od = s.isOverDeclaration())
{
e = new VarExp(loc, od, true);
e.type = Type.tvoid;
return e;
}
if (OverloadSet o = s.isOverloadSet())
{
//printf("'%s' is an overload set\n", o.toChars());
return new OverExp(loc, o);
}
if (Import imp = s.isImport())
{
if (!imp.pkg)
{
.error(loc, "forward reference of import `%s`", imp.toChars());
return ErrorExp.get();
}
auto ie = new ScopeExp(loc, imp.pkg);
return ie.expressionSemantic(sc);
}
if (Package pkg = s.isPackage())
{
auto ie = new ScopeExp(loc, pkg);
return ie.expressionSemantic(sc);
}
if (Module mod = s.isModule())
{
auto ie = new ScopeExp(loc, mod);
return ie.expressionSemantic(sc);
}
if (Nspace ns = s.isNspace())
{
auto ie = new ScopeExp(loc, ns);
return ie.expressionSemantic(sc);
}
if (Type t = s.getType())
{
return (new TypeExp(loc, t)).expressionSemantic(sc);
}
if (TupleDeclaration tup = s.isTupleDeclaration())
{
if (tup.needThis() && hasThis(sc))
e = new DotVarExp(loc, new ThisExp(loc), tup);
else
e = new TupleExp(loc, tup);
e = e.expressionSemantic(sc);
return e;
}
if (TemplateInstance ti = s.isTemplateInstance())
{
ti.dsymbolSemantic(sc);
if (!ti.inst || ti.errors)
return ErrorExp.get();
s = ti.toAlias();
if (!s.isTemplateInstance())
goto Lagain;
e = new ScopeExp(loc, ti);
e = e.expressionSemantic(sc);
return e;
}
if (TemplateDeclaration td = s.isTemplateDeclaration())
{
Dsymbol p = td.toParentLocal();
FuncDeclaration fdthis = hasThis(sc);
AggregateDeclaration ad = p ? p.isAggregateDeclaration() : null;
if (fdthis && ad && fdthis.isMemberLocal() == ad && (td._scope.stc & STC.static_) == 0)
{
e = new DotTemplateExp(loc, new ThisExp(loc), td);
}
else
e = new TemplateExp(loc, td);
e = e.expressionSemantic(sc);
return e;
}
.error(loc, "%s `%s` is not a variable", s.kind(), s.toChars());
return ErrorExp.get();
}
/*************************************************************
* Given var, get the
* right `this` pointer if var is in an outer class, but our
* existing `this` pointer is in an inner class.
* Params:
* loc = location to use for error messages
* sc = context
* ad = struct or class we need the correct `this` for
* e1 = existing `this`
* var = the specific member of ad we're accessing
* flag = if true, return `null` instead of throwing an error
* Returns:
* Expression representing the `this` for the var
*/
private Expression getRightThis(const ref Loc loc, Scope* sc, AggregateDeclaration ad, Expression e1, Dsymbol var, int flag = 0)
{
//printf("\ngetRightThis(e1 = %s, ad = %s, var = %s)\n", e1.toChars(), ad.toChars(), var.toChars());
L1:
Type t = e1.type.toBasetype();
//printf("e1.type = %s, var.type = %s\n", e1.type.toChars(), var.type.toChars());
if (e1.op == EXP.objcClassReference)
{
// We already have an Objective-C class reference, just use that as 'this'.
return e1;
}
else if (ad && ad.isClassDeclaration && ad.isClassDeclaration.classKind == ClassKind.objc &&
var.isFuncDeclaration && var.isFuncDeclaration.isStatic &&
var.isFuncDeclaration.objc.selector)
{
return new ObjcClassReferenceExp(e1.loc, ad.isClassDeclaration());
}
/* Access of a member which is a template parameter in dual-scope scenario
* class A { inc(alias m)() { ++m; } } // `m` needs `this` of `B`
* class B {int m; inc() { new A().inc!m(); } }
*/
if (e1.op == EXP.this_)
{
FuncDeclaration f = hasThis(sc);
if (f && f.hasDualContext())
{
if (f.followInstantiationContext(ad))
{
e1 = new VarExp(loc, f.vthis);
e1 = new PtrExp(loc, e1);
e1 = new IndexExp(loc, e1, IntegerExp.literal!1);
e1 = getThisSkipNestedFuncs(loc, sc, f.toParent2(), ad, e1, t, var);
if (e1.op == EXP.error)
return e1;
goto L1;
}
}
}
/* If e1 is not the 'this' pointer for ad
*/
if (ad &&
!(t.isTypePointer() && t.nextOf().isTypeStruct() && t.nextOf().isTypeStruct().sym == ad) &&
!(t.isTypeStruct() && t.isTypeStruct().sym == ad))
{
ClassDeclaration cd = ad.isClassDeclaration();
ClassDeclaration tcd = t.isClassHandle();
/* e1 is the right this if ad is a base class of e1
*/
if (!cd || !tcd || !(tcd == cd || cd.isBaseOf(tcd, null)))
{
/* Only classes can be inner classes with an 'outer'
* member pointing to the enclosing class instance
*/
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(loc, e1, vthis);
e1.type = vthis.type;
e1.type = e1.type.addMod(t.mod);
// Do not call ensureStaticLinkTo()
//e1 = e1.semantic(sc);
// Skip up over nested functions, and get the enclosing
// class type.
e1 = getThisSkipNestedFuncs(loc, sc, tcd.toParentP(ad), ad, e1, t, var);
if (e1.op == EXP.error)
return e1;
goto L1;
}
/* Can't find a path from e1 to ad
*/
if (flag)
return null;
e1.error("`this` for `%s` needs to be type `%s` not type `%s`", var.toChars(), ad.toChars(), t.toChars());
return ErrorExp.get();
}
}
return e1;
}
/***************************************
* Pull out any properties.
*/
private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = null)
{
//printf("resolvePropertiesX, e1 = %s %s, e2 = %s\n", EXPtoString(e1.op).ptr, e1.toChars(), e2 ? e2.toChars() : null);
Loc loc = e1.loc;
OverloadSet os;
Dsymbol s;
Objects* tiargs;
Type tthis;
if (auto de = e1.isDotExp())
{
if (auto oe = de.e2.isOverExp())
{
tiargs = null;
tthis = de.e1.type;
os = oe.vars;
goto Los;
}
}
else if (e1.isOverExp())
{
tiargs = null;
tthis = null;
os = e1.isOverExp().vars;
Los:
assert(os);
FuncDeclaration fd = null;
if (e2)
{
e2 = e2.expressionSemantic(sc);
if (e2.op == EXP.error)
return ErrorExp.get();
e2 = resolveProperties(sc, e2);
Expressions a;
a.push(e2);
for (size_t i = 0; i < os.a.dim; i++)
{
if (FuncDeclaration f = resolveFuncCall(loc, sc, os.a[i], tiargs, tthis, &a, FuncResolveFlag.quiet))
{
if (f.errors)
return ErrorExp.get();
fd = f;
assert(fd.type.ty == Tfunction);
}
}
if (fd)
{
Expression e = new CallExp(loc, e1, e2);
return e.expressionSemantic(sc);
}
}
{
for (size_t i = 0; i < os.a.dim; i++)
{
if (FuncDeclaration f = resolveFuncCall(loc, sc, os.a[i], tiargs, tthis, null, FuncResolveFlag.quiet))
{
if (f.errors)
return ErrorExp.get();
fd = f;
assert(fd.type.ty == Tfunction);
auto tf = fd.type.isTypeFunction();
if (!tf.isref && e2)
{
error(loc, "%s is not an lvalue", e1.toChars());
return ErrorExp.get();
}
}
}
if (fd)
{
Expression e = new CallExp(loc, e1);
if (e2)
e = new AssignExp(loc, e, e2);
return e.expressionSemantic(sc);
}
}
if (e2)
goto Leprop;
}
else if (auto dti = e1.isDotTemplateInstanceExp())
{
if (!dti.findTempDecl(sc))
goto Leprop;
if (!dti.ti.semanticTiargs(sc))
goto Leprop;
tiargs = dti.ti.tiargs;
tthis = dti.e1.type;
if ((os = dti.ti.tempdecl.isOverloadSet()) !is null)
goto Los;
if ((s = dti.ti.tempdecl) !is null)
goto Lfd;
}
else if (auto dte = e1.isDotTemplateExp())
{
s = dte.td;
tiargs = null;
tthis = dte.e1.type;
goto Lfd;
}
else if (auto se = e1.isScopeExp())
{
s = se.sds;
TemplateInstance ti = s.isTemplateInstance();
if (ti && !ti.semanticRun && ti.tempdecl)
{
//assert(ti.needsTypeInference(sc));
if (!ti.semanticTiargs(sc))
goto Leprop;
tiargs = ti.tiargs;
tthis = null;
if ((os = ti.tempdecl.isOverloadSet()) !is null)
goto Los;
if ((s = ti.tempdecl) !is null)
goto Lfd;
}
}
else if (auto te = e1.isTemplateExp())
{
s = te.td;
tiargs = null;
tthis = null;
goto Lfd;
}
else if (e1.isDotVarExp() && e1.type && (e1.type.toBasetype().isTypeFunction() || e1.isDotVarExp().var.isOverDeclaration()))
{
DotVarExp dve = e1.isDotVarExp();
s = dve.var;
tiargs = null;
tthis = dve.e1.type;
goto Lfd;
}
else if (sc && sc.flags & SCOPE.Cfile && e1.isVarExp() && !e2)
{
// ImportC: do not implicitly call function if no ( ) are present
}
else if (e1.isVarExp() && e1.type && (e1.type.toBasetype().isTypeFunction() || e1.isVarExp().var.isOverDeclaration()))
{
s = e1.isVarExp().var;
tiargs = null;
tthis = null;
Lfd:
assert(s);
if (e2)
{
e2 = e2.expressionSemantic(sc);
if (e2.op == EXP.error)
return ErrorExp.get();
e2 = resolveProperties(sc, e2);
Expressions a;
a.push(e2);
FuncDeclaration fd = resolveFuncCall(loc, sc, s, tiargs, tthis, &a, FuncResolveFlag.quiet);
if (fd && fd.type)
{
if (fd.errors)
return ErrorExp.get();
if (!checkSymbolAccess(sc, fd))
{
// @@@DEPRECATED_2.105@@@
// When turning into error, uncomment the return statement
TypeFunction tf = fd.type.isTypeFunction();
deprecation(loc, "function `%s` of type `%s` is not accessible from module `%s`",
fd.toPrettyChars(), tf.toChars, sc._module.toChars);
//return ErrorExp.get();
}
assert(fd.type.ty == Tfunction);
Expression e = new CallExp(loc, e1, e2);
return e.expressionSemantic(sc);
}
}
{
FuncDeclaration fd = resolveFuncCall(loc, sc, s, tiargs, tthis, null, FuncResolveFlag.quiet);
if (fd && fd.type)
{
if (fd.errors)
return ErrorExp.get();
TypeFunction tf = fd.type.isTypeFunction();
if (!e2 || tf.isref)
{
if (!checkSymbolAccess(sc, fd))
{
// @@@DEPRECATED_2.105@@@
// When turning into error, uncomment the return statement
deprecation(loc, "function `%s` of type `%s` is not accessible from module `%s`",
fd.toPrettyChars(), tf.toChars, sc._module.toChars);
//return ErrorExp.get();
}
Expression e = new CallExp(loc, e1);
if (e2)
e = new AssignExp(loc, e, e2);
return e.expressionSemantic(sc);
}
}
}
if (FuncDeclaration fd = s.isFuncDeclaration())
{
// Keep better diagnostic message for invalid property usage of functions
assert(fd.type.ty == Tfunction);
Expression e = new CallExp(loc, e1, e2);
return e.expressionSemantic(sc);
}
if (e2)
goto Leprop;
}
if (auto ve = e1.isVarExp())
{
if (auto v = ve.var.isVarDeclaration())
{
if (ve.checkPurity(sc, v))
return ErrorExp.get();
}
}
if (e2)
return null;
if (e1.type && !e1.isTypeExp()) // function type is not a property
{
/* Look for e1 being a lazy parameter; rewrite as delegate call
* only if the symbol wasn't already treated as a delegate
*/
auto ve = e1.isVarExp();
if (ve && ve.var.storage_class & STC.lazy_ && !ve.delegateWasExtracted)
{
Expression e = new CallExp(loc, e1);
return e.expressionSemantic(sc);
}
else if (e1.isDotVarExp())
{
// Check for reading overlapped pointer field in @safe code.
if (checkUnsafeAccess(sc, e1, true, true))
return ErrorExp.get();
}
else if (auto ce = e1.isCallExp())
{
// Check for reading overlapped pointer field in @safe code.
if (checkUnsafeAccess(sc, ce.e1, true, true))
return ErrorExp.get();
}
}
if (!e1.type)
{
error(loc, "cannot resolve type for %s", e1.toChars());
e1 = ErrorExp.get();
}
return e1;
Leprop:
error(loc, "not a property %s", e1.toChars());
return ErrorExp.get();
}
extern (C++) Expression resolveProperties(Scope* sc, Expression e)
{
//printf("resolveProperties(%s)\n", e.toChars());
e = resolvePropertiesX(sc, e);
if (e.checkRightThis(sc))
return ErrorExp.get();
return e;
}
/****************************************
* The common type is determined by applying ?: to each pair.
* Output:
* exps[] properties resolved, implicitly cast to common type, rewritten in place
* Returns:
* The common type, or `null` if an error has occured
*/
private Type arrayExpressionToCommonType(Scope* sc, ref Expressions exps)
{
/* Still have a problem with:
* ubyte[][] = [ cast(ubyte[])"hello", [1]];
* which works if the array literal is initialized top down with the ubyte[][]
* type, but fails with this function doing bottom up typing.
*/
//printf("arrayExpressionToCommonType()\n");
scope IntegerExp integerexp = IntegerExp.literal!0;
scope CondExp condexp = new CondExp(Loc.initial, integerexp, null, null);
Type t0 = null;
Expression e0 = null;
bool foundType;
for (size_t i = 0; i < exps.dim; i++)
{
Expression e = exps[i];
if (!e)
continue;
e = resolveProperties(sc, e);
if (!e.type)
{
e.error("`%s` has no value", e.toChars());
t0 = Type.terror;
continue;
}
if (e.op == EXP.type)
{
foundType = true; // do not break immediately, there might be more errors
e.checkValue(); // report an error "type T has no value"
t0 = Type.terror;
continue;
}
if (e.type.ty == Tvoid)
{
// void expressions do not concur to the determination of the common
// type.
continue;
}
if (checkNonAssignmentArrayOp(e))
{
t0 = Type.terror;
continue;
}
e = doCopyOrMove(sc, e);
if (!foundType && t0 && !t0.equals(e.type))
{
/* This applies ?: to merge the types. It's backwards;
* ?: should call this function to merge types.
*/
condexp.type = null;
condexp.e1 = e0;
condexp.e2 = e;
condexp.loc = e.loc;
Expression ex = condexp.expressionSemantic(sc);
if (ex.op == EXP.error)
e = ex;
else
{
// Convert to common type
exps[i] = condexp.e1.castTo(sc, condexp.type);
e = condexp.e2.castTo(sc, condexp.type);
}
}
e0 = e;
t0 = e.type;
if (e.op != EXP.error)
exps[i] = e;
}
// [] is typed as void[]
if (!t0)
return Type.tvoid;
// It's an error, don't do the cast
if (t0.ty == Terror)
return null;
for (size_t i = 0; i < exps.dim; i++)
{
Expression e = exps[i];
if (!e)
continue;
e = e.implicitCastTo(sc, t0);
if (e.op == EXP.error)
{
/* https://issues.dlang.org/show_bug.cgi?id=13024
* a workaround for the bug in typeMerge -
* it should paint e1 and e2 by deduced common type,
* but doesn't in this particular case.
*/
return null;
}
exps[i] = e;
}
return t0;
}
private Expression opAssignToOp(const ref Loc loc, EXP op, Expression e1, Expression e2)
{
Expression e;
switch (op)
{
case EXP.addAssign:
e = new AddExp(loc, e1, e2);
break;
case EXP.minAssign:
e = new MinExp(loc, e1, e2);
break;
case EXP.mulAssign:
e = new MulExp(loc, e1, e2);
break;
case EXP.divAssign:
e = new DivExp(loc, e1, e2);
break;
case EXP.modAssign:
e = new ModExp(loc, e1, e2);
break;
case EXP.andAssign:
e = new AndExp(loc, e1, e2);
break;
case EXP.orAssign:
e = new OrExp(loc, e1, e2);
break;
case EXP.xorAssign:
e = new XorExp(loc, e1, e2);
break;
case EXP.leftShiftAssign:
e = new ShlExp(loc, e1, e2);
break;
case EXP.rightShiftAssign:
e = new ShrExp(loc, e1, e2);
break;
case EXP.unsignedRightShiftAssign:
e = new UshrExp(loc, e1, e2);
break;
default:
assert(0);
}
return e;
}
/*********************
* Rewrite:
* array.length op= e2
* as:
* array.length = array.length op e2
* or:
* auto tmp = &array;
* (*tmp).length = (*tmp).length op e2
*/
private Expression rewriteOpAssign(BinExp exp)
{
ArrayLengthExp ale = exp.e1.isArrayLengthExp();
if (ale.e1.isVarExp())
{
Expression e = opAssignToOp(exp.loc, exp.op, ale, exp.e2);
e = new AssignExp(exp.loc, ale.syntaxCopy(), e);
return e;
}
else
{
/* auto tmp = &array;
* (*tmp).length = (*tmp).length op e2
*/
auto tmp = copyToTemp(0, "__arraylength", new AddrExp(ale.loc, ale.e1));
Expression e1 = new ArrayLengthExp(ale.loc, new PtrExp(ale.loc, new VarExp(ale.loc, tmp)));
Expression elvalue = e1.syntaxCopy();
Expression e = opAssignToOp(exp.loc, exp.op, e1, exp.e2);
e = new AssignExp(exp.loc, elvalue, e);
e = new CommaExp(exp.loc, new DeclarationExp(ale.loc, tmp), e);
return e;
}
}
/****************************************
* Preprocess arguments to function.
* Input:
* reportErrors whether or not to report errors here. Some callers are not
* checking actual function params, so they'll do their own error reporting
* Output:
* exps[] tuples expanded, properties resolved, rewritten in place
* Returns:
* true a semantic error occurred
*/
private bool preFunctionParameters(Scope* sc, Expressions* exps, const bool reportErrors = true)
{
bool err = false;
if (exps)
{
expandTuples(exps);
for (size_t i = 0; i < exps.dim; i++)
{
Expression arg = (*exps)[i];
arg = resolveProperties(sc, arg);
arg = arg.arrayFuncConv(sc);
if (arg.op == EXP.type)
{
// for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
arg = resolveAliasThis(sc, arg);
if (arg.op == EXP.type)
{
if (reportErrors)
{
arg.error("cannot pass type `%s` as a function argument", arg.toChars());
arg = ErrorExp.get();
}
err = true;
}
}
else if (arg.type.toBasetype().ty == Tfunction)
{
if (reportErrors)
{
arg.error("cannot pass function `%s` as a function argument", arg.toChars());
arg = ErrorExp.get();
}
err = true;
}
else if (checkNonAssignmentArrayOp(arg))
{
arg = ErrorExp.get();
err = true;
}
(*exps)[i] = arg;
}
}
return err;
}
/********************************************
* Issue an error if default construction is disabled for type t.
* Default construction is required for arrays and 'out' parameters.
* Returns:
* true an error was issued
*/
private bool checkDefCtor(Loc loc, Type t)
{
if (auto ts = t.baseElemOf().isTypeStruct())
{
StructDeclaration sd = ts.sym;
if (sd.noDefaultCtor)
{
sd.error(loc, "default construction is disabled");
return true;
}
}
return false;
}
/****************************************
* Now that we know the exact type of the function we're calling,
* the arguments[] need to be adjusted:
* 1. implicitly convert argument to the corresponding parameter type
* 2. add default arguments for any missing arguments
* 3. do default promotions on arguments corresponding to ...
* 4. add hidden _arguments[] argument
* 5. call copy constructor for struct value arguments
* Params:
* loc = location of function call
* sc = context
* tf = type of the function
* ethis = `this` argument, `null` if none or not known
* tthis = type of `this` argument, `null` if no `this` argument
* arguments = array of actual arguments to function call
* fd = the function being called, `null` if called indirectly
* prettype = set to return type of function
* peprefix = set to expression to execute before `arguments[]` are evaluated, `null` if none
* Returns:
* true errors happened
*/
private bool functionParameters(const ref Loc loc, Scope* sc,
TypeFunction tf, Expression ethis, Type tthis, Expressions* arguments, FuncDeclaration fd,
Type* prettype, Expression* peprefix)
{
//printf("functionParameters() %s\n", fd ? fd.toChars() : "");
assert(arguments);
assert(fd || tf.next);
size_t nargs = arguments ? arguments.dim : 0;
const size_t nparams = tf.parameterList.length;
const olderrors = global.errors;
bool err = false;
*prettype = Type.terror;
Expression eprefix = null;
*peprefix = null;
if (nargs > nparams && tf.parameterList.varargs == VarArg.none)
{
error(loc, "expected %llu arguments, not %llu for non-variadic function type `%s`", cast(ulong)nparams, cast(ulong)nargs, tf.toChars());
return true;
}
// If inferring return type, and semantic3() needs to be run if not already run
if (!tf.next && fd.inferRetType)
{
fd.functionSemantic();
}
else if (fd && fd.parent)
{
TemplateInstance ti = fd.parent.isTemplateInstance();
if (ti && ti.tempdecl)
{
fd.functionSemantic3();
}
}
/* If calling a pragma(inline, true) function,
* set flag to later scan for inlines.
*/
if (fd && fd.inlining == PINLINE.always)
{
if (sc._module)
sc._module.hasAlwaysInlines = true;
if (sc.func)
sc.func.hasAlwaysInlines = true;
}
const isCtorCall = fd && fd.needThis() && fd.isCtorDeclaration();
const size_t n = (nargs > nparams) ? nargs : nparams; // n = max(nargs, nparams)
/* If the function return type has wildcards in it, we'll need to figure out the actual type
* based on the actual argument types.
* Start with the `this` argument, later on merge into wildmatch the mod bits of the rest
* of the arguments.
*/
MOD wildmatch = (tthis && !isCtorCall) ? tthis.Type.deduceWild(tf, false) : 0;
bool done = false;
foreach (const i; 0 .. n)
{
Expression arg = (i < nargs) ? (*arguments)[i] : null;
if (i < nparams)
{
bool errorArgs()
{
error(loc, "expected %llu function arguments, not %llu", cast(ulong)nparams, cast(ulong)nargs);
return true;
}
Parameter p = tf.parameterList[i];
if (!arg)
{
if (!p.defaultArg)
{
if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nparams)
goto L2;
return errorArgs();
}
arg = p.defaultArg;
arg = inlineCopy(arg, sc);
// __FILE__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__
arg = arg.resolveLoc(loc, sc);
arguments.push(arg);
nargs++;
}
else
{
if (isDefaultInitOp(arg.op))
{
arg = arg.resolveLoc(loc, sc);
(*arguments)[i] = arg;
}
}
if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nparams) // https://dlang.org/spec/function.html#variadic
{
//printf("\t\tvarargs == 2, p.type = '%s'\n", p.type.toChars());
{
MATCH m;
if ((m = arg.implicitConvTo(p.type)) > MATCH.nomatch)
{
if (p.type.nextOf() && arg.implicitConvTo(p.type.nextOf()) >= m)
goto L2;
else if (nargs != nparams)
return errorArgs();
goto L1;
}
}
L2:
Type tb = p.type.toBasetype();
switch (tb.ty)
{
case Tsarray:
case Tarray:
{
/* Create a static array variable v of type arg.type:
* T[dim] __arrayArg = [ arguments[i], ..., arguments[nargs-1] ];
*
* The array literal in the initializer of the hidden variable
* is now optimized.
* https://issues.dlang.org/show_bug.cgi?id=2356
*/
Type tbn = (cast(TypeArray)tb).next; // array element type
Type tret = p.isLazyArray();
auto elements = new Expressions(nargs - i);
foreach (u; 0 .. elements.dim)
{
Expression a = (*arguments)[i + u];
if (tret && a.implicitConvTo(tret))
{
// p is a lazy array of delegates, tret is return type of the delegates
a = a.implicitCastTo(sc, tret)
.optimize(WANTvalue)
.toDelegate(tret, sc);
}
else
a = a.implicitCastTo(sc, tbn);
a = a.addDtorHook(sc);
(*elements)[u] = a;
}
// https://issues.dlang.org/show_bug.cgi?id=14395
// Convert to a static array literal, or its slice.
arg = new ArrayLiteralExp(loc, tbn.sarrayOf(nargs - i), elements);
if (tb.ty == Tarray)
{
arg = new SliceExp(loc, arg, null, null);
arg.type = p.type;
}
break;
}
case Tclass:
{
/* Set arg to be:
* new Tclass(arg0, arg1, ..., argn)
*/
auto args = new Expressions(nargs - i);
foreach (u; i .. nargs)
(*args)[u - i] = (*arguments)[u];
arg = new NewExp(loc, null, p.type, args);
break;
}
default:
if (!arg)
{
error(loc, "not enough arguments");
return true;
}
break;
}
arg = arg.expressionSemantic(sc);
//printf("\targ = '%s'\n", arg.toChars());
arguments.setDim(i + 1);
(*arguments)[i] = arg;
nargs = i + 1;
done = true;
}
L1:
if (!(p.isLazy() && p.type.ty == Tvoid))
{
if (ubyte wm = arg.type.deduceWild(p.type, p.isReference()))
{
wildmatch = wildmatch ? MODmerge(wildmatch, wm) : wm;
//printf("[%d] p = %s, a = %s, wm = %d, wildmatch = %d\n", i, p.type.toChars(), arg.type.toChars(), wm, wildmatch);
}
}
}
if (done)
break;
}
if ((wildmatch == MODFlags.mutable || wildmatch == MODFlags.immutable_) &&
tf.next && tf.next.hasWild() &&
(tf.isref || !tf.next.implicitConvTo(tf.next.immutableOf())))
{
bool errorInout(MOD wildmatch)
{
const(char)* s = wildmatch == MODFlags.mutable ? "mutable" : MODtoChars(wildmatch);
error(loc, "modify `inout` to `%s` is not allowed inside `inout` function", s);
return true;
}
if (fd)
{
/* If the called function may return the reference to
* outer inout data, it should be rejected.
*
* void foo(ref inout(int) x) {
* ref inout(int) bar(inout(int)) { return x; }
* struct S {
* ref inout(int) bar() inout { return x; }
* ref inout(int) baz(alias a)() inout { return x; }
* }
* bar(int.init) = 1; // bad!
* S().bar() = 1; // bad!
* }
* void test() {
* int a;
* auto s = foo(a);
* s.baz!a() = 1; // bad!
* }
*
*/
bool checkEnclosingWild(Dsymbol s)
{
bool checkWild(Dsymbol s)
{
if (!s)
return false;
if (auto ad = s.isAggregateDeclaration())
{
if (ad.isNested())
return checkEnclosingWild(s);
}
else if (auto ff = s.isFuncDeclaration())
{
if (ff.type.isTypeFunction().iswild)
return errorInout(wildmatch);
if (ff.isNested() || ff.isThis())
return checkEnclosingWild(s);
}
return false;
}
Dsymbol ctx0 = s.toParent2();
Dsymbol ctx1 = s.toParentLocal();
if (checkWild(ctx0))
return true;
if (ctx0 != ctx1)
return checkWild(ctx1);
return false;
}
if ((fd.isThis() || fd.isNested()) && checkEnclosingWild(fd))
return true;
}
else if (tf.isWild())
return errorInout(wildmatch);
}
Expression firstArg = ((tf.next && tf.next.ty == Tvoid || isCtorCall) &&
tthis &&
tthis.isMutable() && tthis.toBasetype().ty == Tstruct &&
tthis.hasPointers())
? ethis : null;
assert(nargs >= nparams);
foreach (const i, arg; (*arguments)[0 .. nargs])
{
assert(arg);
if (i < nparams)
{
Parameter p = tf.parameterList[i];
Type targ = arg.type; // keep original type for isCopyable() because alias this
// resolution may hide an uncopyable type
if (!(p.isLazy() && p.type.ty == Tvoid))
{
Type tprm = p.type.hasWild()
? p.type.substWildTo(wildmatch)
: p.type;
const hasCopyCtor = arg.type.isTypeStruct() && arg.type.isTypeStruct().sym.hasCopyCtor;
const typesMatch = arg.type.mutableOf().unSharedOf().equals(tprm.mutableOf().unSharedOf());
if (!((hasCopyCtor && typesMatch) || tprm.equals(arg.type)))
{
//printf("arg.type = %s, p.type = %s\n", arg.type.toChars(), p.type.toChars());
arg = arg.implicitCastTo(sc, tprm);
arg = arg.optimize(WANTvalue, p.isReference());
}
}
// Support passing rvalue to `in` parameters
if ((p.storageClass & (STC.in_ | STC.ref_)) == (STC.in_ | STC.ref_))
{
if (!arg.isLvalue())
{
auto v = copyToTemp(STC.exptemp, "__rvalue", arg);
Expression ev = new DeclarationExp(arg.loc, v);
ev = new CommaExp(arg.loc, ev, new VarExp(arg.loc, v));
arg = ev.expressionSemantic(sc);
}
arg = arg.toLvalue(sc, arg);
// Look for mutable misaligned pointer, etc., in @safe mode
err |= checkUnsafeAccess(sc, arg, false, true);
}
else if (p.storageClass & STC.ref_)
{
if (global.params.rvalueRefParam == FeatureState.enabled &&
!arg.isLvalue() &&
targ.isCopyable())
{ /* allow rvalues to be passed to ref parameters by copying
* them to a temp, then pass the temp as the argument
*/
auto v = copyToTemp(0, "__rvalue", arg);
Expression ev = new DeclarationExp(arg.loc, v);
ev = new CommaExp(arg.loc, ev, new VarExp(arg.loc, v));
arg = ev.expressionSemantic(sc);
}
arg = arg.toLvalue(sc, arg);
// Look for mutable misaligned pointer, etc., in @safe mode
err |= checkUnsafeAccess(sc, arg, false, true);
}
else if (p.storageClass & STC.out_)
{
Type t = arg.type;
if (!t.isMutable() || !t.isAssignable()) // check blit assignable
{
arg.error("cannot modify struct `%s` with immutable members", arg.toChars());
err = true;
}
else
{
// Look for misaligned pointer, etc., in @safe mode
err |= checkUnsafeAccess(sc, arg, false, true);
err |= checkDefCtor(arg.loc, t); // t must be default constructible
}
arg = arg.toLvalue(sc, arg);
}
else if (p.isLazy())
{
// Convert lazy argument to a delegate
auto t = (p.type.ty == Tvoid) ? p.type : arg.type;
arg = toDelegate(arg, t, sc);
}
//printf("arg: %s\n", arg.toChars());
//printf("type: %s\n", arg.type.toChars());
//printf("param: %s\n", p.toChars());
const pStc = tf.parameterStorageClass(tthis, p);
if (firstArg && (pStc & STC.return_))
{
/* Argument value can be assigned to firstArg.
* Check arg to see if it matters.
*/
err |= checkParamArgumentReturn(sc, firstArg, arg, p, false);
}
// Allow 'lazy' to imply 'scope' - lazy parameters can be passed along
// as lazy parameters to the next function, but that isn't escaping.
else if (!(pStc & STC.lazy_))
{
/* Argument value can escape from the called function.
* Check arg to see if it matters.
*/
VarDeclaration vPar = fd ? (fd.parameters ? (*fd.parameters)[i] : null) : null;
err |= checkParamArgumentEscape(sc, fd, p, vPar, cast(STC) pStc, arg, false, false);
}
// Turning heap allocations into stack allocations is dangerous without dip1000, since `scope` inference
// may be unreliable when scope violations only manifest as deprecation warnings.
// However, existing `@nogc` code may rely on it, so still do it when the parameter is explicitly marked `scope`
const explicitScope = p.isLazy() ||
((p.storageClass & STC.scope_) && !(p.storageClass & STC.scopeinferred));
if ((pStc & (STC.scope_ | STC.lazy_)) &&
((global.params.useDIP1000 == FeatureState.enabled) || explicitScope) &&
!(pStc & STC.return_))
{
/* Argument value cannot escape from the called function.
*/
Expression a = arg;
if (auto ce = a.isCastExp())
a = ce.e1;
ArrayLiteralExp ale;
if (p.type.toBasetype().ty == Tarray &&
(ale = a.isArrayLiteralExp()) !is null && ale.elements && ale.elements.length > 0)
{
// allocate the array literal as temporary static array on the stack
ale.type = ale.type.nextOf().sarrayOf(ale.elements.length);
auto tmp = copyToTemp(0, "__arrayliteral_on_stack", ale);
auto declareTmp = new DeclarationExp(ale.loc, tmp);
auto castToSlice = new CastExp(ale.loc, new VarExp(ale.loc, tmp),
p.type.substWildTo(MODFlags.mutable));
arg = CommaExp.combine(declareTmp, castToSlice);
arg = arg.expressionSemantic(sc);
}
else if (auto fe = a.isFuncExp())
{
/* Function literals can only appear once, so if this
* appearance was scoped, there cannot be any others.
*/
fe.fd.tookAddressOf = 0;
}
else if (auto de = a.isDelegateExp())
{
/* For passing a delegate to a scoped parameter,
* this doesn't count as taking the address of it.
* We only worry about 'escaping' references to the function.
*/
if (auto ve = de.e1.isVarExp())
{
if (auto f = ve.var.isFuncDeclaration())
{
if (f.tookAddressOf)
--f.tookAddressOf;
//printf("--tookAddressOf = %d\n", f.tookAddressOf);
}
}
}
}
if (!p.isReference())
err |= arg.checkSharedAccess(sc);
arg = arg.optimize(WANTvalue, p.isReference());
/* Determine if this parameter is the "first reference" parameter through which
* later "return" arguments can be stored.
*/
if (i == 0 && !tthis && p.isReference() && p.type &&
(tf.next && tf.next.ty == Tvoid || isCtorCall))
{
Type tb = p.type.baseElemOf();
if (tb.isMutable() && tb.hasPointers())
{
firstArg = arg;
}
}
}
else
{
// These will be the trailing ... arguments
// If not D linkage, do promotions
if (tf.linkage != LINK.d)
{
// Promote bytes, words, etc., to ints
arg = integralPromotions(arg, sc);
// Promote floats to doubles
switch (arg.type.ty)
{
case Tfloat32:
arg = arg.castTo(sc, Type.tfloat64);
break;
case Timaginary32:
arg = arg.castTo(sc, Type.timaginary64);
break;
default:
break;
}
if (tf.parameterList.varargs == VarArg.variadic)
{
const(char)* p = tf.linkage == LINK.c ? "extern(C)" : "extern(C++)";
if (arg.type.ty == Tarray)
{
arg.error("cannot pass dynamic arrays to `%s` vararg functions", p);
err = true;
}
if (arg.type.ty == Tsarray)
{
arg.error("cannot pass static arrays to `%s` vararg functions", p);
err = true;
}
}
}
// Do not allow types that need destructors or copy constructors.
if (arg.type.needsDestruction())
{
arg.error("cannot pass types that need destruction as variadic arguments");
err = true;
}
if (arg.type.needsCopyOrPostblit())
{
arg.error("cannot pass types with postblits or copy constructors as variadic arguments");
err = true;
}
// Convert static arrays to dynamic arrays
// BUG: I don't think this is right for D2
Type tb = arg.type.toBasetype();
if (auto ts = tb.isTypeSArray())
{
Type ta = ts.next.arrayOf();
if (ts.size(arg.loc) == 0)
arg = new NullExp(arg.loc, ta);
else
arg = arg.castTo(sc, ta);
}
if (tb.ty == Tstruct)
{
//arg = callCpCtor(sc, arg);
}
// Give error for overloaded function addresses
if (auto se = arg.isSymOffExp())
{
if (se.hasOverloads && !se.var.isFuncDeclaration().isUnique())
{
arg.error("function `%s` is overloaded", arg.toChars());
err = true;
}
}
err |= arg.checkValue();
err |= arg.checkSharedAccess(sc);
arg = arg.optimize(WANTvalue);
}
(*arguments)[i] = arg;
}
/* If calling C scanf(), printf(), or any variants, check the format string against the arguments
*/
const isVa_list = tf.parameterList.varargs == VarArg.none;
if (fd && fd.printf)
{
if (auto se = (*arguments)[nparams - 1 - isVa_list].isStringExp())
{
checkPrintfFormat(se.loc, se.peekString(), (*arguments)[nparams .. nargs], isVa_list);
}
}
else if (fd && fd.scanf)
{
if (auto se = (*arguments)[nparams - 1 - isVa_list].isStringExp())
{
checkScanfFormat(se.loc, se.peekString(), (*arguments)[nparams .. nargs], isVa_list);
}
}
else
{
// TODO: not checking the "v" functions yet (for those, check format string only, not args)
}
/* Remaining problems:
* 1. order of evaluation - some function push L-to-R, others R-to-L. Until we resolve what array assignment does (which is
* implemented by calling a function) we'll defer this for now.
* 2. value structs (or static arrays of them) that need to be copy constructed
* 3. value structs (or static arrays of them) that have destructors, and subsequent arguments that may throw before the
* function gets called.
* 4. value structs need to be destructed after the function call for platforms where the caller destroys the arguments.
* 2, 3 and 4 are handled by doing the argument construction in 'eprefix' so that if a later argument throws, they are cleaned
* up properly. Pushing arguments on the stack then cannot fail.
*/
{
/* TODO: tackle problem 1)
*/
const bool leftToRight = true; // TODO: Any cases that need rightToLeft?
if (!leftToRight)
assert(nargs == nparams); // no variadics for RTL order, as they would probably be evaluated LTR and so add complexity
/* Does Problem (4) apply?
*/
const bool callerDestroysArgs = !target.isCalleeDestroyingArgs(tf);
const ptrdiff_t start = (leftToRight ? 0 : cast(ptrdiff_t)nargs - 1);
const ptrdiff_t end = (leftToRight ? cast(ptrdiff_t)nargs : -1);
const ptrdiff_t step = (leftToRight ? 1 : -1);
/* Compute indices of last throwing argument and first arg needing destruction.
* Used to not set up destructors unless an arg needs destruction on a throw
* in a later argument.
*/
ptrdiff_t lastthrow = -1; // last argument that may throw
ptrdiff_t firstdtor = -1; // first argument that needs destruction
ptrdiff_t lastdtor = -1; // last argument that needs destruction
for (ptrdiff_t i = start; i != end; i += step)
{
Expression arg = (*arguments)[i];
if (canThrow(arg, sc.func, false))
lastthrow = i;
if (arg.type.needsDestruction())
{
Parameter p = (i >= nparams ? null : tf.parameterList[i]);
if (!(p && (p.isLazy() || p.isReference())))
{
if (firstdtor == -1)
firstdtor = i;
lastdtor = i;
}
}
}
/* Do we need 'eprefix' for problems 3 or 4?
*/
const bool needsPrefix = callerDestroysArgs
? firstdtor >= 0 // true if any argument needs destruction
: firstdtor >= 0 && lastthrow >= 0 &&
(lastthrow - firstdtor) * step > 0; // last throw after first destruction
const ptrdiff_t lastPrefix = callerDestroysArgs
? lastdtor // up to last argument requiring destruction
: lastthrow; // up to last potentially throwing argument
/* Problem 3: initialize 'eprefix' by declaring the gate
*/
VarDeclaration gate;
if (needsPrefix && !callerDestroysArgs)
{
// eprefix => bool __gate [= false]
Identifier idtmp = Identifier.generateId("__gate");
gate = new VarDeclaration(loc, Type.tbool, idtmp, null);
gate.storage_class |= STC.temp | STC.ctfe | STC.volatile_;
gate.dsymbolSemantic(sc);
auto ae = new DeclarationExp(loc, gate);
eprefix = ae.expressionSemantic(sc);
}
for (ptrdiff_t i = start; i != end; i += step)
{
Expression arg = (*arguments)[i];
//printf("arg[%d]: %s\n", cast(int)i, arg.toChars());
Parameter parameter = (i >= nparams ? null : tf.parameterList[i]);
const bool isRef = parameter && parameter.isReference();
const bool isLazy = parameter && parameter.isLazy();
/* Skip lazy parameters
*/
if (isLazy)
continue;
/* Do we have 'eprefix' and aren't past 'lastPrefix' yet?
* Then declare a temporary variable for this arg and append that declaration
* to 'eprefix', which will implicitly take care of potential problem 2) for
* this arg.
* 'eprefix' will therefore finally contain all args up to and including 'lastPrefix',
* excluding all lazy parameters.
*/
if (needsPrefix && (lastPrefix - i) * step >= 0)
{
const bool needsDtor = !isRef && arg.type.needsDestruction() &&
// Problem 3: last throwing arg doesn't require dtor patching
(callerDestroysArgs || i != lastPrefix);
/* Declare temporary 'auto __pfx = arg' (needsDtor) or 'auto __pfy = arg' (!needsDtor)
*/
auto tmp = copyToTemp(
(parameter ? parameter.storageClass : tf.parameterList.stc) & (STC.scope_),
needsDtor ? "__pfx" : "__pfy",
!isRef ? arg : arg.addressOf());
tmp.dsymbolSemantic(sc);
if (callerDestroysArgs)
{
/* Problem 4: Normal temporary, destructed after the call
*/
if (needsDtor)
tmp.isArgDtorVar = true; // mark it so that the backend passes it by ref to the function being called
}
else
{
/* Problem 3: Modify the destructor so it only runs if gate==false,
* i.e., only if there was a throw while constructing the args
*/
if (!needsDtor)
{
if (tmp.edtor)
{
assert(i == lastPrefix);
tmp.edtor = null;
}
}
else
{
// edtor => (__gate || edtor)
assert(tmp.edtor);
Expression e = tmp.edtor;
e = new LogicalExp(e.loc, EXP.orOr, new VarExp(e.loc, gate), e);
tmp.edtor = e.expressionSemantic(sc);
//printf("edtor: %s\n", tmp.edtor.toChars());
}
}
// eprefix => (eprefix, auto __pfx/y = arg)
auto ae = new DeclarationExp(loc, tmp);
eprefix = Expression.combine(eprefix, ae.expressionSemantic(sc));
// arg => __pfx/y
arg = new VarExp(loc, tmp);
arg = arg.expressionSemantic(sc);
if (isRef)
{
arg = new PtrExp(loc, arg);
arg = arg.expressionSemantic(sc);
}
/* Problem 3: Last throwing arg?
* Then finalize eprefix => (eprefix, gate = true), i.e., disable the
* dtors right after constructing the last throwing arg.
* From now on, the callee will take care of destructing the args because
* the args are implicitly moved into function parameters.
*/
if (!callerDestroysArgs && i == lastPrefix)
{
auto e = new AssignExp(gate.loc, new VarExp(gate.loc, gate), IntegerExp.createBool(true));
eprefix = Expression.combine(eprefix, e.expressionSemantic(sc));
}
}
else // not part of 'eprefix'
{
/* Handle problem 2) by calling the copy constructor for value structs
* (or static arrays of them) if appropriate.
*/
Type tv = arg.type.baseElemOf();
if (!isRef && tv.ty == Tstruct)
arg = doCopyOrMove(sc, arg, parameter ? parameter.type : null);
}
(*arguments)[i] = arg;
}
}
//if (eprefix) printf("eprefix: %s\n", eprefix.toChars());
/* Test compliance with DIP1021
*/
if (global.params.useDIP1021 &&
tf.trust != TRUST.system && tf.trust != TRUST.trusted)
err |= checkMutableArguments(sc, fd, tf, ethis, arguments, false);
// If D linkage and variadic, add _arguments[] as first argument
if (tf.isDstyleVariadic())
{
assert(arguments.dim >= nparams);
auto args = new Parameters(arguments.dim - nparams);
for (size_t i = 0; i < arguments.dim - nparams; i++)
{
auto arg = new Parameter(STC.in_, (*arguments)[nparams + i].type, null, null, null);
(*args)[i] = arg;
}
auto tup = new TypeTuple(args);
Expression e = (new TypeidExp(loc, tup)).expressionSemantic(sc);
arguments.insert(0, e);
}
/* Determine function return type: tret
*/
Type tret = tf.next;
if (isCtorCall)
{
//printf("[%s] fd = %s %s, %d %d %d\n", loc.toChars(), fd.toChars(), fd.type.toChars(),
// wildmatch, tf.isWild(), fd.isReturnIsolated());
if (!tthis)
{
assert(sc.intypeof || global.errors);
tthis = fd.isThis().type.addMod(fd.type.mod);
}
if (tf.isWild() && !fd.isReturnIsolated())
{
if (wildmatch)
tret = tret.substWildTo(wildmatch);
int offset;
if (!tret.implicitConvTo(tthis) && !(MODimplicitConv(tret.mod, tthis.mod) && tret.isBaseOf(tthis, &offset) && offset == 0))
{
const(char)* s1 = tret.isNaked() ? " mutable" : tret.modToChars();
const(char)* s2 = tthis.isNaked() ? " mutable" : tthis.modToChars();
.error(loc, "`inout` constructor `%s` creates%s object, not%s", fd.toPrettyChars(), s1, s2);
err = true;
}
}
tret = tthis;
}
else if (wildmatch && tret)
{
/* Adjust function return type based on wildmatch
*/
//printf("wildmatch = x%x, tret = %s\n", wildmatch, tret.toChars());
tret = tret.substWildTo(wildmatch);
}
*prettype = tret;
*peprefix = eprefix;
return (err || olderrors != global.errors);
}
/**
* Determines whether a symbol represents a module or package
* (Used as a helper for is(type == module) and is(type == package))
*
* Params:
* sym = the symbol to be checked
*
* Returns:
* the symbol which `sym` represents (or `null` if it doesn't represent a `Package`)
*/
Package resolveIsPackage(Dsymbol sym)
{
Package pkg;
if (Import imp = sym.isImport())
{
if (imp.pkg is null)
{
.error(sym.loc, "internal compiler error: unable to process forward-referenced import `%s`",
imp.toChars());
assert(0);
}
pkg = imp.pkg;
}
else if (auto mod = sym.isModule())
pkg = mod.isPackageFile ? mod.pkg : sym.isPackage();
else
pkg = sym.isPackage();
if (pkg)
pkg.resolvePKGunknown();
return pkg;
}
private Module loadStdMath()
{
__gshared Import impStdMath = null;
__gshared Identifier[1] stdID;
if (!impStdMath)
{
stdID[0] = Id.std;
auto s = new Import(Loc.initial, stdID[], Id.math, null, false);
// Module.load will call fatal() if there's no std.math available.
// Gag the error here, pushing the error handling to the caller.
uint errors = global.startGagging();
s.load(null);
if (s.mod)
{
s.mod.importAll(null);
s.mod.dsymbolSemantic(null);
}
global.endGagging(errors);
impStdMath = s;
}
return impStdMath.mod;
}
private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
alias visit = Visitor.visit;
Scope* sc;
Expression result;
this(Scope* sc)
{
this.sc = sc;
}
private void setError()
{
result = ErrorExp.get();
}
/**************************
* Semantically analyze Expression.
* Determine types, fold constants, etc.
*/
override void visit(Expression e)
{
static if (LOGSEMANTIC)
{
printf("Expression::semantic() %s\n", e.toChars());
}
if (e.type)
e.type = e.type.typeSemantic(e.loc, sc);
else
e.type = Type.tvoid;
result = e;
}
override void visit(IntegerExp e)
{
assert(e.type);
if (e.type.ty == Terror)
return setError();
assert(e.type.deco);
e.setInteger(e.getInteger());
result = e;
}
override void visit(RealExp e)
{
if (!e.type)
e.type = Type.tfloat64;
else
e.type = e.type.typeSemantic(e.loc, sc);
result = e;
}
override void visit(ComplexExp e)
{
if (!e.type)
e.type = Type.tcomplex80;
else
e.type = e.type.typeSemantic(e.loc, sc);
result = e;
}
override void visit(IdentifierExp exp)
{
static if (LOGSEMANTIC)
{
printf("IdentifierExp::semantic('%s')\n", exp.ident.toChars());
}
if (exp.type) // This is used as the dummy expression
{
result = exp;
return;
}
Dsymbol scopesym;
Dsymbol s = sc.search(exp.loc, exp.ident, &scopesym);
if (s)
{
if (s.errors)
return setError();
Expression e;
/* See if the symbol was a member of an enclosing 'with'
*/
WithScopeSymbol withsym = scopesym.isWithScopeSymbol();
if (withsym && withsym.withstate.wthis && symbolIsVisible(sc, s))
{
/* Disallow shadowing
*/
// First find the scope of the with
Scope* scwith = sc;
while (scwith.scopesym != scopesym)
{
scwith = scwith.enclosing;
assert(scwith);
}
// Look at enclosing scopes for symbols with the same name,
// in the same function
for (Scope* scx = scwith; scx && scx.func == scwith.func; scx = scx.enclosing)
{
Dsymbol s2;
if (scx.scopesym && scx.scopesym.symtab && (s2 = scx.scopesym.symtab.lookup(s.ident)) !is null && s != s2)
{
exp.error("with symbol `%s` is shadowing local symbol `%s`", s.toPrettyChars(), s2.toPrettyChars());
return setError();
}
}
s = s.toAlias();
// Same as wthis.ident
// TODO: DotIdExp.semantic will find 'ident' from 'wthis' again.
// The redudancy should be removed.
e = new VarExp(exp.loc, withsym.withstate.wthis);
e = new DotIdExp(exp.loc, e, exp.ident);
e = e.expressionSemantic(sc);
}
else
{
if (withsym)
{
if (withsym.withstate.exp.type.ty != Tvoid)
{
// 'with (exp)' is a type expression
// or 's' is not visible there (for error message)
e = new TypeExp(exp.loc, withsym.withstate.exp.type);
}
else
{
// 'with (exp)' is a Package/Module
e = withsym.withstate.exp;
}
e = new DotIdExp(exp.loc, e, exp.ident);
result = e.expressionSemantic(sc);
return;
}
/* If f is really a function template,
* then replace f with the function template declaration.
*/
FuncDeclaration f = s.isFuncDeclaration();
if (f)
{
TemplateDeclaration td = getFuncTemplateDecl(f);
if (td)
{
if (td.overroot) // if not start of overloaded list of TemplateDeclaration's
td = td.overroot; // then get the start
e = new TemplateExp(exp.loc, td, f);
e = e.expressionSemantic(sc);
result = e;
return;
}
}
if (global.params.fixAliasThis)
{
ExpressionDsymbol expDsym = scopesym.isExpressionDsymbol();
if (expDsym)
{
//printf("expDsym = %s\n", expDsym.exp.toChars());
result = expDsym.exp.expressionSemantic(sc);
return;
}
}
// Haven't done overload resolution yet, so pass 1
e = symbolToExp(s, exp.loc, sc, true);
}
result = e;
return;
}
if (!global.params.fixAliasThis && hasThis(sc))
{
for (AggregateDeclaration ad = sc.getStructClassScope(); ad;)
{
if (ad.aliasthis)
{
Expression e;
e = new ThisExp(exp.loc);
e = new DotIdExp(exp.loc, e, ad.aliasthis.ident);
e = new DotIdExp(exp.loc, e, exp.ident);
e = e.trySemantic(sc);
if (e)
{
result = e;
return;
}
}
auto cd = ad.isClassDeclaration();
if (cd && cd.baseClass && cd.baseClass != ClassDeclaration.object)
{
ad = cd.baseClass;
continue;
}
break;
}
}
if (exp.ident == Id.ctfe)
{
if (sc.flags & SCOPE.ctfe)
{
exp.error("variable `__ctfe` cannot be read at compile time");
return setError();
}
// Create the magic __ctfe bool variable
auto vd = new VarDeclaration(exp.loc, Type.tbool, Id.ctfe, null);
vd.storage_class |= STC.temp;
vd.semanticRun = PASS.semanticdone;
Expression e = new VarExp(exp.loc, vd);
e = e.expressionSemantic(sc);
result = e;
return;
}
// If we've reached this point and are inside a with() scope then we may
// try one last attempt by checking whether the 'wthis' object supports
// dynamic dispatching via opDispatch.
// This is done by rewriting this expression as wthis.ident.
// The innermost with() scope of the hierarchy to satisfy the condition
// above wins.
// https://issues.dlang.org/show_bug.cgi?id=6400
for (Scope* sc2 = sc; sc2; sc2 = sc2.enclosing)
{
if (!sc2.scopesym)
continue;
if (auto ss = sc2.scopesym.isWithScopeSymbol())
{
if (ss.withstate.wthis)
{
Expression e;
e = new VarExp(exp.loc, ss.withstate.wthis);
e = new DotIdExp(exp.loc, e, exp.ident);
e = e.trySemantic(sc);
if (e)
{
result = e;
return;
}
}
// Try Type.opDispatch (so the static version)
else if (ss.withstate.exp && ss.withstate.exp.op == EXP.type)
{
if (Type t = ss.withstate.exp.isTypeExp().type)
{
Expression e;
e = new TypeExp(exp.loc, t);
e = new DotIdExp(exp.loc, e, exp.ident);
e = e.trySemantic(sc);
if (e)
{
result = e;
return;
}
}
}
}
}
/* Look for what user might have meant
*/
if (const n = importHint(exp.ident.toString()))
exp.error("`%s` is not defined, perhaps `import %.*s;` is needed?", exp.ident.toChars(), cast(int)n.length, n.ptr);
else if (auto s2 = sc.search_correct(exp.ident))
exp.error("undefined identifier `%s`, did you mean %s `%s`?", exp.ident.toChars(), s2.kind(), s2.toChars());
else if (const p = Scope.search_correct_C(exp.ident))
exp.error("undefined identifier `%s`, did you mean `%s`?", exp.ident.toChars(), p);
else if (exp.ident == Id.dollar)
exp.error("undefined identifier `$`");
else
exp.error("undefined identifier `%s`", exp.ident.toChars());
result = ErrorExp.get();
}
override void visit(DsymbolExp e)
{
result = symbolToExp(e.s, e.loc, sc, e.hasOverloads);
}
override void visit(ThisExp e)
{
static if (LOGSEMANTIC)
{
printf("ThisExp::semantic()\n");
}
if (e.type)
{
result = e;
return;
}
FuncDeclaration fd = hasThis(sc); // fd is the uplevel function with the 'this' variable
AggregateDeclaration ad;
/* Special case for typeof(this) and typeof(super) since both
* should work even if they are not inside a non-static member function
*/
if (!fd && sc.intypeof == 1)
{
// Find enclosing struct or class
for (Dsymbol s = sc.getStructClassScope(); 1; s = s.parent)
{
if (!s)
{
e.error("`%s` is not in a class or struct scope", e.toChars());
goto Lerr;
}</