diff --git a/elab_lval.cc b/elab_lval.cc index 86cecdcb0..3c5c8011e 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -343,6 +343,40 @@ NetAssign_* PEIdent::elaborate_lval_method_class_member_(Design*des, return 0; } + // Detect assignment to constant properties. Note that the + // initializer constructor MAY assign to constant properties, + // as this is how the property gets its value. + property_qualifier_t qual = class_type->get_prop_qual(pidx); + if (qual.test_const()) { + if (class_type->get_prop_initialized(pidx)) { + cerr << get_fileline() << ": error: " + << "Property " << class_type->get_prop_name(pidx) + << " is constant in this method." + << " (scope=" << scope_path(scope) << ")" << endl; + des->errors += 1; + + } else if (scope->basename()!="new" && scope->basename()!="new@") { + cerr << get_fileline() << ": error: " + << "Property " << class_type->get_prop_name(pidx) + << " is constant in this method." + << " (scope=" << scope_path(scope) << ")" << endl; + des->errors += 1; + + } else { + + // Mark this property as initilized. This is used + // to know that we have initialized the constant + // object so the next assignment will be marked as + // illegal. + class_type->set_prop_initialized(pidx); + + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_lval_method_class_member_: " + << "Found initilzers for property " << class_type->get_prop_name(pidx) << endl; + } + } + } + NetAssign_*this_lval = new NetAssign_(this_net); this_lval->set_property(member_name); @@ -842,6 +876,12 @@ bool PEIdent::elaborate_lval_net_class_member_(Design*des, NetScope*scope, << " is not accessible (l-value) in this context." << " (scope=" << scope_path(scope) << ")" << endl; des->errors += 1; + + } else if (qual.test_const()) { + cerr << get_fileline() << ": error: " + << "Property " << class_type->get_prop_name(pidx) + << " is constant in this context." << endl; + des->errors += 1; } lv->set_property(method_name); diff --git a/elaborate.cc b/elaborate.cc index 418206c20..204af613d 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -4883,6 +4883,13 @@ static void elaborate_classes(Design*des, NetScope*scope, ; cur != classes.end() ; ++ cur) { netclass_t*use_class = scope->find_class(cur->second->pscope_name()); use_class->elaborate(des, cur->second); + + if (use_class->test_for_missing_initializers()) { + cerr << cur->second->get_fileline() << ": error: " + << "Const properties of class " << use_class->get_name() + << " are missing initialization." << endl; + des->errors += 1; + } } } diff --git a/netclass.cc b/netclass.cc index 91636b95d..abf42071a 100644 --- a/netclass.cc +++ b/netclass.cc @@ -43,6 +43,7 @@ bool netclass_t::set_property(perm_string pname, property_qualifier_t qual, ivl_ tmp.name = pname; tmp.qual = qual; tmp.type = ptype; + tmp.initialized_flag = false; property_table_.push_back(tmp); properties_[pname] = property_table_.size()-1; @@ -88,6 +89,31 @@ ivl_type_t netclass_t::get_prop_type(size_t idx) const return property_table_[idx].type; } +bool netclass_t::get_prop_initialized(size_t idx) const +{ + assert(idx < property_table_.size()); + return property_table_[idx].initialized_flag; +} + +void netclass_t::set_prop_initialized(size_t idx) const +{ + assert(idx < property_table_.size()); + assert(! property_table_[idx].initialized_flag); + property_table_[idx].initialized_flag = true; +} + +bool netclass_t::test_for_missing_initializers() const +{ + for (size_t idx = 0 ; idx < property_table_.size() ; idx += 1) { + if (property_table_[idx].initialized_flag) + continue; + if (property_table_[idx].qual.test_const()) + return true; + } + + return false; +} + NetScope*netclass_t::method_from_name(perm_string name) const { NetScope*task = class_scope_->child( hname_t(name) ); diff --git a/netclass.h b/netclass.h index 8aac64583..71cd32d6a 100644 --- a/netclass.h +++ b/netclass.h @@ -57,6 +57,15 @@ class netclass_t : public ivl_type_s { property_qualifier_t get_prop_qual(size_t idx) const; ivl_type_t get_prop_type(size_t idx) const; + // These methods are used by the elaborator to note the + // initializer for constant properties. Properties start out + // as not initialized, and when elaboration detects an + // assignment to the property, it is marked initialized. + bool get_prop_initialized(size_t idx) const; + void set_prop_initialized(size_t idx) const; + + bool test_for_missing_initializers(void) const; + // Map the name of a property to its index. int property_idx_from_name(perm_string pname) const; @@ -84,6 +93,7 @@ class netclass_t : public ivl_type_s { perm_string name; property_qualifier_t qual; ivl_type_s* type; + mutable bool initialized_flag; }; std::vector property_table_; diff --git a/parse.y b/parse.y index bc120c050..32548fb11 100644 --- a/parse.y +++ b/parse.y @@ -581,7 +581,8 @@ static void current_function_set_statement(const YYLTYPE&loc, vector %type struct_data_type %type class_item_qualifier property_qualifier -%type property_qualifier_list property_qualifier_opt +%type class_item_qualifier_list property_qualifier_list +%type class_item_qualifier_opt property_qualifier_opt %type random_qualifier %type range range_opt variable_dimension @@ -695,6 +696,7 @@ class_identifier // lexor detects the name as a type. perm_string name = lex_strings.make($1); class_type_t*tmp = new class_type_t(name); + FILE_NAME(tmp, @1); pform_set_typedef(name, tmp); delete[]$1; $$ = tmp; @@ -793,6 +795,9 @@ class_item /* IEEE1800-2005: A.1.8 */ | property_qualifier_opt data_type list_of_variable_decl_assignments ';' { pform_class_property(@2, $1, $2, $3); } + | K_const class_item_qualifier_opt data_type list_of_variable_decl_assignments ';' + { pform_class_property(@1, $2 | property_qualifier_t::make_const(), $3, $4); } + /* Class methods... */ | method_qualifier_opt task_declaration @@ -837,6 +842,16 @@ class_item_qualifier /* IEEE1800-2005 A.1.8 */ | K_local { $$ = property_qualifier_t::make_local(); } ; +class_item_qualifier_list + : class_item_qualifier_list class_item_qualifier { $$ = $1 | $2; } + | class_item_qualifier { $$ = $1; } + ; + +class_item_qualifier_opt + : class_item_qualifier_list { $$ = $1; } + | { $$ = property_qualifier_t::make_none(); } + ; + class_new /* IEEE1800-2005 A.2.4 */ : K_new '(' ')' { PENewClass*tmp = new PENewClass; @@ -2056,6 +2071,7 @@ type_declaration // lexor detects the name as a type. perm_string name = lex_strings.make($3); class_type_t*tmp = new class_type_t(name); + FILE_NAME(tmp, @3); pform_set_typedef(name, tmp); delete[]$3; } @@ -2070,6 +2086,7 @@ type_declaration // lexor detects the name as a type. perm_string name = lex_strings.make($2); class_type_t*tmp = new class_type_t(name); + FILE_NAME(tmp, @2); pform_set_typedef(name, tmp); delete[]$2; } diff --git a/property_qual.h b/property_qual.h index df02c7080..aaaa37b86 100644 --- a/property_qual.h +++ b/property_qual.h @@ -39,6 +39,9 @@ class property_qualifier_t { static inline property_qualifier_t make_randc() { property_qualifier_t res; res.mask_ = 16; return res; } + static inline property_qualifier_t make_const() + { property_qualifier_t res; res.mask_ = 32; return res; } + inline property_qualifier_t operator | (property_qualifier_t r) { property_qualifier_t res; res.mask_ = mask_ | r.mask_; return res; } @@ -48,6 +51,7 @@ class property_qualifier_t { inline bool test_local() const { return mask_ & 4; } inline bool test_rand() const { return mask_ & 8; } inline bool test_randc() const { return mask_ & 16; } + inline bool test_const() const { return mask_ & 32; } private: int mask_;