Support scoped `new` (#4199).
This commit is contained in:
parent
bb41c6b26b
commit
77908447e6
1
Changes
1
Changes
|
@ -14,6 +14,7 @@ Verilator 5.037 devel
|
|||
**Other:**
|
||||
|
||||
* Support redeclaring type as non-type; major parsing change (#2412) (#6020) (#6042) (#6044).
|
||||
* Support scoped `new` (#4199).
|
||||
* Support constrained random for associative arrays (#5985) (#5986). [Yilou Wang]
|
||||
* Support assignments to concatenations with impure RHS (#6002). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Support SARIF JSON diagnostic output with `--diagnostics-sarif`. (#6017)
|
||||
|
|
|
@ -4472,14 +4472,20 @@ class AstNew final : public AstNodeFTaskRef {
|
|||
// New as constructor
|
||||
// Don't need the class we are extracting from, as the "fromp()"'s datatype can get us to it
|
||||
bool m_isImplicit = false; // Implicitly generated from extends args
|
||||
bool m_isScoped = false; // Had :: scope when parsed
|
||||
public:
|
||||
AstNew(FileLine* fl, AstNodeExpr* pinsp)
|
||||
: ASTGEN_SUPER_New(fl, "new", pinsp) {}
|
||||
AstNew(FileLine* fl, AstNodeExpr* pinsp, bool isScoped = false)
|
||||
: ASTGEN_SUPER_New(fl, "new", pinsp)
|
||||
, m_isScoped{isScoped} {}
|
||||
ASTGEN_MEMBERS_AstNew;
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
bool isImplicit() const { return m_isImplicit; }
|
||||
void isImplicit(bool flag) { m_isImplicit = flag; }
|
||||
bool isScoped() const { return m_isScoped; }
|
||||
void isScoped(bool flag) { m_isScoped = flag; }
|
||||
};
|
||||
class AstTaskRef final : public AstNodeFTaskRef {
|
||||
// A reference to a task
|
||||
|
|
|
@ -454,6 +454,17 @@ void AstNetlist::timeprecisionMerge(FileLine*, const VTimescale& value) {
|
|||
}
|
||||
}
|
||||
|
||||
void AstNew::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (isImplicit()) str << " [IMPLICIT]";
|
||||
if (isScoped()) str << " [SCOPED]";
|
||||
}
|
||||
void AstNew::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFunc(str, isImplicit);
|
||||
dumpJsonBoolFunc(str, isScoped);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
|
||||
bool AstVar::isSigPublic() const {
|
||||
return (m_sigPublic || (v3Global.opt.allPublic() && !isTemp() && !isGenVar()))
|
||||
&& !isIfaceRef();
|
||||
|
|
|
@ -557,6 +557,33 @@ size_t V3ParseImp::tokenPipeScanTypeEq(size_t depth) {
|
|||
return depth;
|
||||
}
|
||||
|
||||
size_t V3ParseImp::tokenPipeScanEqNew(size_t depth) {
|
||||
// Search around IEEE class_new to see if is expression
|
||||
// Return location of following token, or input if not found
|
||||
// '=' { packageClassScopeNoId } yNEW__LEX
|
||||
UINFO(9, "tokenPipelineScanEqNew tok=" << yylval.token);
|
||||
UASSERT(yylval.token == '=', "Start with '='");
|
||||
while (true) {
|
||||
const int tok = tokenPeekp(depth)->token;
|
||||
if (tok == 0) { // LCOV_EXCL_BR_LINE
|
||||
UINFO(9, "tokenPipeScanEqNew hit EOF; probably syntax error to come");
|
||||
break; // LCOV_EXCL_LINE
|
||||
} else if (tok == yNEW__LEX) {
|
||||
break;
|
||||
} else if (tok == yaID__LEX) {
|
||||
++depth; // yaID__LEX
|
||||
depth = tokenPipeScanParam(depth, false);
|
||||
if (tokenPeekp(depth)->token != yP_COLONCOLON) return 0;
|
||||
++depth; // yP_COLONCOLON
|
||||
continue;
|
||||
} else {
|
||||
return 0; // Miss
|
||||
}
|
||||
++depth;
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
int V3ParseImp::tokenPipelineId(int token) {
|
||||
const V3ParseBisonYYSType* nexttokp = tokenPeekp(0); // First char after yaID
|
||||
const int nexttok = nexttokp->token;
|
||||
|
@ -585,6 +612,7 @@ void V3ParseImp::tokenPipeline() {
|
|||
// If a paren, read another
|
||||
if (token == '(' //
|
||||
|| token == ':' //
|
||||
|| token == '=' //
|
||||
|| token == yCONST__LEX //
|
||||
|| token == yGLOBAL__LEX //
|
||||
|| token == yLOCAL__LEX //
|
||||
|
@ -609,6 +637,10 @@ void V3ParseImp::tokenPipeline() {
|
|||
} else if (nexttok == yFORK) {
|
||||
token = yP_COLON__FORK;
|
||||
}
|
||||
} else if (token == '=') {
|
||||
if (nexttokp->token == yNEW__LEX || tokenPipeScanEqNew(0)) {
|
||||
token = yP_EQ__NEW;
|
||||
} // else still '='
|
||||
} else if (token == yCONST__LEX) {
|
||||
if (nexttok == yREF) {
|
||||
token = yCONST__REF;
|
||||
|
|
|
@ -308,6 +308,7 @@ private:
|
|||
size_t tokenPipeScanBracket(size_t depth) VL_MT_DISABLED;
|
||||
size_t tokenPipeScanParam(size_t depth, bool forInst) VL_MT_DISABLED;
|
||||
size_t tokenPipeScanTypeEq(size_t depth) VL_MT_DISABLED;
|
||||
size_t tokenPipeScanEqNew(size_t depth) VL_MT_DISABLED;
|
||||
const V3ParseBisonYYSType* tokenPeekp(size_t depth) VL_MT_DISABLED;
|
||||
void preprocDumps(std::ostream& os, bool forInputs) VL_MT_DISABLED;
|
||||
};
|
||||
|
|
|
@ -4383,11 +4383,28 @@ class WidthVisitor final : public VNVisitor {
|
|||
bool assign = false;
|
||||
if (VN_IS(nodep->backp(), Assign)) { // assignment case
|
||||
assign = true;
|
||||
AstClassRefDType* const refp
|
||||
= m_vup ? VN_CAST(m_vup->dtypeNullSkipRefp(), ClassRefDType) : nullptr;
|
||||
AstNode* warnp = nullptr;
|
||||
AstClassRefDType* refp = nullptr;
|
||||
|
||||
if (nodep->isScoped()) { // = ClassOrPackage::new
|
||||
UASSERT_OBJ(nodep->classOrPackagep(), nodep, "Unlinked classOrPackage");
|
||||
warnp = nodep->classOrPackagep();
|
||||
if (AstClass* const classp = VN_CAST(warnp, Class)) {
|
||||
AstClassRefDType* const adtypep
|
||||
= new AstClassRefDType{nodep->fileline(), classp, nullptr};
|
||||
v3Global.rootp()->typeTablep()->addTypesp(adtypep);
|
||||
refp = adtypep;
|
||||
}
|
||||
} else { // = new
|
||||
warnp = m_vup->dtypeNullp();
|
||||
refp = m_vup ? VN_CAST(m_vup->dtypeNullSkipRefp(), ClassRefDType) : nullptr;
|
||||
}
|
||||
if (!refp) { // e.g. int a = new;
|
||||
nodep->v3error("new() assignment not legal to non-class data type "
|
||||
+ (m_vup->dtypeNullp() ? m_vup->dtypep()->prettyDTypeNameQ() : ""));
|
||||
nodep->v3error("new() assignment not legal to non-class "
|
||||
+ (VN_IS(warnp, NodeDType) ? (
|
||||
"data type "s + VN_AS(warnp, NodeDType)->prettyDTypeNameQ())
|
||||
: warnp ? warnp->prettyNameQ()
|
||||
: ""));
|
||||
nodep->dtypep(m_vup->dtypep());
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -531,7 +531,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
|
|||
%token<fl> ':' // See also yP_COLON__BEGIN or yP_COLON__FORK
|
||||
%token<fl> ';'
|
||||
%token<fl> '<'
|
||||
%token<fl> '='
|
||||
%token<fl> '=' // See also yP_EQ__NEW
|
||||
%token<fl> '>'
|
||||
%token<fl> '?'
|
||||
%token<fl> '@'
|
||||
|
@ -1024,8 +1024,9 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
|
|||
%token<fl> yP_SSRIGHT ">>>"
|
||||
%token<fl> yP_POW "**"
|
||||
|
||||
%token<fl> yP_COLON__BEGIN ":-begin"
|
||||
%token<fl> yP_COLON__FORK ":-fork"
|
||||
%token<fl> yP_COLON__BEGIN ":-then-begin"
|
||||
%token<fl> yP_COLON__FORK ":-then-fork"
|
||||
%token<fl> yP_EQ__NEW "=-then-new"
|
||||
%token<fl> yP_PAR__IGNORE "(-ignored" // Used when sequence_expr:expr:( is ignored
|
||||
%token<fl> yP_PAR__STRENGTH "(-for-strength"
|
||||
|
||||
|
@ -2377,10 +2378,12 @@ variable_decl_assignment<varp>: // ==IEEE: variable_decl_assignment
|
|||
| idSVKwd { $$ = nullptr; }
|
||||
//
|
||||
// // IEEE: "dynamic_array_variable_identifier '[' ']' [ '=' dynamic_array_new ]"
|
||||
// // Matches above with variable_dimensionE = "[]"
|
||||
| id variable_dimensionListE sigAttrListE yP_EQ__NEW dynamic_array_new
|
||||
{ $$ = VARDONEA($<fl>1, *$1, $2, $3); $$->valuep($5); }
|
||||
// // IEEE: "class_variable_identifier [ '=' class_new ]"
|
||||
// // variable_dimensionE must be empty
|
||||
// // Pushed into variable_declExpr:dynamic_array_new
|
||||
| id variable_dimensionListE sigAttrListE yP_EQ__NEW class_new
|
||||
{ $$ = VARDONEA($<fl>1, *$1, $2, $3); $$->valuep($5); }
|
||||
;
|
||||
|
||||
list_of_tf_variable_identifiers<nodep>: // ==IEEE: list_of_tf_variable_identifiers
|
||||
|
@ -3666,8 +3669,8 @@ statement_item<nodep>: // IEEE: statement_item
|
|||
// // IEEE: blocking_assignment
|
||||
// // 1800-2009 restricts LHS of assignment to new to not have a range
|
||||
// // This is ignored to avoid conflicts
|
||||
| fexprLvalue '=' class_newNoScope ';' { $$ = new AstAssign{$2, $1, $3}; }
|
||||
| fexprLvalue '=' dynamic_array_new ';' { $$ = new AstAssign{$2, $1, $3}; }
|
||||
| fexprLvalue yP_EQ__NEW dynamic_array_new ';' { $$ = new AstAssign{$2, $1, $3}; }
|
||||
| fexprLvalue yP_EQ__NEW class_new ';' { $$ = new AstAssign{$2, $1, $3}; }
|
||||
// // IEEE: inc_or_dec_expression
|
||||
| finc_or_dec_expression ';' { $$ = $1; }
|
||||
//
|
||||
|
@ -3919,7 +3922,19 @@ pinc_or_dec_expression<nodeExprp>: // IEEE: inc_or_dec_expression (for property
|
|||
//UNSUP BISONPRE_COPY(inc_or_dec_expression,{s/~l~/pev_/g}) // {copied}
|
||||
//UNSUP ;
|
||||
|
||||
class_newNoScope<nodeExprp>: // IEEE: class_new but no packageClassScope (issue #4199)
|
||||
class_new<nodeExprp>: // IEEE: class_new
|
||||
// // See V3ParseImp::tokenPipeScanEqNew that searches for '=' ... yNEW__LEX
|
||||
class_newNoScope
|
||||
{ $$ = $1; }
|
||||
// // Special precedence so (...) doesn't match expr
|
||||
// // A scope is not legal in front of a AstNewCopy
|
||||
| packageClassScopeNoId yNEW__ETC
|
||||
{ $$ = AstDot::newIfPkg($<fl>2, $1, new AstNew{$2, nullptr, true}); }
|
||||
| packageClassScopeNoId yNEW__PAREN '(' list_of_argumentsE ')'
|
||||
{ $$ = AstDot::newIfPkg($<fl>2, $1, new AstNew{$2, $4, true}); }
|
||||
;
|
||||
|
||||
class_newNoScope<nodeExprp>: // IEEE: class_new but no packageClassScope
|
||||
// // Special precedence so (...) doesn't match expr
|
||||
yNEW__ETC { $$ = new AstNew{$1, nullptr}; }
|
||||
| yNEW__ETC expr { $$ = new AstNewCopy{$1, $2}; }
|
||||
|
@ -7425,7 +7440,7 @@ class_typeExtImpOne<nodeExprp>: // part of IEEE: class_type, where we either ge
|
|||
|
||||
//=== Below rules assume special scoping per above
|
||||
|
||||
packageClassScopeNoId<nodep>: // IEEE: [package_scope] not followed by yaID
|
||||
packageClassScopeNoId<nodeExprp>: // IEEE: [package_scope] not followed by yaID
|
||||
packageClassScope { $$ = $1; }
|
||||
;
|
||||
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
%Error: t/t_class_new_scoped.v:45:21: syntax error, unexpected new, expecting IDENTIFIER-for-type
|
||||
45 | b = ClsNoArg::new;
|
||||
| ^~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: t/t_class_new_scoped.v:50:19: syntax error, unexpected new-then-paren, expecting IDENTIFIER-for-type
|
||||
50 | b = ClsArg::new(20, 1);
|
||||
| ^~~
|
||||
%Error: t/t_class_new_scoped.v:55:27: syntax error, unexpected new-then-paren, expecting IDENTIFIER-for-type
|
||||
55 | b = ClsParam#(100)::new(33);
|
||||
| ^~~
|
||||
%Error: t/t_class_new_scoped.v:60:27: syntax error, unexpected new-then-paren, expecting IDENTIFIER-for-type
|
||||
60 | b = ClsParam#(200)::new(44);
|
||||
| ^~~
|
||||
%Error: Exiting due to
|
|
@ -11,6 +11,8 @@ import vltest_bootstrap
|
|||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.lint(fails=True, expect_filename=test.golden_filename)
|
||||
test.compile()
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
|
@ -8,59 +8,59 @@
|
|||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
|
||||
class Base;
|
||||
int imembera = 10;
|
||||
function new(int i);
|
||||
imembera = i;
|
||||
endfunction
|
||||
int m_ia = 10;
|
||||
function new(int i);
|
||||
m_ia = i;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class ClsNoArg extends Base;
|
||||
function new();
|
||||
super.new(5);
|
||||
endfunction : new
|
||||
function new();
|
||||
super.new(5);
|
||||
endfunction : new
|
||||
endclass
|
||||
|
||||
class ClsArg extends Base;
|
||||
function new(int i, int j);
|
||||
super.new(i + j);
|
||||
endfunction
|
||||
function new(int i, int j);
|
||||
super.new(i + j);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class ClsParam #(int ADD = 100) extends Base;
|
||||
function new(int def = 42);
|
||||
super.new(def + ADD);
|
||||
endfunction
|
||||
function new(int def = 42);
|
||||
super.new(def + ADD);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t (/*AUTOARG*/);
|
||||
initial begin
|
||||
Base b;
|
||||
ClsNoArg c1;
|
||||
ClsArg c2;
|
||||
ClsParam#(100) c3;
|
||||
ClsParam#(200) c4;
|
||||
initial begin
|
||||
Base b;
|
||||
ClsNoArg c1;
|
||||
ClsArg c2;
|
||||
ClsParam#(100) c3;
|
||||
ClsParam#(200) c4;
|
||||
|
||||
c1 = new;
|
||||
`checkd(c1.imembera, 5);
|
||||
b = ClsNoArg::new;
|
||||
`checkd(b.imembera, 5);
|
||||
c1 = new;
|
||||
`checkd(c1.m_ia, 5);
|
||||
b = ClsNoArg::new;
|
||||
`checkd(b.m_ia, 5);
|
||||
|
||||
c2 = new(20, 1);
|
||||
`checkd(c2.imembera, 21);
|
||||
b = ClsArg::new(20, 1);
|
||||
`checkd(b.imembera, 21);
|
||||
c2 = new(20, 1);
|
||||
`checkd(c2.m_ia, 21);
|
||||
b = ClsArg::new(20, 1);
|
||||
`checkd(b.m_ia, 21);
|
||||
|
||||
c3 = new(33);
|
||||
`checkd(c3.imembera, 133);
|
||||
b = ClsParam#(100)::new(33);
|
||||
`checkd(b.imembera, 133);
|
||||
c3 = new(33);
|
||||
`checkd(c3.m_ia, 133);
|
||||
b = ClsParam#(100)::new(33);
|
||||
`checkd(b.m_ia, 133);
|
||||
|
||||
c4 = new(44);
|
||||
`checkd(c4.imembera, 244);
|
||||
b = ClsParam#(200)::new(44);
|
||||
`checkd(b.imembera, 244);
|
||||
c4 = new(44);
|
||||
`checkd(c4.m_ia, 244);
|
||||
b = ClsParam#(200)::new(44);
|
||||
`checkd(b.m_ia, 244);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
%Error: t/t_class_new_scoped_bad.v:17:16: new() assignment not legal to non-class 'Pkg'
|
||||
: ... note: In instance 't'
|
||||
17 | c = Pkg::new;
|
||||
| ^~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: Exiting due to
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2024 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('linter')
|
||||
|
||||
test.lint(fails=True, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
|
@ -0,0 +1,19 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
|
||||
package Pkg;
|
||||
endpackage
|
||||
|
||||
class C;
|
||||
endclass
|
||||
|
||||
module t;
|
||||
C c;
|
||||
initial begin
|
||||
c = Pkg::new; // Bad
|
||||
end
|
||||
endmodule
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2024 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile()
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
|
@ -0,0 +1,30 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t ();
|
||||
class SuperCls;
|
||||
int s = 2;
|
||||
function new(int def = 3);
|
||||
s = def;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class Cls extends SuperCls;
|
||||
function new(int def = 42);
|
||||
s = def;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
SuperCls super_obj;
|
||||
|
||||
initial begin
|
||||
super_obj = Cls::new;
|
||||
if (super_obj.s != 42) $stop;
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
Loading…
Reference in New Issue