diff --git a/changelog/dmd.uda-template-args.dd b/changelog/dmd.uda-template-args.dd new file mode 100644 index 0000000000..c1f948f471 --- /dev/null +++ b/changelog/dmd.uda-template-args.dd @@ -0,0 +1,27 @@ +User Defined Attributes now parse Template Arguments + +It was already allowed to put types in UDAs, but the parser would reject basic types written directly, requiring the use of an alias. + +--- +alias Tint = int; + +@Tint void f(); +--- + +Also, simple literals that can appear in template instantiations without brackets (example: `foo!"arg"`) require parentheses when used as an attribute: +--- +@("my test") unittest +{ + +} +--- + +Now, arguments that can appear after a template instantiation `foo!` can also appear after an `@` attribute. +--- +@int void f(); + +@"my test" unittest +{ + +} +--- diff --git a/compiler/src/dmd/parse.d b/compiler/src/dmd/parse.d index 9a2448fb57..da257601a8 100644 --- a/compiler/src/dmd/parse.d +++ b/compiler/src/dmd/parse.d @@ -1316,12 +1316,38 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer return 0; } + AST.Expression templateArgToExp(RootObject o, const ref Loc loc) + { + switch (o.dyncast) + { + case DYNCAST.expression: + return cast(AST.Expression) o; + case DYNCAST.type: + return new AST.TypeExp(loc, cast(AST.Type)o); + default: + assert(0); + } + } + if (token.value == TOK.leftParenthesis) { // Multi-UDAs ( `@( ArgumentList )`) form, concatenate with existing if (peekNext() == TOK.rightParenthesis) error("empty attribute list is not allowed"); - udas = AST.UserAttributeDeclaration.concat(udas, parseArguments()); + + if (udas is null) + udas = new AST.Expressions(); + auto args = parseTemplateArgumentList(); + foreach (arg; *args) + udas.push(templateArgToExp(arg, token.loc)); + return 0; + } + + if (auto o = parseTemplateSingleArgument()) + { + if (udas is null) + udas = new AST.Expressions(); + udas.push(templateArgToExp(o, token.loc)); return 0; } @@ -1778,7 +1804,16 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer else { // ident!template_argument - tiargs = parseTemplateSingleArgument(); + RootObject o = parseTemplateSingleArgument(); + if (!o) + { + error("template argument expected following `!`"); + } + else + { + tiargs = new AST.Objects(); + tiargs.push(o); + } } if (token.value == TOK.not) { @@ -1844,11 +1879,11 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * foo!arg * Input: * current token is the arg + * Returns: An AST.Type, AST.Expression, or `null` on error */ - private AST.Objects* parseTemplateSingleArgument() + private RootObject parseTemplateSingleArgument() { //printf("parseTemplateSingleArgument()\n"); - auto tiargs = new AST.Objects(); AST.Type ta; switch (token.value) { @@ -1956,9 +1991,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer ta = AST.Type.tdchar; goto LabelX; LabelX: - tiargs.push(ta); nextToken(); - break; + return ta; case TOK.int32Literal: case TOK.uns32Literal: @@ -1988,15 +2022,11 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.this_: { // Template argument is an expression - AST.Expression ea = parsePrimaryExp(); - tiargs.push(ea); - break; + return parsePrimaryExp(); } default: - error("template argument expected following `!`"); - break; + return null; } - return tiargs; } /********************************** diff --git a/compiler/test/compilable/user_defined_attributes.d b/compiler/test/compilable/user_defined_attributes.d new file mode 100644 index 0000000000..169ca49eb0 --- /dev/null +++ b/compiler/test/compilable/user_defined_attributes.d @@ -0,0 +1,22 @@ + +enum Test; + +@true @null @byte int x; +@(int) int y; +@"test" @`test2` @30 @'a' @__LINE__ void f(); + +@Test void h(); + +static assert( __traits(getAttributes, x)[0] == true); +static assert( __traits(getAttributes, x)[1] == null); +static assert(is(__traits(getAttributes, x)[2] == byte)); + +static assert(is(__traits(getAttributes, y)[0] == int)); + +static assert( __traits(getAttributes, f)[0] == "test"); +static assert( __traits(getAttributes, f)[1] == "test2"); +static assert( __traits(getAttributes, f)[2] == 30); +static assert( __traits(getAttributes, f)[3] == 'a'); +static assert( __traits(getAttributes, f)[4] == 6); + +static assert(is(__traits(getAttributes, h)[0] == enum)); diff --git a/compiler/test/fail_compilation/named_arguments_parse.d b/compiler/test/fail_compilation/named_arguments_parse.d index 19e230ee51..096c499790 100644 --- a/compiler/test/fail_compilation/named_arguments_parse.d +++ b/compiler/test/fail_compilation/named_arguments_parse.d @@ -1,13 +1,13 @@ /** TEST_OUTPUT: --- -fail_compilation/named_arguments_parse.d(10): Error: named arguments not allowed here fail_compilation/named_arguments_parse.d(13): Error: named arguments not allowed here fail_compilation/named_arguments_parse.d(14): Error: named arguments not allowed here --- */ -@(attribute: 3) + +// @(attribute: 3) Currently gives an ugly parse error, will be better when named template arguments are implemented void main() { mixin(thecode: "{}");