diff --git a/changelog/argname.dd b/changelog/argname.dd new file mode 100644 index 000000000000..60f0d7399a38 --- /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, with tuples (string static array) and non-tuples (string) + +Eg: +--- +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 +} + +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 294495aae2be..b20d4157f3dc 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.getCallerSource, __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..88b780b39d5b 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 { @@ -2441,6 +2463,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 +7124,57 @@ 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.getCallerSource, __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, string value) + { + Expression e = new StringExp(loc, cast(void*)value.ptr, value.length); + e = e.expressionSemantic(sc); + e = e.castTo(sc, type); + 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); + } +} + /*********************************************************** */ extern (C++) final class LineInitExp : DefaultInitExp diff --git a/src/dmd/expression.h b/src/dmd/expression.h index bf3c916391de..00db67512a58 100644 --- a/src/dmd/expression.h +++ b/src/dmd/expression.h @@ -1305,6 +1305,12 @@ class FileInitExp : public DefaultInitExp void accept(Visitor *v) { v->visit(this); } }; +class ArgStringInitExp : public DefaultInitExp +{ +public: + 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..baec7bd3db23 100644 --- a/src/dmd/expressionsem.d +++ b/src/dmd/expressionsem.d @@ -598,8 +598,96 @@ 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; + + TypeFunction orig = cast(TypeFunction)fd.originalType; + TypeFunction temp2 = cast(TypeFunction)fd.type; + assert(orig); + string[]values; + for(int u=0, u2=0; uvararg_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: 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; + } + + 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); + return true; + } + } + else + { + // __FILE__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__ + arg = arg.resolveLoc(loc, sc); + } + arguments.push(arg); nargs++; } @@ -9296,6 +9384,49 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = e; } + override void visit(ArgStringInitExp e) + { + 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; + } + override void visit(LineInitExp e) { e.type = Type.tint32; @@ -10062,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/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/id.d b/src/dmd/id.d index 10c29b25fe2d..30d24f83fb48 100644 --- a/src/dmd/id.d +++ b/src/dmd/id.d @@ -395,6 +395,7 @@ immutable Msgtable[] msgtable = { "getUnitTests" }, { "getVirtualIndex" }, { "getPointerBitmap" }, + { "getCallerSource" }, // For C++ mangling { "allocator" }, 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/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 c0885db914b8..b692726270f7 100644 --- a/src/dmd/parse.d +++ b/src/dmd/parse.d @@ -6249,7 +6249,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__. */ AST.Expression parseDefaultInitExp() { @@ -6278,7 +6278,16 @@ final class Parser(AST) : Lexer return e; } } + AST.Expression e = parseAssignExp(); + if(auto e2=cast(AST.TraitsExp)e)// TODO: why not just TraitsExp? + { + if(e2.ident==Id.getCallerSource) + { + import dmd.traits:traitsGetSource; + return traitsGetSource(e2); + } + } 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..7cbce82dbdbf 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, @@ -302,11 +302,13 @@ enum TOK : int vector, pound, - // 239 + // 240 interval, voidExpression, cantExpression, + getCallerSource, + max_, } @@ -591,6 +593,8 @@ extern (C++) struct Token TOK.interval: "interval", TOK.voidExpression: "voidexp", TOK.cantExpression: "cantexp", + + TOK.getCallerSource: "getCallerSource", ]; static assert(() { diff --git a/src/dmd/traits.d b/src/dmd/traits.d index ab2663e7906c..a942bf625088 100644 --- a/src/dmd/traits.d +++ b/src/dmd/traits.d @@ -143,6 +143,7 @@ shared static this() "getUnitTests", "getVirtualIndex", "getPointerBitmap", + "getCallerSource", ]; traitsStringTable._init(40); @@ -412,6 +413,31 @@ extern (C++) Expression pointerBitmap(TraitsExp e) return ale; } +/** +* __traits(getCallerSource, 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.getCallerSource && 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.getCallerSource) + { + e.error("`%s` unexpected in this context", Id.getCallerSource); + 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 922a2856430d..13f923ef6764 100644 --- a/src/dmd/visitor.h +++ b/src/dmd/visitor.h @@ -274,6 +274,7 @@ class IdentityExp; class CondExp; class DefaultInitExp; class FileInitExp; +class ArgStringInitExp; 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(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/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..207438cfb9ef --- /dev/null +++ b/test/runnable/testargnames.d @@ -0,0 +1,219 @@ +/* +PERMUTE_ARGS: +*/ + +// to optionally avoid depending on phobos +version (easy_debug) + enum easy_debug = true; +else + enum easy_debug = false; + +static if (easy_debug) +{ + import std.conv : text; + import core.stdc.stdio; +} +else +{ + string text(T...)(T a) + { + return ""; + } +} + +string getLoc(string file = __FILE__, int line = __LINE__) +{ + return text(file, ":", line, " "); +} + +void check(string[] name, string[] expected, string file = __FILE__, int line = __LINE__) +{ + 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 = __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(getCallerSource, b), string file = __FILE__, int line = __LINE__) +{ + check(name, expected, file, line); +} + +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(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(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(getCallerSource, a1), + string name_a2 = __traits(getCallerSource, a2), int a6 = 10000) +{ + return names ~ "-" ~ name_a2; +} + +struct A +{ + int x = 1; + + int myfun() + { + return x * x; + } +} + +// Simple yet useful log function +static if (easy_debug) +{ + + string log(T...)(T a, string[T.length] names = __traits(getCallerSource, 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(getCallerSource, a)) + { + import std.conv; + + return text(names, ": ", a); + } + +} + +void main() +{ + int a = 42; + + 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`); + + 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 + 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__"); + + // 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 + // 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 +}