diff --git a/ivtest/ivltests/br_gh670.v b/ivtest/ivltests/br_gh670.v new file mode 100644 index 000000000..d267dc51d --- /dev/null +++ b/ivtest/ivltests/br_gh670.v @@ -0,0 +1,37 @@ +// Regression test for GitHub issue #670. +// Check that a class function can have the same name as the class. + +module test; + + reg failed; + + `define check(value, expected, error) \ + if ((value) !== (expected)) begin \ + $display("FAILED(%0d). %s", `__LINE__, error); \ + $display(" expected %0h, got %0h", expected, value); \ + failed = 1'b1; \ + end + + class test; + int value; + + function void test(); + value = 32'd42; + endfunction + endclass + + initial begin + test tst; + + failed = 1'b0; + tst = new; + tst.test(); + + `check(tst.value, 32'd42, "Class function name did not hide class name"); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_function_name.v b/ivtest/ivltests/sv_type_identifier_function_name.v new file mode 100644 index 000000000..eba36bfd8 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_function_name.v @@ -0,0 +1,51 @@ +// Check that function names can shadow visible type identifiers. + +typedef int T; +typedef int U; + +module test; + + reg failed; + + `define check(value, expected, error) \ + if ((value) !== (expected)) begin \ + $display("FAILED(%0d). %s", `__LINE__, error); \ + $display(" expected %0h, got %0h", expected, value); \ + failed = 1'b1; \ + end + + function int T(input int value); + return value + 32'd1; + endfunction + + function U; + U = 1'b1; + endfunction + + class C; + function int T(input int value); + return value + 32'd2; + endfunction + + function int C(input int value); + return value + 32'd3; + endfunction + endclass + + initial begin + C c; + + failed = 1'b0; + c = new; + + `check(T(32'd41), 32'd42, "Function name did not hide typedef"); + `check(U(), 1'b1, "Implicit function name did not hide typedef"); + `check(c.T(32'd40), 32'd42, "Class function name did not hide typedef"); + `check(c.C(32'd39), 32'd42, "Class function name did not hide class name"); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_task_name.v b/ivtest/ivltests/sv_type_identifier_task_name.v new file mode 100644 index 000000000..fefcbfc98 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_task_name.v @@ -0,0 +1,62 @@ +// Check that task names can shadow visible type identifiers. + +typedef int T; +typedef int U; + +module test; + + reg failed; + + `define check(value, expected, error) \ + if ((value) !== (expected)) begin \ + $display("FAILED(%0d). %s", `__LINE__, error); \ + $display(" expected %0h, got %0h", expected, value); \ + failed = 1'b1; \ + end + + task T(input int value, output int result); + result = value + 32'd1; + endtask + + task U; + output int result; + + result = 32'd33; + endtask + + class C; + task T(input int value, output int result); + result = value + 32'd2; + endtask + + task C(input int value, output int result); + result = value + 32'd3; + endtask + endclass + + initial begin + C c; + int r0; + int r1; + int r2; + int r3; + + failed = 1'b0; + c = new; + + T(32'd41, r0); + U(r1); + c.T(32'd40, r2); + c.C(32'd39, r3); + + `check(r0, 32'd42, "Task name did not hide typedef"); + `check(r1, 32'd33, "Non-ANSI task name did not hide typedef"); + `check(r2, 32'd42, "Class task name did not hide typedef"); + `check(r3, 32'd42, "Class task name did not hide class name"); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index b7bf85cc5..b8a152ca4 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -37,6 +37,7 @@ br_gh440 vvp_tests/br_gh440.json br_gh483a vvp_tests/br_gh483a.json br_gh483b vvp_tests/br_gh483b.json br_gh552 vvp_tests/br_gh552.json +br_gh670 vvp_tests/br_gh670.json br_gh687 vvp_tests/br_gh687.json br_gh703 vvp_tests/br_gh703.json br_gh710a vvp_tests/br_gh710a.json @@ -387,6 +388,7 @@ sv_super_member_fail vvp_tests/sv_super_member_fail.json sv_type_identifier_ams_name_fields vvp_tests/sv_type_identifier_ams_name_fields.json sv_type_identifier_config_name vvp_tests/sv_type_identifier_config_name.json sv_type_identifier_foreach_name vvp_tests/sv_type_identifier_foreach_name.json +sv_type_identifier_function_name vvp_tests/sv_type_identifier_function_name.json sv_type_identifier_genvar_name vvp_tests/sv_type_identifier_genvar_name.json sv_type_identifier_modport_name vvp_tests/sv_type_identifier_modport_name.json sv_type_identifier_module_name vvp_tests/sv_type_identifier_module_name.json @@ -399,6 +401,7 @@ sv_type_identifier_parameter_type_param_name vvp_tests/sv_type_identifier_parame sv_type_identifier_port_name vvp_tests/sv_type_identifier_port_name.json sv_type_identifier_specparam_name vvp_tests/sv_type_identifier_specparam_name.json sv_type_identifier_task_function_argument_name vvp_tests/sv_type_identifier_task_function_argument_name.json +sv_type_identifier_task_name vvp_tests/sv_type_identifier_task_name.json sv_type_identifier_variable_name vvp_tests/sv_type_identifier_variable_name.json sv_type_param_restrict_class1 vvp_tests/sv_type_param_restrict_class1.json sv_type_param_restrict_class2 vvp_tests/sv_type_param_restrict_class2.json diff --git a/ivtest/vvp_tests/br_gh670.json b/ivtest/vvp_tests/br_gh670.json new file mode 100644 index 000000000..5effa2a3f --- /dev/null +++ b/ivtest/vvp_tests/br_gh670.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "br_gh670.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Classes are not supported", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_type_identifier_function_name.json b/ivtest/vvp_tests/sv_type_identifier_function_name.json new file mode 100644 index 000000000..4e5ae1920 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_function_name.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_function_name.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Classes are not supported", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_type_identifier_task_name.json b/ivtest/vvp_tests/sv_type_identifier_task_name.json new file mode 100644 index 000000000..b95d47644 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_task_name.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_task_name.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Classes are not supported", + "type" : "CE" + } +} diff --git a/parse.y b/parse.y index 0803658e8..b2979edc7 100644 --- a/parse.y +++ b/parse.y @@ -1017,7 +1017,9 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, %type forward_type forward_type_without_enum %type data_type_or_implicit_plus_id_base +%type data_type_or_implicit_plus_id %type data_type_or_implicit_plus_id_dim +%type data_type_or_implicit_or_void_plus_id %type partial_port_name_dim %type partial_port_type_plus_id_dim %type partial_port_typedef_plus_id_dim @@ -1808,53 +1810,53 @@ for_step_opt definitions in the func_body to take on the scope of the function instead of the module. */ function_declaration /* IEEE1800-2005: A.2.6 */ - : K_function lifetime_opt data_type_or_implicit_or_void IDENTIFIER ';' + : K_function lifetime_opt data_type_or_implicit_or_void_plus_id ';' { assert(current_function == 0); - current_function = pform_push_function_scope(@1, $4, $2); + current_function = pform_push_function_scope(@1, $3.id, $2); } tf_item_list_opt statement_or_null_list_opt K_endfunction - { current_function->set_ports($7); - current_function->set_return($3); - current_function_set_statement($8? @8 : @4, $8); - pform_set_this_class(@4, current_function); + { current_function->set_ports($6); + current_function->set_return($3.type); + current_function_set_statement($7 ? @7 : @3, $7); + pform_set_this_class(@3, current_function); pform_pop_scope(); current_function = 0; } label_opt { // Last step: check any closing name. - check_end_label(@11, "function", $4, $11); - delete[]$4; + check_end_label(@10, "function", $3.id, $10); + delete[]$3.id; } - | K_function lifetime_opt data_type_or_implicit_or_void IDENTIFIER + | K_function lifetime_opt data_type_or_implicit_or_void_plus_id { assert(current_function == 0); - current_function = pform_push_function_scope(@1, $4, $2); + current_function = pform_push_function_scope(@1, $3.id, $2); } '(' tf_port_list_opt ')' ';' block_item_decls_opt statement_or_null_list_opt K_endfunction - { current_function->set_ports($7); - current_function->set_return($3); - current_function_set_statement($11? @11 : @4, $11); - pform_set_this_class(@4, current_function); + { current_function->set_ports($6); + current_function->set_return($3.type); + current_function_set_statement($10 ? @10 : @3, $10); + pform_set_this_class(@3, current_function); pform_pop_scope(); current_function = 0; - if ($7 == 0) { - pform_requires_sv(@4, "Functions with no ports"); + if ($6 == 0) { + pform_requires_sv(@3, "Functions with no ports"); } } label_opt { // Last step: check any closing name. - check_end_label(@14, "function", $4, $14); - delete[]$4; + check_end_label(@13, "function", $3.id, $13); + delete[]$3.id; } /* Detect and recover from some errors. */ - | K_function lifetime_opt data_type_or_implicit_or_void IDENTIFIER error K_endfunction + | K_function lifetime_opt data_type_or_implicit_or_void_plus_id error K_endfunction { /* */ if (current_function) { pform_pop_scope(); @@ -1866,8 +1868,8 @@ function_declaration /* IEEE1800-2005: A.2.6 */ } label_opt { // Last step: check any closing name. - check_end_label(@8, "function", $4, $8); - delete[]$4; + check_end_label(@7, "function", $3.id, $7); + delete[]$3.id; } ; @@ -2662,7 +2664,11 @@ streaming_concatenation /* IEEE1800-2005: A.8.1 */ task_declaration /* IEEE1800-2005: A.2.7 */ - : K_task lifetime_opt IDENTIFIER ';' + /* Tasks do not have a return type, so the leading identifier is always the + task name. Use identifier_name so a name tokenized as TYPE_IDENTIFIER can + still declare a task that shadows a visible type name. */ + + : K_task lifetime_opt identifier_name ';' { assert(current_task == 0); current_task = pform_push_task_scope(@1, $3, $2); } @@ -2689,7 +2695,7 @@ task_declaration /* IEEE1800-2005: A.2.7 */ delete[]$3; } - | K_task lifetime_opt IDENTIFIER '(' + | K_task lifetime_opt identifier_name '(' { assert(current_task == 0); current_task = pform_push_task_scope(@1, $3, $2); } @@ -2718,7 +2724,7 @@ task_declaration /* IEEE1800-2005: A.2.7 */ delete[]$3; } - | K_task lifetime_opt IDENTIFIER error K_endtask + | K_task lifetime_opt identifier_name error K_endtask { if (current_task) { pform_pop_scope(); @@ -2765,6 +2771,20 @@ data_type_or_implicit_plus_id_base } ; + // Function declarations have the same type/name ambiguity as other + // declarations. For `function T;`, T can be the function name even when the + // lexer returns TYPE_IDENTIFIER, while `function T f;` still uses T as the + // explicit return type. Unlike variable/net declarations, function names do + // not allow unpacked dimensions. +data_type_or_implicit_plus_id + : TYPE_IDENTIFIER + { set_type_id_range($$, nullptr, $1.text, @1, nullptr); + } + | data_type_or_implicit_plus_id_base + { $$ = $1; + } + ; + data_type_or_implicit_plus_id_dim : TYPE_IDENTIFIER dimensions_opt { set_type_id_range($$, nullptr, $1.text, @1, $2); @@ -2775,6 +2795,20 @@ data_type_or_implicit_plus_id_dim } ; + // `void` is only an explicit return type. The following identifier is still a + // function name and may be tokenized as TYPE_IDENTIFIER when it shadows a + // visible type name. +data_type_or_implicit_or_void_plus_id + : data_type_or_implicit_plus_id + { $$ = $1; + } + | K_void identifier_name + { void_type_t*tmp = new void_type_t; + FILE_NAME(tmp, @1); + set_type_id_range($$, tmp, $2, @2, nullptr); + } + ; + // Partial ANSI port declarations such as `input a, integer b` can redeclare // the data type without repeating the direction. Keep this narrower than // data_type_or_implicit_plus_id_dim so a bare identifier after a comma is