-
-
Notifications
You must be signed in to change notification settings - Fork 701
Fix issue 18919 - __FILE__ and __LINE__ should work when used in defa… #15968
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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__)) | ||
| { | ||
| // 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); | ||
| } | ||
| --- | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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)) | ||
|
|
@@ -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); | ||
|
|
@@ -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; | ||
| } | ||
|
|
||
|
|
@@ -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); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -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); | ||
|
|
@@ -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) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
| } | ||
|
|
||
|
|
@@ -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) | ||
|
|
@@ -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 | ||
|
|
@@ -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); | ||
|
|
@@ -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) | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
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