From 4ffeb748f477a338ba58995e59cf58f16d29a381 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 6 Aug 2026 22:37:14 -0700 Subject: [PATCH] Accept initialized const static properties Static property declaration initializers are elaborated as standalone initialization processes instead of through a class constructor. The const-property bookkeeping therefore does not see the initializer and reports an initialized `const static` property as missing initialization. Record whether each parsed property has a declaration initializer and mark an initialized static const property when its signal is created. Also mark the signal as const and its declaration assignment as an initializer. This permits that initial assignment while rejecting later writes. Uninitialized static const properties remain errors. Signed-off-by: Lars-Peter Clausen --- elab_lval.cc | 6 ++++++ elab_sig.cc | 8 ++++++++ pform_pclass.cc | 5 +++-- pform_types.h | 5 ++++- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/elab_lval.cc b/elab_lval.cc index 51e674be9..c12250d6b 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -1132,6 +1132,12 @@ NetAssign_* PEIdent::elaborate_lval_net_class_member_(Design*des, NetScope*scope // part of the sig, as the l-value. NetNet*psig = class_type->find_static_property(method_name); ivl_assert(*this, psig); + if (psig->get_const()) { + cerr << get_fileline() << ": error: Assignment to const signal `" + << psig->name() << "` is not allowed." << endl; + des->errors++; + return nullptr; + } lv = new NetAssign_(psig); return lv; diff --git a/elab_sig.cc b/elab_sig.cc index dfb2a44c2..f023e0dbb 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -419,6 +419,14 @@ void netclass_t::elaborate_sig(Design*des, PClass*pclass) auto sig = new NetNet(class_scope_, cur->first, NetNet::REG, use_type); sig->set_line(cur->second); + sig->set_const(cur->second.qual.test_const()); + + if (cur->second.qual.test_const() && + cur->second.has_initializer) { + int pidx = property_idx_from_name(cur->first); + ivl_assert(cur->second, pidx >= 0); + set_prop_initialized(pidx); + } } for (map::iterator cur = pclass->funcs.begin() diff --git a/pform_pclass.cc b/pform_pclass.cc index e9276a49a..2e6694677 100644 --- a/pform_pclass.cc +++ b/pform_pclass.cc @@ -87,13 +87,14 @@ void pform_class_property(const struct vlltype&loc, } pform_cur_class->type->properties[curp->name.first] - = class_type_t::prop_info_t(property_qual,use_type); + = class_type_t::prop_info_t(property_qual, use_type, + curp->expr != nullptr); FILE_NAME(&pform_cur_class->type->properties[curp->name.first], loc); if (PExpr*rval = curp->expr.release()) { PExpr*lval = new PEIdent(curp->name.first, curp->name.second); FILE_NAME(lval, loc); - PAssign*tmp = new PAssign(lval, rval); + auto tmp = new PAssign(lval, rval, false, true); FILE_NAME(tmp, loc); if (property_qual.test_static()) diff --git a/pform_types.h b/pform_types.h index 021229e53..f0ff20ada 100644 --- a/pform_types.h +++ b/pform_types.h @@ -401,11 +401,14 @@ struct class_type_t : public data_type_t { // This is a map of the properties. Map the name to the type. struct prop_info_t : public LineInfo { inline prop_info_t() : qual(property_qualifier_t::make_none()) { } - inline prop_info_t(property_qualifier_t q, data_type_t*t) : qual(q), type(t) { } + inline prop_info_t(property_qualifier_t q, data_type_t *t, + bool init) + : qual(q), type(t), has_initializer(init) { } prop_info_t(prop_info_t&&) = default; prop_info_t& operator=(prop_info_t&&) = default; property_qualifier_t qual; std::unique_ptr type; + bool has_initializer = false; }; std::map properties;