Skip to content
Merged
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
44 changes: 44 additions & 0 deletions changelog/dmd.default-init.dd
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
Keywords like `__FILE__` are always evaluated at the call site

Default arguments for functions can contain the keywords `__FILE__`,
`__FILE_FULL_PATH__`, `__MODULE__`, `__LINE__`, `__FUNCTION__`
and `__PRETTY_FUNCTION__`. They are now evaluated at the source location
of the calling function in more complex expressions as long as used in
an initializer, directly or not. Previously they had to be used directly
in the initializer to be evaluated at the call site. Here are some
examples, where more complex initializers are now evaluated at the
call site:

---
void func1(const(char)* file = __FILE__.ptr, size_t line = __LINE__)
{
// This now prints the filename of the calling function.
// Previously it was the filename of func1 itself.
printf("%s:%zd\n", file, line);
}

struct Loc
{
string file;
size_t line;
}

void func2(Loc loc = Loc(__FILE__, __LINE__))
Copy link
Copy Markdown

@ghost ghost Dec 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as a code comment you should mention that this case in particular did not work

{
// Variable loc now contains file and line of the calling function.
// Previously it was the location of func2.
writeln(loc.file, ":", loc.line);
}

Loc defaultLoc(string file = __FILE__, size_t line = __LINE__)
{
return Loc(file, line);
}

void func3(Loc loc = defaultLoc)
{
// Variable loc contains file and line of the calling function of
// func3 and not the location of func3 or defaultLoc.
writeln(loc.file, ":", loc.line);
}
---
1 change: 1 addition & 0 deletions compiler/src/dmd/dscope.d
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ extern (C++) struct Scope
Dsymbol inunion; /// != null if processing members of a union
bool nofree; /// true if shouldn't free it
bool inLoop; /// true if inside a loop (where constructor calls aren't allowed)
bool inDefaultArg; /// true if inside a default argument (where __FILE__, etc are evaluated at the call site)
Comment thread
ibuclaw marked this conversation as resolved.
int intypeof; /// in typeof(exp)
VarDeclaration lastVar; /// Previous symbol used to prevent goto-skips-init
ErrorSink eSink; /// sink for error messages
Expand Down
13 changes: 12 additions & 1 deletion compiler/src/dmd/dtemplate.d
Original file line number Diff line number Diff line change
Expand Up @@ -5632,7 +5632,11 @@ extern (C++) final class TemplateValueParameter : TemplateParameter
if (e)
{
e = e.syntaxCopy();
if ((e = e.expressionSemantic(sc)) is null)
Scope* sc2 = sc.push();
sc2.inDefaultArg = true;
e = e.expressionSemantic(sc2);
sc2.pop();
if (e is null)
return null;
if (auto te = e.isTemplateExp())
{
Expand Down Expand Up @@ -6698,6 +6702,12 @@ extern (C++) class TemplateInstance : ScopeDsymbol
if (!tiargs)
return true;
bool err = false;

// The arguments are not treated as part of a default argument,
// because they are evaluated at compile time.
sc = sc.push();
sc.inDefaultArg = false;

for (size_t j = 0; j < tiargs.length; j++)
{
RootObject o = (*tiargs)[j];
Expand Down Expand Up @@ -6929,6 +6939,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol
}
//printf("1: (*tiargs)[%d] = %p\n", j, (*tiargs)[j]);
}
sc.pop();
version (none)
{
printf("-TemplateInstance.semanticTiargs()\n");
Expand Down
175 changes: 152 additions & 23 deletions compiler/src/dmd/expressionsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -7446,7 +7446,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
result = (cast(BinExp)e).reorderSettingAAElem(sc);
}

private Expression compileIt(MixinExp exp)
private Expression compileIt(MixinExp exp, Scope *sc)
{
OutBuffer buf;
if (expressionsToString(buf, sc, exp.exps))
Expand Down Expand Up @@ -7487,7 +7487,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
printf("MixinExp::semantic('%s')\n", exp.toChars());
}

auto e = compileIt(exp);
// The expression is not treated as part of a default argument,
// because it is evaluated at compile time.
Scope* sc2 = sc.push();
sc2.inDefaultArg = false;

auto e = compileIt(exp, sc2);
sc2.pop();
if (!e)
return setError();
result = e.expressionSemantic(sc);
Expand Down Expand Up @@ -7730,7 +7736,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
// if the assert condition is a mixin expression, try to compile it
if (auto ce = exp.e1.isMixinExp())
{
if (auto e1 = compileIt(ce))
if (auto e1 = compileIt(ce, sc))
exp.e1 = e1;
}

Expand Down Expand Up @@ -13933,45 +13939,34 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
//printf("FileInitExp::semantic()\n");
e.type = Type.tstring;
result = e;
result = e.resolveLoc(e.loc, sc);
}

override void visit(LineInitExp e)
{
e.type = Type.tint32;
result = e;
result = e.resolveLoc(e.loc, sc);
}

override void visit(ModuleInitExp e)
{
//printf("ModuleInitExp::semantic()\n");
e.type = Type.tstring;
result = e;
result = e.resolveLoc(e.loc, sc);
}

override void visit(FuncInitExp e)
{
//printf("FuncInitExp::semantic()\n");
e.type = Type.tstring;
if (sc.func)
{
result = e.resolveLoc(Loc.initial, sc);
return;
}
result = e;
result = e.resolveLoc(e.loc, sc);
}

override void visit(PrettyFuncInitExp e)
{
//printf("PrettyFuncInitExp::semantic()\n");
e.type = Type.tstring;
if (sc.func)
{
result = e.resolveLoc(Loc.initial, sc);
return;
}

result = e;
result = e.resolveLoc(e.loc, sc);
}
}

Expand Down Expand Up @@ -15056,10 +15051,21 @@ bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false)
*/
Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc)
{
// Don't replace the special keywords, while we are inside a default
// argument. They are replaced later when copied to the call site.
if (sc.inDefaultArg)
return exp;

exp.loc = loc;

Expression visit(Expression exp)
{
if (auto binExp = exp.isBinExp())
{
binExp.e1 = binExp.e1.resolveLoc(loc, sc);
binExp.e2 = binExp.e2.resolveLoc(loc, sc);
return binExp;
}
if (auto unaExp = exp.isUnaExp())
{
unaExp.e1 = unaExp.e1.resolveLoc(loc, sc);
Expand All @@ -15068,10 +15074,121 @@ Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc)
return exp;
}

Expression visitCond(CondExp exp)
{
exp.e1 = exp.e1.resolveLoc(loc, sc);
exp.e2 = exp.e2.resolveLoc(loc, sc);
exp.econd = exp.econd.resolveLoc(loc, sc);
return exp;
}

Expression visitCat(CatExp exp)
{
exp.e1 = exp.e1.resolveLoc(loc, sc);
exp.e2 = exp.e2.resolveLoc(loc, sc);
if (exp.lowering)
exp.lowering = exp.lowering.resolveLoc(loc, sc);
return exp;
}

Expression visitStructLiteral(StructLiteralExp exp)
{
foreach (ref element; *exp.elements)
{
if (element)
element = element.resolveLoc(loc, sc);
}

return exp;
}

Expression visitNew(NewExp exp)
{
if (exp.thisexp)
exp.thisexp = exp.thisexp.resolveLoc(loc, sc);
if (exp.argprefix)
exp.argprefix = exp.argprefix.resolveLoc(loc, sc);
if (exp.lowering)
exp.lowering = exp.lowering.resolveLoc(loc, sc);

foreach (ref element; *exp.arguments)
{
if (element)
element = element.resolveLoc(loc, sc);
}

return exp;
}

Expression visitCall(CallExp exp)
{
foreach (ref element; *exp.arguments)
{
if (element)
element = element.resolveLoc(loc, sc);
}

return exp;
}

Expression visitArray(ArrayExp exp)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is not covered by tests

{
exp.e1 = exp.e1.resolveLoc(loc, sc);

foreach (ref element; *exp.arguments)
{
if (element)
element = element.resolveLoc(loc, sc);
}

return exp;
}

Expression visitSlice(SliceExp exp)
{
exp.e1 = exp.e1.resolveLoc(loc, sc);
exp.lwr = exp.lwr.resolveLoc(loc, sc);
exp.upr = exp.upr.resolveLoc(loc, sc);

return exp;
}

Expression visitInterval(IntervalExp exp)
{
exp.lwr = exp.lwr.resolveLoc(loc, sc);
exp.upr = exp.upr.resolveLoc(loc, sc);

return exp;
}

Expression visitArrayLiteral(ArrayLiteralExp exp)
{
if (exp.basis)
exp.basis = exp.basis.resolveLoc(loc, sc);

foreach (ref element; *exp.elements)
{
if (element)
element = element.resolveLoc(loc, sc);
}

return exp;
}

Expression visitAssocArrayLiteral(AssocArrayLiteralExp exp)
{
foreach (ref element; *exp.keys)
{
if (element)
element = element.resolveLoc(loc, sc);
}

foreach (ref element; *exp.values)
{
if (element)
element = element.resolveLoc(loc, sc);
}

return exp;
}

Expand All @@ -15088,20 +15205,20 @@ Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc)
return e.expressionSemantic(sc);
}

Expression visitLineInit(LineInitExp _)
Expression visitLineInit(LineInitExp exp)
{
Expression e = new IntegerExp(loc, loc.linnum, Type.tint32);
return e.expressionSemantic(sc);
}

Expression visitModuleInit(ModuleInitExp _)
Expression visitModuleInit(ModuleInitExp exp)
{
const auto s = (sc.callsc ? sc.callsc : sc)._module.toPrettyChars().toDString();
Expression e = new StringExp(loc, s);
return e.expressionSemantic(sc);
}

Expression visitFuncInit(FuncInitExp _)
Expression visitFuncInit(FuncInitExp exp)
{
const(char)* s;
if (sc.callsc && sc.callsc.func)
Expand All @@ -15114,7 +15231,7 @@ Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc)
return e.expressionSemantic(sc);
}

Expression visitPrettyFunc(PrettyFuncInitExp _)
Expression visitPrettyFunc(PrettyFuncInitExp exp)
{
FuncDeclaration fd = (sc.callsc && sc.callsc.func)
? sc.callsc.func
Expand Down Expand Up @@ -15142,7 +15259,16 @@ Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc)
switch(exp.op)
{
default: return visit(exp);
case EXP.structLiteral: return visitStructLiteral(exp.isStructLiteralExp());
case EXP.new_: return visitNew(exp.isNewExp());
case EXP.concatenate: return visitCat(exp.isCatExp());
case EXP.call: return visitCall(exp.isCallExp());
case EXP.question: return visitCond(exp.isCondExp());
case EXP.array: return visitArray(exp.isArrayExp());
case EXP.slice: return visitSlice(exp.isSliceExp());
case EXP.interval: return visitInterval(exp.isIntervalExp());
case EXP.arrayLiteral: return visitArrayLiteral(exp.isArrayLiteralExp());
case EXP.assocArrayLiteral: return visitAssocArrayLiteral(exp.isAssocArrayLiteralExp());
case EXP.file:
case EXP.fileFullPath: return visitFileInit(exp.isFileInitExp());
case EXP.line: return visitLineInit(exp.isLineInitExp);
Expand Down Expand Up @@ -16202,7 +16328,10 @@ Expression getVarExp(EnumMember em, const ref Loc loc, Scope* sc)
em.checkDisabled(loc, sc);

if (em.depdecl && !em.depdecl._scope)
{
em.depdecl._scope = sc;
em.depdecl._scope.setNoFree();
}
em.checkDeprecated(loc, sc);

if (em.errors)
Expand Down
Loading