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 4c52d41c47f6..059eb49ba4b7 100644 --- a/src/dmd/astbase.d +++ b/src/dmd/astbase.d @@ -1734,15 +1734,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) @@ -1809,7 +1811,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) @@ -3623,7 +3625,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 3203b69208ef..f4c110b2711c 100644 --- a/src/dmd/attrib.d +++ b/src/dmd/attrib.d @@ -1263,6 +1263,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 1b45934a7375..02f51c2374f3 100644 --- a/src/dmd/clone.d +++ b/src/dmd/clone.d @@ -255,7 +255,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; @@ -504,7 +504,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); @@ -529,8 +529,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); @@ -574,7 +574,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); @@ -649,8 +649,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); @@ -757,7 +757,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); @@ -1009,7 +1009,7 @@ private DtorDeclaration buildWindowsCppDtor(AggregateDeclaration ad, DtorDeclara // // TODO: if (del) delete (char*)this; // return (void*) this; // } - Parameter delparam = new Parameter(STC.undefined_, Type.tuns32, Identifier.idPool("del"), new IntegerExp(dtor.loc, 0, Type.tuns32)); + Parameter delparam = new Parameter(STC.undefined_, Type.tuns32, Identifier.idPool("del"), new IntegerExp(dtor.loc, 0, Type.tuns32), null); Parameters* params = new Parameters; params.push(delparam); auto ftype = new TypeFunction(params, Type.tvoidptr, false, LINK.cpp, dtor.storage_class); diff --git a/src/dmd/cond.d b/src/dmd/cond.d index 581f0062fef5..3fcc32e27158 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 3bcb599bba9e..8b8d739cf71f 100644 --- a/src/dmd/declaration.d +++ b/src/dmd/declaration.d @@ -621,7 +621,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 bdca7f9136bd..ecf54d901751 100644 --- a/src/dmd/dsymbolsem.d +++ b/src/dmd/dsymbolsem.d @@ -3581,6 +3581,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 bdc201317285..1d288ac304a6 100644 --- a/src/dmd/dtemplate.d +++ b/src/dmd/dtemplate.d @@ -6684,7 +6684,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 4fcf35cf08ac..34775aec387b 100644 --- a/src/dmd/expressionsem.d +++ b/src/dmd/expressionsem.d @@ -1106,7 +1106,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); @@ -3934,7 +3934,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); } @@ -3981,7 +3981,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 db71082713c7..64ab2e20e519 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 3f16309bbc13..f83cb36fb310 100644 --- a/src/dmd/hdrgen.d +++ b/src/dmd/hdrgen.d @@ -3114,6 +3114,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 24fa15f4a152..5cdbfb5c9763 100644 --- a/src/dmd/mtype.d +++ b/src/dmd/mtype.d @@ -20,6 +20,7 @@ import core.stdc.string; import dmd.aggregate; import dmd.arraytypes; +import dmd.attrib; import dmd.gluelayer; import dmd.dclass; import dmd.declaration; @@ -4615,7 +4616,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; @@ -6040,7 +6041,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; } } @@ -6066,15 +6067,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 @@ -6215,27 +6216,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 b3e8fe4b54a9..6b8fadefa3fa 100644 --- a/src/dmd/mtype.h +++ b/src/dmd/mtype.h @@ -821,8 +821,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 c64947b99609..62afdd17600b 100644 --- a/src/dmd/parse.d +++ b/src/dmd/parse.d @@ -1428,7 +1428,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; } @@ -2765,9 +2765,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: @@ -2801,7 +2802,27 @@ 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; @@ -2918,6 +2939,30 @@ 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: @@ -2926,11 +2971,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(); @@ -4271,6 +4316,23 @@ final class Parser(AST) : Lexer tpl = parseTemplateParameterList(); check(TOK.assign); + bool hasParsedAttributes; + void parseAttributes() + { + if (hasParsedAttributes) // only parse once + return; + hasParsedAttributes = true; + udas = null; + storage_class = AST.STC.undefined_; + link = linkage; + setAlignment = false; + ealign = null; + parseStorageClasses(storage_class, link, setAlignment, ealign, udas); + } + + if (token.value == TOK.at) + parseAttributes; + AST.Declaration v; if (token.value == TOK.function_ || token.value == TOK.delegate_ || @@ -4289,21 +4351,30 @@ final class Parser(AST) : Lexer // identifier => expression AST.Dsymbol s = parseFunctionLiteral(); + + if (udas !is null) + { + if (storage_class != 0) + error("Cannot put a storage-class in an alias declaration."); + // parseAttributes shouldn't have set these variables + assert(link == linkage && !setAlignment && ealign is null); + auto tpl_ = cast(AST.TemplateDeclaration) s; + assert(tpl_ !is null && tpl_.members.dim == 1); + auto fd = cast(AST.FuncLiteralDeclaration) (*tpl_.members)[0]; + auto tf = cast(AST.TypeFunction) fd.type; + assert(tf.parameters.dim > 0); + auto as = new AST.Dsymbols(); + (*tf.parameters)[0].userAttribDecl = new AST.UserAttributeDeclaration(udas, as); + } + v = new AST.AliasDeclaration(loc, ident, s); } else { + parseAttributes(); // StorageClasses type - - storage_class = AST.STC.undefined_; - link = linkage; - setAlignment = false; - ealign = null; - udas = null; - 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); @@ -4476,7 +4547,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) { @@ -4706,7 +4777,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); @@ -5101,7 +5172,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) { @@ -5645,14 +5716,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 38458ed1785c..768422dd4b1b 100644 --- a/src/dmd/semantic2.d +++ b/src/dmd/semantic2.d @@ -353,7 +353,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()); @@ -446,13 +445,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 3128a8af576a..d35b7d0ce09f 100644 --- a/src/dmd/semantic3.d +++ b/src/dmd/semantic3.d @@ -466,6 +466,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 01790f61e26d..58830eac4c52 100644 --- a/src/dmd/statementsem.d +++ b/src/dmd/statementsem.d @@ -1667,14 +1667,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]); } @@ -1737,13 +1737,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) @@ -1913,7 +1913,7 @@ else Statement s = new ExpStatement(fs.loc, 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 51b1d9b8a1dd..914005d53f3d 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; @@ -425,7 +426,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(); @@ -719,8 +721,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 { @@ -1079,12 +1085,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) { @@ -1098,13 +1126,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 f86667ff34e0..15fa340310c5 100644 --- a/src/dmd/typesem.d +++ b/src/dmd/typesem.d @@ -438,8 +438,8 @@ private Type stripDefaultArgs(Type t) static Parameter stripParameter(Parameter p) { Type t = stripDefaultArgs(p.type); - return (t != p.type || p.defaultArg || p.ident) - ? new Parameter(p.storageClass, t, null, null) + return (t != p.type || p.defaultArg || p.ident || p.userAttribDecl) + ? new Parameter(p.storageClass, t, null, null, null) : null; } @@ -1447,7 +1447,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); } @@ -3285,7 +3285,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/header1.d b/test/compilable/extra-files/header1.d index b0a73ff2c368..720b4f0fe403 100644 --- a/test/compilable/extra-files/header1.d +++ b/test/compilable/extra-files/header1.d @@ -581,3 +581,21 @@ struct SafeS int* p; } + +void test13x(@(10) int a, @(20) int, @(30) @(40) int[] arr...) {} + +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 test15x(@(20) void delegate(int) @safe dg){} diff --git a/test/compilable/extra-files/header1.di b/test/compilable/extra-files/header1.di index 0b456aac3bfb..b9fd4c05330c 100644 --- a/test/compilable/extra-files/header1.di +++ b/test/compilable/extra-files/header1.di @@ -515,3 +515,15 @@ struct SafeS int* p; } } +void test13x(@(10) int a, @(20) int, @(tuple(30), tuple(40)) int[] arr...); +enum Test14UDA1 ; +struct Test14UDA2 +{ + string str; +} +Test14UDA2 test14uda3(string name); +struct Test14UDA4(string v) +{ +} +void test14x(@(Test14UDA1) int, @Test14UDA2("1") int, @test14uda3("2") int, @(Test14UDA4!"3") int); +void test15x(@(20) void delegate(int) @safe dg); diff --git a/test/compilable/extra-files/header1i.di b/test/compilable/extra-files/header1i.di index 132140cd7157..b5fef24dcf78 100644 --- a/test/compilable/extra-files/header1i.di +++ b/test/compilable/extra-files/header1i.di @@ -665,3 +665,24 @@ struct SafeS int* p; } } +void test13x(@(10) int a, @(20) int, @(tuple(30), tuple(40)) int[] arr...) +{ +} +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 test15x(@(20) void delegate(int) @safe dg) +{ +} 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..ec760bd60b76 --- /dev/null +++ b/test/fail_compilation/udaparams.d @@ -0,0 +1,57 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/udaparams.d(31): Error: variadic parameter cannot have user-defined attributes +fail_compilation/udaparams.d(32): Error: variadic parameter cannot have user-defined attributes +fail_compilation/udaparams.d(34): Error: user-defined attributes cannot appear as postfixes +fail_compilation/udaparams.d(35): Error: user-defined attributes cannot appear as postfixes +fail_compilation/udaparams.d(36): Error: user-defined attributes cannot appear as postfixes +fail_compilation/udaparams.d(38): Error: `@safe` attribute for function parameter is not supported +fail_compilation/udaparams.d(39): Error: `@safe` attribute for function parameter is not supported +fail_compilation/udaparams.d(40): Error: `@safe` attribute for function parameter is not supported +fail_compilation/udaparams.d(43): Error: `@system` attribute for function parameter is not supported +fail_compilation/udaparams.d(44): Error: `@trusted` attribute for function parameter is not supported +fail_compilation/udaparams.d(45): Error: `@nogc` attribute for function parameter is not supported +fail_compilation/udaparams.d(51): Error: Cannot put a storage-class in an alias declaration. +fail_compilation/udaparams.d(52): Error: Cannot put a storage-class in an alias declaration. +fail_compilation/udaparams.d(53): Error: semicolon expected to close `alias` declaration +fail_compilation/udaparams.d(53): Error: declaration expected, not `=>` +fail_compilation/udaparams.d(54): Error: semicolon expected to close `alias` declaration +fail_compilation/udaparams.d(54): Error: declaration expected, not `=>` +fail_compilation/udaparams.d(57): Error: basic type expected, not `@` +fail_compilation/udaparams.d(57): Error: identifier expected for template value parameter +fail_compilation/udaparams.d(57): Error: found `@` when expecting `)` +fail_compilation/udaparams.d(57): Error: basic type expected, not `3` +fail_compilation/udaparams.d(57): Error: found `3` when expecting `)` +fail_compilation/udaparams.d(57): Error: semicolon expected following function declaration +fail_compilation/udaparams.d(57): Error: declaration expected, not `)` +--- +*/ + +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); + +// lambdas without parentheses +alias test19a = @safe b => 1 + 2; +alias test19b = @system b => 1 + 2; +alias test19c = @nogc b => 1 + 2; +alias test19d = @(2) @system b => 1 + 2; +alias test19e = @safe @(2) b => 1 + 2; +alias test19f = extern(C++) b => 1 + 2; +alias test19g = align(2) b => 1 + 2; + +// UDAs on Template parameter aren't supported +void test21(@(3) T)(T t) {} diff --git a/test/runnable/uda.d b/test/runnable/uda.d index c1782c90a7bf..4c8c6ede260b 100644 --- a/test/runnable/uda.d +++ b/test/runnable/uda.d @@ -467,6 +467,192 @@ 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)); +} + +template MinimalFunctionTypeOf(func...) +if (func.length == 1) +{ + static if (is(func[0] T) || is(typeof(func[0]) T) && is(T Fdlg == delegate)) + alias MinimalFunctionTypeOf = Fdlg; // HIT: delegate + else + static assert(0); +} + +template Parameters(func...) +if (func.length == 1) +{ + static if (is(MinimalFunctionTypeOf!func P == function)) + alias Parameters = P; + else + static assert(0, "argument has no parameters"); +} + +void test15y(@(20) void delegate(@Test14UDA2("2") @("test15yUDA") int) @safe dg) +{ + static assert([__traits(getAttributes, dg)] == [20]); + static assert(is(typeof(dg) == void delegate(int) @safe)); + auto foo = (@("myUDA") int x){ + static assert([__traits(getAttributes, x)] == ["myUDA"]); + }; + static assert(__traits(getAttributes, Parameters!dg)[0] == Test14UDA2("2")); + static assert(__traits(getAttributes, Parameters!dg)[1] == "test15yUDA"); +} + +void test15z() +{ + test15y((@(15) @(16) int x){ + static assert([__traits(getAttributes, x)] == [15, 16]); + }); +} + +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")); +} + +// Lambdas with parentheses: +void test19() +{ + // lambdas without parentheses + alias test19a = @(3) b => 1 + 2; + alias test19b = @(3) @(4) b => 1 + 2; + + alias test19c = (@(3) c, @(5) d) => 1 + 2; + alias test19d = (@(3) @(4) c, @(5) d) => 1 + 2; + auto test19e = (@(3) int c, @(5) int d) => 1 + 2; + + // still allow alias function declarations + alias FuncType = @safe void function(); + alias FuncType2 = @safe nothrow void function(); + alias FuncType3 = nothrow void function(); + alias FuncType4 = nothrow @safe void function(); +} + +void test20() +{ + // Using a delegate with inferred parameter type + void test20a(int delegate(int) t){ t(1); } + test20a((@(19) a) { + static assert([__traits(getAttributes, a)] == [19]); + return a + 2; + }); +} + +/************************************************/ + int main() { test1(); @@ -482,6 +668,13 @@ int main() test11(); test12(); test9178(); + test13(); + test14(); + test16(); + test17(); + test18(); + test19(); + test20(); printf("Success\n"); return 0;