vhdlpp: Support for subprogram overloading.

This commit is contained in:
Maciej Suminski 2016-01-29 16:34:10 +01:00
parent cfa038e75c
commit 2c4deee0c3
15 changed files with 288 additions and 115 deletions

View File

@ -61,9 +61,14 @@ int Architecture::elaborate(Entity*entity)
}
// Elaborate subprograms
for (map<perm_string,SubprogramHeader*>::const_iterator cur = cur_subprograms_.begin()
for (map<perm_string,SubHeaderList>::const_iterator cur = cur_subprograms_.begin()
; cur != cur_subprograms_.end() ; ++cur) {
errors += cur->second->elaborate();
const SubHeaderList& subp_list = cur->second;
for(SubHeaderList::const_iterator it = subp_list.begin();
it != subp_list.end(); ++it) {
errors += (*it)->elaborate();
}
}
// Create 'initial' and 'final' blocks for implicit
// initalization and clean-up actions

View File

@ -103,11 +103,18 @@ int Architecture::emit(ostream&out, Entity*entity)
errors += emit_signals(out, entity, this);
errors += emit_variables(out, entity, this);
for (map<perm_string,SubprogramHeader*>::const_iterator cur = cur_subprograms_.begin()
for (map<perm_string,SubHeaderList>::const_iterator cur = cur_subprograms_.begin()
; cur != cur_subprograms_.end() ; ++ cur) {
// Do not emit unbounded functions, we will just need fixed instances later
if(!cur->second->unbounded())
errors += cur->second->emit_package(out);
const SubHeaderList& subp_list = cur->second;
for(SubHeaderList::const_iterator it = subp_list.begin();
it != subp_list.end(); ++it) {
SubprogramHeader*subp = *it;
// Do not emit unbounded functions, we will just need fixed instances later
if(!subp->unbounded())
errors += subp->emit_package(out);
}
}
for (list<Architecture::Statement*>::iterator cur = statements_.begin()

View File

@ -150,22 +150,35 @@ void ScopeBase::dump_scope(ostream&out) const
}
// Dump subprograms
out << " -- Imported Subprograms" << endl;
for (map<perm_string,SubprogramHeader*>::const_iterator cur = use_subprograms_.begin()
; cur != use_subprograms_.end() ; ++cur) {
out << " subprogram " << cur->first << " is" << endl;
cur->second->dump(out);
if(cur->second->body())
cur->second->body()->dump(out);
out << " end subprogram " << cur->first << endl;
for (map<perm_string,SubHeaderList>::const_iterator cur = use_subprograms_.begin()
; cur != cur_subprograms_.end() ; ++ cur) {
const SubHeaderList& subp_list = cur->second;
for(SubHeaderList::const_iterator it = subp_list.begin();
it != subp_list.end(); ++it) {
const SubprogramHeader*subp = *it;
out << " subprogram " << cur->first << " is" << endl;
subp->dump(out);
if(subp->body())
subp->body()->dump(out);
out << " end subprogram " << cur->first << endl;
}
}
out << " -- Subprograms from this scope" << endl;
for (map<perm_string,SubprogramHeader*>::const_iterator cur = cur_subprograms_.begin()
for (map<perm_string,SubHeaderList>::const_iterator cur = cur_subprograms_.begin()
; cur != cur_subprograms_.end() ; ++cur) {
out << " subprogram " << cur->first << " is" << endl;
cur->second->dump(out);
if(cur->second->body())
cur->second->body()->dump(out);
out << " end subprogram " << cur->first << endl;
const SubHeaderList& subp_list = cur->second;
for(SubHeaderList::const_iterator it = subp_list.begin();
it != subp_list.end(); ++it) {
const SubprogramHeader*subp = *it;
out << " subprogram " << cur->first << " is" << endl;
subp->dump(out);
if(subp->body())
subp->body()->dump(out);
out << " end subprogram " << cur->first << endl;
}
}
// Dump component declarations
out << " -- Components" << endl;

View File

@ -783,22 +783,29 @@ int ExpConditional::case_t::elaborate_expr(Entity*ent, ScopeBase*scope, const VT
return errors;
}
const VType*ExpFunc::probe_type(Entity*, ScopeBase*scope) const
const VType*ExpFunc::probe_type(Entity*ent, ScopeBase*scope) const
{
SubprogramHeader*prog = def_;
if(!prog) {
prog = scope->find_subprogram(name_);
}
list<const VType*> arg_types;
if(!prog)
prog = library_find_subprogram(name_);
for(vector<Expression*>::const_iterator it = argv_.begin();
it != argv_.end(); ++it) {
arg_types.push_back((*it)->probe_type(ent, scope));
}
if(!prog) {
cerr << get_fileline() << ": sorry: could not find function ";
emit_subprogram_sig(cerr, name_, arg_types);
cerr << endl;
ivl_assert(*this, false);
prog = scope->match_subprogram(name_, &arg_types);
if(!prog)
prog = library_match_subprogram(name_, &arg_types);
if(!prog) {
cerr << get_fileline() << ": sorry: could not find function ";
emit_subprogram_sig(cerr, name_, arg_types);
cerr << endl;
ivl_assert(*this, false);
}
}
return prog->peek_return_type();
@ -808,13 +815,19 @@ int ExpFunc::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*)
{
int errors = 0;
ivl_assert(*this, scope);
SubprogramHeader*prog = scope->find_subprogram(name_);
ivl_assert(*this, def_ == 0); // do not elaborate twice
if(!prog)
prog = library_find_subprogram(name_);
// Create a list of argument types to find a matching subprogram
list<const VType*> arg_types;
for(vector<Expression*>::iterator it = argv_.begin();
it != argv_.end(); ++it)
arg_types.push_back((*it)->probe_type(ent, scope));
def_ = scope->match_subprogram(name_, &arg_types);
if(!def_)
def_ = library_match_subprogram(name_, &arg_types);
def_ = prog;
if(!def_) {
cerr << get_fileline() << ": error: could not find function ";
emit_subprogram_sig(cerr, name_, arg_types);
@ -824,15 +837,15 @@ int ExpFunc::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*)
// Elaborate arguments
for (size_t idx = 0; idx < argv_.size(); ++idx) {
errors += prog->elaborate_argument(argv_[idx], idx, ent, scope);
errors += def_->elaborate_argument(argv_[idx], idx, ent, scope);
}
// SystemVerilog functions work only with defined size data types, therefore
// if header does not specify argument or return type size, create a function
// instance that work with this particular size.
if(def_ && !def_->is_std() && def_->unbounded()) {
def_ = prog->make_instance(argv_, scope);
name_ = def_->name();
def_ = def_->make_instance(argv_, scope);
name_ = def_->name(); // TODO necessary?
}
return errors;

View File

@ -1,6 +1,8 @@
/*
* Copyright (c) 2011-2013 Stephen Williams (steve@icarus.com)
* Copyright CERN 2013 / Stephen Williams (steve@icarus.com)
* Copyright CERN 2016
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -73,9 +75,9 @@ void library_add_directory(const char*directory)
library_search_path.push_front(directory);
}
SubprogramHeader*library_find_subprogram(perm_string name)
SubprogramHeader*library_match_subprogram(perm_string name, const list<const VType*>*params)
{
SubprogramHeader*subp = NULL;
SubprogramHeader*subp;
map<perm_string,struct library_contents>::const_iterator lib_it;
for(lib_it = libraries.begin(); lib_it != libraries.end(); ++lib_it) {
@ -83,7 +85,7 @@ SubprogramHeader*library_find_subprogram(perm_string name)
map<perm_string,Package*>::const_iterator pack_it;
for(pack_it = lib.packages.begin(); pack_it != lib.packages.end(); ++pack_it) {
if((subp = pack_it->second->find_subprogram(name)))
if((subp = pack_it->second->match_subprogram(name, params)))
return subp;
}
}

View File

@ -19,15 +19,18 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
class SubprogramHeader;
#include <list>
extern void library_set_work_path(const char*work_path);
extern void library_add_directory(const char*directory);
class SubprogramHeader;
class VType;
void library_set_work_path(const char*work_path);
void library_add_directory(const char*directory);
int elaborate_libraries(void);
int emit_packages(void);
extern SubprogramHeader*library_find_subprogram(perm_string name);
SubprogramHeader*library_match_subprogram(perm_string name, const list<const VType*>*params);
#endif /* IVL_library_H */

View File

@ -24,6 +24,8 @@
# include "parse_misc.h"
# include "std_types.h"
# include "ivl_assert.h"
# include <list>
# include <iterator>
Package::Package(perm_string n, const ActiveScope&ref)
: Scope(ref), name_(n)
@ -45,9 +47,14 @@ int Package::elaborate()
{
int errors = 0;
for (map<perm_string,SubprogramHeader*>::const_iterator cur = cur_subprograms_.begin()
; cur != cur_subprograms_.end() ; ++cur) {
errors += cur->second->elaborate();
for (map<perm_string,SubHeaderList>::iterator cur = cur_subprograms_.begin()
; cur != cur_subprograms_.end() ; ++ cur) {
SubHeaderList& subp_list = cur->second;
for(SubHeaderList::iterator it = subp_list.begin();
it != subp_list.end(); ++it) {
errors += (*it)->elaborate();
}
}
return errors;
@ -108,10 +115,15 @@ void Package::write_to_stream(ostream&fd) const
fd << ";" << endl;
}
for (map<perm_string,SubprogramHeader*>::const_iterator cur = cur_subprograms_.begin()
for (map<perm_string,SubHeaderList>::const_iterator cur = cur_subprograms_.begin()
; cur != cur_subprograms_.end() ; ++cur) {
cur->second->write_to_stream(fd);
fd << ";" << endl;
const SubHeaderList& subp_list = cur->second;
for(SubHeaderList::const_iterator it = subp_list.begin();
it != subp_list.end(); ++it) {
(*it)->write_to_stream(fd);
fd << ";" << endl;
}
}
for (map<perm_string,ComponentBase*>::const_iterator cur = old_components_.begin()
@ -128,14 +140,20 @@ void Package::write_to_stream(ostream&fd) const
fd << "end package " << name_ << ";" << endl;
fd << "package body " << name_ << " is" << endl;
for (map<perm_string,SubprogramHeader*>::const_iterator cur = cur_subprograms_.begin()
for (map<perm_string,SubHeaderList>::const_iterator cur = cur_subprograms_.begin()
; cur != cur_subprograms_.end() ; ++cur) {
SubprogramHeader*subp = cur->second;
if(subp->body()) {
subp->write_to_stream(fd);
fd << " is" << endl;
subp->body()->write_to_stream(fd);
const SubHeaderList& subp_list = cur->second;
for(SubHeaderList::const_iterator it = subp_list.begin();
it != subp_list.end(); ++it) {
const SubprogramHeader*subp = *it;
if(subp->body()) {
subp->write_to_stream(fd);
fd << " is" << endl;
subp->body()->write_to_stream(fd);
}
}
}
}
fd << "end " << name_ << ";" << endl;
}

View File

@ -20,8 +20,9 @@
# include "package.h"
# include "subprogram.h"
# include <iostream>
# include "ivl_assert.h"
# include <iostream>
# include <list>
using namespace std;
@ -64,14 +65,22 @@ int Package::emit_package(ostream&fd) const
//}
fd << "package \\" << name() << " ;" << endl;
for (map<perm_string,SubprogramHeader*>::const_iterator cur = cur_subprograms_.begin()
for (map<perm_string,SubHeaderList>::const_iterator cur = cur_subprograms_.begin()
; cur != cur_subprograms_.end() ; ++ cur) {
// Do not emit unbounded functions, we will just need fixed instances later
if(!cur->second->unbounded())
errors += cur->second->emit_package(fd);
else
fd << "/* function " << cur->second->name() <<
" has to be instantiated, skipping */" << endl;
const SubHeaderList& subp_list = cur->second;
for(SubHeaderList::const_iterator it = subp_list.begin();
it != subp_list.end(); ++it) {
SubprogramHeader*header = *it;
// Do not emit unbounded functions, we will just need fixed instances later
if(!header->unbounded())
errors += header->emit_package(fd);
else
fd << "/* function " << header->name()
<< " has to be instantiated, skipping */" << endl;
}
}
fd << "endpackage /* " << name() << " */" << endl;

View File

@ -1701,7 +1701,8 @@ name /* IEEE 1076-2008 P8.1 */
tmp = parse_char_enums($1);
if(!tmp) {
perm_string name = lex_strings.make($1);
if(active_scope->find_subprogram(name) && !parse_type_by_name(name))
/* There are functions that have the same name types, e.g. integer */
if(!active_scope->find_subprogram(name).empty() && !parse_type_by_name(name))
tmp = new ExpFunc(name);
else
tmp = new ExpName(name);
@ -2565,12 +2566,10 @@ subprogram_body /* IEEE 1076-2008 P4.3 */
K_begin subprogram_statement_part K_end
subprogram_kind_opt identifier_opt ';'
{ SubprogramHeader*prog = $1;
SubprogramHeader*tmp = active_scope->recall_subprogram(prog->name());
if (tmp && prog->compare_specification(tmp)) {
SubprogramHeader*tmp = active_scope->recall_subprogram(prog);
if (tmp) {
delete prog;
prog = tmp;
} else if (tmp) {
errormsg(@1, "Subprogram specification for %s doesn't match specification in package header.\n", prog->name().str());
}
SubprogramBody*body = new SubprogramBody();

View File

@ -70,9 +70,14 @@ ScopeBase::ScopeBase(const ActiveScope&ref)
// an active scope and is making the actual scope. At this point
// we know that "this" is the parent scope for the subprograms,
// so set it now.
for (map<perm_string,SubprogramHeader*>::iterator cur = cur_subprograms_.begin()
; cur != cur_subprograms_.end(); ++cur) {
cur->second->set_parent(this);
for (map<perm_string,SubHeaderList>::iterator cur = cur_subprograms_.begin()
; cur != cur_subprograms_.end(); ++cur) {
SubHeaderList& subp_list = cur->second;
for (SubHeaderList::iterator it = subp_list.begin();
it != subp_list.end(); ++it) {
(*it)->set_parent(this);
}
}
}
@ -94,7 +99,10 @@ void ScopeBase::cleanup()
delete_all(new_components_);
delete_all(cur_types_);
delete_all(cur_constants_);
delete_all(cur_subprograms_);
for (map<perm_string,SubHeaderList>::iterator cur = cur_subprograms_.begin()
; cur != cur_subprograms_.end() ; ++cur) {
delete_all(cur->second);
}
}
const VType*ScopeBase::find_type(perm_string by_name)
@ -157,32 +165,42 @@ const InterfacePort* ScopeBase::find_param(perm_string) const
const InterfacePort* ScopeBase::find_param_all(perm_string by_name) const
{
for(map<perm_string,SubprogramHeader*>::const_iterator it = use_subprograms_.begin();
it != use_subprograms_.end(); ++it) {
if(const InterfacePort*port = it->second->find_param(by_name))
return port;
for(map<perm_string,SubHeaderList>::const_iterator cur = use_subprograms_.begin();
cur != use_subprograms_.end(); ++cur) {
const SubHeaderList& subp_list = cur->second;
for(SubHeaderList::const_iterator it = subp_list.begin();
it != subp_list.end(); ++it) {
if(const InterfacePort*port = (*it)->find_param(by_name))
return port;
}
}
for(map<perm_string,SubprogramHeader*>::const_iterator it = cur_subprograms_.begin();
it != cur_subprograms_.end(); ++it) {
if(const InterfacePort*port = it->second->find_param(by_name))
return port;
for(map<perm_string,SubHeaderList>::const_iterator cur = cur_subprograms_.begin();
cur != cur_subprograms_.end(); ++cur) {
const SubHeaderList& subp_list = cur->second;
for(SubHeaderList::const_iterator it = subp_list.begin();
it != subp_list.end(); ++it) {
if(const InterfacePort*port = (*it)->find_param(by_name))
return port;
}
}
return NULL;
}
SubprogramHeader* ScopeBase::find_subprogram(perm_string name) const
SubHeaderList ScopeBase::find_subprogram(perm_string name) const
{
map<perm_string,SubprogramHeader*>::const_iterator cur;
map<perm_string,SubHeaderList>::const_iterator cur;
cur = cur_subprograms_.find(name);
if (cur != cur_subprograms_.end())
return cur->second;
return cur->second;
cur = use_subprograms_.find(name);
if (cur != use_subprograms_.end())
return cur->second;
return cur->second;
return find_std_subprogram(name);
}
@ -217,9 +235,9 @@ void ScopeBase::do_use_from(const ScopeBase*that)
old_components_[cur->first] = cur->second;
}
for (map<perm_string,SubprogramHeader*>::const_iterator cur = that->cur_subprograms_.begin()
for (map<perm_string,SubHeaderList>::const_iterator cur = that->cur_subprograms_.begin()
; cur != that->cur_subprograms_.end() ; ++ cur) {
if (cur->second == 0)
if (cur->second.empty())
continue;
use_subprograms_[cur->first] = cur->second;
}
@ -266,21 +284,86 @@ void ScopeBase::transfer_from(ScopeBase&ref, transfer_type_t what)
}
}
SubprogramHeader*ScopeBase::match_subprogram(perm_string name,
const list<const VType*>*params) const
{
int req_param_count = params ? params->size() : 0;
// Find all subprograms with matching name
SubHeaderList l = find_std_subprogram(name);
map<perm_string,SubHeaderList>::const_iterator cur;
cur = use_subprograms_.find(name);
if (cur != use_subprograms_.end())
copy(cur->second.begin(), cur->second.end(),
front_insert_iterator<SubHeaderList>(l));
cur = cur_subprograms_.find(name);
if(cur != cur_subprograms_.end())
copy(cur->second.begin(), cur->second.end(),
front_insert_iterator<SubHeaderList>(l));
// Find the matching one
for(SubHeaderList::iterator it = l.begin(); it != l.end(); ++it) {
SubprogramHeader*subp = *it;
if(req_param_count != subp->param_count())
continue;
// Do not check the return type here, it might depend on the arguments
if(params) {
list<const VType*>::const_iterator p = params->begin();
bool ok = true;
for(int i = 0; i < req_param_count; ++i) {
const VType*param_type = subp->peek_param_type(i);
if(*p && param_type && !param_type->type_match(*p)) {
ok = false;
break;
}
++p;
}
if(!ok)
continue; // check another function
}
// Yay, we have a match!
return subp;
}
return NULL;
}
void ActiveScope::set_package_header(Package*pkg)
{
assert(package_header_ == 0);
package_header_ = pkg;
}
SubprogramHeader* ActiveScope::recall_subprogram(perm_string name) const
SubprogramHeader* ActiveScope::recall_subprogram(const SubprogramHeader*subp) const
{
if (SubprogramHeader*tmp = find_subprogram(name))
return tmp;
list<const VType*> arg_types;
SubprogramHeader*tmp;
if (package_header_)
return package_header_->find_subprogram(name);
for(int i = 0; i < subp->param_count(); ++i)
arg_types.push_back(subp->peek_param_type(i));
return 0;
if ((tmp = match_subprogram(subp->name(), &arg_types))) {
assert(!tmp->body());
return tmp;
}
if (package_header_) {
tmp = package_header_->match_subprogram(subp->name(), &arg_types);
assert(!tmp || !tmp->body());
return tmp;
}
return NULL;
}
bool ActiveScope::is_vector_name(perm_string name) const

View File

@ -36,6 +36,8 @@ class SubprogramHeader;
class VType;
class SequentialStmt;
typedef list<SubprogramHeader*> SubHeaderList;
template<typename T>
struct delete_object{
void operator()(T* item) { delete item; }
@ -59,7 +61,7 @@ class ScopeBase {
Variable* find_variable(perm_string by_name) const;
virtual const InterfacePort* find_param(perm_string by_name) const;
const InterfacePort* find_param_all(perm_string by_name) const;
SubprogramHeader* find_subprogram(perm_string by_name) const;
SubHeaderList find_subprogram(perm_string by_name) const;
// Checks if a string is one of possible enum values. If so, the enum
// type is returned, otherwise NULL.
const VTypeEnum* is_enum_name(perm_string name) const;
@ -70,10 +72,10 @@ class ScopeBase {
void transfer_from(ScopeBase&ref, transfer_type_t what = ALL);
inline void bind_subprogram(perm_string name, SubprogramHeader*obj)
{ map<perm_string, SubprogramHeader*>::iterator it;
{ map<perm_string, SubHeaderList>::iterator it;
if((it = use_subprograms_.find(name)) != use_subprograms_.end() )
use_subprograms_.erase(it);
cur_subprograms_[name] = obj;
it->second.remove(obj);
cur_subprograms_[name].push_back(obj);
}
// Adds a statement to implicit initializers list
@ -92,6 +94,10 @@ class ScopeBase {
void dump_scope(ostream&out) const;
// Looks for a subprogram with specified name and parameter types.
SubprogramHeader*match_subprogram(perm_string name,
const list<const VType*>*params) const;
protected:
void cleanup();
@ -134,8 +140,8 @@ class ScopeBase {
std::map<perm_string, struct const_t*> use_constants_; //imported constants
std::map<perm_string, struct const_t*> cur_constants_; //current constants
std::map<perm_string, SubprogramHeader*> use_subprograms_; //imported
std::map<perm_string, SubprogramHeader*> cur_subprograms_; //current
std::map<perm_string, SubHeaderList> use_subprograms_; //imported
std::map<perm_string, SubHeaderList> cur_subprograms_; //current
std::list<const VTypeEnum*> use_enums_;
@ -191,7 +197,7 @@ class ActiveScope : public ScopeBase {
// Locate the subprogram by name. The subprogram body uses
// this to locate the subprogram declaration. Note that the
// subprogram may be in a package header.
SubprogramHeader* recall_subprogram(perm_string name) const;
SubprogramHeader* recall_subprogram(const SubprogramHeader*subp) const;
/* All bind_name function check if the given name was present
* in previous scopes. If it is found, it is erased (but the pointer

View File

@ -178,10 +178,22 @@ int ProcedureCall::elaborate(Entity*ent, ScopeBase*scope)
{
int errors = 0;
def_ = scope->find_subprogram(name_);
assert(!def_); // do not elaborate twice
// Create a list of argument types to find a matching subprogram
list<const VType*> arg_types;
if(param_list_) {
for(list<named_expr_t*>::iterator it = param_list_->begin();
it != param_list_->end(); ++it) {
named_expr_t* e = *it;
arg_types.push_back(e->expr()->probe_type(ent, scope));
}
}
def_ = scope->match_subprogram(name_, &arg_types);
if(!def_)
def_ = library_find_subprogram(name_);
def_ = library_match_subprogram(name_, &arg_types);
if(!def_) {
cerr << get_fileline() << ": error: could not find procedure ";

View File

@ -22,11 +22,11 @@
#include "std_types.h"
#include "scope.h"
static std::map<perm_string,SubprogramHeader*> std_subprograms;
static std::map<perm_string,SubHeaderList> std_subprograms;
static inline void register_std_subprogram(SubprogramHeader*header)
{
std_subprograms[header->name()] = header;
std_subprograms[header->name()].push_back(header);
}
// Special case: to_integer function
@ -356,17 +356,20 @@ void preload_std_funcs(void)
void delete_std_funcs()
{
for(std::map<perm_string,SubprogramHeader*>::iterator it = std_subprograms.begin();
it != std_subprograms.end(); ++it) {
delete it->second;
for(std::map<perm_string,SubHeaderList>::iterator cur = std_subprograms.begin();
cur != std_subprograms.end(); ++cur) {
for(SubHeaderList::const_iterator it = cur->second.begin();
it != cur->second.end(); ++it) {
delete *it;
}
}
}
SubprogramHeader*find_std_subprogram(perm_string name)
SubHeaderList find_std_subprogram(perm_string name)
{
map<perm_string,SubprogramHeader*>::const_iterator cur = std_subprograms.find(name);
if (cur != std_subprograms.end())
return cur->second;
map<perm_string,SubHeaderList>::const_iterator cur = std_subprograms.find(name);
if(cur != std_subprograms.end())
return cur->second;
return NULL;
return SubHeaderList();
}

View File

@ -29,6 +29,6 @@ void preload_std_funcs();
void delete_std_funcs();
// Returns subprogram header for a requested function or NULL if it does not exist.
SubprogramHeader*find_std_subprogram(perm_string name);
SubHeaderList find_std_subprogram(perm_string name);
#endif /* IVL_std_funcs_H */

View File

@ -32,7 +32,6 @@
class InterfacePort;
class SequentialStmt;
class VType;
class SubprogramHeader;
class SubprogramBody : public LineInfo, public ScopeBase {
@ -73,6 +72,7 @@ class SubprogramHeader : public LineInfo {
// matches this subprogram and that subprogram.
bool compare_specification(SubprogramHeader*that) const;
int param_count() const { return ports_ ? ports_->size() : 0; }
const InterfacePort*find_param(perm_string nam) const;
const InterfacePort*peek_param(int idx) const;
const VType*peek_param_type(int idx) const;