From ad9f5a3aa6e42fcf914b449398ec6a150b987fe3 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 26 Feb 2022 12:50:41 +0100 Subject: [PATCH 1/7] Add parser helper rule for optional task port list There are a few places in the grammar where it is possible to specify a task/function port list in parenthesis or nothing. E.g. task and function prototypes. Factor this out into a common rule to be able to remove some duplicated code. Signed-off-by: Lars-Peter Clausen --- parse.y | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/parse.y b/parse.y index 1e802d8a4..f700e3c73 100644 --- a/parse.y +++ b/parse.y @@ -621,7 +621,8 @@ static void current_function_set_statement(const YYLTYPE&loc, std::vector enum_data_type enum_base_type %type tf_item_declaration tf_item_list tf_item_list_opt -%type tf_port_declaration tf_port_item tf_port_item_list tf_port_list tf_port_list_opt +%type tf_port_declaration tf_port_item tf_port_item_list +%type tf_port_list tf_port_list_opt tf_port_list_parens_opt %type modport_simple_port port_name parameter_value_byname %type port_name_list parameter_value_byname_list @@ -928,25 +929,14 @@ class_item /* IEEE1800-2005: A.1.8 */ /* External class method definitions... */ - | K_extern method_qualifier_opt K_function K_new ';' - { yyerror(@1, "sorry: External constructors are not yet supported."); } - | K_extern method_qualifier_opt K_function K_new '(' tf_port_list_opt ')' ';' + | K_extern method_qualifier_opt K_function K_new tf_port_list_parens_opt ';' { yyerror(@1, "sorry: External constructors are not yet supported."); } | K_extern method_qualifier_opt K_function data_type_or_implicit_or_void - IDENTIFIER ';' + IDENTIFIER tf_port_list_parens_opt ';' { yyerror(@1, "sorry: External methods are not yet supported."); delete[] $5; } - | K_extern method_qualifier_opt K_function data_type_or_implicit_or_void - IDENTIFIER '(' tf_port_list_opt ')' ';' - { yyerror(@1, "sorry: External methods are not yet supported."); - delete[] $5; - } - | K_extern method_qualifier_opt K_task IDENTIFIER ';' - { yyerror(@1, "sorry: External methods are not yet supported."); - delete[] $4; - } - | K_extern method_qualifier_opt K_task IDENTIFIER '(' tf_port_list_opt ')' ';' + | K_extern method_qualifier_opt K_task IDENTIFIER tf_port_list_parens_opt ';' { yyerror(@1, "sorry: External methods are not yet supported."); delete[] $4; } @@ -1937,10 +1927,8 @@ modport_simple_port ; modport_tf_port - : K_task IDENTIFIER - | K_task IDENTIFIER '(' tf_port_list_opt ')' - | K_function data_type_or_implicit_or_void IDENTIFIER - | K_function data_type_or_implicit_or_void IDENTIFIER '(' tf_port_list_opt ')' + : K_task IDENTIFIER tf_port_list_parens_opt + | K_function data_type_or_implicit_or_void IDENTIFIER tf_port_list_parens_opt ; non_integer_type /* IEEE1800-2005: A.2.2.1 */ @@ -6817,6 +6805,13 @@ tf_port_list_opt | { $$ = 0; } ; + /* A task or function prototype can be declared with the task/function name + * followed by a port list in parenthesis or or just the task/function name by + * itself. When a port list is used it might be empty. */ +tf_port_list_parens_opt + : '(' tf_port_list_opt ')' { $$ = $2; } + | { $$ = 0; } + /* Note that the lexor notices the "table" keyword and starts the UDPTABLE state. It needs to happen there so that all the characters in the table are interpreted in that mode. It is still From 9810ce6e6073c255c9cfae3b9bc26906f783c04c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 26 Feb 2022 12:53:18 +0100 Subject: [PATCH 2/7] Add parser helper rules for optional argument list There are a few places in the grammar where it is possible to specify a argument list in parenthesis or nothing. E.g. a task invocation. ``` task t(int a = 10); endtask initial begin // All 3 are valid syntax t(1); t(); t; end ``` Factor this out into a common rule to be able to remove some duplicated code. Signed-off-by: Lars-Peter Clausen --- parse.y | 64 ++++++++++++++++++++------------------------------------- 1 file changed, 22 insertions(+), 42 deletions(-) diff --git a/parse.y b/parse.y index f700e3c73..e028d16a1 100644 --- a/parse.y +++ b/parse.y @@ -649,6 +649,7 @@ static void current_function_set_statement(const YYLTYPE&loc, std::vector delay_value delay_value_simple %type delay1 delay3 delay3_opt delay_value_list %type expression_list_with_nuls expression_list_proper +%type argument_list_parens_opt %type cont_assign cont_assign_list %type variable_decl_assignment @@ -862,13 +863,9 @@ class_declaration_endlabel_opt a data_type. */ class_declaration_extends_opt /* IEEE1800-2005: A.1.2 */ - : K_extends ps_type_identifier + : K_extends ps_type_identifier argument_list_parens_opt { $$.type = $2; - $$.exprs = 0; - } - | K_extends ps_type_identifier '(' expression_list_with_nuls ')' - { $$.type = $2; - $$.exprs = $4; + $$.exprs = $3; } | { $$.type = 0; $$.exprs = 0; } @@ -992,12 +989,12 @@ class_item_qualifier_opt ; class_new /* IEEE1800-2005 A.2.4 */ - : K_new '(' expression_list_with_nuls ')' - { std::list*expr_list = $3; + : K_new argument_list_parens_opt + { std::list*expr_list = $2; strip_tail_items(expr_list); PENewClass*tmp = new PENewClass(*expr_list); FILE_NAME(tmp, @1); - delete $3; + delete $2; $$ = tmp; } | K_new hierarchy_identifier @@ -1008,11 +1005,6 @@ class_new /* IEEE1800-2005 A.2.4 */ delete $2; $$ = tmp; } - | K_new - { PENewClass*tmp = new PENewClass; - FILE_NAME(tmp, @1); - $$ = tmp; - } ; /* The concurrent_assertion_item pulls together the @@ -3625,6 +3617,13 @@ expression_list_with_nuls } ; + /* A task or function can be invoked with the task/function name followed by + * an argument list in parenthesis or with just the task/function name by + * itself. When an argument list is used it might be empty. */ +argument_list_parens_opt + : '(' expression_list_with_nuls ')' { $$ = $2; } + | { $$ = new std::list; } + expression_list_proper : expression_list_proper ',' expression { std::list*tmp = $1; @@ -5181,15 +5180,10 @@ module_item { pform_endgenerate(true); } /* Elaboration system tasks. */ - | SYSTEM_IDENTIFIER '(' expression_list_with_nuls ')' ';' - { pform_make_elab_task(@1, lex_strings.make($1), *$3); - delete[]$1; - delete $3; - } - | SYSTEM_IDENTIFIER ';' - { std::listpt; - pform_make_elab_task(@1, lex_strings.make($1), pt); + | SYSTEM_IDENTIFIER argument_list_parens_opt ';' + { pform_make_elab_task(@1, lex_strings.make($1), *$2); delete[]$1; + delete $2; } | modport_declaration @@ -6626,25 +6620,18 @@ statement_item /* This is roughly statement_item in the LRM */ FILE_NAME(tmp,@1); $$ = tmp; } - | SYSTEM_IDENTIFIER '(' expression_list_with_nuls ')' ';' - { PCallTask*tmp = new PCallTask(lex_strings.make($1), *$3); - FILE_NAME(tmp,@1); - delete[]$1; - delete $3; - $$ = tmp; - } - | SYSTEM_IDENTIFIER ';' - { std::listpt; - PCallTask*tmp = new PCallTask(lex_strings.make($1), pt); + | SYSTEM_IDENTIFIER argument_list_parens_opt ';' + { PCallTask*tmp = new PCallTask(lex_strings.make($1), *$2); FILE_NAME(tmp,@1); delete[]$1; + delete $2; $$ = tmp; } - | hierarchy_identifier '(' expression_list_with_nuls ')' ';' - { PCallTask*tmp = pform_make_call_task(@1, *$1, *$3); + | hierarchy_identifier argument_list_parens_opt ';' + { PCallTask*tmp = pform_make_call_task(@1, *$1, *$2); delete $1; - delete $3; + delete $2; $$ = tmp; } @@ -6677,13 +6664,6 @@ statement_item /* This is roughly statement_item in the LRM */ $$ = tmp; } - | hierarchy_identifier ';' - { std::listpt; - PCallTask*tmp = pform_make_call_task(@1, *$1, pt); - delete $1; - $$ = tmp; - } - /* IEEE1800 A.1.8: class_constructor_declaration with a call to parent constructor. Note that the implicit_class_handle must be K_super ("this.new" makes little sense) but that would From 967e3455fe7e4bc110034ae65a385af82ad4f690 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 26 Feb 2022 13:09:52 +0100 Subject: [PATCH 3/7] Add parser helper rule for class identifiers There are a few places in the grammar that follow the pattern of `implicit_class_handle '.' hierarchy_identifier` and then splice the two identifier paths into a single one. Factor this into a common helper rule to avoid duplicated code. Signed-off-by: Lars-Peter Clausen --- parse.y | 55 ++++++++++++++++++++----------------------------------- 1 file changed, 20 insertions(+), 35 deletions(-) diff --git a/parse.y b/parse.y index e028d16a1..161512153 100644 --- a/parse.y +++ b/parse.y @@ -639,7 +639,7 @@ static void current_function_set_statement(const YYLTYPE&loc, std::vector let_port_list_opt let_port_list %type let_port_item -%type hierarchy_identifier implicit_class_handle +%type hierarchy_identifier implicit_class_handle class_hierarchy_identifier %type assignment_pattern expression expr_mintypmax %type expr_primary_or_typename expr_primary %type class_new dynamic_array_new let_default_opt @@ -1536,6 +1536,15 @@ implicit_class_handle /* IEEE1800-2005: A.8.4 */ | K_super { $$ = pform_create_super(); } ; +/* `this` or `super` followed by an identifier */ +class_hierarchy_identifier + : implicit_class_handle '.' hierarchy_identifier + { $1->splice($1->end(), *$3); + delete $3; + $$ = $1; + } + ; + /* SystemVerilog adds support for the increment/decrement expressions, which look like a++, --a, etc. These are primaries but are in their own rules because they can also be @@ -3759,17 +3768,11 @@ expr_primary delete $2; $$ = tmp; } - | implicit_class_handle '.' hierarchy_identifier '(' expression_list_with_nuls ')' - { pform_name_t*t_name = $1; - while (! $3->empty()) { - t_name->push_back($3->front()); - $3->pop_front(); - } - list*expr_list = $5; + | class_hierarchy_identifier '(' expression_list_with_nuls ')' + { list*expr_list = $3; strip_tail_items(expr_list); - PECallFunction*tmp = pform_make_call_function(@1, *t_name, *expr_list); + PECallFunction*tmp = pform_make_call_function(@1, *$1, *expr_list); delete $1; - delete $3; $$ = tmp; } | SYSTEM_IDENTIFIER '(' expression_list_proper ')' @@ -3803,16 +3806,10 @@ expr_primary $$ = tmp; } - | implicit_class_handle '.' hierarchy_identifier - { pform_name_t*t_name = $1; - while (! $3->empty()) { - t_name->push_back($3->front()); - $3->pop_front(); - } - PEIdent*tmp = new PEIdent(*t_name); - FILE_NAME(tmp,@1); + | class_hierarchy_identifier + { PEIdent*tmp = new PEIdent(*$1); + FILE_NAME(tmp, @1); delete $1; - delete $3; $$ = tmp; } @@ -4610,17 +4607,11 @@ lpvalue delete $1; } - | implicit_class_handle '.' hierarchy_identifier - { pform_name_t*t_name = $1; - while (!$3->empty()) { - t_name->push_back($3->front()); - $3->pop_front(); - } - PEIdent*tmp = new PEIdent(*t_name); + | class_hierarchy_identifier + { PEIdent*tmp = new PEIdent(*$1); FILE_NAME(tmp, @1); $$ = tmp; delete $1; - delete $3; } | '{' expression_list_proper '}' @@ -6650,17 +6641,11 @@ statement_item /* This is roughly statement_item in the LRM */ $$ = tmp; } - | implicit_class_handle '.' hierarchy_identifier '(' expression_list_with_nuls ')' ';' - { pform_name_t*t_name = $1; - while (! $3->empty()) { - t_name->push_back($3->front()); - $3->pop_front(); - } - PCallTask*tmp = new PCallTask(*t_name, *$5); + | class_hierarchy_identifier '(' expression_list_with_nuls ')' ';' + { PCallTask*tmp = new PCallTask(*$1, *$3); FILE_NAME(tmp, @1); delete $1; delete $3; - delete $5; $$ = tmp; } From e3bc99dbf34d58bbc17cf462dd1040b5e16b7213 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 26 Feb 2022 18:51:07 +0100 Subject: [PATCH 4/7] Don't allow non-ANSI ports for class constructors Class constructors don't allow for non-ANSI ports. E.g. the following is not valid. ``` class C; function new(); input int i; endfunction endclass ``` The parser will currently accept this, but otherwise ignore the non-ANSI port. Modify the parser rules so that this is a syntax error. Signed-off-by: Lars-Peter Clausen --- parse.y | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parse.y b/parse.y index 161512153..ff6c25f19 100644 --- a/parse.y +++ b/parse.y @@ -892,7 +892,7 @@ class_item /* IEEE1800-2005: A.1.8 */ current_function = pform_push_constructor_scope(@3); } '(' tf_port_list_opt ')' ';' - tf_item_list_opt + block_item_decls_opt statement_or_null_list_opt K_endfunction endnew_opt { current_function->set_ports($6); From da5b9a4e5ffc7f4fb26156c899c267e65b9118d5 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 26 Feb 2022 12:53:51 +0100 Subject: [PATCH 5/7] Support class constructor without parenthesis Class constructors can be declared without parenthesis after the `new` when no arguments are required. Just like for normal function. In a similar way the base class constructor can also be invoked without parenthesis after the `new`. ``` class C extends D; function new; super.new; endfunction endclass ``` Add support for this by making the parenthesis optional in the parser. Signed-off-by: Lars-Peter Clausen --- parse.y | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/parse.y b/parse.y index ff6c25f19..2b5876868 100644 --- a/parse.y +++ b/parse.y @@ -891,14 +891,14 @@ class_item /* IEEE1800-2005: A.1.8 */ { assert(current_function==0); current_function = pform_push_constructor_scope(@3); } - '(' tf_port_list_opt ')' ';' + tf_port_list_parens_opt ';' block_item_decls_opt statement_or_null_list_opt K_endfunction endnew_opt - { current_function->set_ports($6); + { current_function->set_ports($5); pform_set_constructor_return(current_function); pform_set_this_class(@3, current_function); - current_function_set_statement(@3, $10); + current_function_set_statement(@3, $8); pform_pop_scope(); current_function = 0; } @@ -6656,8 +6656,8 @@ statement_item /* This is roughly statement_item in the LRM */ beginning of a constructor, but let the elaborator figure that out. */ - | implicit_class_handle '.' K_new '(' expression_list_with_nuls ')' ';' - { PChainConstructor*tmp = new PChainConstructor(*$5); + | implicit_class_handle '.' K_new argument_list_parens_opt ';' + { PChainConstructor*tmp = new PChainConstructor(*$4); FILE_NAME(tmp, @3); if (peek_head_name(*$1) == THIS_TOKEN) { yyerror(@1, "error: this.new is invalid syntax. Did you mean super.new?"); From 51eae02e78cfe6141b1496acd6e66ded233f35f7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 26 Feb 2022 12:54:24 +0100 Subject: [PATCH 6/7] Support class method calls without parenthesis It is possible to call a class method without parenthesis if no arguments are specified. At the moment this works when calling a class method by name. But when using the implicit class handle `this` or `super` it does not work. Add support for this. Signed-off-by: Lars-Peter Clausen --- parse.y | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/parse.y b/parse.y index 2b5876868..16b9b7931 100644 --- a/parse.y +++ b/parse.y @@ -6641,11 +6641,11 @@ statement_item /* This is roughly statement_item in the LRM */ $$ = tmp; } - | class_hierarchy_identifier '(' expression_list_with_nuls ')' ';' - { PCallTask*tmp = new PCallTask(*$1, *$3); + | class_hierarchy_identifier argument_list_parens_opt ';' + { PCallTask*tmp = new PCallTask(*$1, *$2); FILE_NAME(tmp, @1); delete $1; - delete $3; + delete $2; $$ = tmp; } From 571f222a73169ae264e0a981d4ba2ede878f6fae Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 26 Mar 2022 20:37:41 +0100 Subject: [PATCH 7/7] Add additional regression tests for class syntax Check that it is possible to both declare and call class constructors without using parenthesis after the `new` keyword. Check that a non-ANSI port for a class constructor results in an error. Check that it is possible to invoke a class task through a implicit class handle (`this` or `super`) without using parenthesis after the task name. Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/sv_class_constructor1.v | 14 +++++++++++++ ivtest/ivltests/sv_class_constructor_fail.v | 14 +++++++++++++ ivtest/ivltests/sv_class_super1.v | 21 ++++++++++++++++++++ ivtest/ivltests/sv_class_super2.v | 21 ++++++++++++++++++++ ivtest/ivltests/sv_class_task1.v | 22 +++++++++++++++++++++ ivtest/regress-sv.list | 5 +++++ ivtest/regress-vlog95.list | 4 ++++ 7 files changed, 101 insertions(+) create mode 100644 ivtest/ivltests/sv_class_constructor1.v create mode 100644 ivtest/ivltests/sv_class_constructor_fail.v create mode 100644 ivtest/ivltests/sv_class_super1.v create mode 100644 ivtest/ivltests/sv_class_super2.v create mode 100644 ivtest/ivltests/sv_class_task1.v diff --git a/ivtest/ivltests/sv_class_constructor1.v b/ivtest/ivltests/sv_class_constructor1.v new file mode 100644 index 000000000..e886b6f57 --- /dev/null +++ b/ivtest/ivltests/sv_class_constructor1.v @@ -0,0 +1,14 @@ +// Check that a class constructor declaration without parenthesis after the +// `new` is supported. + +module test; + + class C; + function new; + $display("PASSED"); + endfunction + endclass + + C c = new; + +endmodule diff --git a/ivtest/ivltests/sv_class_constructor_fail.v b/ivtest/ivltests/sv_class_constructor_fail.v new file mode 100644 index 000000000..66490d706 --- /dev/null +++ b/ivtest/ivltests/sv_class_constructor_fail.v @@ -0,0 +1,14 @@ +// Check that a class constructor with non-ANSI port results in an error. + +module test; + + class C; + function new; + input x; // This is a syntax error + $display("FAILED"); + endfunction + endclass + + C c = new; + +endmodule diff --git a/ivtest/ivltests/sv_class_super1.v b/ivtest/ivltests/sv_class_super1.v new file mode 100644 index 000000000..ddc7b1a77 --- /dev/null +++ b/ivtest/ivltests/sv_class_super1.v @@ -0,0 +1,21 @@ +// Check that it is possible to explicitly call the base class constructor, even +// if it does not take any parameters. Check that it is possible to call it with +// parenthesis after the `new`. + +module test; + + class B; + function new(); + $display("PASSED"); + endfunction + endclass + + class C extends B; + function new(); + super.new(); + endfunction + endclass + + C c = new; + +endmodule diff --git a/ivtest/ivltests/sv_class_super2.v b/ivtest/ivltests/sv_class_super2.v new file mode 100644 index 000000000..b21165d1f --- /dev/null +++ b/ivtest/ivltests/sv_class_super2.v @@ -0,0 +1,21 @@ +// Check that it is possible to explicitly call the base class constructor, even +// if it does not take any parameters. Check that it is possible to call it +// without parenthesis after the `new`. + +module test; + + class B; + function new(); + $display("PASSED"); + endfunction + endclass + + class C extends B; + function new(); + super.new; + endfunction + endclass + + C c = new; + +endmodule diff --git a/ivtest/ivltests/sv_class_task1.v b/ivtest/ivltests/sv_class_task1.v new file mode 100644 index 000000000..5674ec950 --- /dev/null +++ b/ivtest/ivltests/sv_class_task1.v @@ -0,0 +1,22 @@ +// Check that it is possible to call a class task without parenthesis after the +// task name when using the implicit `this` class handle. + +module test; + + class C; + task a; + $display("PASSED"); + endtask + + task b; + this.a; + endtask + endclass + + C c = new; + + initial begin + c.b; + end + +endmodule diff --git a/ivtest/regress-sv.list b/ivtest/regress-sv.list index 8312bd6b3..4cdfd6f39 100644 --- a/ivtest/regress-sv.list +++ b/ivtest/regress-sv.list @@ -474,11 +474,16 @@ sv_class21 normal,-g2009 ivltests sv_class22 normal,-g2009 ivltests sv_class23 normal,-g2009 ivltests sv_class24 normal,-g2009 ivltests +sv_class_constructor1 normal,-g2009 ivltests +sv_class_constructor_fail CE,-g2009 ivltests sv_class_empty_item normal,-g2009 ivltests sv_class_extends_scoped normal,-g2009 ivltests sv_class_localparam normal,-g2009 ivltests sv_class_new_init normal,-g2009 ivltests sv_class_in_module_decl normal,-g2009 ivltests +sv_class_super1 normal,-g2009 ivltests +sv_class_super2 normal,-g2009 ivltests +sv_class_task1 normal,-g2009 ivltests sv_darray1 normal,-g2009 ivltests sv_darray2 normal,-g2009 ivltests sv_darray3 normal,-g2009 ivltests diff --git a/ivtest/regress-vlog95.list b/ivtest/regress-vlog95.list index f5944b788..a9ddc0ff2 100644 --- a/ivtest/regress-vlog95.list +++ b/ivtest/regress-vlog95.list @@ -372,11 +372,15 @@ sv_class21 CE,-g2009 ivltests sv_class22 CE,-g2009 ivltests sv_class23 CE,-g2009 ivltests sv_class24 CE,-g2009 ivltests +sv_class_constructor1 CE,-g2009 ivltests sv_class_empty_item CE,-g2009 ivltests sv_class_extends_scoped CE,-g2009 ivltests sv_class_localparam CE,-g2009 ivltests sv_class_new_init CE,-g2009 ivltests sv_class_in_module_decl CE,-g2009 ivltests +sv_class_super1 CE,-g2009 ivltests +sv_class_super2 CE,-g2009 ivltests +sv_class_task1 CE,-g2009 ivltests sv_end_label CE,-g2009 ivltests # Also generate sv_foreach2 CE,-g2009,-pallowsigned=1 ivltests sv_foreach3 CE,-g2009 ivltests