From 4679f239e4ce1b8399a4e9240e639d919fad0609 Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Tue, 20 Mar 2018 14:09:16 +0100 Subject: [PATCH] UDAs for function arguments --- changelog/uda-function-arguments.dd | 10 ++ src/dmd/astbase.d | 8 +- src/dmd/attrib.d | 1 + src/dmd/clone.d | 16 +-- src/dmd/cond.d | 2 +- src/dmd/declaration.d | 2 +- src/dmd/dsymbolsem.d | 8 ++ src/dmd/dtemplate.d | 2 +- src/dmd/expressionsem.d | 6 +- src/dmd/func.d | 2 +- src/dmd/hdrgen.d | 12 ++ src/dmd/mtype.d | 23 ++-- src/dmd/mtype.h | 4 +- src/dmd/parse.d | 64 +++++++-- src/dmd/semantic2.d | 15 ++- src/dmd/semantic3.d | 2 + src/dmd/statementsem.d | 24 ++-- src/dmd/traits.d | 40 ++++-- src/dmd/typesem.d | 9 +- .../compilable/extra-files/headerudamodule.di | 1 + test/compilable/testheaderudamodule.d | 2 + test/fail_compilation/fail10207.d | 2 +- test/fail_compilation/udaparams.d | 32 +++++ test/runnable/uda.d | 127 ++++++++++++++++++ 24 files changed, 347 insertions(+), 67 deletions(-) create mode 100644 changelog/uda-function-arguments.dd create mode 100644 test/fail_compilation/udaparams.d diff --git a/changelog/uda-function-arguments.dd b/changelog/uda-function-arguments.dd new file mode 100644 index 000000000000..ee1e46ee590d --- /dev/null +++ b/changelog/uda-function-arguments.dd @@ -0,0 +1,10 @@ +UDAs on function arguments are now supported + +User-defined attributes on function arguments behave analogous to existing UDAs: + +--- +void test(A)(@(22) A a) +{ + static assert([__traits(getAttributes, a)] == [22]); +} +--- diff --git a/src/dmd/astbase.d b/src/dmd/astbase.d index 76e4614a7ac0..c1ae51fcdab9 100644 --- a/src/dmd/astbase.d +++ b/src/dmd/astbase.d @@ -1727,15 +1727,17 @@ struct ASTBase Type type; Identifier ident; Expression defaultArg; + UserAttributeDeclaration userAttribDecl; // user defined attributes extern (D) alias ForeachDg = int delegate(size_t idx, Parameter param); - final extern (D) this(StorageClass storageClass, Type type, Identifier ident, Expression defaultArg) + final extern (D) this(StorageClass storageClass, Type type, Identifier ident, Expression defaultArg, UserAttributeDeclaration userAttribDecl) { this.storageClass = storageClass; this.type = type; this.ident = ident; this.defaultArg = defaultArg; + this.userAttribDecl = userAttribDecl; } static size_t dim(Parameters* parameters) @@ -1802,7 +1804,7 @@ struct ASTBase Parameter syntaxCopy() { - return new Parameter(storageClass, type ? type.syntaxCopy() : null, ident, defaultArg ? defaultArg.syntaxCopy() : null); + return new Parameter(storageClass, type ? type.syntaxCopy() : null, ident, defaultArg ? defaultArg.syntaxCopy() : null, userAttribDecl ? cast(UserAttributeDeclaration) userAttribDecl.syntaxCopy(null) : null); } void accept(Visitor v) @@ -3616,7 +3618,7 @@ struct ASTBase Expression e = (*exps)[i]; if (e.type.ty == Ttuple) e.error("cannot form tuple of tuples"); - auto arg = new Parameter(STC.undefined_, e.type, null, null); + auto arg = new Parameter(STC.undefined_, e.type, null, null, null); (*arguments)[i] = arg; } } diff --git a/src/dmd/attrib.d b/src/dmd/attrib.d index ee0c26d5007a..075584d8cbfc 100644 --- a/src/dmd/attrib.d +++ b/src/dmd/attrib.d @@ -1253,6 +1253,7 @@ extern (C++) final class CompileDeclaration : AttribDeclaration /*********************************************************** * User defined attributes look like: + * @foo(args, ...) * @(args, ...) */ extern (C++) final class UserAttributeDeclaration : AttribDeclaration diff --git a/src/dmd/clone.d b/src/dmd/clone.d index 129cc2c40801..bc14e1e5e97b 100644 --- a/src/dmd/clone.d +++ b/src/dmd/clone.d @@ -248,7 +248,7 @@ extern (C++) FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) } auto fparams = new Parameters(); - fparams.push(new Parameter(STC.nodtor, sd.type, Id.p, null)); + fparams.push(new Parameter(STC.nodtor, sd.type, Id.p, null, null)); auto tf = new TypeFunction(fparams, sd.handleType(), 0, LINK.d, stc | STC.ref_); auto fop = new FuncDeclaration(declLoc, Loc.initial, Id.assign, stc, tf); fop.storage_class |= STC.inference; @@ -498,7 +498,7 @@ extern (C++) FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc) /* const bool opEquals(ref const S s); */ auto parameters = new Parameters(); - parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, null, null)); + parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, null, null, null)); tfeqptr = new TypeFunction(parameters, Type.tbool, 0, LINK.d); tfeqptr.mod = MODFlags.const_; tfeqptr = cast(TypeFunction)tfeqptr.typeSemantic(Loc.initial, &scx); @@ -523,8 +523,8 @@ extern (C++) FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc) Loc declLoc; // loc is unnecessary so __xopEquals is never called directly Loc loc; // loc is unnecessary so errors are gagged auto parameters = new Parameters(); - parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.p, null)); - parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.q, null)); + parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.p, null, null)); + parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.q, null, null)); auto tf = new TypeFunction(parameters, Type.tbool, 0, LINK.d); Identifier id = Id.xopEquals; auto fop = new FuncDeclaration(declLoc, Loc.initial, id, STC.static_, tf); @@ -568,7 +568,7 @@ extern (C++) FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) /* const int opCmp(ref const S s); */ auto parameters = new Parameters(); - parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, null, null)); + parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, null, null, null)); tfcmpptr = new TypeFunction(parameters, Type.tint32, 0, LINK.d); tfcmpptr.mod = MODFlags.const_; tfcmpptr = cast(TypeFunction)tfcmpptr.typeSemantic(Loc.initial, &scx); @@ -643,8 +643,8 @@ extern (C++) FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) Loc declLoc; // loc is unnecessary so __xopCmp is never called directly Loc loc; // loc is unnecessary so errors are gagged auto parameters = new Parameters(); - parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.p, null)); - parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.q, null)); + parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.p, null, null)); + parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.q, null, null)); auto tf = new TypeFunction(parameters, Type.tint32, 0, LINK.d); Identifier id = Id.xopCmp; auto fop = new FuncDeclaration(declLoc, Loc.initial, id, STC.static_, tf); @@ -751,7 +751,7 @@ extern (C++) FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc) Loc declLoc; // loc is unnecessary so __xtoHash is never called directly Loc loc; // internal code should have no loc to prevent coverage auto parameters = new Parameters(); - parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.p, null)); + parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.p, null, null)); auto tf = new TypeFunction(parameters, Type.thash_t, 0, LINK.d, STC.nothrow_ | STC.trusted); Identifier id = Id.xtoHash; auto fop = new FuncDeclaration(declLoc, Loc.initial, id, STC.static_, tf); diff --git a/src/dmd/cond.d b/src/dmd/cond.d index 876253df0a8c..c4803d0686ff 100644 --- a/src/dmd/cond.d +++ b/src/dmd/cond.d @@ -291,7 +291,7 @@ extern (C++) final class StaticForeach : RootObject foreach (params; pparams) { auto p = aggrfe ? (*aggrfe.parameters)[i] : rangefe.prm; - params.push(new Parameter(p.storageClass, p.type, p.ident, null)); + params.push(new Parameter(p.storageClass, p.type, p.ident, null, null)); } } Expression[2] res; diff --git a/src/dmd/declaration.d b/src/dmd/declaration.d index b7502ef79d1a..296ee0f63001 100644 --- a/src/dmd/declaration.d +++ b/src/dmd/declaration.d @@ -613,7 +613,7 @@ extern (C++) final class TupleDeclaration : Declaration } else { - auto arg = new Parameter(0, t, null, null); + auto arg = new Parameter(0, t, null, null, null); } (*args)[i] = arg; if (!t.deco) diff --git a/src/dmd/dsymbolsem.d b/src/dmd/dsymbolsem.d index 1a24a7f7c9aa..236236724502 100644 --- a/src/dmd/dsymbolsem.d +++ b/src/dmd/dsymbolsem.d @@ -3574,6 +3574,14 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor genCmain(sc); assert(funcdecl.type.ty != Terror || funcdecl.errors); + + // semantic for parameters' UDAs + foreach (i; 0 .. Parameter.dim(f.parameters)) + { + Parameter param = Parameter.getNth(f.parameters, i); + if (param && param.userAttribDecl) + param.userAttribDecl.dsymbolSemantic(sc); + } } /// Do the semantic analysis on the external interface to the function. diff --git a/src/dmd/dtemplate.d b/src/dmd/dtemplate.d index b662b115c269..e9ae3df20872 100644 --- a/src/dmd/dtemplate.d +++ b/src/dmd/dtemplate.d @@ -6692,7 +6692,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol for (size_t i = 0; i < dim; i++) { Parameter arg = (*tt.arguments)[i]; - if (flags & 2 && arg.ident) + if (flags & 2 && (arg.ident || arg.userAttribDecl)) tiargs.insert(j + i, arg); else tiargs.insert(j + i, arg.type); diff --git a/src/dmd/expressionsem.d b/src/dmd/expressionsem.d index 55ec272d7af6..071b187b59a0 100644 --- a/src/dmd/expressionsem.d +++ b/src/dmd/expressionsem.d @@ -1098,7 +1098,7 @@ private bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type tthis, args.setDim(arguments.dim - nparams); for (size_t i = 0; i < arguments.dim - nparams; i++) { - auto arg = new Parameter(STC.in_, (*arguments)[nparams + i].type, null, null); + auto arg = new Parameter(STC.in_, (*arguments)[nparams + i].type, null, null, null); (*args)[i] = arg; } auto tup = new TypeTuple(args); @@ -3888,7 +3888,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor for (size_t i = 0; i < cd.baseclasses.dim; i++) { BaseClass* b = (*cd.baseclasses)[i]; - args.push(new Parameter(STC.in_, b.type, null, null)); + args.push(new Parameter(STC.in_, b.type, null, null, null)); } tded = new TypeTuple(args); } @@ -3935,7 +3935,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ if (e.tok2 == TOK.parameters && arg.defaultArg && arg.defaultArg.op == TOK.error) return setError(); - args.push(new Parameter(arg.storageClass, arg.type, (e.tok2 == TOK.parameters) ? arg.ident : null, (e.tok2 == TOK.parameters) ? arg.defaultArg : null)); + args.push(new Parameter(arg.storageClass, arg.type, (e.tok2 == TOK.parameters) ? arg.ident : null, (e.tok2 == TOK.parameters) ? arg.defaultArg : null, arg.userAttribDecl)); } tded = new TypeTuple(args); break; diff --git a/src/dmd/func.d b/src/dmd/func.d index 86a740a91a78..5fb684c147a7 100644 --- a/src/dmd/func.d +++ b/src/dmd/func.d @@ -2170,7 +2170,7 @@ extern (C++) class FuncDeclaration : Declaration Parameter p = null; if (canBuildResultVar()) { - p = new Parameter(STC.ref_ | STC.const_, f.nextOf(), Id.result, null); + p = new Parameter(STC.ref_ | STC.const_, f.nextOf(), Id.result, null, null); fparams.push(p); } auto tf = new TypeFunction(fparams, Type.tvoid, 0, LINK.d); diff --git a/src/dmd/hdrgen.d b/src/dmd/hdrgen.d index 37538108e59d..c864d99aa16e 100644 --- a/src/dmd/hdrgen.d +++ b/src/dmd/hdrgen.d @@ -3103,6 +3103,18 @@ public: //////////////////////////////////////////////////////////////////////////// override void visit(Parameter p) { + if (p.userAttribDecl) + { + buf.writestring("@"); + scope(exit) buf.writestring(" "); + + bool isAnonymous = p.userAttribDecl.atts.dim > 0 && (*p.userAttribDecl.atts)[0].op != TOK.call; + if (isAnonymous) + buf.writestring("("); + argsToBuffer(p.userAttribDecl.atts); + if (isAnonymous) + buf.writestring(")"); + } if (p.storageClass & STC.auto_) buf.writestring("auto "); if (p.storageClass & STC.return_) diff --git a/src/dmd/mtype.d b/src/dmd/mtype.d index f5c51902ce9a..6e06ffb9b31d 100644 --- a/src/dmd/mtype.d +++ b/src/dmd/mtype.d @@ -21,6 +21,7 @@ import core.stdc.string; import dmd.access; import dmd.aggregate; import dmd.arraytypes; +import dmd.attrib; import dmd.gluelayer; import dmd.complex; import dmd.dclass; @@ -4757,7 +4758,7 @@ extern (C++) final class TypeFunction : TypeNext continue; if (params == parameters) params = parameters.copy(); - (*params)[i] = new Parameter(p.storageClass, t, null, null); + (*params)[i] = new Parameter(p.storageClass, t, null, null, null); } if (next == tret && params == parameters) return this; @@ -6555,7 +6556,7 @@ extern (C++) final class TypeTuple : Type Expression e = (*exps)[i]; if (e.type.ty == Ttuple) e.error("cannot form tuple of tuples"); - auto arg = new Parameter(STC.undefined_, e.type, null, null); + auto arg = new Parameter(STC.undefined_, e.type, null, null, null); (*arguments)[i] = arg; } } @@ -6581,15 +6582,15 @@ extern (C++) final class TypeTuple : Type { super(Ttuple); arguments = new Parameters(); - arguments.push(new Parameter(0, t1, null, null)); + arguments.push(new Parameter(0, t1, null, null, null)); } extern (D) this(Type t1, Type t2) { super(Ttuple); arguments = new Parameters(); - arguments.push(new Parameter(0, t1, null, null)); - arguments.push(new Parameter(0, t2, null, null)); + arguments.push(new Parameter(0, t1, null, null, null)); + arguments.push(new Parameter(0, t2, null, null, null)); } override const(char)* kind() const @@ -6751,27 +6752,31 @@ extern (C++) final class TypeNull : Type */ extern (C++) final class Parameter : RootObject { + import dmd.attrib : UserAttributeDeclaration; + StorageClass storageClass; Type type; Identifier ident; Expression defaultArg; + UserAttributeDeclaration userAttribDecl; // user defined attributes - extern (D) this(StorageClass storageClass, Type type, Identifier ident, Expression defaultArg) + extern (D) this(StorageClass storageClass, Type type, Identifier ident, Expression defaultArg, UserAttributeDeclaration userAttribDecl) { this.type = type; this.ident = ident; this.storageClass = storageClass; this.defaultArg = defaultArg; + this.userAttribDecl = userAttribDecl; } - static Parameter create(StorageClass storageClass, Type type, Identifier ident, Expression defaultArg) + static Parameter create(StorageClass storageClass, Type type, Identifier ident, Expression defaultArg, UserAttributeDeclaration userAttribDecl) { - return new Parameter(storageClass, type, ident, defaultArg); + return new Parameter(storageClass, type, ident, defaultArg, userAttribDecl); } Parameter syntaxCopy() { - return new Parameter(storageClass, type ? type.syntaxCopy() : null, ident, defaultArg ? defaultArg.syntaxCopy() : null); + return new Parameter(storageClass, type ? type.syntaxCopy() : null, ident, defaultArg ? defaultArg.syntaxCopy() : null, userAttribDecl ? cast(UserAttributeDeclaration) userAttribDecl.syntaxCopy(null) : null); } /**************************************************** diff --git a/src/dmd/mtype.h b/src/dmd/mtype.h index 5281130e0e4b..1250ed7d8daa 100644 --- a/src/dmd/mtype.h +++ b/src/dmd/mtype.h @@ -842,8 +842,10 @@ class Parameter : public RootObject Type *type; Identifier *ident; Expression *defaultArg; + UserAttributeDeclaration *userAttribDecl; // user defined attributes - static Parameter *create(StorageClass storageClass, Type *type, Identifier *ident, Expression *defaultArg); + static Parameter *create(StorageClass storageClass, Type *type, Identifier *ident, + Expression *defaultArg, UserAttributeDeclaration *userAttribDecl); Parameter *syntaxCopy(); Type *isLazyArray(); // kludge for template.isType() diff --git a/src/dmd/parse.d b/src/dmd/parse.d index 64788b4f1fa9..fd76046ab731 100644 --- a/src/dmd/parse.d +++ b/src/dmd/parse.d @@ -1424,7 +1424,7 @@ final class Parser(AST) : Lexer // Disallow: // void function() @uda fp; // () @uda { return 1; } - error("user defined attributes cannot appear as postfixes"); + error("user-defined attributes cannot appear as postfixes"); } continue; } @@ -2761,9 +2761,10 @@ final class Parser(AST) : Lexer StorageClass storageClass = 0; StorageClass stc; AST.Expression ae; - + AST.Expressions* udas = null; for (; 1; nextToken()) { + L3: switch (token.value) { case TOK.rightParentheses: @@ -2797,7 +2798,25 @@ final class Parser(AST) : Lexer goto default; stc = AST.STC.wild; goto L2; - + case TOK.at: + { + AST.Expressions* exps = null; + StorageClass stc2 = parseAttribute(&exps); + if (stc2 == AST.STC.property || stc2 == AST.STC.nogc || stc2 == AST.STC.disable || stc2 == AST.STC.safe || stc2 == AST.STC.trusted || stc2 == AST.STC.system) + { + error("`@%s` attribute for function parameter is not supported", token.toChars()); + } + else + { + udas = AST.UserAttributeDeclaration.concat(udas, exps); + } + if (token.value == TOK.dotDotDot) + error("variadic parameter cannot have user-defined attributes"); + if (stc2) + nextToken(); + goto L3; + // Don't call nextToken again. + } case TOK.in_: stc = AST.STC.in_; goto L2; @@ -2872,6 +2891,7 @@ final class Parser(AST) : Lexer } default: { + int i = 1; // Deprecated in 2018-04. // Change to error in 2019-04. // @@@DEPRECATED_2019-04@@@. @@ -2919,6 +2939,28 @@ final class Parser(AST) : Lexer if (hasdefault) error("default argument expected for `%s`", ai ? ai.toChars() : at.toChars()); } + auto param = new AST.Parameter(storageClass, at, ai, ae, null); + if (udas) + { + auto a = new AST.Dsymbols(); + auto udad = new AST.UserAttributeDeclaration(udas, a); + param.userAttribDecl = udad; + } + if (token.value == TOK.at) + { + AST.Expressions* exps = null; + StorageClass stc2 = parseAttribute(&exps); + if (stc2 == AST.STC.property || stc2 == AST.STC.nogc || stc2 == AST.STC.disable || stc2 == AST.STC.safe || stc2 == AST.STC.trusted || stc2 == AST.STC.system) + { + error("`@%s` attribute for function parameter is not supported", token.toChars()); + } + else + { + error("user-defined attributes cannot appear as postfixes", token.toChars()); + } + if (stc2) + nextToken(); + } if (token.value == TOK.dotDotDot) { /* This is: @@ -2927,11 +2969,11 @@ final class Parser(AST) : Lexer if (storageClass & (AST.STC.out_ | AST.STC.ref_)) error("variadic argument cannot be `out` or `ref`"); varargs = 2; - parameters.push(new AST.Parameter(storageClass, at, ai, ae)); + parameters.push(param); nextToken(); break; } - parameters.push(new AST.Parameter(storageClass, at, ai, ae)); + parameters.push(param); if (token.value == TOK.comma) { nextToken(); @@ -4234,7 +4276,7 @@ final class Parser(AST) : Lexer parseStorageClasses(storage_class, link, setAlignment, ealign, udas); if (udas) - error("user defined attributes not allowed for `%s` declarations", Token.toChars(tok)); + error("user-defined attributes not allowed for `%s` declarations", Token.toChars(tok)); t = parseType(); v = new AST.AliasDeclaration(loc, ident, t); @@ -4407,7 +4449,7 @@ final class Parser(AST) : Lexer */ if (udas) - error("user defined attributes not allowed for `%s` declarations", Token.toChars(tok)); + error("user-defined attributes not allowed for `%s` declarations", Token.toChars(tok)); if (token.value == TOK.assign) { @@ -4637,7 +4679,7 @@ final class Parser(AST) : Lexer parameters = new AST.Parameters(); Identifier id = Identifier.generateId("__T"); AST.Type t = new AST.TypeIdentifier(loc, id); - parameters.push(new AST.Parameter(0, t, token.ident, null)); + parameters.push(new AST.Parameter(0, t, token.ident, null, null)); tpl = new AST.TemplateParameters(); AST.TemplateParameter tp = new AST.TemplateTypeParameter(loc, id, null, null); @@ -5032,7 +5074,7 @@ final class Parser(AST) : Lexer if (!ai) error("no identifier for declarator `%s`", at.toChars()); Larg: - auto p = new AST.Parameter(storageClass, at, ai, null); + auto p = new AST.Parameter(storageClass, at, ai, null, null); parameters.push(p); if (token.value == TOK.comma) { @@ -5576,14 +5618,14 @@ final class Parser(AST) : Lexer AST.Type at = null; // infer parameter type nextToken(); check(TOK.assign); - param = new AST.Parameter(storageClass, at, ai, null); + param = new AST.Parameter(storageClass, at, ai, null, null); } else if (isDeclaration(&token, NeedDeclaratorId.must, TOK.assign, null)) { Identifier ai; AST.Type at = parseType(&ai); check(TOK.assign); - param = new AST.Parameter(storageClass, at, ai, null); + param = new AST.Parameter(storageClass, at, ai, null, null); } condition = parseExpression(); diff --git a/src/dmd/semantic2.d b/src/dmd/semantic2.d index a24bd6932469..260569e9e919 100644 --- a/src/dmd/semantic2.d +++ b/src/dmd/semantic2.d @@ -354,7 +354,6 @@ private extern(C++) final class Semantic2Visitor : Visitor if (fd.semanticRun >= PASS.semantic2done) return; assert(fd.semanticRun <= PASS.semantic2); - fd.semanticRun = PASS.semantic2; //printf("FuncDeclaration::semantic2 [%s] fd0 = %s %s\n", loc.toChars(), toChars(), type.toChars()); @@ -447,13 +446,25 @@ private extern(C++) final class Semantic2Visitor : Visitor return 0; }); } - objc.setSelector(fd, sc); objc.validateSelector(fd); if (ClassDeclaration cd = fd.parent.isClassDeclaration()) { objc.checkLinkage(fd); } + if (!fd.type || fd.type.ty != Tfunction) + return; + TypeFunction f = cast(TypeFunction) fd.type; + if (!f.parameters) + return; + size_t nparams = Parameter.dim(f.parameters); + //semantic for parameters' UDAs + foreach (i; 0..nparams) + { + Parameter param = Parameter.getNth(f.parameters, i); + if (param && param.userAttribDecl) + param.userAttribDecl.semantic2(sc); + } } override void visit(Import i) diff --git a/src/dmd/semantic3.d b/src/dmd/semantic3.d index 6d4a21c27804..c559f252cc95 100644 --- a/src/dmd/semantic3.d +++ b/src/dmd/semantic3.d @@ -467,6 +467,8 @@ private extern(C++) final class Semantic3Visitor : Visitor funcdecl.parameters.push(v); funcdecl.localsymtab.insert(v); v.parent = funcdecl; + if (fparam.userAttribDecl) + v.userAttribDecl = fparam.userAttribDecl; } } diff --git a/src/dmd/statementsem.d b/src/dmd/statementsem.d index a6a57f163a31..21252af396c2 100644 --- a/src/dmd/statementsem.d +++ b/src/dmd/statementsem.d @@ -1669,14 +1669,14 @@ private extern (C++) final class StatementSemanticVisitor : Visitor if (!fdapply[i]) { auto params = new Parameters(); - params.push(new Parameter(0, Type.tvoid.pointerTo(), null, null)); - params.push(new Parameter(STC.in_, Type.tsize_t, null, null)); + params.push(new Parameter(0, Type.tvoid.pointerTo(), null, null, null)); + params.push(new Parameter(STC.in_, Type.tsize_t, null, null, null)); auto dgparams = new Parameters(); - dgparams.push(new Parameter(0, Type.tvoidptr, null, null)); + dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); if (dim == 2) - dgparams.push(new Parameter(0, Type.tvoidptr, null, null)); + dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); fldeTy[i] = new TypeDelegate(new TypeFunction(dgparams, Type.tint32, 0, LINK.d)); - params.push(new Parameter(0, fldeTy[i], null, null)); + params.push(new Parameter(0, fldeTy[i], null, null, null)); fdapply[i] = FuncDeclaration.genCfunc(params, Type.tint32, name[i]); } @@ -1739,13 +1739,13 @@ private extern (C++) final class StatementSemanticVisitor : Visitor FuncDeclaration fdapply; TypeDelegate dgty; auto params = new Parameters(); - params.push(new Parameter(STC.in_, tn.arrayOf(), null, null)); + params.push(new Parameter(STC.in_, tn.arrayOf(), null, null, null)); auto dgparams = new Parameters(); - dgparams.push(new Parameter(0, Type.tvoidptr, null, null)); + dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); if (dim == 2) - dgparams.push(new Parameter(0, Type.tvoidptr, null, null)); + dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); dgty = new TypeDelegate(new TypeFunction(dgparams, Type.tint32, 0, LINK.d)); - params.push(new Parameter(0, dgty, null, null)); + params.push(new Parameter(0, dgty, null, null, null)); fdapply = FuncDeclaration.genCfunc(params, Type.tint32, fdname.ptr); if (tab.ty == Tsarray) @@ -1915,7 +1915,7 @@ else Statement s = new ExpStatement(Loc.initial, v); fs._body = new CompoundStatement(fs.loc, s, fs._body); } - params.push(new Parameter(stc, p.type, id, null)); + params.push(new Parameter(stc, p.type, id, null, null)); } // https://issues.dlang.org/show_bug.cgi?id=13840 // Throwable nested function inside nothrow function is acceptable. @@ -3521,7 +3521,7 @@ else cs.push(new ExpStatement(ss.loc, tmp)); auto args = new Parameters(); - args.push(new Parameter(0, ClassDeclaration.object.type, null, null)); + args.push(new Parameter(0, ClassDeclaration.object.type, null, null, null)); FuncDeclaration fdenter = FuncDeclaration.genCfunc(args, Type.tvoid, Id.monitorenter); Expression e = new CallExp(ss.loc, fdenter, new VarExp(ss.loc, tmp)); @@ -3563,7 +3563,7 @@ else cs.push(new ExpStatement(ss.loc, v)); auto args = new Parameters(); - args.push(new Parameter(0, t.pointerTo(), null, null)); + args.push(new Parameter(0, t.pointerTo(), null, null, null)); FuncDeclaration fdenter = FuncDeclaration.genCfunc(args, Type.tvoid, Id.criticalenter, STC.nothrow_); Expression int0 = new IntegerExp(ss.loc, dinteger_t(0), Type.tint8); diff --git a/src/dmd/traits.d b/src/dmd/traits.d index c66e613bf2ae..eae1f2db387a 100644 --- a/src/dmd/traits.d +++ b/src/dmd/traits.d @@ -17,6 +17,7 @@ import core.stdc.string; import dmd.aggregate; import dmd.arraytypes; +import dmd.attrib; import dmd.canthrow; import dmd.dclass; import dmd.declaration; @@ -424,7 +425,8 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) if (e.ident != Id.compiles && e.ident != Id.isSame && e.ident != Id.identifier && - e.ident != Id.getProtection) + e.ident != Id.getProtection && + e.ident != Id.getAttributes) { if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 1)) return new ErrorExp(); @@ -715,8 +717,12 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) Identifier id; if (auto po = isParameter(o)) { + if (!po.ident) + { + e.error("argument `%s` has no identifier", po.type.toChars()); + return new ErrorExp(); + } id = po.ident; - assert(id); } else { @@ -1048,12 +1054,34 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) } if (e.ident == Id.getAttributes) { + /* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that + * a symbol should not be folded to a constant. + * Bit 1 means don't convert Parameter to Type if Parameter has an identifier + */ + if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 3)) + return new ErrorExp(); + if (dim != 1) return dimError(1); auto o = (*e.args)[0]; + auto po = isParameter(o); auto s = getDsymbolWithoutExpCtx(o); - if (!s) + UserAttributeDeclaration udad = null; + if (po) + { + udad = po.userAttribDecl; + } + else if (s) + { + if (s.isImport()) + { + s = s.isImport().mod; + } + //printf("getAttributes %s, attrs = %p, scope = %p\n", s.toChars(), s.userAttribDecl, s.scope); + udad = s.userAttribDecl; + } + else { version (none) { @@ -1067,13 +1095,7 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) e.error("first argument is not a symbol"); return new ErrorExp(); } - if (auto imp = s.isImport()) - { - s = imp.mod; - } - //printf("getAttributes %s, attrs = %p, scope = %p\n", s.toChars(), s.userAttribDecl, s.scope); - auto udad = s.userAttribDecl; auto exps = udad ? udad.getAttributes() : new Expressions(); auto tup = new TupleExp(e.loc, exps); return tup.expressionSemantic(sc); diff --git a/src/dmd/typesem.d b/src/dmd/typesem.d index a2af55f90650..8b5782c2dedc 100644 --- a/src/dmd/typesem.d +++ b/src/dmd/typesem.d @@ -71,7 +71,7 @@ private Type stripDefaultArgs(Type t) { Parameter p = (*params)[i]; Type ta = stripDefaultArgs(p.type); - if (ta != p.type || p.defaultArg || p.ident) + if (ta != p.type || p.defaultArg || p.ident || p.userAttribDecl) { if (params == parameters) { @@ -80,7 +80,8 @@ private Type stripDefaultArgs(Type t) foreach (j; 0 .. params.dim) (*params)[j] = (*parameters)[j]; } - (*params)[i] = new Parameter(p.storageClass, ta, null, null); + StorageClass stc = p.storageClass & (~STC.auto_); // issue 14656 + (*params)[i] = new Parameter(stc, ta, null, null, null); } } } @@ -1100,7 +1101,7 @@ private extern (C++) final class TypeSemanticVisitor : Visitor } (*newparams)[j] = new Parameter( - stc, narg.type, narg.ident, narg.defaultArg); + stc, narg.type, narg.ident, narg.defaultArg, narg.userAttribDecl); } fparam.type = new TypeTuple(newparams); } @@ -2928,7 +2929,7 @@ private extern(C++) final class DotExpVisitor : Visitor if (fd_aaLen is null) { auto fparams = new Parameters(); - fparams.push(new Parameter(STC.in_, mt, null, null)); + fparams.push(new Parameter(STC.in_, mt, null, null, null)); fd_aaLen = FuncDeclaration.genCfunc(fparams, Type.tsize_t, Id.aaLen); TypeFunction tf = fd_aaLen.type.toTypeFunction(); tf.purity = PURE.const_; diff --git a/test/compilable/extra-files/headerudamodule.di b/test/compilable/extra-files/headerudamodule.di index 89b7fbc5fafa..a86bc238ae99 100644 --- a/test/compilable/extra-files/headerudamodule.di +++ b/test/compilable/extra-files/headerudamodule.di @@ -5,3 +5,4 @@ struct UDA int a; } void main(); +void foo(@(1) int bar, @UDA(2) string bebe); diff --git a/test/compilable/testheaderudamodule.d b/test/compilable/testheaderudamodule.d index a62f365b3186..b0d4515f96de 100644 --- a/test/compilable/testheaderudamodule.d +++ b/test/compilable/testheaderudamodule.d @@ -11,3 +11,5 @@ struct UDA } void main() {} + +void foo(@(1) int bar, @UDA(2) string bebe) {} diff --git a/test/fail_compilation/fail10207.d b/test/fail_compilation/fail10207.d index ac8b4eef11a8..3f09a27541a9 100644 --- a/test/fail_compilation/fail10207.d +++ b/test/fail_compilation/fail10207.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail10207.d(7): Error: user defined attributes not allowed for `alias` declarations +fail_compilation/fail10207.d(7): Error: user-defined attributes not allowed for `alias` declarations --- */ alias @Safe int __externC; diff --git a/test/fail_compilation/udaparams.d b/test/fail_compilation/udaparams.d new file mode 100644 index 000000000000..bcccd6978937 --- /dev/null +++ b/test/fail_compilation/udaparams.d @@ -0,0 +1,32 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/udaparams.d(18): Error: variadic parameter cannot have user-defined attributes +fail_compilation/udaparams.d(19): Error: variadic parameter cannot have user-defined attributes +fail_compilation/udaparams.d(21): Error: user-defined attributes cannot appear as postfixes +fail_compilation/udaparams.d(22): Error: user-defined attributes cannot appear as postfixes +fail_compilation/udaparams.d(23): Error: user-defined attributes cannot appear as postfixes +fail_compilation/udaparams.d(25): Error: `@safe` attribute for function parameter is not supported +fail_compilation/udaparams.d(26): Error: `@safe` attribute for function parameter is not supported +fail_compilation/udaparams.d(27): Error: `@safe` attribute for function parameter is not supported +fail_compilation/udaparams.d(30): Error: `@system` attribute for function parameter is not supported +fail_compilation/udaparams.d(31): Error: `@trusted` attribute for function parameter is not supported +fail_compilation/udaparams.d(32): Error: `@nogc` attribute for function parameter is not supported +--- +*/ + +void vararg1(int a, @(10) ...); +extern(C) void vararg2(int a, @(10) ...); + +void rhsuda(int a @(10)); +void rhsuda2(int @(10)); +void rhsuda3(int[] arr @(10) ...); + +void wrongAttr1(@safe int); +void wrongAttr2(@safe void function()); +void wrongAttr3(@safe void delegate()); + + +void test16(A)(A a @system); +void test16(A)(A a @trusted); +void test16(A)(A a @nogc); diff --git a/test/runnable/uda.d b/test/runnable/uda.d index c1782c90a7bf..3f09c0339ea5 100644 --- a/test/runnable/uda.d +++ b/test/runnable/uda.d @@ -467,6 +467,128 @@ static assert(__traits(getAttributes, FileData11844.member)[0](new FileData11844 /************************************************/ +template AliasSeq(T...) +{ + alias AliasSeq = T; +} + +template ParameterUDA(size_t p_num, func...) + if (func.length == 1 && is(typeof(func[0]) PT == __parameters) && PT.length > p_num) +{ + static if (is(typeof(func[0]) PT == __parameters)) + { + template Get(size_t i) + { + static if (//!isFunctionPointer!func && !isDelegate!func + // Parameters without UDA may yield CT error. + /*&&*/ is(typeof(__traits(getAttributes, PT[i..i+1]))x)) + { + alias Get = AliasSeq!(__traits(getAttributes, PT[i..i+1])); + } + else + { + alias Get = AliasSeq!(); + } + } + } + else + { + static assert(0, func[0].stringof ~ "is not a function"); + + // Define dummy entities to avoid pointless errors + template Get(size_t i) { alias Get = AliasSeq!(); } + alias PT = AliasSeq!(); + } + + alias ParameterUDA = Get!p_num; +} + +void test13x(@(10) int a, @(20) int, @(30) @(40) int[] arr...) {} + +void test13() +{ + static assert([ParameterUDA!(0, test13x)] == [10]); + static assert([ParameterUDA!(1, test13x)] == [20]); + static assert([ParameterUDA!(2, test13x)] == [30, 40]); +} + +template Test13t(F) +{ + static assert(!__traits(compiles, ParameterUDA!(0, F))); + static assert(!__traits(compiles, ParameterUDA!(1, F))); + static assert(!__traits(compiles, ParameterUDA!(2, F))); + enum Test13t = true; +} + +alias test13t = Test13t!(typeof(test13x)); + +enum Test14UDA1; + +struct Test14UDA2 +{ + string str; +} + +Test14UDA2 test14uda3(string name) +{ + return Test14UDA2(name); +} + +struct Test14UDA4(string v) +{ +} + +void test14x(@Test14UDA1 int, @Test14UDA2("1") int, @test14uda3("2") int, @Test14UDA4!"3" int) {} +void test14() +{ + static assert(is(ParameterUDA!(0, test14x)[0] == Test14UDA1)); + static assert(ParameterUDA!(1, test14x)[0] == Test14UDA2("1")); + static assert(ParameterUDA!(2, test14x)[0] == test14uda3("2")); + static assert(is(ParameterUDA!(3, test14x)[0] == Test14UDA4!"3")); +} + +void test15x(@(20) void delegate(int) @safe dg) +{ + static assert([__traits(getAttributes, dg)] == [20]); + static assert(is(typeof(dg) == void delegate(int) @safe)); +} + +void test16x(A)(@(22) A a) +{ + static assert([__traits(getAttributes, a)] == [22]); +} + +void test16() +{ + static assert([ParameterUDA!(0, test16x!int)] == [22]); +} + +void test17() +{ + void test17x(A)(@(23) A a) + { + static assert([__traits(getAttributes, a)] == [23]); + } + static assert([ParameterUDA!(0, test17x!int)] == [23]); +} + +void test18() +{ + void test18a(@(Tuple!(18, "a")) int a) + { + static assert(__traits(getAttributes, a) == Tuple!(18, "a")); + } + void test18b(@Tuple!(18, "b") int a) + { + static assert(__traits(getAttributes, a) == Tuple!(18, "b")); + } + + static assert(ParameterUDA!(0, test18a) == Tuple!(18, "a")); + static assert(ParameterUDA!(0, test18b) == Tuple!(18, "b")); +} + +/************************************************/ + int main() { test1(); @@ -482,6 +604,11 @@ int main() test11(); test12(); test9178(); + test13(); + test14(); + test16(); + test17(); + test18(); printf("Success\n"); return 0;