Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 90 additions & 1 deletion compiler/src/dmd/dcast.d
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import dmd.globals;
import dmd.hdrgen;
import dmd.location;
import dmd.impcnvtab;
import dmd.id;
import dmd.importc;
import dmd.init;
import dmd.intrange;
Expand Down Expand Up @@ -284,6 +285,26 @@ Expression implicitCastTo(Expression e, Scope* sc, Type t)
return result;
}

Expression visitCall(CallExp e)
{
if (e.e1.isDollarExp() && e.e1.type && e.e1.type.ty == Tvoid)
{
auto n = new CallExp(e.loc, new TypeExp(e.e1.loc, t), e.arguments);
return n.expressionSemantic(sc);
}
return visit(e);
}

Expression visitDotId(DotIdExp e)
{
if (e.e1.isDollarExp() && e.e1.type && e.e1.type.ty == Tvoid)
{
auto n = new DotIdExp(e.loc, new TypeExp(e.e1.loc, t), e.ident);
return n.expressionSemantic(sc);
}
return visit(e);
}

switch (e.op)
{
default : return visit (e);
Expand All @@ -292,6 +313,8 @@ Expression implicitCastTo(Expression e, Scope* sc, Type t)
case EXP.function_ : return visitFunc (e.isFuncExp());
case EXP.arrayLiteral: return visitArrayLiteral(e.isArrayLiteralExp());
case EXP.slice : return visitSlice (e.isSliceExp());
case EXP.call : return visitCall (e.isCallExp());
case EXP.dotIdentifier: return visitDotId (e.isDotIdExp());
}
}

Expand Down Expand Up @@ -325,7 +348,15 @@ MATCH implicitConvTo(Expression e, Type t)
error(e.loc, "`%s` is not an expression", e.toChars());
e.type = Type.terror;
}

if (e.type.ty == Tvoid)
{
if (e.isDollarExp() ||
(e.op == EXP.call && (cast(CallExp)e).e1.isDollarExp()) ||
(e.op == EXP.dotIdentifier && (cast(DotIdExp)e).e1.isDollarExp()))
{
return MATCH.convert;
}
}
Expression ex = e.optimize(WANTvalue);
if (ex.type.equals(t))
{
Expand Down Expand Up @@ -3280,12 +3311,70 @@ Expression inferType(Expression e, Type t, int flag = 0)
return ce;
}

Expression visitDollar(DollarExp de)
{
// Handle $ - infer $ from the context type
if (t)
{
if (t.ty == Tstruct || t.ty == Tenum || t.ty == Tclass)
return new TypeExp(de.loc, t);
Type tb = t.toBasetype();
if (tb.ty == Tstruct || tb.ty == Tenum || tb.ty == Tclass)
return new TypeExp(de.loc, t);
}
return de;
}

Expression visitDotId(DotIdExp die)
{
// Handle $.ident - infer $ from the context type
if (die.e1.isDollarExp() && t)
{
if (t.ty == Tstruct || t.ty == Tenum || t.ty == Tclass)
{
die.e1 = new TypeExp(die.e1.loc, t);
}
else
{
Type tb = t.toBasetype();
if (tb.ty == Tstruct || tb.ty == Tenum || tb.ty == Tclass)
die.e1 = new TypeExp(die.e1.loc, t);
}
}
return die;
}

Expression visitCall(CallExp ce)
{
// Handle $(args) - infer $ from the context type
if (ce.e1.isDollarExp() && t)
{
if (t.ty == Tstruct || t.ty == Tenum || t.ty == Tclass)
{
ce.e1 = new TypeExp(ce.e1.loc, t);
}
else
{
Type tb = t.toBasetype();
if (tb.ty == Tstruct || tb.ty == Tenum || tb.ty == Tclass)
ce.e1 = new TypeExp(ce.e1.loc, t);
}
}
return ce;
}

// Handle DollarExp - note: DollarExp has op == EXP.identifier
if (auto de = e.isDollarExp())
return visitDollar(de);

if (t) switch (e.op)
{
case EXP.arrayLiteral: return visitAle(e.isArrayLiteralExp());
case EXP.assocArrayLiteral: return visitAar(e.isAssocArrayLiteralExp());
case EXP.function_: return visitFun(e.isFuncExp());
case EXP.question: return visitTer(e.isCondExp());
case EXP.dotIdentifier: return visitDotId(e.isDotIdExp());
case EXP.call: return visitCall(e.isCallExp());
default:
}
return e;
Expand Down
24 changes: 22 additions & 2 deletions compiler/src/dmd/dsymbolsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -2328,8 +2328,28 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
{
if (inferred)
{
.error(dsym.loc, "%s `%s` - type `%s` is inferred from initializer `%s`, and variables cannot be of type `void`",
dsym.kind, dsym.toPrettyChars, dsym.type.toChars(), toChars(dsym._init));
bool hasDollarInit = false;
if (auto ei = dsym._init.isExpInitializer())
{
if (ei.exp.isDollarExp() ||
(ei.exp.op == EXP.call && (cast(CallExp)ei.exp).e1.isDollarExp()) ||
(ei.exp.op == EXP.dotIdentifier && (cast(DotIdExp)ei.exp).e1.isDollarExp()))
{
hasDollarInit = true;
}
}

if (hasDollarInit)
{
.error(dsym.loc, "%s `%s` - `$` requires a known type context for inference, but type is inferred from initializer `%s`",
dsym.kind, dsym.toPrettyChars, toChars(dsym._init));
.errorSupplemental(dsym.loc, "`auto` does not provide a type context for `$`, consider using an explicit type", dsym.ident.toChars());
}
else
{
.error(dsym.loc, "%s `%s` - type `%s` is inferred from initializer `%s`, and variables cannot be of type `void`",
dsym.kind, dsym.toPrettyChars, dsym.type.toChars(), toChars(dsym._init));
}
}
else
.error(dsym.loc, "%s `%s` - variables cannot be of type `void`", dsym.kind, dsym.toPrettyChars);
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dmd/expression.d
Original file line number Diff line number Diff line change
Expand Up @@ -770,9 +770,9 @@ extern (C++) class IdentifierExp : Expression
{
Identifier ident;

extern (D) this(Loc loc, Identifier ident) scope @safe
extern (D) this(Loc loc, Identifier ident, EXP op = EXP.identifier) scope @safe
{
super(loc, EXP.identifier);
super(loc, op);
this.ident = ident;
}

Expand All @@ -796,7 +796,7 @@ extern (C++) final class DollarExp : IdentifierExp
{
extern (D) this(Loc loc)
{
super(loc, Id.dollar);
super(loc, Id.dollar, EXP.dollar);
}

override void accept(Visitor v)
Expand Down
83 changes: 83 additions & 0 deletions compiler/src/dmd/expressionsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -5385,6 +5385,33 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
result = e;
}

override void visit(DollarExp exp)
{
static if (LOGSEMANTIC)
{
printf("DollarExp::semantic('%s')\n", exp.ident.toChars());
}

Dsymbol scopesym;
Dsymbol s = sc.search(exp.loc, exp.ident, scopesym);
if (s)
{
// if we found it, it's the expected length of an array slice, etc.
visit(cast(IdentifierExp)exp);
return;
}

if (sc.scopesym && sc.scopesym.isArrayScopeSymbol())
{
visit(cast(IdentifierExp)exp);
return;
}

// if not found, it's $ waiting for type inference via implicitCastTo
exp.type = Type.tvoid;
result = exp;
}

override void visit(IdentifierExp exp)
{
static if (LOGSEMANTIC)
Expand Down Expand Up @@ -7766,6 +7793,17 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
result = exp.e1;
return;
}

if (exp.e1.type == Type.tvoid)
{
if (exp.e1.isDollarExp())
{
exp.type = Type.tvoid;
result = exp;
return;
}
}

if (arrayExpressionSemantic(exp.arguments.peekSlice(), sc) ||
preFunctionParameters(sc, exp.argumentList, global.errorSink))
return setError();
Expand Down Expand Up @@ -15545,8 +15583,33 @@ Expression binSemantic(BinExp e, Scope* sc)
{
printf("BinExp::semantic('%s')\n", e.toChars());
}

static bool containsDollarExp(Expression exp)
{
if (!exp)
return false;
if (exp.isDollarExp())
return true;
if (auto ce = exp.isCallExp())
return containsDollarExp(ce.e1);
if (auto die = exp.isDotIdExp())
return containsDollarExp(die.e1);
return false;
}

Expression e1x = e.e1.expressionSemantic(sc);
// If e1 has a type and e2 contains a DollarExp, infer type from e1
if (e1x.type && e1x.type.ty != Tvoid && e1x.type.ty != Terror && containsDollarExp(e.e2))
{
e.e2 = inferType(e.e2, e1x.type);
}
Expression e2x = e.e2.expressionSemantic(sc);
// If e2 has a type and e1 is still void (could happen if e1 was DollarExp), infer type from e2
if (e2x.type && e2x.type.ty != Tvoid && e2x.type.ty != Terror && e1x.type && e1x.type.ty == Tvoid && containsDollarExp(e.e1))
{
e.e1 = inferType(e.e1, e2x.type);
e.e1 = e.e1.expressionSemantic(sc);
}

// for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
if (e1x.op == EXP.type)
Expand Down Expand Up @@ -15636,6 +15699,16 @@ private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc)
if (Expression ex = unaSemantic(exp, sc))
return ex;

if (exp.e1.type == Type.tvoid)
{
if (exp.e1.isDollarExp())
{
auto n = new DotIdExp(exp.loc, exp.e1, exp.ident);
n.type = Type.tvoid;
return n;
}
}

if (!sc.inCfile && exp.ident == Id._mangleof)
{
// symbol.mangleof
Expand Down Expand Up @@ -16645,6 +16718,16 @@ bool checkValue(Expression e)

if (e.type && e.type.toBasetype().ty == Tvoid)
{
if (e.isDollarExp()) return false;
if (auto ce = e.isCallExp())
{
if (ce.e1.isDollarExp()) return false;
}
if (auto de = e.isDotIdExp())
{
if (de.e1.isDollarExp()) return false;
}

error(e.loc, "expression `%s` is `void` and has no value", e.toErrMsg());
//print(); assert(0);
if (!global.gag)
Expand Down
24 changes: 24 additions & 0 deletions compiler/src/dmd/funcsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -1975,6 +1975,30 @@ FuncDeclaration resolveFuncCall(Loc loc, Scope* sc, Dsymbol s,
const(char)* lastprms = parametersTypeToChars(tf1.parameterList);
const(char)* nextprms = parametersTypeToChars(tf2.parameterList);

bool hasDollarArg = false;
if (fargs)
{
foreach (arg; *fargs)
{
if (arg.isDollarExp() ||
(arg.op == EXP.call && (cast(CallExp)arg).e1.isDollarExp()) ||
(arg.op == EXP.dotIdentifier && (cast(DotIdExp)arg).e1.isDollarExp()))
{
hasDollarArg = true;
break;
}
}
}

if (hasDollarArg)
{
.error(loc, "`%s.%s` with inferred type from `$` matches multiple overloads:\n%s: `%s%s%s`\nand:\n%s: `%s%s%s`\n`$` type inference is ambiguous - consider using an explicit type cast",
s.parent.toPrettyChars(), s.ident.toChars(),
m.lastf.loc.toChars(), m.lastf.toPrettyChars(), lastprms, tf1.modToChars(),
m.nextf.loc.toChars(), m.nextf.toPrettyChars(), nextprms, tf2.modToChars());
return null;
}

string match = "";
final switch (m.last)
{
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dmd/hdrgen.d
Original file line number Diff line number Diff line change
Expand Up @@ -4608,6 +4608,7 @@ string EXPtoString(EXP op)
EXP.dotVariable : "dotvar",
EXP.scope_ : "scope",
EXP.identifier : "identifier",
EXP.dollar : "$",
EXP.this_ : "this",
EXP.super_ : "super",
EXP.int64 : "long",
Expand Down
3 changes: 1 addition & 2 deletions compiler/src/dmd/parse.d
Original file line number Diff line number Diff line change
Expand Up @@ -8172,8 +8172,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
break;
}
case TOK.dollar:
if (!inBrackets)
error("`$` is valid only inside [] of index or slice");
e = new AST.DollarExp(loc);
nextToken();
break;
Expand Down Expand Up @@ -9754,6 +9752,7 @@ immutable PREC[EXP.max + 1] precedence =
EXP.dotVariable : PREC.primary,
EXP.scope_ : PREC.primary,
EXP.identifier : PREC.primary,
EXP.dollar : PREC.primary,
EXP.this_ : PREC.primary,
EXP.super_ : PREC.primary,
EXP.int64 : PREC.primary,
Expand Down
35 changes: 35 additions & 0 deletions compiler/test/fail_compilation/dollarinfer.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
REQUIRED_ARGS:
TEST_OUTPUT:
---
fail_compilation/dollarinfer.d(28): Error: `dollarinfer.ambig` with inferred type from `$` matches multiple overloads:
fail_compilation/dollarinfer.d(24): `dollarinfer.ambig(Data d)`
and:
fail_compilation/dollarinfer.d(25): `dollarinfer.ambig(Other o)`
`$` type inference is ambiguous - consider using an explicit type cast
fail_compilation/dollarinfer.d(30): Error: variable `dollarinfer.main.unknown` - `$` requires a known type context for inference, but type is inferred from initializer `$.VALUE_A`
fail_compilation/dollarinfer.d(30): `auto` does not provide a type context for `$`, consider using an explicit type
fail_compilation/dollarinfer.d(32): Error: no property `VALUE_C` for type `MyEnum`. Did you mean `MyEnum.VALUE_A` ?
fail_compilation/dollarinfer.d(18): enum `MyEnum` defined here
fail_compilation/dollarinfer.d(34): Error: cannot cast expression `$` of type `void` to `int`
---
*/

enum MyEnum { VALUE_A, VALUE_B }
struct Data { int x, y; }
struct Other { int x, y; }

void call(MyEnum m) {}
void call(int i) {}
void ambig(Data d) {}
void ambig(Other o) {}

void main() {
ambig($(1, 2));

auto unknown = $.VALUE_A;

MyEnum m = $.VALUE_C;

int x = $;
}
Loading
Loading