mirror of
https://github.com/dlang/dmd.git
synced 2025-04-27 13:40:11 +03:00
Parse UDAs like template arguments (#14881)
This commit is contained in:
parent
d98f586771
commit
ef1bca90e0
4 changed files with 93 additions and 14 deletions
27
changelog/dmd.uda-template-args.dd
Normal file
27
changelog/dmd.uda-template-args.dd
Normal file
|
@ -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
|
||||
{
|
||||
|
||||
}
|
||||
---
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**********************************
|
||||
|
|
22
compiler/test/compilable/user_defined_attributes.d
Normal file
22
compiler/test/compilable/user_defined_attributes.d
Normal file
|
@ -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));
|
|
@ -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: "{}");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue