From b63ed032ee68c247987428a69cd7efe4d4ce418e Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 30 Jan 2018 19:42:53 -0800 Subject: [PATCH 1/4] function argument stringification: __ARG_STRING__ --- changelog/argname.dd | 16 ++++ src/dmd/astbase.d | 13 +++ src/dmd/backend/cgobj.c | 2 + src/dmd/dinifile.d | 2 +- src/dmd/dmodule.d | 19 +++- src/dmd/expression.d | 42 +++++++++ src/dmd/expression.h | 7 ++ src/dmd/expressionsem.d | 79 +++++++++++++++- src/dmd/globals.d | 14 ++- src/dmd/globals.h | 5 +- src/dmd/lexer.d | 3 +- src/dmd/mars.d | 12 +++ src/dmd/parse.d | 36 +++++++- src/dmd/parsetimevisitor.d | 1 + src/dmd/strictvisitor.d | 1 + src/dmd/tokens.d | 5 +- src/dmd/visitor.h | 2 + test/runnable/imports/import_world.d | 25 +++++ test/runnable/testargnames.d | 132 +++++++++++++++++++++++++++ 19 files changed, 404 insertions(+), 12 deletions(-) create mode 100644 changelog/argname.dd create mode 100644 test/runnable/imports/import_world.d create mode 100644 test/runnable/testargnames.d diff --git a/changelog/argname.dd b/changelog/argname.dd new file mode 100644 index 000000000000..fa33bb159b76 --- /dev/null +++ b/changelog/argname.dd @@ -0,0 +1,16 @@ +Stringify caller's function argument, analog to C's `#` stringification macro (except it's type safe). + +NOTE: it also works with UFCS. + +Eg: +--- +string log(T)(T a, string name=__ARG_STRING__!a) +{ + import std.conv; + return text(name, ": ", a); +} + +int x = 3; +assert(log(x * 2) == "x * 2: 6"); +assert(log(__LINE__) == "__LINE__: " ~ __LINE__); +--- diff --git a/src/dmd/astbase.d b/src/dmd/astbase.d index 294495aae2be..e1d55ceb4659 100644 --- a/src/dmd/astbase.d +++ b/src/dmd/astbase.d @@ -5248,6 +5248,19 @@ struct ASTBase } } + extern (C++) final class ArgStringInitExp : DefaultInitExp + { + extern (D) this(Loc loc) + { + super(loc, TOK.argString, __traits(classInstanceSize, ArgStringInitExp)); + } + + override void accept(Visitor v) + { + v.visit(this); + } + } + extern (C++) final class LineInitExp : DefaultInitExp { extern (D) this(Loc loc) diff --git a/src/dmd/backend/cgobj.c b/src/dmd/backend/cgobj.c index 06abebef708f..7fc2963918b4 100644 --- a/src/dmd/backend/cgobj.c +++ b/src/dmd/backend/cgobj.c @@ -37,12 +37,14 @@ struct Loc char *filename; unsigned linnum; unsigned charnum; + unsigned bytes; // TODO: needed? Loc(int y, int x) { linnum = y; charnum = x; filename = NULL; + bytes = 0; } }; diff --git a/src/dmd/dinifile.d b/src/dmd/dinifile.d index 4da1a10e452e..77f9eca6bae7 100644 --- a/src/dmd/dinifile.d +++ b/src/dmd/dinifile.d @@ -351,7 +351,7 @@ void parseConfFile(StringTable* environment, const(char)* filename, const(char)* { if (!writeToEnv(environment, strdup(pn))) { - error(Loc(filename, lineNum, 0), "Use `NAME=value` syntax, not `%s`", pn); + error(Loc(filename, lineNum, 0, cast(uint)linestart), "Use `NAME=value` syntax, not `%s`", pn); fatal(); } static if (LOG) diff --git a/src/dmd/dmodule.d b/src/dmd/dmodule.d index 45733fbbaf6e..2842f5bea879 100644 --- a/src/dmd/dmodule.d +++ b/src/dmd/dmodule.d @@ -309,6 +309,15 @@ extern (C++) final class Module : Package modules = new DsymbolTable(); } + extern (C++) void releaseResources() + { + // printf("Module::releaseResources %s\n", this.toChars()); + if (srcfile._ref == 0) + .free(srcfile.buffer); + srcfile.buffer = null; + srcfile.len = 0; + } + extern (C++) static __gshared AggregateDeclaration moduleinfo; const(char)* arg; // original argument name @@ -875,10 +884,12 @@ extern (C++) final class Module : Package if (p.errors) ++global.errors; } - if (srcfile._ref == 0) - .free(srcfile.buffer); - srcfile.buffer = null; - srcfile.len = 0; + + if(global.params.disposeSrcContent) + { + releaseResources(); + } + /* The symbol table into which the module is to be inserted. */ DsymbolTable dst; diff --git a/src/dmd/expression.d b/src/dmd/expression.d index 5106a3b3e0e4..c6f40c49416a 100644 --- a/src/dmd/expression.d +++ b/src/dmd/expression.d @@ -2441,6 +2441,12 @@ extern (C++) abstract class Expression : RootObject return false; } + // TODO: can we avoid this? + ArgStringInitExp isArgStringInitExp() + { + return null; + } + final Expression op_overload(Scope* sc) { return .op_overload(this, sc); @@ -7096,6 +7102,42 @@ extern (C++) final class FileInitExp : DefaultInitExp } } +/*********************************************************** + */ +extern (C++) final class ArgStringInitExp : DefaultInitExp +{ + // function parameter we want stringified + Identifier ident; + + extern (D) this(const ref Loc loc) + { + super(loc, TOK.argString, __traits(classInstanceSize, ArgStringInitExp)); + } + + void setIdent(Identifier ident) + { + this.ident = ident; + } + + override ArgStringInitExp isArgStringInitExp() + { + return this; + } + + extern (D) Expression resolveArgString(const ref Loc loc, Scope* sc, const(char)[] value) + { + Expression e = new StringExp(loc, cast(void*)value.ptr, value.length); + e = e.expressionSemantic(sc); + e = e.castTo(sc, type); + return e; + } + + override void accept(Visitor v) + { + v.visit(this); + } +} + /*********************************************************** */ extern (C++) final class LineInitExp : DefaultInitExp diff --git a/src/dmd/expression.h b/src/dmd/expression.h index bf3c916391de..49cfd64aaced 100644 --- a/src/dmd/expression.h +++ b/src/dmd/expression.h @@ -1305,6 +1305,13 @@ class FileInitExp : public DefaultInitExp void accept(Visitor *v) { v->visit(this); } }; +class ArgnameInitExp : public DefaultInitExp +{ +public: + Expression *resolveArgname(Loc loc, Scope* sc, const(char)* argname); + void accept(Visitor *v) { v->visit(this); } +}; + class LineInitExp : public DefaultInitExp { public: diff --git a/src/dmd/expressionsem.d b/src/dmd/expressionsem.d index 331f5ed4d1c0..ad3b7034f210 100644 --- a/src/dmd/expressionsem.d +++ b/src/dmd/expressionsem.d @@ -598,8 +598,76 @@ private bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type tthis, } arg = p.defaultArg; arg = inlineCopy(arg, sc); - // __FILE__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__ - arg = arg.resolveLoc(loc, sc); + + if(auto argexp = arg.isArgStringInitExp()) + { + if(argexp.ident is null) + { + error(argexp.loc, "argexp.ident null"); + return true; + } + assert(nargs > 0); + bool found = false; + // TODO: should Parameters._foreach be used? + foreach(u; 0..nparams) + { + Parameter pu = Parameter.getNth(tf.parameters, u); + if(pu.ident == argexp.ident) + { + assert(u < nargs); + auto argu = (*arguments)[u]; + const(char)[] argname; + if(global.params.disposeSrcContent) + { + // D20180130T161632 NOTE: this does some partial constant folding, not 100% faithful to source code; also, doens't this expands special tokens like __FILE__ + auto temp = argu.toChars(); + argname = temp[0..temp.strlen()]; + } + else + { + auto temp = sc._module.srcfile; + assert(temp.buffer !is null); + auto arg_loc = argu.loc; + auto txt = cast(char[]) temp.buffer[arg_loc.bytes..temp.len]; + scope parser = new Parser!ASTCodegen(sc._module, txt, false); + parser.nextToken(); + // NOTE: parseExpression() would match `a, b` instead of just `a` in `fun(a, b)` + auto expr = parser.parseAssignExp(); + assert(!parser.errors); + + // strip trailing whitespace + auto end = parser.token.loc.bytes; + while(end > 0) + { + auto c = txt.ptr[end-1]; + if(c == ' ' || c == '\n' || c == '\r') + end--; + else + break; + } + + // CHECKME: we keep files contenst in memory until end of semantic analysis so should be fine to use a slice; if not, use `OutBuffer` + argname = txt[0..end]; + } + + arg=argexp.resolveArgString(loc, sc, argname); + found = true; + break; + } + } + + if(!found) + { + error(argexp.loc, "unable to resolve %s", argexp.ident.toChars); + return true; + } + } + else + { + // __FILE__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__ + arg = arg.resolveLoc(loc, sc); + } + arguments.push(arg); nargs++; } @@ -9296,6 +9364,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = e; } + override void visit(ArgStringInitExp e) + { + //printf("ArgStringInitExp::semantic()\n"); + e.type = Type.tstring; + result = e; + } + override void visit(LineInitExp e) { e.type = Type.tint32; diff --git a/src/dmd/globals.d b/src/dmd/globals.d index 5093b4b1779f..110c74a1b11e 100644 --- a/src/dmd/globals.d +++ b/src/dmd/globals.d @@ -84,6 +84,8 @@ struct Param bool verbose; // verbose compile bool vcg_ast; // write-out codegen-ast bool showColumns; // print character (column) numbers in diagnostics + bool showBytes; // print bytes offset in diagnostics + bool disposeSrcContent; // dispose of source file contents after parsing bool vtls; // identify thread local variables bool vgc; // identify gc usage bool vfield; // identify non-mutable field variables @@ -378,15 +380,18 @@ struct Loc const(char)* filename; uint linnum; uint charnum; + // 0-based offset from origin in bytes (allows O(1) access) + uint bytes; static immutable Loc initial; /// use for default initialization of const ref Loc's nothrow: - extern (D) this(const(char)* filename, uint linnum, uint charnum) + extern (D) this(const(char)* filename, uint linnum, uint charnum, uint bytes) { this.linnum = linnum; this.charnum = charnum; this.filename = filename; + this.bytes = bytes; } extern (C++) const(char)* toChars() const @@ -405,6 +410,12 @@ nothrow: buf.writeByte(','); buf.print(charnum); } + if (global.params.showBytes) + { + // instead of ',' to avoid confusing with charnum if !showColumns + buf.writeByte(':'); + buf.print(bytes); + } buf.writeByte(')'); } return buf.extractString(); @@ -412,6 +423,7 @@ nothrow: extern (C++) bool equals(ref const(Loc) loc) const { + // TODO: how about: `return (bytes == loc.bytes) && FileName.equals(filename, loc.filename);` ? return (!global.params.showColumns || charnum == loc.charnum) && linnum == loc.linnum && FileName.equals(filename, loc.filename); diff --git a/src/dmd/globals.h b/src/dmd/globals.h index 098539f5c940..3ba90347b745 100644 --- a/src/dmd/globals.h +++ b/src/dmd/globals.h @@ -73,6 +73,7 @@ struct Param bool verbose; // verbose compile bool vcg_ast; // write-out codegen-ast bool showColumns; // print character (column) numbers in diagnostics + bool showBytes; // print bytes offset in diagnostics bool vtls; // identify thread local variables char vgc; // identify gc usage bool vfield; // identify non-mutable field variables @@ -300,15 +301,17 @@ struct Loc const char *filename; unsigned linnum; unsigned charnum; + unsigned bytes; Loc() { linnum = 0; charnum = 0; filename = NULL; + bytes = 0; } - Loc(const char *filename, unsigned linnum, unsigned charnum); + Loc(const char *filename, unsigned linnum, unsigned charnum, uint bytes); const char *toChars() const; bool equals(const Loc& loc); diff --git a/src/dmd/lexer.d b/src/dmd/lexer.d index 080104b40585..8d0c44fde650 100644 --- a/src/dmd/lexer.d +++ b/src/dmd/lexer.d @@ -228,7 +228,7 @@ class Lexer */ this(const(char)* filename, const(char)* base, size_t begoffset, size_t endoffset, bool doDocComment, bool commentToken) { - scanloc = Loc(filename, 1, 1); + scanloc = Loc(filename, 1, 1, 0); //printf("Lexer::Lexer(%p,%d)\n",base,length); //printf("lexer.filename = %s\n", filename); token = Token.init; @@ -2227,6 +2227,7 @@ class Lexer final Loc loc() { scanloc.charnum = cast(uint)(1 + p - line); + scanloc.bytes = cast(uint)(p - base); return scanloc; } diff --git a/src/dmd/mars.d b/src/dmd/mars.d index a946590aee82..5433a76ab9d7 100644 --- a/src/dmd/mars.d +++ b/src/dmd/mars.d @@ -842,6 +842,14 @@ private int tryMain(size_t argc, const(char)** argv) printf("%.*s", cast(int)ob.offset, ob.data); } + if (!global.params.disposeSrcContent) + { + // We didn't dispose greedily after parsing, so lets do it now + // TODO: can we do it at some earlier point? + foreach (m; Module.amodules) + m.releaseResources(); + } + printCtfePerformanceStats(); Library library = null; @@ -1648,6 +1656,10 @@ private bool parseCommandLine(const ref Strings arguments, const size_t argc, re params.vtls = true; else if (arg == "-vcolumns") // https://dlang.org/dmd.html#switch-vcolumns params.showColumns = true; + else if (arg == "-vbytes") // https://dlang.org/dmd.html#switch-bytes + params.showBytes = true; + else if (arg == "-disposesrc") + params.disposeSrcContent = true; else if (arg == "-vgc") // https://dlang.org/dmd.html#switch-vgc params.vgc = true; else if (startsWith(p + 1, "verrors")) // https://dlang.org/dmd.html#switch-verrors diff --git a/src/dmd/parse.d b/src/dmd/parse.d index c0885db914b8..1397c9c680d2 100644 --- a/src/dmd/parse.d +++ b/src/dmd/parse.d @@ -69,6 +69,7 @@ __gshared PREC[TOK.max_] precedence = TOK.classReference : PREC.primary, TOK.file : PREC.primary, TOK.fileFullPath : PREC.primary, + TOK.argString : PREC.primary, TOK.line : PREC.primary, TOK.moduleString : PREC.primary, TOK.functionString : PREC.primary, @@ -2041,6 +2042,9 @@ final class Parser(AST) : Lexer tiargs.push(ea); break; } + case TOK.argString: + error("illegal use of " ~TOK.argString); + break; default: error("template argument expected following `!`"); break; @@ -6249,7 +6253,7 @@ final class Parser(AST) : Lexer /***************************************** * Parses default argument initializer expression that is an assign expression, - * with special handling for __FILE__, __FILE_DIR__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__. + * with special handling for __FILE__, __FILE_DIR__, __LINE__, __MODULE__, __FUNCTION__, __PRETTY_FUNCTION__, __ARG_STRING__. */ AST.Expression parseDefaultInitExp() { @@ -6278,6 +6282,36 @@ final class Parser(AST) : Lexer return e; } } + + if(token.value == TOK.argString) + { + AST.ArgStringInitExp e = new AST.ArgStringInitExp(token.loc); + nextToken(); + if(token.value == TOK.not) + { + if(peekNext() != TOK.identifier) + { + error("identifier expected following `__ARG_STRING__!`"); + return null; + } + + nextToken(); + e.setIdent(token.ident); + nextToken(); + } else { + error("expected !"); + return null; + } + + if (token.value == TOK.comma || token.value == TOK.rightParentheses) + { + return e; + } else { + error("expected , or )"); + return null; + } + } + AST.Expression e = parseAssignExp(); return e; } diff --git a/src/dmd/parsetimevisitor.d b/src/dmd/parsetimevisitor.d index 6b1d3673453b..cf5b36bca1ab 100644 --- a/src/dmd/parsetimevisitor.d +++ b/src/dmd/parsetimevisitor.d @@ -213,6 +213,7 @@ public: void visit(AST.FuncInitExp e) { visit(cast(AST.DefaultInitExp)e); } void visit(AST.PrettyFuncInitExp e) { visit(cast(AST.DefaultInitExp)e); } void visit(AST.FileInitExp e) { visit(cast(AST.DefaultInitExp)e); } + void visit(AST.ArgStringInitExp e) { visit(cast(AST.DefaultInitExp)e); } void visit(AST.LineInitExp e) { visit(cast(AST.DefaultInitExp)e); } void visit(AST.ModuleInitExp e) { visit(cast(AST.DefaultInitExp)e); } diff --git a/src/dmd/strictvisitor.d b/src/dmd/strictvisitor.d index 442bd40a8416..7e3e4513c104 100644 --- a/src/dmd/strictvisitor.d +++ b/src/dmd/strictvisitor.d @@ -172,6 +172,7 @@ extern(C++) class StrictVisitor(AST) : ParseTimeVisitor!AST override void visit(AST.FuncInitExp) { assert(0); } override void visit(AST.PrettyFuncInitExp) { assert(0); } override void visit(AST.FileInitExp) { assert(0); } + override void visit(AST.ArgStringInitExp) { assert(0); } override void visit(AST.LineInitExp) { assert(0); } override void visit(AST.ModuleInitExp) { assert(0); } override void visit(AST.CommaExp) { assert(0); } diff --git a/src/dmd/tokens.d b/src/dmd/tokens.d index 1771fe2465d0..6198e4e133b3 100644 --- a/src/dmd/tokens.d +++ b/src/dmd/tokens.d @@ -291,6 +291,7 @@ enum TOK : int line, file, fileFullPath, + argString, moduleString, functionString, prettyFunction, @@ -302,7 +303,7 @@ enum TOK : int vector, pound, - // 239 + // 241 interval, voidExpression, cantExpression, @@ -447,6 +448,7 @@ extern (C++) struct Token TOK.overloadSet: "__overloadset", TOK.file: "__FILE__", TOK.fileFullPath: "__FILE_FULL_PATH__", + TOK.argString: "__ARG_STRING__", TOK.line: "__LINE__", TOK.moduleString: "__MODULE__", TOK.functionString: "__FUNCTION__", @@ -941,6 +943,7 @@ private immutable TOK[] keywords = TOK.overloadSet, TOK.file, TOK.fileFullPath, + TOK.argString, TOK.line, TOK.moduleString, TOK.functionString, diff --git a/src/dmd/visitor.h b/src/dmd/visitor.h index 922a2856430d..d1790410d0d3 100644 --- a/src/dmd/visitor.h +++ b/src/dmd/visitor.h @@ -274,6 +274,7 @@ class IdentityExp; class CondExp; class DefaultInitExp; class FileInitExp; +class ArgnameInitExp; class LineInitExp; class ModuleInitExp; class FuncInitExp; @@ -495,6 +496,7 @@ class ParseTimeVisitor virtual void visit(FuncInitExp *e) { visit((DefaultInitExp *)e); } virtual void visit(PrettyFuncInitExp *e) { visit((DefaultInitExp *)e); } virtual void visit(FileInitExp *e) { visit((DefaultInitExp *)e); } + virtual void visit(ArgnameInitExp *e) { visit((DefaultInitExp *)e); } virtual void visit(LineInitExp *e) { visit((DefaultInitExp *)e); } virtual void visit(ModuleInitExp *e) { visit((DefaultInitExp *)e); } diff --git a/test/runnable/imports/import_world.d b/test/runnable/imports/import_world.d new file mode 100644 index 000000000000..6a28a8c3d4da --- /dev/null +++ b/test/runnable/imports/import_world.d @@ -0,0 +1,25 @@ +/+ +Useful to test performance stats based on flags, eg: + +gtime -v dmd -unittest -version=StdUnittest -version=import_std -main -o- -i=std test/runnable/imports/import_world.d + +TODO: also add blocks with: +`version (import_core)` +etc ++/ + +version (import_std) +{ + // adapted from rdmd importWorld + import std.stdio, std.algorithm, std.array, std.ascii, std.base64, + std.bigint, std.bitmanip, std.compiler, std.complex, std.concurrency, + std.container, std.conv, std.csv, std.datetime, std.demangle, + std.digest.md, std.encoding, std.exception, std.file, std.format, + std.functional, std.getopt, std.json, std.math, std.mathspecial, + std.mmfile, std.numeric, std.outbuffer, std.parallelism, std.path, + std.process, std.random, std.range, std.regex, std.signals, std.socket, + std.stdint, std.stdio, std.string, std.windows.syserror, std.system, + std.traits, std.typecons, std.typetuple, std.uni, std.uri, std.utf, + std.variant, std.xml, std.zip, std.zlib; +} + diff --git a/test/runnable/testargnames.d b/test/runnable/testargnames.d new file mode 100644 index 000000000000..aa1c230c1626 --- /dev/null +++ b/test/runnable/testargnames.d @@ -0,0 +1,132 @@ +/* +PERMUTE_ARGS: +*/ + +// to optionally avoid depending on phobos +enum easy_debug = true; + +static if (easy_debug) +{ + import std.conv : text; + import core.stdc.stdio; +} +else +{ + string text(T...)(T a) + { + return ""; + } +} + +void check(string name, string expected, string file = __FILE__, int line = __LINE__) +{ + if (name == expected) + return; + assert(0, text("expected {", expected, "} got {", name, "} at ", file, ":", line)); +} + +// Simple yet useful log function +string log(T)(T a, string name = __ARG_STRING__!a) +{ + return text(name, ":", a); +} + +void fun1(int a, string expected, string name = __ARG_STRING__!a, + string file = __FILE__, int line = __LINE__) +{ + check(name, expected, file, line); +} + +void fun2(int a, string b, double c, string expected, + string name = __ARG_STRING__!b, string file = __FILE__, int line = __LINE__) +{ + check(name, expected, file, line); +} + +void fun_UFCS(int a, string expected, string name = __ARG_STRING__!a, + string file = __FILE__, int line = __LINE__) +{ + check(name, expected, file, line); +} + +void fun_template(T)(T a, string expected, string name = __ARG_STRING__!a, + string file = __FILE__, int line = __LINE__) +{ + check(name, expected, file, line); +} + +struct A +{ + int x = 1; + + int myfun() + { + return x * x; + } +} + +void main() +{ + int a = 42; + + check(log(1 + a), `1 + a:43`); + + fun1(41 + a, `41 + a`); + + string bar = "bob"; + fun2(41 + a, "foo" ~ bar, 0.0, `"foo" ~ bar`); + + (1 + 1).fun_UFCS("1 + 1"); + + fun_template(1 + 3, `1 + 3`); + + fun1(a + a + a, `a + a + a`); + + // Checks that no constant folding happens, cf D20180130T161632. + fun1(1 + 1 + 2, `1 + 1 + 2`); + + static const int x = 44; + fun1(x + x + x, `x + x + x`); + + fun1(A.init.x + A(a).myfun(), `A.init.x + A(a).myfun()`); + + enum t = 44; + fun1(t + t, `t + t`); + + // parenthesis are removed: + fun1((t + t), `t + t`); + + // Check that special tokens dont't get expanded + fun1(__LINE__, "__LINE__"); + fun_template(__FILE__, "__FILE__"); + + // Formatting intentionally bad to test behavior + { + // dfmt off + // Tests behavior when argument is not pretty-printed, eg: `t+t` instead of `t + t` + fun1(t+t, `t+t`); + + // Tests behavior with trailing whitespace + fun_template(1 ,`1`); + + // Tests behavior with weird whitespace + fun_template( + "foo" +~ " bar", `"foo" +~ " bar"`); + + // Tests behavior with comments + fun_template( "foo" ~ /+ comment +/ "bar", `"foo" ~ /+ comment +/ "bar"`); + // dfmt on + } + + // Tests that still works with #line primitive + // dfmt bug:https://github.com/dlang-community/dfmt/issues/321 + // dfmt off + #line 100 + fun1(t + 1, `t + 1`); + + #line 200 "anotherfile" + fun1(t + 1, `t + 1`); + // dfmt on +} From 1fc314ecb1019b422fcc2737963995e72dd63295 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 2 Feb 2018 23:22:02 -0800 Subject: [PATCH 2/4] support variadics; use __traits(getSource, symbol) instead of __ARG_STRING__ --- changelog/argname.dd | 12 +-- src/dmd/astbase.d | 2 +- src/dmd/expression.d | 41 +++++++- src/dmd/expression.h | 3 +- src/dmd/expressionsem.d | 175 ++++++++++++++++++++++++++--------- src/dmd/id.d | 1 + src/dmd/mtype.d | 8 ++ src/dmd/parse.d | 39 ++------ src/dmd/tokens.d | 6 +- src/dmd/traits.d | 33 +++++++ src/dmd/typesem.d | 1 + src/dmd/visitor.h | 4 +- test/runnable/testargnames.d | 115 ++++++++++++++++++++--- 13 files changed, 338 insertions(+), 102 deletions(-) diff --git a/changelog/argname.dd b/changelog/argname.dd index fa33bb159b76..a02b7cd9bf99 100644 --- a/changelog/argname.dd +++ b/changelog/argname.dd @@ -1,16 +1,16 @@ Stringify caller's function argument, analog to C's `#` stringification macro (except it's type safe). -NOTE: it also works with UFCS. +NOTE: it also works with UFCS, with tuples (string static array) and non-tuples (string) Eg: --- -string log(T)(T a, string name=__ARG_STRING__!a) +string logSimple(T...)(T a, string[T.length] names = __traits(getSource, a)) { import std.conv; - return text(name, ": ", a); + return text(names, ": ", a); // see testargnames for a better log function } -int x = 3; -assert(log(x * 2) == "x * 2: 6"); -assert(log(__LINE__) == "__LINE__: " ~ __LINE__); +double x = 1.5; +logSimple(x * 2, 'a') // ["x * 2", "'a'"]: 3a +logSimple(__LINE__) // ["__LINE__"]: 9 --- diff --git a/src/dmd/astbase.d b/src/dmd/astbase.d index e1d55ceb4659..98e1c8b3c10b 100644 --- a/src/dmd/astbase.d +++ b/src/dmd/astbase.d @@ -5252,7 +5252,7 @@ struct ASTBase { extern (D) this(Loc loc) { - super(loc, TOK.argString, __traits(classInstanceSize, ArgStringInitExp)); + super(loc, TOK.getSource, __traits(classInstanceSize, ArgStringInitExp)); } override void accept(Visitor v) diff --git a/src/dmd/expression.d b/src/dmd/expression.d index c6f40c49416a..cc3e8ef1a720 100644 --- a/src/dmd/expression.d +++ b/src/dmd/expression.d @@ -1604,6 +1604,8 @@ extern (C++) abstract class Expression : RootObject return buf.extractString(); } + // TODO: https://issues.dlang.org/show_bug.cgi?id=18371 Issue 18371 - allow default parameters after `...` (not just template variadics, which are ok now) + version(all) final void error(const(char)* format, ...) const { if (type != Type.terror) @@ -1614,6 +1616,26 @@ extern (C++) abstract class Expression : RootObject va_end(ap); } } + else + { + pragma(inline, true) + extern(D) + final void error(string file=__FILE__, int line=__LINE__)(const(char)* format, ...) const + { + version(with_debug){ + import std.conv:text; + writelnL(file, ":", line); + } + if (type != Type.terror) + { + va_list ap; + va_start(ap, format); + .verror(loc, format, ap); + va_end(ap); + } + } + } + final void warning(const(char)* format, ...) const { @@ -7107,11 +7129,13 @@ extern (C++) final class FileInitExp : DefaultInitExp extern (C++) final class ArgStringInitExp : DefaultInitExp { // function parameter we want stringified + // NOTE: redundant with exp.args[0],toChars Identifier ident; + TraitsExp exp; extern (D) this(const ref Loc loc) { - super(loc, TOK.argString, __traits(classInstanceSize, ArgStringInitExp)); + super(loc, TOK.getSource, __traits(classInstanceSize, ArgStringInitExp)); } void setIdent(Identifier ident) @@ -7124,7 +7148,7 @@ extern (C++) final class ArgStringInitExp : DefaultInitExp return this; } - extern (D) Expression resolveArgString(const ref Loc loc, Scope* sc, const(char)[] value) + extern (D) Expression resolveArgString(const ref Loc loc, Scope* sc, string value) { Expression e = new StringExp(loc, cast(void*)value.ptr, value.length); e = e.expressionSemantic(sc); @@ -7132,6 +7156,19 @@ extern (C++) final class ArgStringInitExp : DefaultInitExp return e; } + extern (D) Expression resolveArgStrings(const ref Loc loc, Scope* sc, string[] values) + { + auto elements = new Expressions(); + elements.setDim(values.length); + foreach(j, value; values){ + (*elements)[j] = new StringExp(loc, cast(void*)value.ptr, value.length); + } + Expression e = new ArrayLiteralExp(loc, elements); + e = e.expressionSemantic(sc); + e = e.castTo(sc, type); + return e; + } + override void accept(Visitor v) { v.visit(this); diff --git a/src/dmd/expression.h b/src/dmd/expression.h index 49cfd64aaced..00db67512a58 100644 --- a/src/dmd/expression.h +++ b/src/dmd/expression.h @@ -1305,10 +1305,9 @@ class FileInitExp : public DefaultInitExp void accept(Visitor *v) { v->visit(this); } }; -class ArgnameInitExp : public DefaultInitExp +class ArgStringInitExp : public DefaultInitExp { public: - Expression *resolveArgname(Loc loc, Scope* sc, const(char)* argname); void accept(Visitor *v) { v->visit(this); } }; diff --git a/src/dmd/expressionsem.d b/src/dmd/expressionsem.d index ad3b7034f210..baec7bd3db23 100644 --- a/src/dmd/expressionsem.d +++ b/src/dmd/expressionsem.d @@ -606,59 +606,79 @@ private bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type tthis, error(argexp.loc, "argexp.ident null"); return true; } - assert(nargs > 0); + //assert(nargs > 0); bool found = false; - // TODO: should Parameters._foreach be used? - foreach(u; 0..nparams) + + TypeFunction orig = cast(TypeFunction)fd.originalType; + TypeFunction temp2 = cast(TypeFunction)fd.type; + assert(orig); + string[]values; + for(int u=0, u2=0; u 0) + auto ident2 = (*fd.parameters)[u2].ident; + if(ident2==pu.ident) { - auto c = txt.ptr[end-1]; - if(c == ' ' || c == '\n' || c == '\r') - end--; - else + u2++; + is_variadic_param=false; break; } + else + { + // TODO: factor with where this magic string is defined + enum vararg_name = "_param_"; + // TODO: define/use startsWith + auto ident2b=ident2.toString; + if(ident2b.length>vararg_name.length && ident2b[0..vararg_name.length]==vararg_name) + { + continue; + } + else + { + break; + } + } + } + + if(pu.ident != argexp.ident) continue; + if(u2 > nargs){ + error(argexp.loc, "cannot refer to param `%s`", argexp.ident.toChars); + return true; + } + + // take care of variadics + foreach(u2i ; u2_begin..u2) + { + assert(u2i < nargs); + auto argu = (*arguments)[u2i]; - // CHECKME: we keep files contenst in memory until end of semantic analysis so should be fine to use a slice; if not, use `OutBuffer` - argname = txt[0..end]; - } + // CHECKME: is that the simplest way to distinguish whether or not argument was explicitly given by caller (via explicit template instantiation in case of variadics)? + if(argu.loc==loc){ + assert(pu.defaultArg); + error(argexp.loc, "cannot refer to default arg `%s` not explicitly set by caller", argexp.ident.toChars); + return true; + } - arg=argexp.resolveArgString(loc, sc, argname); - found = true; - break; + auto argname = getArgSring(argu, sc).idup; + values~=argname; } + if(is_variadic_param){ + arg=argexp.resolveArgStrings(loc, sc, values); + } else{ + assert(values.length==1); + arg=argexp.resolveArgString(loc, sc, values[0]); + } + found = true; + break; } if(!found) { - error(argexp.loc, "unable to resolve %s", argexp.ident.toChars); + error(argexp.loc, "unable to resolve `%s`", argexp.ident.toChars); return true; } } @@ -9366,8 +9386,44 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(ArgStringInitExp e) { - //printf("ArgStringInitExp::semantic()\n"); - e.type = Type.tstring; + Parameter findParamByName(){ + // CHECKME: do we need to go all way up? + for (Scope* sc2 = sc; sc2; sc2 = sc2.enclosing) + { + if (!sc2.scopesym) continue; + auto s=sc2.scopesym; + auto s2=cast(ScopeDsymbol)s; + if(!s2) continue; + if(!s2.members) continue; + foreach(i; 0..s2.members.dim) + { + auto s2i=(*s2.members)[i]; + auto fdx=s2i.isFuncDeclaration(); + if(!fdx) continue; + auto t2=cast(TypeFunction)fdx.type; + assert(t2); + auto param=t2.searchByName(e.ident.toString); + if(!param) continue; + return param; + } + } + return null; + } + + auto param=findParamByName(); + assert(param); // CHECKME + auto type=param.type; + auto type2=type.trySemantic(e.loc, sc); + if(type2.ty == Ttuple) + { + TypeTuple type3 = cast(TypeTuple)type2; + auto dim=type3.arguments?type3.arguments.dim:0; + e.type = Type.tstring.sarrayOf(dim); + } + else + { + e.type = Type.tstring; + } result = e; } @@ -10137,3 +10193,38 @@ private bool checkFunctionAttributes(Expression exp, Scope* sc, FuncDeclaration return error; } } + +// Get source code for `expr` +private const(char)[] getArgSring(ref Expression expr, Scope* sc){ + if(global.params.disposeSrcContent) + { + // NOTE: this does some partial constant folding (eg: 1+1+1 => 2+1), not 100% faithful to source code; also, doens't this expands special tokens like __FILE__ + auto temp = expr.toChars(); + // TODO: see if prettystring works? + return temp[0..temp.strlen()]; + } + else + { + auto temp = sc._module.srcfile; + assert(temp.buffer !is null); + auto arg_loc = expr.loc; + auto txt = cast(char[]) temp.buffer[arg_loc.bytes..temp.len]; + scope parser = new Parser!ASTCodegen(sc._module, txt, false); + parser.nextToken(); + // NOTE: parseExpression() would match `a, b` instead of just `a` in `fun(a, b)` + auto expr_parsed = parser.parseAssignExp(); + assert(!parser.errors); + // strip trailing whitespace; TODO: stripRight + auto end = parser.token.loc.bytes; + while(end > 0) + { + auto c = txt.ptr[end-1]; + if(c == ' ' || c == '\n' || c == '\r') + end--; + else + break; + } + // CHECKME: we keep files contenst in memory until end of semantic analysis so should be fine to use a slice; if not, use `OutBuffer` + return txt[0..end]; + } +} diff --git a/src/dmd/id.d b/src/dmd/id.d index 10c29b25fe2d..da2762071848 100644 --- a/src/dmd/id.d +++ b/src/dmd/id.d @@ -395,6 +395,7 @@ immutable Msgtable[] msgtable = { "getUnitTests" }, { "getVirtualIndex" }, { "getPointerBitmap" }, + { "getSource" }, // For C++ mangling { "allocator" }, diff --git a/src/dmd/mtype.d b/src/dmd/mtype.d index 6923d66b4a02..668d26a7891c 100644 --- a/src/dmd/mtype.d +++ b/src/dmd/mtype.d @@ -5494,6 +5494,14 @@ extern (C++) final class TypeFunction : TypeNext this.trust = TRUST.trusted; } + extern(D) Parameter searchByName(const(char)[] name){ + foreach(i;0..parameters.dim){ + auto pi=(*parameters)[i]; + if(pi.ident && pi.ident.toString() == name) return pi; + } + return null; + } + static TypeFunction create(Parameters* parameters, Type treturn, int varargs, LINK linkage, StorageClass stc = 0) { return new TypeFunction(parameters, treturn, varargs, linkage, stc); diff --git a/src/dmd/parse.d b/src/dmd/parse.d index 1397c9c680d2..66a3543f11f2 100644 --- a/src/dmd/parse.d +++ b/src/dmd/parse.d @@ -69,7 +69,7 @@ __gshared PREC[TOK.max_] precedence = TOK.classReference : PREC.primary, TOK.file : PREC.primary, TOK.fileFullPath : PREC.primary, - TOK.argString : PREC.primary, + TOK.getSource : PREC.primary, TOK.line : PREC.primary, TOK.moduleString : PREC.primary, TOK.functionString : PREC.primary, @@ -2042,8 +2042,8 @@ final class Parser(AST) : Lexer tiargs.push(ea); break; } - case TOK.argString: - error("illegal use of " ~TOK.argString); + case TOK.getSource: + error("illegal use of " ~TOK.getSource); break; default: error("template argument expected following `!`"); @@ -6253,7 +6253,7 @@ final class Parser(AST) : Lexer /***************************************** * Parses default argument initializer expression that is an assign expression, - * with special handling for __FILE__, __FILE_DIR__, __LINE__, __MODULE__, __FUNCTION__, __PRETTY_FUNCTION__, __ARG_STRING__. + * with special handling for __FILE__, __FILE_DIR__, __LINE__, __MODULE__, __FUNCTION__, __PRETTY_FUNCTION__. */ AST.Expression parseDefaultInitExp() { @@ -6283,36 +6283,15 @@ final class Parser(AST) : Lexer } } - if(token.value == TOK.argString) + AST.Expression e = parseAssignExp(); + if(auto e2=cast(AST.TraitsExp)e)// TODO: why not just TraitsExp? { - AST.ArgStringInitExp e = new AST.ArgStringInitExp(token.loc); - nextToken(); - if(token.value == TOK.not) - { - if(peekNext() != TOK.identifier) + if(e2.ident==Id.getSource) { - error("identifier expected following `__ARG_STRING__!`"); - return null; + import dmd.traits:traitsGetSource; + return traitsGetSource(e2); } - - nextToken(); - e.setIdent(token.ident); - nextToken(); - } else { - error("expected !"); - return null; - } - - if (token.value == TOK.comma || token.value == TOK.rightParentheses) - { - return e; - } else { - error("expected , or )"); - return null; - } } - - AST.Expression e = parseAssignExp(); return e; } diff --git a/src/dmd/tokens.d b/src/dmd/tokens.d index 6198e4e133b3..cabf9a35f91a 100644 --- a/src/dmd/tokens.d +++ b/src/dmd/tokens.d @@ -291,7 +291,7 @@ enum TOK : int line, file, fileFullPath, - argString, + getSource, moduleString, functionString, prettyFunction, @@ -448,7 +448,7 @@ extern (C++) struct Token TOK.overloadSet: "__overloadset", TOK.file: "__FILE__", TOK.fileFullPath: "__FILE_FULL_PATH__", - TOK.argString: "__ARG_STRING__", + TOK.getSource: "__getSource", // TODO: do we need this or can we reuse TOK.traits? TOK.line: "__LINE__", TOK.moduleString: "__MODULE__", TOK.functionString: "__FUNCTION__", @@ -943,7 +943,7 @@ private immutable TOK[] keywords = TOK.overloadSet, TOK.file, TOK.fileFullPath, - TOK.argString, + TOK.getSource, TOK.line, TOK.moduleString, TOK.functionString, diff --git a/src/dmd/traits.d b/src/dmd/traits.d index ab2663e7906c..c092f2a2d993 100644 --- a/src/dmd/traits.d +++ b/src/dmd/traits.d @@ -143,6 +143,7 @@ shared static this() "getUnitTests", "getVirtualIndex", "getPointerBitmap", + "getSource", ]; traitsStringTable._init(40); @@ -412,6 +413,31 @@ extern (C++) Expression pointerBitmap(TraitsExp e) return ale; } +/** +* __traits(getSource, symbol) => get symbol source code (string[T.length] or string) +*/ +extern (C++) Expression traitsGetSource(TraitsExp e) +{ + if (!e.args || e.args.dim < 1|| e.args.dim > 2) + { + error(e.loc, "TODO"); + return new ErrorExp(); + } + auto ret = new ArgStringInitExp(e.loc); + auto ident_expr=(*e.args)[0]; + auto t=isType(ident_expr); + if(!t){ + error(e.loc, "TODO.2"); + return new ErrorExp(); + } + auto name=ident_expr.toChars(); + // TODO: lookup? + auto id = Identifier.idPool(name, name.strlen); + ret.setIdent(id); + ret.exp=e; + return ret; +} + extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) { static if (LOGSEMANTIC) @@ -422,6 +448,7 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) if (e.ident != Id.compiles && e.ident != Id.isSame && e.ident != Id.identifier && + e.ident != Id.getSource && e.ident != Id.getProtection) { if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 1)) @@ -1599,6 +1626,12 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) return pointerBitmap(e); } + if (e.ident == Id.getSource) + { + e.error("`%s` unexpected in this context", Id.getSource); + return new ErrorExp(); + } + extern (D) void* trait_search_fp(const(char)* seed, ref int cost) { //printf("trait_search_fp('%s')\n", seed); diff --git a/src/dmd/typesem.d b/src/dmd/typesem.d index 481a9f3c56df..23347a133da6 100644 --- a/src/dmd/typesem.d +++ b/src/dmd/typesem.d @@ -714,6 +714,7 @@ private extern (C++) final class TypeSemanticVisitor : Visitor result = mtype; return; } + //printf("TypeFunction::semantic() this = %p\n", this); //printf("TypeFunction::semantic() %s, sc.stc = %llx, fargs = %p\n", toChars(), sc.stc, fargs); diff --git a/src/dmd/visitor.h b/src/dmd/visitor.h index d1790410d0d3..13f923ef6764 100644 --- a/src/dmd/visitor.h +++ b/src/dmd/visitor.h @@ -274,7 +274,7 @@ class IdentityExp; class CondExp; class DefaultInitExp; class FileInitExp; -class ArgnameInitExp; +class ArgStringInitExp; class LineInitExp; class ModuleInitExp; class FuncInitExp; @@ -496,7 +496,7 @@ class ParseTimeVisitor virtual void visit(FuncInitExp *e) { visit((DefaultInitExp *)e); } virtual void visit(PrettyFuncInitExp *e) { visit((DefaultInitExp *)e); } virtual void visit(FileInitExp *e) { visit((DefaultInitExp *)e); } - virtual void visit(ArgnameInitExp *e) { visit((DefaultInitExp *)e); } + virtual void visit(ArgStringInitExp *e) { visit((DefaultInitExp *)e); } virtual void visit(LineInitExp *e) { visit((DefaultInitExp *)e); } virtual void visit(ModuleInitExp *e) { visit((DefaultInitExp *)e); } diff --git a/test/runnable/testargnames.d b/test/runnable/testargnames.d index aa1c230c1626..e0edb5c62eb3 100644 --- a/test/runnable/testargnames.d +++ b/test/runnable/testargnames.d @@ -3,7 +3,10 @@ PERMUTE_ARGS: */ // to optionally avoid depending on phobos -enum easy_debug = true; +version (easy_debug) + enum easy_debug = true; +else + enum easy_debug = false; static if (easy_debug) { @@ -18,43 +21,62 @@ else } } -void check(string name, string expected, string file = __FILE__, int line = __LINE__) +string getLoc(string file = __FILE__, int line = __LINE__) { - if (name == expected) - return; - assert(0, text("expected {", expected, "} got {", name, "} at ", file, ":", line)); + return text(file, ":", line, " "); } -// Simple yet useful log function -string log(T)(T a, string name = __ARG_STRING__!a) +void check(string[] name, string[] expected, string file = __FILE__, int line = __LINE__) { - return text(name, ":", a); + assert(name.length == expected.length, text(getLoc(file, line), name, " ", expected)); + foreach (i; 0 .. name.length) + check(name[i], expected[i], file, line); +} + +void check(string name, string expected, string file = __FILE__, int line = __LINE__) +{ + if (name == expected) + return; + assert(0, text(getLoc(file, line), "expected {", expected, "} got {", name, "}")); } -void fun1(int a, string expected, string name = __ARG_STRING__!a, +void fun1(int a, string expected, string name = __traits(getSource, a), string file = __FILE__, int line = __LINE__) { check(name, expected, file, line); } void fun2(int a, string b, double c, string expected, - string name = __ARG_STRING__!b, string file = __FILE__, int line = __LINE__) + string name = __traits(getSource, b), string file = __FILE__, int line = __LINE__) { check(name, expected, file, line); } -void fun_UFCS(int a, string expected, string name = __ARG_STRING__!a, +void fun_UFCS(int a, string expected, string name = __traits(getSource, a), string file = __FILE__, int line = __LINE__) { check(name, expected, file, line); } -void fun_template(T)(T a, string expected, string name = __ARG_STRING__!a, +void fun_template(T)(T a, string expected, string name = __traits(getSource, a), string file = __FILE__, int line = __LINE__) { check(name, expected, file, line); } +auto fun_variadic(T...)(T a_var, string[T.length] names = __traits(getSource, a_var)) +{ + return names; +} + +// more complex variadic +auto fun_variadic2(T...)(int a0, T a1, int a2, int a3 = 1000, + string[T.length] names = __traits(getSource, a1), + string name_a2 = __traits(getSource, a2), int a6 = 10000) +{ + return names ~ "-" ~ name_a2; +} + struct A { int x = 1; @@ -65,11 +87,46 @@ struct A } } +// Simple yet useful log function +static if (easy_debug) +{ + + string log(T...)(T a, string[T.length] names = __traits(getSource, a), + string file = __FILE__, int line = __LINE__) + { + string ret = getLoc(file, line); + static foreach (i; 0 .. T.length) + { + ret ~= text(" ", names[i], ":", a[i]); + } + return ret; + } + + string logSimple(T...)(T a, string[T.length] names = __traits(getSource, a)) + { + import std.conv; + + return text(names, ": ", a); + } + +} + void main() { int a = 42; - check(log(1 + a), `1 + a:43`); + static if (easy_debug) + { + { + check(log(a), text(getLoc, ` a:42`)); + string b = "bar"; + check(log(a + 1, b), text(getLoc, ` a + 1:43 b:bar`)); + + double x = 1.5; + check(logSimple(x * 2, 'a'), `["x * 2", "'a'"]: 3a`); + check(logSimple(__LINE__), text(`["__LINE__"]: `, __LINE__)); + } + } fun1(41 + a, `41 + a`); @@ -82,7 +139,7 @@ void main() fun1(a + a + a, `a + a + a`); - // Checks that no constant folding happens, cf D20180130T161632. + // Checks that no constant folding happens fun1(1 + 1 + 2, `1 + 1 + 2`); static const int x = 44; @@ -100,6 +157,36 @@ void main() fun1(__LINE__, "__LINE__"); fun_template(__FILE__, "__FILE__"); + // variadic test + { + auto ret = fun_variadic(1 + 1 + 1, "foo"); + static assert(ret.length == 2); + check(ret, [`1 + 1 + 1`, `"foo"`]); + } + { + auto ret = fun_variadic(1 + 1 + 1); + static assert(ret.length == 1); + check(ret, [`1 + 1 + 1`]); + } + { + auto ret = fun_variadic(); + static assert(ret.length == 0); + check(ret, []); + } + // complex variadic test + { + check(fun_variadic2(-0, 11, 12, 13, 100), ["11", "12", "13", "-", "100"]); + check(fun_variadic2(-0, 11, 12, 100), ["11", "12", "-", "100"]); + check(fun_variadic2(-0, 11, 100), ["11", "-", "100"]); + // UFCS + check(0.fun_variadic2(11, 100), ["11", "-", "100"]); + // empty tuple + check(fun_variadic2(0, 100), ["-", "100"]); + + // explicit instantiation + check(fun_variadic2!int(0, 11, 100), ["11", "-", "100"]); + } + // Formatting intentionally bad to test behavior { // dfmt off From 6d6f5fd0e97a2b47a373152bf136e412c9c42387 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 6 Feb 2018 12:02:43 -0800 Subject: [PATCH 3/4] no need to make getSource a keyword --- src/dmd/parse.d | 4 ---- src/dmd/tokens.d | 11 ++++++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/dmd/parse.d b/src/dmd/parse.d index 66a3543f11f2..996fd583841b 100644 --- a/src/dmd/parse.d +++ b/src/dmd/parse.d @@ -69,7 +69,6 @@ __gshared PREC[TOK.max_] precedence = TOK.classReference : PREC.primary, TOK.file : PREC.primary, TOK.fileFullPath : PREC.primary, - TOK.getSource : PREC.primary, TOK.line : PREC.primary, TOK.moduleString : PREC.primary, TOK.functionString : PREC.primary, @@ -2042,9 +2041,6 @@ final class Parser(AST) : Lexer tiargs.push(ea); break; } - case TOK.getSource: - error("illegal use of " ~TOK.getSource); - break; default: error("template argument expected following `!`"); break; diff --git a/src/dmd/tokens.d b/src/dmd/tokens.d index cabf9a35f91a..7849150b784c 100644 --- a/src/dmd/tokens.d +++ b/src/dmd/tokens.d @@ -81,7 +81,7 @@ enum TOK : int delegatePointer, delegateFunctionPointer, - // 54 + // 54 // TODO: instead of these non robust numbers, use `cast(TOK) number` // Operators lessThan, greaterThan, @@ -291,7 +291,6 @@ enum TOK : int line, file, fileFullPath, - getSource, moduleString, functionString, prettyFunction, @@ -303,11 +302,13 @@ enum TOK : int vector, pound, - // 241 + // 240 interval, voidExpression, cantExpression, + getSource, + max_, } @@ -448,7 +449,6 @@ extern (C++) struct Token TOK.overloadSet: "__overloadset", TOK.file: "__FILE__", TOK.fileFullPath: "__FILE_FULL_PATH__", - TOK.getSource: "__getSource", // TODO: do we need this or can we reuse TOK.traits? TOK.line: "__LINE__", TOK.moduleString: "__MODULE__", TOK.functionString: "__FUNCTION__", @@ -593,6 +593,8 @@ extern (C++) struct Token TOK.interval: "interval", TOK.voidExpression: "voidexp", TOK.cantExpression: "cantexp", + + TOK.getSource: "getSource", ]; static assert(() { @@ -943,7 +945,6 @@ private immutable TOK[] keywords = TOK.overloadSet, TOK.file, TOK.fileFullPath, - TOK.getSource, TOK.line, TOK.moduleString, TOK.functionString, From 842b3ab3d3f8976457a254aa79cab95483b3d56c Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 6 Feb 2018 12:08:43 -0800 Subject: [PATCH 4/4] s/getSource/getCallerSource/ as suggested by @jacob-carlborg to avoid ambiguity with a future getSource --- changelog/argname.dd | 2 +- src/dmd/astbase.d | 2 +- src/dmd/expression.d | 2 +- src/dmd/id.d | 2 +- src/dmd/parse.d | 2 +- src/dmd/tokens.d | 4 ++-- src/dmd/traits.d | 10 +++++----- test/runnable/testargnames.d | 18 +++++++++--------- 8 files changed, 21 insertions(+), 21 deletions(-) diff --git a/changelog/argname.dd b/changelog/argname.dd index a02b7cd9bf99..60f0d7399a38 100644 --- a/changelog/argname.dd +++ b/changelog/argname.dd @@ -4,7 +4,7 @@ NOTE: it also works with UFCS, with tuples (string static array) and non-tuples Eg: --- -string logSimple(T...)(T a, string[T.length] names = __traits(getSource, a)) +string logSimple(T...)(T a, string[T.length] names = __traits(getCallerSource, a)) { import std.conv; return text(names, ": ", a); // see testargnames for a better log function diff --git a/src/dmd/astbase.d b/src/dmd/astbase.d index 98e1c8b3c10b..b20d4157f3dc 100644 --- a/src/dmd/astbase.d +++ b/src/dmd/astbase.d @@ -5252,7 +5252,7 @@ struct ASTBase { extern (D) this(Loc loc) { - super(loc, TOK.getSource, __traits(classInstanceSize, ArgStringInitExp)); + super(loc, TOK.getCallerSource, __traits(classInstanceSize, ArgStringInitExp)); } override void accept(Visitor v) diff --git a/src/dmd/expression.d b/src/dmd/expression.d index cc3e8ef1a720..88b780b39d5b 100644 --- a/src/dmd/expression.d +++ b/src/dmd/expression.d @@ -7135,7 +7135,7 @@ extern (C++) final class ArgStringInitExp : DefaultInitExp extern (D) this(const ref Loc loc) { - super(loc, TOK.getSource, __traits(classInstanceSize, ArgStringInitExp)); + super(loc, TOK.getCallerSource, __traits(classInstanceSize, ArgStringInitExp)); } void setIdent(Identifier ident) diff --git a/src/dmd/id.d b/src/dmd/id.d index da2762071848..30d24f83fb48 100644 --- a/src/dmd/id.d +++ b/src/dmd/id.d @@ -395,7 +395,7 @@ immutable Msgtable[] msgtable = { "getUnitTests" }, { "getVirtualIndex" }, { "getPointerBitmap" }, - { "getSource" }, + { "getCallerSource" }, // For C++ mangling { "allocator" }, diff --git a/src/dmd/parse.d b/src/dmd/parse.d index 996fd583841b..b692726270f7 100644 --- a/src/dmd/parse.d +++ b/src/dmd/parse.d @@ -6282,7 +6282,7 @@ final class Parser(AST) : Lexer AST.Expression e = parseAssignExp(); if(auto e2=cast(AST.TraitsExp)e)// TODO: why not just TraitsExp? { - if(e2.ident==Id.getSource) + if(e2.ident==Id.getCallerSource) { import dmd.traits:traitsGetSource; return traitsGetSource(e2); diff --git a/src/dmd/tokens.d b/src/dmd/tokens.d index 7849150b784c..7cbce82dbdbf 100644 --- a/src/dmd/tokens.d +++ b/src/dmd/tokens.d @@ -307,7 +307,7 @@ enum TOK : int voidExpression, cantExpression, - getSource, + getCallerSource, max_, } @@ -594,7 +594,7 @@ extern (C++) struct Token TOK.voidExpression: "voidexp", TOK.cantExpression: "cantexp", - TOK.getSource: "getSource", + TOK.getCallerSource: "getCallerSource", ]; static assert(() { diff --git a/src/dmd/traits.d b/src/dmd/traits.d index c092f2a2d993..a942bf625088 100644 --- a/src/dmd/traits.d +++ b/src/dmd/traits.d @@ -143,7 +143,7 @@ shared static this() "getUnitTests", "getVirtualIndex", "getPointerBitmap", - "getSource", + "getCallerSource", ]; traitsStringTable._init(40); @@ -414,7 +414,7 @@ extern (C++) Expression pointerBitmap(TraitsExp e) } /** -* __traits(getSource, symbol) => get symbol source code (string[T.length] or string) +* __traits(getCallerSource, symbol) => get symbol source code (string[T.length] or string) */ extern (C++) Expression traitsGetSource(TraitsExp e) { @@ -448,7 +448,7 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) if (e.ident != Id.compiles && e.ident != Id.isSame && e.ident != Id.identifier && - e.ident != Id.getSource && + e.ident != Id.getCallerSource && e.ident != Id.getProtection) { if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 1)) @@ -1626,9 +1626,9 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) return pointerBitmap(e); } - if (e.ident == Id.getSource) + if (e.ident == Id.getCallerSource) { - e.error("`%s` unexpected in this context", Id.getSource); + e.error("`%s` unexpected in this context", Id.getCallerSource); return new ErrorExp(); } diff --git a/test/runnable/testargnames.d b/test/runnable/testargnames.d index e0edb5c62eb3..207438cfb9ef 100644 --- a/test/runnable/testargnames.d +++ b/test/runnable/testargnames.d @@ -40,39 +40,39 @@ void check(string name, string expected, string file = __FILE__, int line = __LI assert(0, text(getLoc(file, line), "expected {", expected, "} got {", name, "}")); } -void fun1(int a, string expected, string name = __traits(getSource, a), +void fun1(int a, string expected, string name = __traits(getCallerSource, a), string file = __FILE__, int line = __LINE__) { check(name, expected, file, line); } void fun2(int a, string b, double c, string expected, - string name = __traits(getSource, b), string file = __FILE__, int line = __LINE__) + string name = __traits(getCallerSource, b), string file = __FILE__, int line = __LINE__) { check(name, expected, file, line); } -void fun_UFCS(int a, string expected, string name = __traits(getSource, a), +void fun_UFCS(int a, string expected, string name = __traits(getCallerSource, a), string file = __FILE__, int line = __LINE__) { check(name, expected, file, line); } -void fun_template(T)(T a, string expected, string name = __traits(getSource, a), +void fun_template(T)(T a, string expected, string name = __traits(getCallerSource, a), string file = __FILE__, int line = __LINE__) { check(name, expected, file, line); } -auto fun_variadic(T...)(T a_var, string[T.length] names = __traits(getSource, a_var)) +auto fun_variadic(T...)(T a_var, string[T.length] names = __traits(getCallerSource, a_var)) { return names; } // more complex variadic auto fun_variadic2(T...)(int a0, T a1, int a2, int a3 = 1000, - string[T.length] names = __traits(getSource, a1), - string name_a2 = __traits(getSource, a2), int a6 = 10000) + string[T.length] names = __traits(getCallerSource, a1), + string name_a2 = __traits(getCallerSource, a2), int a6 = 10000) { return names ~ "-" ~ name_a2; } @@ -91,7 +91,7 @@ struct A static if (easy_debug) { - string log(T...)(T a, string[T.length] names = __traits(getSource, a), + string log(T...)(T a, string[T.length] names = __traits(getCallerSource, a), string file = __FILE__, int line = __LINE__) { string ret = getLoc(file, line); @@ -102,7 +102,7 @@ static if (easy_debug) return ret; } - string logSimple(T...)(T a, string[T.length] names = __traits(getSource, a)) + string logSimple(T...)(T a, string[T.length] names = __traits(getCallerSource, a)) { import std.conv;