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;
|
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)
|
if (token.value == TOK.leftParenthesis)
|
||||||
{
|
{
|
||||||
// Multi-UDAs ( `@( ArgumentList )`) form, concatenate with existing
|
// Multi-UDAs ( `@( ArgumentList )`) form, concatenate with existing
|
||||||
if (peekNext() == TOK.rightParenthesis)
|
if (peekNext() == TOK.rightParenthesis)
|
||||||
error("empty attribute list is not allowed");
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1778,7 +1804,16 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// ident!template_argument
|
// 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)
|
if (token.value == TOK.not)
|
||||||
{
|
{
|
||||||
|
@ -1844,11 +1879,11 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
|
||||||
* foo!arg
|
* foo!arg
|
||||||
* Input:
|
* Input:
|
||||||
* current token is the arg
|
* 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");
|
//printf("parseTemplateSingleArgument()\n");
|
||||||
auto tiargs = new AST.Objects();
|
|
||||||
AST.Type ta;
|
AST.Type ta;
|
||||||
switch (token.value)
|
switch (token.value)
|
||||||
{
|
{
|
||||||
|
@ -1956,9 +1991,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
|
||||||
ta = AST.Type.tdchar;
|
ta = AST.Type.tdchar;
|
||||||
goto LabelX;
|
goto LabelX;
|
||||||
LabelX:
|
LabelX:
|
||||||
tiargs.push(ta);
|
|
||||||
nextToken();
|
nextToken();
|
||||||
break;
|
return ta;
|
||||||
|
|
||||||
case TOK.int32Literal:
|
case TOK.int32Literal:
|
||||||
case TOK.uns32Literal:
|
case TOK.uns32Literal:
|
||||||
|
@ -1988,15 +2022,11 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
|
||||||
case TOK.this_:
|
case TOK.this_:
|
||||||
{
|
{
|
||||||
// Template argument is an expression
|
// Template argument is an expression
|
||||||
AST.Expression ea = parsePrimaryExp();
|
return parsePrimaryExp();
|
||||||
tiargs.push(ea);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
error("template argument expected following `!`");
|
return null;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
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:
|
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(13): Error: named arguments not allowed here
|
||||||
fail_compilation/named_arguments_parse.d(14): 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()
|
void main()
|
||||||
{
|
{
|
||||||
mixin(thecode: "{}");
|
mixin(thecode: "{}");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue