Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

3692 lines (2953 sloc) 98.467 kb
//
// Copyright (C) 2011-2012 Nick Gasson
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#include "phase.h"
#include "util.h"
#include <assert.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
struct ident_list {
ident_t ident;
struct ident_list *next;
};
struct btree {
tree_t tree;
ident_t name;
struct btree *left;
struct btree *right;
};
struct scope {
struct btree *decls;
tree_t subprog;
// For design unit scopes
ident_t prefix;
struct ident_list *imported;
struct scope *down;
};
#define MAX_OVERLOADS 32
#define MAX_TS_MEMBERS 32
struct type_set {
type_t members[MAX_TS_MEMBERS];
unsigned n_members;
bool universal;
struct type_set *down;
};
static bool sem_check_constrained(tree_t t, type_t type);
static bool sem_check_array_ref(tree_t t);
static bool sem_declare(tree_t decl);
static bool sem_locally_static(tree_t t);
static struct scope *top_scope = NULL;
static int errors = 0;
static struct type_set *top_type_set = NULL;
static ident_t builtin_i;
static ident_t std_standard_i;
#define sem_error(t, ...) do { \
error_at(t ? tree_loc(t) : NULL , __VA_ARGS__); \
errors++; \
return false; \
} while (0)
static void scope_push(ident_t prefix)
{
struct scope *s = xmalloc(sizeof(struct scope));
s->decls = NULL;
s->prefix = prefix;
s->imported = NULL;
s->down = top_scope;
s->subprog = (top_scope ? top_scope->subprog : NULL) ;
top_scope = s;
}
static void scope_btree_free(struct btree *b)
{
if (b != NULL) {
scope_btree_free(b->left);
scope_btree_free(b->right);
free(b);
}
}
static void scope_ident_list_free(struct ident_list *list)
{
struct ident_list *it = list;
while (it != NULL) {
struct ident_list *next = it->next;
free(it);
it = next;
}
}
static void scope_pop(void)
{
assert(top_scope != NULL);
scope_ident_list_free(top_scope->imported);
scope_btree_free(top_scope->decls);
struct scope *s = top_scope;
top_scope = s->down;
free(s);
}
static void scope_ident_list_add(struct ident_list **list, ident_t i)
{
struct ident_list *c = xmalloc(sizeof(struct ident_list));
c->ident = i;
c->next = *list;
*list = c;
}
static void scope_apply_prefix(tree_t t)
{
if (top_scope->prefix)
tree_set_ident(t, ident_prefix(top_scope->prefix,
tree_ident(t), '.'));
}
#if 0
static void scope_dump_aux(struct btree *b)
{
printf("%-30s%s\n", istr(b->name), type_pp(tree_type(b->tree)));
if (b->left)
scope_dump_aux(b->left);
if (b->right)
scope_dump_aux(b->right);
}
static void scope_dump(void)
{
struct scope *s = top_scope;
while (s != NULL) {
printf("---------------------------\n");
if (s->decls)
scope_dump_aux(s->decls);
s = s->down;
}
}
#endif
static tree_t scope_find_in(ident_t i, struct scope *s, bool recur, int k)
{
if (s == NULL)
return NULL;
else {
struct btree *search = s->decls;
while (search != NULL) {
if (search->name == i) {
if (k == 0)
return search->tree;
else
--k;
}
search = ((i < search->name) ? search->left : search->right);
}
return (recur ? scope_find_in(i, s->down, true, k) : NULL);
}
}
static tree_t scope_find(ident_t i)
{
return scope_find_in(i, top_scope, true, 0);
}
static tree_t scope_find_nth(ident_t i, int n)
{
return scope_find_in(i, top_scope, true, n);
}
static bool scope_can_overload(tree_t t)
{
return tree_kind(t) == T_ENUM_LIT
|| tree_kind(t) == T_FUNC_DECL
|| tree_kind(t) == T_FUNC_BODY;
}
static bool scope_hides(tree_t a, tree_t b)
{
// True if declaration of b hides a
if ((tree_kind(a) == T_COMPONENT) || (tree_kind(b) == T_COMPONENT))
return false;
else if (type_eq(tree_type(a), tree_type(b))) {
return (tree_attr_str(a, builtin_i) != NULL)
&& (tree_attr_str(b, builtin_i) == NULL);
}
else
return false;
}
static struct btree *scope_btree_new(tree_t t, ident_t name)
{
struct btree *b = xmalloc(sizeof(struct btree));
b->tree = t;
b->name = name;
b->left = NULL;
b->right = NULL;
return b;
}
static void scope_insert_at(tree_t t, ident_t name, struct btree *where)
{
if (scope_hides(where->tree, t))
where->tree = t;
else {
struct btree **nextp =
((name < where->name) ? &where->left : &where->right);
if (*nextp == NULL)
*nextp = scope_btree_new(t, name);
else
scope_insert_at(t, name, *nextp);
}
}
static void scope_replace_at(tree_t t, tree_t with, struct btree *where)
{
assert(where != NULL);
if (where->tree == t)
where->tree = with;
// We need to walk over the whole tree as this may appear under
// multiple names
if (where->left != NULL)
scope_replace_at(t, with, where->left);
if (where->right != NULL)
scope_replace_at(t, with, where->right);
}
static bool scope_insert(tree_t t)
{
assert(top_scope != NULL);
if (!scope_can_overload(t)
&& scope_find_in(tree_ident(t), top_scope, false, 0))
sem_error(t, "%s already declared in this scope",
istr(tree_ident(t)));
if (top_scope->decls == NULL)
top_scope->decls = scope_btree_new(t, tree_ident(t));
else
scope_insert_at(t, tree_ident(t), top_scope->decls);
return true;
}
static void scope_insert_alias(tree_t t, ident_t name)
{
assert(top_scope != NULL);
scope_insert_at(t, name, top_scope->decls);
}
static void scope_replace(tree_t t, tree_t with)
{
assert(top_scope != NULL);
scope_replace_at(t, with, top_scope->decls);
}
static bool sem_check_stale(lib_t lib, tree_t t)
{
// Check if the source file corresponding to t has been modified
// more recently than the library unit
const loc_t *l = tree_loc(t);
if (l->file == NULL)
return true;
struct stat st;
if (stat(l->file, &st) < 0) {
if (errno != ENOENT)
fatal_errno("%s", l->file);
else
return true;
}
if (st.st_mtime > lib_mtime(lib, tree_ident(t)))
sem_error(NULL, "source file %s for unit %s has changed and must "
"be reanalysed", l->file, istr(tree_ident(t)));
else
return true;
}
static bool scope_import_unit(context_t ctx, lib_t lib, bool all)
{
// Check we haven't already imported this
for (struct scope *s = top_scope; s != NULL; s = s->down) {
struct ident_list *it;
for (it = s->imported; it != NULL; it = it->next) {
if (it->ident == ctx.name)
return true;
}
}
tree_t unit = lib_get(lib, ctx.name);
if (unit == NULL) {
error_at(&ctx.loc, "unit %s not found in library %s",
istr(ctx.name), istr(lib_name(lib)));
errors++;
return false;
}
if (!sem_check_stale(lib, unit))
return false;
for (unsigned n = 0; n < tree_decls(unit); n++) {
tree_t decl = tree_decl(unit, n);
if (tree_kind(decl) == T_ATTR_SPEC)
continue;
if (!sem_declare(decl))
return false;
// Make unqualified and package qualified names visible
const char *tmp = istr(tree_ident(decl));
const char *pqual = strchr(tmp, '.');
if (pqual != NULL)
scope_insert_alias(decl, ident_new(pqual + 1));
if (all) {
const char *unqual = strrchr(tmp, '.');
if (unqual != NULL)
scope_insert_alias(decl, ident_new(unqual + 1));
}
}
scope_ident_list_add(&top_scope->imported, ctx.name);
return true;
}
static void type_set_push(void)
{
struct type_set *t = xmalloc(sizeof(struct type_set));
t->n_members = 0;
t->down = top_type_set;
t->universal = false;
top_type_set = t;
}
static void type_set_push_universal(void)
{
type_set_push();
top_type_set->universal = true;
}
static void type_set_pop(void)
{
assert(top_type_set != NULL);
struct type_set *old = top_type_set;
top_type_set = old->down;
free(old);
}
static void type_set_add(type_t t)
{
assert(top_type_set != NULL);
assert(top_type_set->n_members < MAX_TS_MEMBERS);
assert(t != NULL);
assert(type_kind(t) != T_UNRESOLVED);
for (unsigned i = 0; i < top_type_set->n_members; i++) {
if (top_type_set->members[i] == t)
return;
}
top_type_set->members[top_type_set->n_members++] = t;
}
static void type_set_force(type_t t)
{
assert(top_type_set != NULL);
assert(t != NULL);
assert(type_kind(t) != T_UNRESOLVED);
top_type_set->members[0] = t;
top_type_set->n_members = 1;
top_type_set->universal = false;
}
static bool type_set_uniq_composite(type_t *pt)
{
assert(top_type_set != NULL);
*pt = NULL;
for (int i = 0; i < top_type_set->n_members; i++) {
type_t type = top_type_set->members[i];
bool comp = type_is_array(type);
if (comp) {
if (*pt != NULL)
return false;
else
*pt = top_type_set->members[i];
}
}
return (*pt != NULL);
}
#if 0
static void type_set_dump(void)
{
printf("type_set: { ");
if (top_type_set) {
for (unsigned n = 0; n < top_type_set->n_members; n++)
printf("%s ", istr(type_ident(top_type_set->members[n])));
}
printf("}\n");
}
#endif
static bool type_set_member(type_t t)
{
if (top_type_set == NULL || top_type_set->n_members == 0)
return true;
for (unsigned n = 0; n < top_type_set->n_members; n++) {
if (type_eq(top_type_set->members[n], t))
return true;
}
return top_type_set->universal;
}
static type_t sem_std_type(const char *name)
{
ident_t name_i = ident_new(name);
ident_t qual = ident_prefix(std_standard_i, name_i, '.');
tree_t decl = scope_find(qual);
if (decl == NULL)
fatal("cannot find %s type", istr(qual));
return tree_type(decl);
}
static tree_t sem_make_int(int i)
{
literal_t l;
l.kind = L_INT;
l.i = i;
tree_t t = tree_new(T_LITERAL);
tree_set_literal(t, l);
tree_set_type(t, sem_std_type("INTEGER"));
return t;
}
static tree_t sem_make_ref(tree_t to)
{
tree_t t = tree_new(T_REF);
tree_set_ident(t, tree_ident(to));
tree_set_ref(t, to);
tree_set_type(t, tree_type(to));
return t;
}
static tree_t sem_builtin_fn(ident_t name, type_t result,
const char *builtin, ...)
{
type_t f = type_new(T_FUNC);
type_set_ident(f, name);
type_set_result(f, result);
va_list ap;
va_start(ap, builtin);
type_t arg;
while ((arg = va_arg(ap, type_t)))
type_add_param(f, arg);
va_end(ap);
tree_t d = tree_new(T_FUNC_DECL);
tree_set_ident(d, name);
tree_set_type(d, f);
tree_add_attr_str(d, builtin_i, ident_new(builtin));
return d;
}
static void sem_declare_binary(ident_t name, type_t lhs, type_t rhs,
type_t result, const char *builtin)
{
tree_t d = sem_builtin_fn(name, result, builtin, NULL);
type_add_param(tree_type(d), lhs);
type_add_param(tree_type(d), rhs);
scope_insert(d);
}
static void sem_declare_unary(ident_t name, type_t operand,
type_t result, const char *builtin)
{
tree_t d = sem_builtin_fn(name, result, builtin, operand, NULL);
scope_insert(d);
}
static tree_t sem_bool_lit(type_t std_bool, bool v)
{
tree_t lit = type_enum_literal(std_bool, v ? 1 : 0);
return sem_make_ref(lit);
}
static tree_t sem_int_lit(type_t type, int64_t i)
{
literal_t l;
l.kind = L_INT;
l.i = i;
tree_t f = tree_new(T_LITERAL);
tree_set_literal(f, l);
tree_set_type(f, type);
return f;
}
static void sem_declare_predefined_ops(tree_t decl)
{
// Prefined operators are defined in LRM 93 section 7.2
type_t t = tree_type(decl);
ident_t mult = ident_new("\"*\"");
ident_t div = ident_new("\"/\"");
ident_t plus = ident_new("\"+\"");
ident_t minus = ident_new("\"-\"");
// Predefined operators
type_t std_bool = sem_std_type("BOOLEAN");
type_t std_int = sem_std_type("INTEGER");
type_kind_t kind = type_kind(t);
switch (kind) {
case T_SUBTYPE:
// Use operators of base type
break;
case T_CARRAY:
case T_UARRAY:
// Operators on arrays
sem_declare_binary(ident_new("\"=\""), t, t, std_bool, "aeq");
sem_declare_binary(ident_new("\"/=\""), t, t, std_bool, "aneq");
sem_declare_binary(ident_new("\"<\""), t, t, std_bool, "alt");
sem_declare_binary(ident_new("\"<=\""), t, t, std_bool, "aleq");
sem_declare_binary(ident_new("\">\""), t, t, std_bool, "agt");
sem_declare_binary(ident_new("\">=\""), t, t, std_bool, "ageq");
break;
case T_PHYSICAL:
// Multiplication
sem_declare_binary(mult, t, std_int, t, "mul");
//sem_declare_binary(mult, t, std_real, t, "mul");
sem_declare_binary(mult, std_int, t, t, "mul");
//sem_declare_binary(mult, std_real, t, t, "mul");
// Division
sem_declare_binary(div, t, std_int, t, "div");
//sem_declare_binary(div, t, std_real, t, "div");
sem_declare_binary(div, t, t, std_int, "div");
// Fall-through
case T_INTEGER:
// Modulus
sem_declare_binary(ident_new("\"mod\""), t, t, t, "mod");
// Remainder
sem_declare_binary(ident_new("\"rem\""), t, t, t, "rem");
// Fall-through
case T_REAL:
// Addition
sem_declare_binary(plus, t, t, t, "add");
// Subtraction
sem_declare_binary(minus, t, t, t, "sub");
// Multiplication
sem_declare_binary(mult, t, t, t, "mul");
// Division
sem_declare_binary(div, t, t, t, "div");
// Sign operators
sem_declare_unary(plus, t, t, "identity");
sem_declare_unary(minus, t, t, "neg");
// Exponentiation
sem_declare_binary(ident_new("\"**\""), t, std_int, t, "exp");
// Absolute value
sem_declare_unary(ident_new("\"abs\""), t, t, "abs");
// Fall-through
case T_ENUM:
sem_declare_binary(ident_new("\"<\""), t, t, std_bool, "lt");
sem_declare_binary(ident_new("\"<=\""), t, t, std_bool, "leq");
sem_declare_binary(ident_new("\">\""), t, t, std_bool, "gt");
sem_declare_binary(ident_new("\">=\""), t, t, std_bool, "geq");
// Fall-through
default:
sem_declare_binary(ident_new("\"=\""), t, t, std_bool, "eq");
sem_declare_binary(ident_new("\"/=\""), t, t, std_bool, "neq");
break;
}
// Logical operators
ident_t boolean_i = ident_new("STD.STANDARD.BOOLEAN");
ident_t bit_i = ident_new("STD.STANDARD.BIT");
bool logical = (type_ident(t) == boolean_i || type_ident(t) == bit_i);
if (logical) {
sem_declare_binary(ident_new("\"and\""), t, t, t, "and");
sem_declare_binary(ident_new("\"or\""), t, t, t, "or");
sem_declare_binary(ident_new("\"xor\""), t, t, t, "xor");
sem_declare_binary(ident_new("\"nand\""), t, t, t, "nand");
sem_declare_binary(ident_new("\"nor\""), t, t, t, "nor");
sem_declare_binary(ident_new("\"xnor\""), t, t, t, "xnor");
sem_declare_unary(ident_new("\"not\""), t, t, "not");
}
bool vec_logical = false;
if (kind == T_CARRAY || kind == T_UARRAY) {
type_t base = type_elem(t);
vec_logical = (type_ident(base) == boolean_i
|| type_ident(base) == bit_i);
}
if (vec_logical) {
sem_declare_binary(ident_new("\"and\""), t, t, t, "v_and");
sem_declare_binary(ident_new("\"or\""), t, t, t, "v_or");
sem_declare_binary(ident_new("\"xor\""), t, t, t, "v_xor");
sem_declare_binary(ident_new("\"nand\""), t, t, t, "v_nand");
sem_declare_binary(ident_new("\"nor\""), t, t, t, "v_nor");
sem_declare_binary(ident_new("\"xnor\""), t, t, t, "v_xnor");
sem_declare_unary(ident_new("\"not\""), t, t, "v_not");
}
// Predefined attributes
switch (kind) {
case T_INTEGER:
case T_REAL:
case T_PHYSICAL:
case T_SUBTYPE:
{
range_t r = type_dim(t, 0);
tree_add_attr_tree(decl, ident_new("LEFT"), r.left);
tree_add_attr_tree(decl, ident_new("RIGHT"), r.right);
tree_add_attr_tree(decl, ident_new("ASCENDING"),
sem_bool_lit(std_bool, r.kind == RANGE_TO));
if (r.kind == RANGE_TO) {
tree_add_attr_tree(decl, ident_new("LOW"), r.left);
tree_add_attr_tree(decl, ident_new("HIGH"), r.right);
}
else {
tree_add_attr_tree(decl, ident_new("HIGH"), r.left);
tree_add_attr_tree(decl, ident_new("LOW"), r.right);
}
tree_t image = sem_builtin_fn(ident_new("NVC.BUILTIN.IMAGE"),
sem_std_type("STRING"),
"image", t, NULL);
tree_add_attr_tree(decl, ident_new("IMAGE"), image);
}
break;
case T_ENUM:
{
tree_t left = type_enum_literal(t, 0);
tree_t right = type_enum_literal(t, type_enum_literals(t) - 1);
tree_add_attr_tree(decl, ident_new("LEFT"), sem_make_ref(left));
tree_add_attr_tree(decl, ident_new("RIGHT"), sem_make_ref(right));
tree_add_attr_tree(decl, ident_new("LOW"), sem_make_ref(left));
tree_add_attr_tree(decl, ident_new("HIGH"), sem_make_ref(right));
tree_t image = sem_builtin_fn(ident_new("NVC.BUILTIN.IMAGE"),
sem_std_type("STRING"),
"image", t, NULL);
tree_add_attr_tree(decl, ident_new("IMAGE"), image);
}
break;
default:
break;
}
bool array_attrs = ((kind == T_CARRAY)
|| (kind == T_SUBTYPE && type_is_array(t)));
if (array_attrs) {
ident_t length_i = ident_new("LENGTH");
tree_add_attr_tree(decl, length_i,
sem_builtin_fn(length_i,
sem_std_type("INTEGER"),
"length", t, NULL));
}
switch (type_kind(t)) {
case T_INTEGER:
case T_REAL:
case T_PHYSICAL:
case T_SUBTYPE:
case T_ENUM:
{
tree_t succ = sem_builtin_fn(ident_new("NVC.BUILTIN.SUCC"),
t, "succ", t, NULL);
tree_add_attr_tree(decl, ident_new("SUCC"), succ);
tree_t pred = sem_builtin_fn(ident_new("NVC.BUILTIN.PRED"),
t, "pred", t, NULL);
tree_add_attr_tree(decl, ident_new("PRED"), pred);
tree_t leftof = sem_builtin_fn(ident_new("NVC.BUILTIN.LEFTOF"),
t, "leftof", t, NULL);
tree_add_attr_tree(decl, ident_new("LEFTOF"), leftof);
tree_t rightof = sem_builtin_fn(ident_new("NVC.BUILTIN.RIGHTOF"),
t, "rightof", t, NULL);
tree_add_attr_tree(decl, ident_new("RIGHTOF"), rightof);
}
break;
default:
break;
}
}
static bool sem_check_subtype(tree_t t, type_t type, type_t *pbase)
{
// Resolve a subtype to its base type
while (type_kind(type) == T_SUBTYPE) {
type_t base = type_base(type);
if (type_kind(base) == T_UNRESOLVED) {
tree_t base_decl = scope_find(type_ident(base));
if (base_decl == NULL)
sem_error(t, "type %s is not defined", istr(type_ident(base)));
base = tree_type(base_decl);
type_set_base(type, base);
}
// If the subtype is not constrained then give it the same
// range as its base type
if (type_dims(type) == 0) {
switch (type_kind(base)) {
case T_ENUM:
{
range_t r = {
.kind = RANGE_TO,
.left = sem_make_int(0),
.right = sem_make_int(type_enum_literals(base) - 1)
};
type_add_dim(type, r);
}
break;
case T_UARRAY:
sem_error(t, "sorry, this form of subtype is not supported");
case T_CARRAY:
case T_SUBTYPE:
case T_INTEGER:
case T_REAL:
for (unsigned i = 0; i < type_dims(base); i++)
type_add_dim(type, type_dim(base, i));
break;
default:
assert(false);
}
}
type = base;
}
if (pbase)
*pbase = type;
return true;
}
static bool sem_declare(tree_t decl)
{
// Handle special cases of scope insertion such as enumeration
// literals, physical unit names, and predefined types
// Resolve the base type if necessary
if (!sem_check_subtype(decl, tree_type(decl), NULL))
return false;
// If this is full type declarataion then replace any previous
// incomplete type declaration
tree_t forward = scope_find(tree_ident(decl));
if (forward != NULL && tree_kind(forward) == T_TYPE_DECL) {
type_t incomplete = tree_type(forward);
if (type_kind(incomplete) == T_INCOMPLETE) {
// Replace the incomplete type with the one we are defining
type_replace(incomplete, tree_type(decl));
tree_set_type(decl, incomplete);
// Create a new incomplete type and attach that to the
// forward declaration: this is useful when we serialise
// the tree to avoid circular references
type_t ni = type_new(T_INCOMPLETE);
type_set_ident(ni, type_ident(incomplete));
tree_set_type(forward, ni);
scope_replace(forward, decl);
}
}
else if (!scope_insert(decl))
return false;
// Incomplete types cannot be checked any further
if (type_kind(tree_type(decl)) == T_INCOMPLETE)
return true;
// Declare any predefined operators and attributes
if (tree_kind(decl) == T_TYPE_DECL)
sem_declare_predefined_ops(decl);
bool ok = true;
type_t type = tree_type(decl);
switch (type_kind(type)) {
case T_ENUM:
// Need to add each literal to the scope
for (unsigned i = 0; i < type_enum_literals(type); i++)
ok = ok && scope_insert(type_enum_literal(type, i));
break;
case T_PHYSICAL:
// Create constant declarations for each unit
for (unsigned i = 0; i < type_units(type); i++) {
unit_t u = type_unit(type, i);
ok = ok && sem_check_constrained(u.multiplier, type);
tree_set_type(u.multiplier, type);
tree_t c = tree_new(T_CONST_DECL);
tree_set_loc(c, tree_loc(u.multiplier));
tree_set_ident(c, u.name);
tree_set_type(c, type);
tree_set_value(c, u.multiplier);
ok = ok && scope_insert(c);
}
break;
default:
break;
}
return ok;
}
static bool sem_check_range(range_t *r)
{
if (r->kind == RANGE_EXPR) {
assert(r->right == NULL);
assert(tree_kind(r->left) == T_ATTR_REF
|| tree_kind(r->left) == T_REF);
tree_t decl = scope_find(tree_ident(r->left));
if (decl == NULL)
sem_error(r->left, "undefined identifier %s",
istr(tree_ident(r->left)));
type_t type = tree_type(decl);
type_kind_t kind = type_kind(type);
switch (kind) {
case T_CARRAY:
*r = type_dim(type, 0);
return true;
case T_ENUM:
case T_UARRAY:
{
tree_t a = tree_new(T_ATTR_REF);
tree_set_ident(a, tree_ident(r->left));
tree_set_ident2(a, ident_new("LEFT"));
tree_t b = tree_new(T_ATTR_REF);
tree_set_ident(b, tree_ident(r->left));
tree_set_ident2(b, ident_new("RIGHT"));
// If this is an unconstrained array then we can
// only find out the direction at runtime
r->kind = (kind == T_UARRAY ? RANGE_DYN : RANGE_TO);
r->left = a;
r->right = b;
}
break;
default:
sem_error(r->left, "%s does not have range",
istr(tree_ident(r->left)));
}
}
if (!(sem_check(r->left) && sem_check(r->right)))
return false;
if (!type_eq(tree_type(r->left), tree_type(r->right)))
sem_error(r->right, "type mismatch in range");
return true;
}
static bool sem_check_context(tree_t t)
{
// The std.standard package is also implicit unless we are
// bootstrapping
if (!opt_get_int("bootstrap")) {
lib_t std = lib_find("std", true, true);
if (std == NULL)
fatal("failed to find std library");
context_t c = {
.name = std_standard_i,
.loc = LOC_INVALID
};
if (!scope_import_unit(c, std, true))
return false;
}
bool ok = true;
for (unsigned n = 0; n < tree_contexts(t); n++) {
context_t c = tree_context(t, n);
ident_t all = ident_strip(c.name, ident_new(".all"));
if (all)
c.name = all;
lib_t lib = lib_find(istr(ident_until(c.name, '.')), true, true);
if (lib != NULL)
ok = scope_import_unit(c, lib, all != NULL) && ok;
else
ok = false;
}
return ok;
}
static bool sem_check_constrained(tree_t t, type_t type)
{
type_set_push();
type_set_add(type);
bool ok = sem_check(t);
type_set_pop();
return ok;
}
static bool sem_readable(tree_t t)
{
switch (tree_kind(t)) {
case T_REF:
{
tree_t decl = tree_ref(t);
if (tree_kind(decl) == T_PORT_DECL
&& tree_port_mode(decl) == PORT_OUT)
sem_error(t, "cannot read output port %s",
istr(tree_ident(t)));
return true;
}
default:
return true;
}
}
static bool sem_check_array_dims(type_t type, type_t constraint)
{
for (unsigned i = 0; i < type_dims(type); i++) {
range_t r = type_dim(type, i);
type_t index_type =
constraint ? type_index_constr(constraint, i) : NULL;
type_set_push();
if (index_type)
type_set_add(index_type);
bool ok = sem_check_range(&r);
type_set_pop();
if (!ok)
return false;
if (index_type) {
tree_t error = NULL;
if (!type_eq(tree_type(r.left), index_type))
error = r.left;
else if (!type_eq(tree_type(r.right), index_type))
error = r.right;
if (error)
sem_error(error, "type of bound does not match type of index %s",
type_pp(index_type));
tree_set_type(r.left, index_type);
tree_set_type(r.right, index_type);
}
type_change_dim(type, i, r);
}
return true;
}
static bool sem_check_type(tree_t t, type_t *ptype)
{
// Check a type at the point where it is used not declared
switch (type_kind(*ptype)) {
case T_SUBTYPE:
{
type_t base;
if (!sem_check_subtype(t, *ptype, &base))
return false;
switch (type_kind(base)) {
case T_UARRAY:
{
// Create a new constrained array type for this instance
if (type_dims(*ptype) != type_index_constrs(base))
sem_error(t, "expected %d array dimensions but %d given",
type_index_constrs(base), type_dims(*ptype));
if (!sem_check_array_dims(*ptype, base))
return false;
type_t collapse = type_new(T_CARRAY);
type_set_ident(collapse, type_ident(base));
type_set_elem(collapse, type_elem(base)); // Element type
for (unsigned i = 0; i < type_dims(*ptype); i++)
type_add_dim(collapse, type_dim(*ptype, i));
*ptype = collapse;
}
break;
default:
break;
}
return true;
}
case T_UNRESOLVED:
{
tree_t type_decl = scope_find(type_ident(*ptype));
if (type_decl == NULL)
sem_error(t, "type %s is not defined", istr(type_ident(*ptype)));
*ptype = tree_type(type_decl);
}
return true;
default:
assert(false);
}
}
static bool sem_check_resolution(type_t type)
{
// Resolution functions are described in LRM 93 section 2.4
assert(type_kind(type) == T_SUBTYPE);
tree_t ref = type_resolution(type);
tree_t fdecl = scope_find(tree_ident(ref));
if (fdecl == NULL)
sem_error(ref, "undefined resolution function %s",
istr(tree_ident(ref)));
if (tree_kind(fdecl) != T_FUNC_DECL)
sem_error(ref, "declaration %s is not a function",
istr(tree_ident(ref)));
type_t ftype = tree_type(fdecl);
// Must take a single parameter of array of base type
if (type_params(ftype) != 1)
sem_error(fdecl, "resolution function must have single argument");
type_t param = type_param(ftype, 0);
if (type_kind(param) != T_UARRAY)
sem_error(fdecl, "parameter of resolution function must be "
"an unconstrained array type");
if (!type_eq(type_elem(param), type))
sem_error(fdecl, "parameter of resolution function must be "
"array of %s", type_pp(type));
// Return type must be the resolved type
if (!type_eq(type_result(ftype), type))
sem_error(fdecl, "result of resolution function must %s",
type_pp(type));
tree_set_ref(ref, fdecl);
return true;
}
static bool sem_check_type_decl(tree_t t)
{
// We need to insert the type into the scope before performing
// further checks as when bootstrapping we need INTEGER defined
// before we can check any ranges. Adding a type with errors to
// the symbol table should also avoid spurious type-not-defined
// errors later on
scope_apply_prefix(t);
if (!sem_declare(t))
return false;
type_t type = tree_type(t);
// Nothing more to do for incomplete types
if (type_kind(type) == T_INCOMPLETE)
return true;
// Prefix the package name to the type name
if (top_scope->prefix)
type_set_ident(type, ident_prefix(top_scope->prefix,
type_ident(type), '.'));
type_t base;
if (!sem_check_subtype(t, type, &base))
return false;
switch (type_kind(type)) {
case T_CARRAY:
case T_UARRAY:
{
type_t elem_type = type_elem(base);
if (!sem_check_type(t, &elem_type))
return false;
type_set_elem(base, elem_type);
}
break;
default:
break;
}
switch (type_kind(type)) {
case T_CARRAY:
return sem_check_array_dims(base, NULL);
case T_UARRAY:
for (unsigned i = 0; i < type_index_constrs(type); i++) {
type_t index_type = type_index_constr(type, i);
if (!sem_check_type(t, &index_type))
return false;
type_change_index_constr(type, i, index_type);
}
return true;
case T_INTEGER:
case T_PHYSICAL:
case T_REAL:
{
range_t r = type_dim(type, 0);
// Check the range expressions as if they were INTEGERs
// when there is no base type
type_set_push();
type_set_add(sem_std_type(type_kind(type) == T_REAL
? "REAL" : "INTEGER"));
bool ok = sem_check(r.left) && sem_check(r.right);
type_set_pop();
if (ok) {
// Standard specifies type of 'LEFT and 'RIGHT are same
// as the declared type
tree_set_type(r.left, type);
tree_set_type(r.right, type);
}
return ok;
}
case T_SUBTYPE:
{
bool ok = true;
for (unsigned i = 0; i < type_dims(type); i++) {
range_t r = type_dim(type, i);
type_t index = NULL;
switch (type_kind(base)) {
case T_CARRAY:
index = tree_type(type_dim(base, i).left);
break;
case T_UARRAY:
index = type_index_constr(base, i);
break;
default:
index = base;
break;
}
type_set_push();
type_set_add(index);
ok = sem_check(r.left) && sem_check(r.right) && ok;
type_set_pop();
if (ok) {
tree_set_type(r.left, index);
tree_set_type(r.right, index);
}
}
if (type_has_resolution(type))
ok = ok && sem_check_resolution(type);
return ok;
}
default:
return true;
}
}
static void sem_add_attributes(tree_t decl)
{
type_t std_bool = sem_std_type("BOOLEAN");
type_t type = tree_type(decl);
type_kind_t kind = type_kind(type);
if (kind == T_UARRAY) {
const char *funs[] = { "LOW", "HIGH", "LEFT", "RIGHT", NULL };
const char *impl[] = { "uarray_low", "uarray_high", "uarray_left",
"uarray_right", NULL };
const char **f, **imp;
for (f = funs, imp = impl; *f != NULL; f++, imp++) {
ident_t id = ident_new(*f);
tree_add_attr_tree(decl, id,
sem_builtin_fn(id, type_index_constr(type, 0),
*imp, type, NULL));
}
ident_t asc_i = ident_new("ASCENDING");
tree_add_attr_tree(decl, asc_i,
sem_builtin_fn(asc_i, std_bool,
"uarray_asc", type, NULL));
}
else if (type_is_array(type)) {
range_t r = type_dim(type, 0);
tree_add_attr_tree(decl, ident_new("LEFT"), r.left);
tree_add_attr_tree(decl, ident_new("RIGHT"), r.right);
if (r.kind != RANGE_DYN)
tree_add_attr_tree(decl, ident_new("ASCENDING"),
sem_bool_lit(std_bool, r.kind == RANGE_TO));
else {
ident_t asc_i = ident_new("ASCENDING");
tree_add_attr_tree(decl, asc_i,
sem_builtin_fn(asc_i, std_bool,
"uarray_asc", type, NULL));
}
if (r.kind == RANGE_TO) {
tree_add_attr_tree(decl, ident_new("LOW"), r.left);
tree_add_attr_tree(decl, ident_new("HIGH"), r.right);
}
else {
tree_add_attr_tree(decl, ident_new("HIGH"), r.left);
tree_add_attr_tree(decl, ident_new("LOW"), r.right);
}
}
if (type_is_array(type)) {
ident_t length_i = ident_new("LENGTH");
tree_add_attr_tree(decl, length_i,
sem_builtin_fn(length_i,
sem_std_type("INTEGER"),
"length", type, NULL));
}
if ((tree_kind(decl) == T_PORT_DECL && tree_class(decl) == C_SIGNAL)
|| (tree_kind(decl) == T_SIGNAL_DECL)) {
type_t std_string = sem_std_type("STRING");
ident_t event_i = ident_new("EVENT");
ident_t last_value_i = ident_new("LAST_VALUE");
ident_t active_i = ident_new("ACTIVE");
ident_t inst_name_i = ident_new("INSTANCE_NAME");
tree_add_attr_tree(decl, event_i,
sem_builtin_fn(event_i, std_bool, "event",
type, NULL));
tree_add_attr_tree(decl, active_i,
sem_builtin_fn(active_i, std_bool, "active",
type, NULL));
tree_add_attr_tree(decl, last_value_i,
sem_builtin_fn(last_value_i, type, "last_value",
type, NULL));
tree_add_attr_tree(decl, inst_name_i,
sem_builtin_fn(inst_name_i, std_string,
"instance_name", type, NULL));
}
}
static tree_t sem_default_value(type_t type)
{
type_t base;
(void)sem_check_subtype(NULL, type, &base);
switch (type_kind(base)) {
case T_UARRAY:
assert(type_kind(type) == T_SUBTYPE);
// Fall-through
case T_CARRAY:
{
tree_t def = NULL;
for (int i = type_dims(type) - 1 ; i >= 0; i--) {
tree_t val = (def ? def : sem_default_value(type_elem(base)));
def = tree_new(T_AGGREGATE);
assoc_t a = {
.kind = A_OTHERS,
.value = val
};
tree_add_assoc(def, a);
}
return def;
}
case T_INTEGER:
case T_PHYSICAL:
case T_REAL:
return type_dim(type, 0).left;
case T_ENUM:
return sem_make_ref(type_enum_literal(base, 0));
default:
assert(false);
}
}
static bool sem_check_decl(tree_t t)
{
type_t type = tree_type(t);
if (!sem_check_type(t, &type))
return false;
tree_set_type(t, type);
tree_kind_t kind = tree_kind(t);
if (!tree_has_value(t) && kind == T_CONST_DECL )
sem_error(t, "constant declaration must have an initial value");
if (!tree_has_value(t) && (tree_kind(t) != T_PORT_DECL))
tree_set_value(t, sem_default_value(type));
if (tree_has_value(t)) {
tree_t value = tree_value(t);
if (!sem_check_constrained(value, type))
return false;
if (!type_eq(type, tree_type(value)))
sem_error(value, "type of initial value %s does not match type "
"of declaration %s", istr(type_ident(tree_type(value))),
istr(type_ident(type)));
}
if (tree_kind(t) == T_PORT_DECL && tree_class(t) == C_DEFAULT)
tree_set_class(t, C_SIGNAL);
sem_add_attributes(t);
scope_apply_prefix(t);
return scope_insert(t);
}
static bool sem_check_port_decl(tree_t t)
{
type_t type = tree_type(t);
if (!sem_check_type(t, &type))
return false;
tree_set_type(t, type);
if (tree_has_value(t)) {
tree_t value = tree_value(t);
if (!sem_check_constrained(value, type))
return false;
if (!type_eq(type, tree_type(value)))
sem_error(value, "type of default value %s does not match type "
"of declaration %s", istr(type_ident(tree_type(value))),
istr(type_ident(type)));
}
sem_add_attributes(t);
return true;
}
static bool sem_check_alias(tree_t t)
{
if (!sem_check(tree_value(t)))
return false;
if (tree_has_type(t)) {
// TODO: this is not correct - check LRM
type_t type = tree_type(t);
if (!sem_check_type(t, &type))
return false;
tree_set_type(t, type);
}
else
tree_set_type(t, tree_type(tree_value(t)));
sem_add_attributes(t);
scope_apply_prefix(t);
return scope_insert(t);
}
static bool sem_check_func_ports(tree_t t)
{
type_t ftype = tree_type(t);
for (unsigned i = 0; i < tree_ports(t); i++) {
tree_t p = tree_port(t, i);
if (tree_port_mode(p) != PORT_IN)
sem_error(p, "function arguments must have mode IN");
if (tree_class(p) == C_DEFAULT)
tree_set_class(p, C_VARIABLE);
if (!sem_check(p))
return false;
type_add_param(ftype, tree_type(p));
}
type_t rtype = type_result(ftype);
if (!sem_check_type(t, &rtype))
return false;
type_set_result(ftype, rtype);
return true;
}
static bool sem_check_duplicate(tree_t t, tree_kind_t kind)
{
tree_t decl;
int n = 0;
do {
if ((decl = scope_find_nth(tree_ident(t), n++))) {
if (tree_kind(decl) != kind)
continue;
if (type_eq(tree_type(t), tree_type(decl))) {
// Allow builtin functions to be hidden
if (tree_attr_str(decl, builtin_i) == NULL)
break;
}
}
} while (decl != NULL);
return decl != NULL;
}
static bool sem_check_func_decl(tree_t t)
{
if (!sem_check_func_ports(t))
return false;
if (sem_check_duplicate(t, T_FUNC_DECL))
sem_error(t, "duplicate declaration of function %s",
istr(tree_ident(t)));
scope_apply_prefix(t);
return scope_insert(t);
}
static bool sem_check_func_body(tree_t t)
{
if (!sem_check_func_ports(t))
return false;
scope_apply_prefix(t);
// If there is no declaration for this function add to the scope
if (!sem_check_duplicate(t, T_FUNC_DECL)) {
if (!scope_insert(t))
return false;
}
scope_push(NULL);
top_scope->subprog = t;
bool ok = true;
for (unsigned i = 0; i < tree_ports(t); i++) {
tree_t p = tree_port(t, i);
sem_add_attributes(p);
ok = scope_insert(p) && ok;
}
for (unsigned i = 0; i < tree_decls(t); i++)
ok = sem_check(tree_decl(t, i)) && ok;
if (ok) {
for (unsigned i = 0; i < tree_stmts(t); i++)
ok = sem_check(tree_stmt(t, i)) && ok;
}
scope_pop();
unsigned nret = tree_visit_only(t, NULL, NULL, T_RETURN);
if (nret == 0)
sem_error(t, "function must contain a return statement");
return ok;
}
static bool sem_check_proc_ports(tree_t t)
{
type_t ptype = tree_type(t);
for (unsigned i = 0; i < tree_ports(t); i++) {
tree_t p = tree_port(t, i);
if (tree_class(p) == C_DEFAULT) {
switch (tree_port_mode(p)) {
case PORT_OUT:
case PORT_INOUT:
tree_set_class(p, C_VARIABLE);
break;
default:
break;
}
}
if (!sem_check(p))
return false;
type_add_param(ptype, tree_type(p));
}
return true;
}
static bool sem_check_proc_decl(tree_t t)
{
if (!sem_check_proc_ports(t))
return false;
if (sem_check_duplicate(t, T_PROC_DECL))
sem_error(t, "duplicate declaration of procedure %s",
istr(tree_ident(t)));
scope_apply_prefix(t);
return scope_insert(t);
}
static bool sem_check_proc_body(tree_t t)
{
if (!sem_check_proc_ports(t))
return false;
scope_apply_prefix(t);
// If there is no declaration for this procedure add to the scope
if (!sem_check_duplicate(t, T_PROC_DECL)) {
if (!scope_insert(t))
return false;
}
scope_push(NULL);
top_scope->subprog = t;
bool ok = true;
for (unsigned i = 0; i < tree_ports(t); i++) {
tree_t p = tree_port(t, i);
sem_add_attributes(p);
ok = scope_insert(p) && ok;
}
for (unsigned i = 0; i < tree_decls(t); i++)
ok = sem_check(tree_decl(t, i)) && ok;
if (ok) {
for (unsigned i = 0; i < tree_stmts(t); i++)
ok = sem_check(tree_stmt(t, i)) && ok;
}
scope_pop();
return ok;
}
static bool sem_check_sensitivity(tree_t t)
{
bool ok = true;
for (unsigned i = 0; i < tree_triggers(t); i++) {
tree_t r = tree_trigger(t, i);
ok = sem_check(r) && sem_readable(r) && ok;
if (ok) {
// Can only reference signals in sensitivity list
tree_t decl = tree_ref(r);
switch (tree_kind(decl)) {
case T_SIGNAL_DECL:
case T_PORT_DECL:
break;
default:
sem_error(r, "name %s in sensitivity list is not a signal",
istr(tree_ident(decl)));
}
}
}
return ok;
}
static bool sem_check_process(tree_t t)
{
scope_push(NULL);
bool ok = sem_check_sensitivity(t);
for (unsigned n = 0; n < tree_decls(t); n++)
ok = sem_check(tree_decl(t, n)) && ok;
if (ok) {
for (unsigned n = 0; n < tree_stmts(t); n++)
ok = sem_check(tree_stmt(t, n)) && ok;
}
scope_pop();
if (tree_triggers(t) > 0) {
// No wait statements allowed in process with sensitivity list
if (tree_visit_only(t, NULL, NULL, T_WAIT) > 0)
sem_error(t, "wait statement not allowed in process "
"with sensitvity list");
}
return ok;
}
static bool sem_check_package(tree_t t)
{
ident_t qual = ident_prefix(lib_name(lib_work()), tree_ident(t), '.');
assert(top_scope == NULL);
scope_push(NULL);
bool ok = sem_check_context(t);
if (ok) {
scope_push(qual);
for (unsigned n = 0; n < tree_decls(t); n++) {
tree_t decl = tree_decl(t, n);
ident_t unqual = tree_ident(decl);
if (sem_check(decl) && (tree_kind(decl) != T_ATTR_SPEC)) {
// Make the unqualified name visible inside the package
scope_insert_alias(decl, unqual);
}
else
ok = false;
}
scope_pop();
}
scope_pop();
tree_set_ident(t, qual);
lib_put(lib_work(), t);
return ok;
}
static bool sem_check_package_body(tree_t t)
{
ident_t qual = ident_prefix(lib_name(lib_work()), tree_ident(t), '.');
assert(top_scope == NULL);
scope_push(NULL);
bool ok = sem_check_context(t);
scope_push(qual);
// Look up package declaration
context_t c = {
.name = qual,
.loc = *tree_loc(t)
};
ok = ok && scope_import_unit(c, lib_work(), true);
if (ok) {
tree_t pack = lib_get(lib_work(), c.name);
assert(pack != NULL);
ok = ok && sem_check_context(pack);
for (unsigned n = 0; n < tree_decls(t); n++) {
tree_t decl = tree_decl(t, n);
ident_t unqual = tree_ident(decl);
ok = sem_check(decl) && ok;
// Make the unqualified name visible inside the package except
// in the case of function bodies where the declaration is
// already visible
bool make_visible =
(tree_kind(decl) != T_FUNC_BODY
|| !sem_check_duplicate(decl, T_FUNC_DECL));
if (make_visible)
scope_insert_alias(decl, unqual);
}
}
scope_pop();
scope_pop();
tree_set_ident(t, ident_prefix(qual, ident_new("body"), '-'));
lib_put(lib_work(), t);
return ok;
}
static bool sem_check_generics(tree_t t)
{
bool ok = true;
for (unsigned n = 0; n < tree_generics(t); n++) {
tree_t g = tree_generic(t, n);
switch (tree_class(g)) {
case C_DEFAULT:
tree_set_class(g, C_CONSTANT);
break;
case C_CONSTANT:
break;
default:
sem_error(g, "invalid object class for generic");
}
ok = sem_check(g) && ok;
}
if (ok) {
// Make generics visible in this region
for (unsigned n = 0; n < tree_generics(t); n++)
ok = scope_insert(tree_generic(t, n)) && ok;
}
return ok;
}
static bool sem_check_ports(tree_t t)
{
bool ok = true;
for (unsigned n = 0; n < tree_ports(t); n++) {
tree_t p = tree_port(t, n);
if (tree_class(p) == C_DEFAULT)
tree_set_class(p, C_SIGNAL);
ok = sem_check(p) && ok;
}
return ok;
}
static bool sem_check_component(tree_t t)
{
scope_push(NULL);
bool ok = sem_check_generics(t) && sem_check_ports(t);
scope_pop();
if (ok) {
scope_apply_prefix(t);
return scope_insert(t);
}
else
return false;
}
static bool sem_check_entity(tree_t t)
{
assert(top_scope == NULL);
scope_push(NULL);
bool ok = sem_check_context(t);
scope_push(NULL);
ok = ok && sem_check_generics(t) && sem_check_ports(t);
scope_pop();
scope_pop();
// Prefix the entity with the current library name
ident_t qual = ident_prefix(lib_name(lib_work()), tree_ident(t), '.');
tree_set_ident(t, qual);
lib_put(lib_work(), t);
return ok;
}
static bool sem_check_arch(tree_t t)
{
// Find the corresponding entity
tree_t e = lib_get(lib_work(),
ident_prefix(lib_name(lib_work()),
tree_ident2(t), '.'));
if (e == NULL)
sem_error(t, "missing declaration for entity %s",
istr(tree_ident2(t)));
if (!sem_check_stale(lib_work(), e))
return false;
assert(top_scope == NULL);
scope_push(NULL);
// Make all port and generic declarations available in this scope
bool ok = sem_check_context(e) && sem_check_context(t);
scope_push(NULL);
for (unsigned n = 0; n < tree_ports(e); n++)
scope_insert(tree_port(e, n));
for (unsigned n = 0; n < tree_generics(e); n++)
scope_insert(tree_generic(e, n));
// Now check the architecture itself
for (unsigned n = 0; n < tree_decls(t); n++)
ok = sem_check(tree_decl(t, n)) && ok;
if (ok) {
for (unsigned n = 0; n < tree_stmts(t); n++)
ok = sem_check(tree_stmt(t, n)) && ok;
}
scope_pop();
scope_pop();
// Prefix the architecture with the current library and entity name
ident_t lname = lib_name(lib_work());
ident_t qual = ident_prefix(ident_prefix(lname, tree_ident2(t), '.'),
tree_ident(t), '-');
tree_set_ident(t, qual);
ident_t ent_qual = ident_prefix(lname, tree_ident2(t), '.');
tree_set_ident2(t, ent_qual);
lib_put(lib_work(), t);
return ok;
}
static tree_t sem_check_lvalue(tree_t t)
{
switch (tree_kind(t)) {
case T_REF:
return sem_check_lvalue(tree_ref(t));
case T_ARRAY_SLICE:
case T_ARRAY_REF:
case T_ALIAS:
return sem_check_lvalue(tree_value(t));
case T_VAR_DECL:
case T_SIGNAL_DECL:
case T_PORT_DECL:
case T_CONST_DECL:
return t;
default:
error_at(tree_loc(t), "not a suitable l-value");
++errors;
return NULL;
}
}
static bool sem_check_var_assign(tree_t t)
{
tree_t target = tree_target(t);
tree_t value = tree_value(t);
bool ok = sem_check(target);
if (!ok)
return false;
ok = sem_check_constrained(value, tree_type(target));
if (!ok)
return false;
ok = sem_readable(value);
if (!ok)
return false;
tree_t decl = sem_check_lvalue(target);
if (decl == NULL)
return false;
bool suitable = (tree_kind(decl) == T_VAR_DECL)
|| (tree_kind(decl) == T_PORT_DECL && tree_class(decl) == C_VARIABLE);
if (!suitable)
sem_error(target, "invalid target of variable assignment");
if (!type_eq(tree_type(target), tree_type(value)))
sem_error(t, "type of value %s does not match type of target %s",
istr(type_ident(tree_type(value))),
istr(type_ident(tree_type(target))));
return ok;
}
static bool sem_check_waveforms(tree_t t, type_t expect)
{
type_t std_time = sem_std_type("TIME");
for (unsigned i = 0; i < tree_waveforms(t); i++) {
tree_t waveform = tree_waveform(t, i);
tree_t value = tree_value(waveform);
if (!sem_check_constrained(value, expect))
return false;
if (!sem_readable(value))
return false;
if (!type_eq(expect, tree_type(value)))
sem_error(t, "type of value %s does not match type of target %s",
istr(type_ident(tree_type(value))),
istr(type_ident(expect)));
if (tree_has_delay(waveform)) {
tree_t delay = tree_delay(waveform);
if (!sem_check(delay))
return false;
if (!type_eq(tree_type(delay), std_time))
sem_error(delay, "type of delay must be %s",
type_pp(std_time));
}
}
return true;
}
static bool sem_check_signal_target(tree_t target)
{
tree_t decl = sem_check_lvalue(target);
if (decl == NULL)
return false;
switch (tree_kind(decl)) {
case T_SIGNAL_DECL:
break;
case T_PORT_DECL:
if (tree_port_mode(decl) == PORT_IN)
sem_error(target, "cannot assign to input port %s",
istr(tree_ident(target)));
break;
default:
sem_error(target, "invalid target of signal assignment");
}
return true;
}
static bool sem_check_reject(tree_t t)
{
if (!sem_check(t))
return false;
type_t std_time = sem_std_type("TIME");
if (!type_eq(tree_type(t), std_time))
sem_error(t, "reject interval must have type TIME");
return true;
}
static bool sem_check_signal_assign(tree_t t)
{
tree_t target = tree_target(t);
if (!sem_check(target))
return false;
if (!sem_check_waveforms(t, tree_type(target)))
return false;
if (!sem_check_signal_target(target))
return false;
if (!sem_check_reject(tree_reject(t)))
return false;
return true;
}
static bool sem_check_cassign(tree_t t)
{
tree_t target = tree_target(t);
if (!sem_check(target))
return false;
type_t std_bool = sem_std_type("BOOLEAN");
for (unsigned i = 0; i < tree_conds(t); i++) {
tree_t c = tree_cond(t, i);
if (tree_has_value(c)) {
tree_t test = tree_value(c);
if (!sem_check(test))
return false;
if (!type_eq(tree_type(test), std_bool))
sem_error(test, "type of condition must be BOOLEAN");
}
if (!sem_check_waveforms(c, tree_type(target)))
return false;
}
if (!sem_check_signal_target(target))
return false;
if (!sem_check_reject(tree_reject(t)))
return false;
return true;
}
static unsigned sem_array_dimension(type_t a)
{
return (type_kind(a) == T_UARRAY
? type_index_constrs(a)
: type_dims(a));
}
static bool sem_check_conversion(tree_t t)
{
// Type conversions are described in LRM 93 section 7.3.5
if (tree_params(t) != 1)
sem_error(t, "type conversions must have exactly one parameter");
// Really we should push the set of types that are closely related
// to the one being converted to
type_set_push_universal();
param_t p = tree_param(t, 0);
bool ok = sem_check(p.value);
type_set_pop();
if (!ok)
return false;
type_t from = tree_type(p.value);
type_t to = tree_type(tree_ref(t));
tree_set_type(t, to);
// Resolve both types to their base types
while (type_kind(from) == T_SUBTYPE)
from = type_base(from);
while (type_kind(to) == T_SUBTYPE)
to = type_base(to);
type_kind_t from_k = type_kind(from);
type_kind_t to_k = type_kind(to);
// Conversions are allowed between any abstract numeric types
if (from_k == T_INTEGER && to_k == T_INTEGER)
return true;
bool from_array = (from_k == T_CARRAY || from_k == T_UARRAY);
bool to_array = (to_k == T_CARRAY || to_k == T_UARRAY);
if (from_array && to_array) {
// Types must have same dimensionality
bool same_dim = (sem_array_dimension(from) == sem_array_dimension(to));
// TODO: index types the same or closely related
// Element types must be the same
bool same_elem = type_eq(type_elem(from), type_elem(to));
if (same_dim && same_elem)
return true;
}
sem_error(t, "conversion only allowed between closely related types");
}
static bool sem_maybe_ambiguous(tree_t t)
{
switch (tree_kind(t)) {
case T_REF:
{
tree_t decl = scope_find(tree_ident(t));
return (decl != NULL && tree_kind(decl) == T_ENUM_LIT);
}
case T_AGGREGATE:
return true;
default:
return false;
}
}
static bool sem_resolve_overload(tree_t t, tree_t *pick, int *matches,
tree_t *overloads, int n_overloads)
{
*pick = NULL;
*matches = 0;
// Work out which parameters have ambiguous interpretations
bool ambiguous[tree_params(t)];
for (unsigned i = 0; i < tree_params(t); i++) {
param_t p = tree_param(t, i);
assert(p.kind == P_POS);
ambiguous[i] = sem_maybe_ambiguous(p.value);
}
// First pass: only check those parameters which are unambiguous
for (unsigned i = 0; i < tree_params(t); i++) {
if (ambiguous[i])
continue;
type_set_push();
for (int j = 0; j < n_overloads; j++) {
if (overloads[j] != NULL)
type_set_add(type_param(tree_type(overloads[j]), i));
}
param_t p = tree_param(t, i);
assert(p.kind == P_POS);
bool ok = sem_check(p.value);
type_set_pop();
if (ok) {
// Delete all overloads which don't match this parameter type
type_t ptype = tree_type(p.value);
for (int j = 0; j < n_overloads; j++) {
if (overloads[j] != NULL) {
if (!type_eq(type_param(tree_type(overloads[j]), i),
ptype))
overloads[j] = NULL;
}
}
}
else
return false;
}
// Second pass: now the set of overloads has been constrained check
// those parameters which might be ambiguous
for (unsigned i = 0; i < tree_params(t); i++) {
if (!ambiguous[i])
continue;
type_set_push();
for (int j = 0; j < n_overloads; j++) {
if (overloads[j] != NULL)
type_set_add(type_param(tree_type(overloads[j]), i));
}
param_t p = tree_param(t, i);
assert(p.kind == P_POS);
bool ok = sem_check(p.value);
type_set_pop();
if (!ok)
return false;
}
for (int n = 0; n < n_overloads; n++) {
if (overloads[n] == NULL)
continue;
// Did argument types match for this overload?
bool match = true;
bool all_universal = true;
type_t func_type = tree_type(overloads[n]);
for (unsigned i = 0; i < tree_params(t); i++) {
type_t ptype = tree_type(tree_param(t, i).value);
match = match && type_eq(type_param(func_type, i), ptype);
all_universal = all_universal && type_is_universal(ptype);
}
if (match) {
(*matches)++;
bool builtin = tree_attr_str(overloads[n], builtin_i);
if (all_universal && builtin) {
// If all the arguments are universal integer or real and
// this is a builtin function then it doesn't matter which
// overload we pick as it will be constant-folded later
type_t f_result = type_result(func_type);
switch (type_kind(f_result)) {
case T_INTEGER:
tree_set_type(t, type_universal_int());
break;
case T_REAL:
tree_set_type(t, type_universal_real());
break;
default:
tree_set_type(t, f_result);
}
tree_set_ref(t, overloads[n]);
return true;
}
else
*pick = overloads[n];
}
else
overloads[n] = NULL;
}
return true;
}
static bool sem_check_fcall(tree_t t)
{
tree_t overloads[MAX_OVERLOADS];
int n_overloads = 0;
tree_t decl;
int n = 0;
do {
if ((decl = scope_find_nth(tree_ident(t), n++))) {
switch (tree_kind(decl)) {
case T_FUNC_DECL:
case T_FUNC_BODY:
break;
case T_TYPE_DECL:
tree_change_kind(t, T_TYPE_CONV);
tree_set_ref(t, decl);
return sem_check_conversion(t);
default:
if (type_is_array(tree_type(decl))) {
// The grammar is ambiguous between function calls and
// array references so must be an array reference
tree_change_kind(t, T_ARRAY_REF);
return sem_check_array_ref(t);
}
else
continue; // Look for the next matching name
}
type_t func_type = tree_type(decl);
if (type_set_member(type_result(func_type))) {
// Number of arguments must match
if (type_params(func_type) != tree_params(t))
continue;
// Same function may appear multiple times in the symbol
// table under different names
bool duplicate = false;
for (int i = 0; i < n_overloads; i++) {
if (overloads[i] == decl)
duplicate = true;
}
if (!duplicate) {
// Found a matching function definition
overloads[n_overloads++] = decl;
}
}
}
} while (decl != NULL);
if (n_overloads == 0)
sem_error(t, "undefined identifier %s", istr(tree_ident(t)));
int matches;
if (!sem_resolve_overload(t, &decl, &matches, overloads, n_overloads))
return false;
if (matches > 0 && decl == NULL)
return true; // Resolved to a builtin function
if (matches > 1) {
char buf[1024];
char *p = buf;
const char *end = buf + sizeof(buf);
const bool operator = !isalpha((uint8_t)*istr(tree_ident(t)));
for (int n = 0; n < n_overloads; n++) {
if (overloads[n] != NULL)
p += snprintf(p, end - p, "\n%s",
type_pp(tree_type(overloads[n])));
}
sem_error(t, "ambiguous %s %s%s",
operator ? "use of operator" : "call to function",
istr(tree_ident(t)), buf);
}
if (decl == NULL) {
char fn[512];
char *p = fn;
const char *end = fn + sizeof(fn);
const char *fname = istr(tree_ident(t));
const bool operator = !isalpha((uint8_t)fname[0]);
const char *quote = (operator && fname[0] != '"') ? "\"" : "";
p += snprintf(p, end - p, "%s%s%s(", quote, fname, quote);
for (unsigned i = 0; i < tree_params(t); i++)
p += snprintf(p, end - p, "%s%s",
(i == 0 ? "" : ", "),
istr(type_ident(tree_type(tree_param(t, i).value))));
p += snprintf(p, end - p, ")");
if (top_type_set != NULL && top_type_set->n_members > 0) {
p += snprintf(p, end - p, " return");
for (int i = 0; i < top_type_set->n_members; i++)
p += snprintf(p, end - p, "%s %s",
(i > 0 ? " |" : ""),
type_pp(top_type_set->members[i]));
}
sem_error(t, (n == 1 ? "undefined %s %s"
: "no suitable overload for %s %s"),
operator ? "operator" : "function",
fn);
}
#if 0
printf("pick: %s\n", type_pp(tree_type(decl)));
fmt_loc(stdout, tree_loc(t));
#endif
tree_set_ref(t, decl);
tree_set_type(t, type_result(tree_type(decl)));
return true;
}
static bool sem_check_pcall(tree_t t)
{
tree_t overloads[MAX_OVERLOADS];
int n_overloads = 0;
tree_t decl;
int n = 0;
do {
if ((decl = scope_find_nth(tree_ident2(t), n++))) {
switch (tree_kind(decl)) {
case T_PROC_DECL:
case T_PROC_BODY:
break;
default:
continue; // Look for the next matching name
}
// Number of arguments must match
if (type_params(tree_type(decl)) != tree_params(t))
continue;
// Found a matching function definition
overloads[n_overloads++] = decl;
}
} while (decl != NULL);
if (n_overloads == 0)
sem_error(t, "undefined identifier %s", istr(tree_ident2(t)));
int matches;
if (!sem_resolve_overload(t, &decl, &matches, overloads, n_overloads))
return false;
if (matches > 0 && decl == NULL)
return true; // Resolved to a builtin function
if (matches > 1) {
char buf[1024];
char *p = buf;
const char *end = buf + sizeof(buf);
for (int n = 0; n < n_overloads; n++) {
if (overloads[n] != NULL)
p += snprintf(p, end - p, "\n %s",
type_pp(tree_type(overloads[n])));
}
sem_error(t, "ambiguous call to procedure %s%s",
istr(tree_ident2(t)), buf);
}
if (decl == NULL) {
char fn[512];
char *p = fn;
const char *end = fn + sizeof(fn);
const char *fname = istr(tree_ident2(t));
p += snprintf(p, end - p, "%s(", fname);
for (unsigned i = 0; i < tree_params(t); i++)
p += snprintf(p, end - p, "%s%s",
(i == 0 ? "" : ", "),
istr(type_ident(tree_type(tree_param(t, i).value))));
p += snprintf(p, end - p, ")");
sem_error(t, (n == 1 ? "undefined procedure %s"
: "no suitable overload for procedure %s"),
fn);
}
for (unsigned i = 0; i < tree_params(t); i++) {
param_t param = tree_param(t, i);
tree_t port = tree_port(decl, i);
if (tree_class(port) == C_VARIABLE) {
switch (tree_kind(param.value)) {
case T_REF:
case T_ARRAY_REF:
break;
default:
sem_error(param.value, "parameter must be a variable");
}
}
}
#if 0
printf("pick: %s\n", type_pp(tree_type(decl)));
fmt_loc(stdout, tree_loc(t));
#endif
tree_set_ref(t, decl);
return true;
}
static bool sem_check_wait(tree_t t)
{
if (tree_has_delay(t)) {
tree_t delay = tree_delay(t);
sem_check(delay);
if (!icmp(type_ident(tree_type(delay)), "STD.STANDARD.TIME"))
sem_error(delay, "type of delay must be TIME");
}
return sem_check_sensitivity(t);
}
static bool sem_check_assert(tree_t t)
{
// Rules for asserion statements are in LRM 93 section 8.2
type_t std_bool = sem_std_type("BOOLEAN");
type_t std_string = sem_std_type("STRING");
type_t std_severity = sem_std_type("SEVERITY_LEVEL");
tree_t value = tree_value(t);
tree_t severity = tree_severity(t);
tree_t message = tree_message(t);
if (!sem_check_constrained(value, std_bool))
return false;
if (!sem_check_constrained(severity, std_severity))
return false;
if (!sem_check_constrained(message, std_string))
return false;
if (!type_eq(tree_type(value), std_bool))
sem_error(value, "type of assertion expression must "
"be %s but is %s", istr(type_ident(std_bool)),
istr(type_ident(tree_type(value))));
if (!type_eq(tree_type(severity), std_severity))
sem_error(severity, "type of severity must be %s but is %s",
istr(type_ident(std_severity)),
istr(type_ident(tree_type(severity))));
if (!type_eq(tree_type(message), std_string))
sem_error(message, "type of message be %s but is %s",
istr(type_ident(std_string)),
istr(type_ident(tree_type(message))));
return true;
}
static tree_t sem_array_len(type_t type)
{
range_t r = type_dim(type, 0);
type_t index_type = tree_type(r.left);
tree_t one = sem_int_lit(index_type, 1);
tree_t tmp;
if (r.kind == RANGE_TO)
tmp = call_builtin("sub", index_type, r.right, r.left, NULL);
else
tmp = call_builtin("sub", index_type, r.left, r.right, NULL);
return call_builtin("add", index_type, tmp, one, NULL);
}
static bool sem_check_concat_param(tree_t t, type_t expect)
{
while (type_kind(expect) == T_SUBTYPE)
expect = type_base(expect);
type_set_push();
type_kind_t expect_k = type_kind(expect);
if (expect_k == T_CARRAY) {
// The bounds of one side should not be used to determine
// those of the other side
type_t u = type_new(T_UARRAY);
type_set_elem(u, type_elem(expect));
type_set_ident(u, type_ident(expect));
for (unsigned i = 0; i < type_dims(expect); i++)
type_add_index_constr(u, tree_type(type_dim(expect, i).left));
type_set_add(u);
}
else
type_set_add(expect);
if (expect_k == T_CARRAY || expect_k == T_UARRAY)
type_set_add(type_elem(expect));
bool ok = sem_check(t);
type_set_pop();
return ok;
}
static type_t sem_index_type(type_t type)
{
if (type_kind(type) == T_UARRAY)
return type_index_constr(type, 0);
else
return tree_type(type_dim(type, 0).left);
}
static bool sem_check_concat(tree_t t)
{
// Concatenation expressions are treated differently to other operators
// as they have special rules. See LRM 93 section 9.2.5
assert(tree_params(t) == 2);
tree_t left = tree_param(t, 0).value;
tree_t right = tree_param(t, 1).value;
type_t composite;
bool uniq_comp = type_set_uniq_composite(&composite);
type_t expect = composite;
bool ok;
tree_t other;
bool left_ambig = sem_maybe_ambiguous(left);
bool right_ambig = sem_maybe_ambiguous(right);
if (left_ambig && right_ambig) {
if (!uniq_comp)
sem_error(t, "type of concatenation is ambiguous");
ok = sem_check_concat_param(left, expect);
other = right;
}
else if (left_ambig) {
if ((ok = sem_check(right))) {
expect = tree_type(right);
other = left;
}
}
else {
if ((ok = sem_check(left))) {
expect = tree_type(left);
other = right;
}
}
if (!(ok && sem_check_concat_param(other, expect)))
return false;
type_t ltype = tree_type(left);
type_t rtype = tree_type(right);
type_kind_t lkind = type_kind(ltype);
type_kind_t rkind = type_kind(rtype);
bool l_array = type_is_array(ltype);
bool r_array = type_is_array(rtype);
if (l_array && r_array) {
if (!type_eq(ltype, rtype))
sem_error(t, "cannot concatenate arrays of different types");
if (sem_array_dimension(ltype) > 1)
sem_error(t, "cannot concatenate arrays with more than one dimension");
type_t index_type = sem_index_type(ltype);
range_t index_r = type_dim(index_type, 0);
type_t std_int = sem_std_type("INTEGER");
tree_t left_len, right_len;
if (lkind == T_UARRAY)
left_len = call_builtin("length", std_int, left, NULL);
else
left_len = sem_array_len(ltype);
if (rkind == T_UARRAY)
right_len = call_builtin("length", std_int, right, NULL);
else
right_len = sem_array_len(rtype);
type_t result = type_new(T_SUBTYPE);
type_set_ident(result, type_ident(ltype));
type_set_base(result, ltype);
tree_t one = sem_int_lit(index_type, 1);
tree_t result_len = call_builtin(
"add", index_type, left_len, right_len, NULL);
tree_t tmp = call_builtin(
"add", index_type, result_len, index_r.left, NULL);
tree_t result_right = call_builtin(
"sub", index_type, tmp, one, NULL);
range_t result_r = {
.kind = index_r.kind,
.left = index_r.left,
.right = result_right
};
type_add_dim(result, result_r);
tree_set_type(t, result);
}
else if (r_array || l_array) {
tree_t array = (l_array ? left : right);
tree_t scalar = (l_array ? right : left);
type_t atype = tree_type(array);
type_t stype = tree_type(scalar);
type_kind_t akind = type_kind(atype);
if (sem_array_dimension(atype) > 1)
sem_error(t, "cannot concatenate arrays with more than one dimension");
if (!type_eq(stype, type_elem(atype)))
sem_error(t, "type of scalar does not match element type of array");
type_t index_type = sem_index_type(atype);
range_t index_r = type_dim(index_type, 0);
type_t std_int = sem_std_type("INTEGER");
tree_t array_len;
if (akind == T_UARRAY)
array_len = call_builtin("length", std_int, array, NULL);
else
array_len = sem_array_len(atype);
tree_t result_right = call_builtin(
"add", index_type, index_r.left, array_len, NULL);
type_t result = type_new(T_SUBTYPE);
type_set_ident(result, type_ident(atype));
type_set_base(result, atype);
range_t result_r = {
.kind = index_r.kind,
.left = index_r.left,
.right = result_right
};
type_add_dim(result, result_r);
tree_set_type(t, result);
}
else {
// Concatenating two scalars
if (!type_eq(ltype, rtype))
sem_error(t, "cannot concatenate values of different types");
type_t index_type = sem_index_type(composite);
range_t index_r = type_dim(index_type, 0);
tree_t result_right = call_builtin(
"add", index_type, index_r.left, sem_int_lit(index_type, 1), NULL);
type_t result = type_new(T_SUBTYPE);
type_set_ident(result, type_ident(composite));
type_set_base(result, composite);
range_t result_r = {
.kind = index_r.kind,
.left = index_r.left,
.right = result_right
};
type_add_dim(result, result_r);
tree_set_type(t, result);
}
return true;
}
static bool sem_check_literal(tree_t t)
{
literal_t l = tree_literal(t);
switch (l.kind) {
case L_INT:
tree_set_type(t, type_universal_int());
break;
case L_REAL:
tree_set_type(t, type_universal_real());
break;
default:
assert(false);
}
return true;
}
static bool sem_check_aggregate(tree_t t)
{
// Rules for aggregates are in LRM 93 section 7.3.2
// The type of an aggregate must be determinable solely from the
// context in which the aggregate appears
type_t composite_type;
if (!type_set_uniq_composite(&composite_type))
sem_error(t, "type of aggregate is ambiguous");
type_t base_type = composite_type;
while (type_kind(base_type) == T_SUBTYPE)
base_type = type_base(base_type);
// All positional associations must appear before named associations
// and those must appear before any others association
enum { POS, NAMED, OTHERS } state = POS;
bool have_named = false;
bool have_pos = false;
for (unsigned i = 0; i < tree_assocs(t); i++) {
assoc_t a = tree_assoc(t, i);
switch (a.kind) {
case A_POS:
if (state > POS)
sem_error(a.value, "positional associations must appear "
"first in aggregate");
have_pos = true;
break;
case A_NAMED:
case A_RANGE:
if (state > NAMED)
sem_error(a.name, "named association must not follow "
"others association in aggregate");
state = NAMED;
have_named = true;
break;
case A_OTHERS:
if (state == OTHERS)
sem_error(a.value, "only a single others association "
"allowed in aggregate");
if (type_kind(composite_type) == T_UARRAY)
sem_error(a.value, "others choice not allowed in this context");
state = OTHERS;
break;
}
}
// Named and positional associations cannot be mixed in array
// aggregates
bool array = (type_kind(base_type) == T_CARRAY
|| type_kind(base_type) == T_UARRAY);
if (array && have_named && have_pos)
sem_error(t, "named and positional associations cannot be "
"mixed in array aggregates");
// If the composite type is unconstrained create a new constrained
// array type
if (type_kind(composite_type) == T_UARRAY) {
type_t tmp = type_new(T_CARRAY);
type_set_ident(tmp, type_ident(composite_type));
type_set_elem(tmp, type_elem(composite_type)); // Element type
assert(type_index_constrs(composite_type) == 1); // TODO
type_t index_type = type_index_constr(composite_type, 0);
range_t index_r = type_dim(index_type, 0);
if (have_named) {
tree_t low = call_builtin("agg_low", index_type, t, NULL);
tree_t high = call_builtin("agg_high", index_type, t, NULL);
range_t r = {
.kind = index_r.kind,
.left = (index_r.kind == RANGE_TO ? low : high),
.right = (index_r.kind == RANGE_TO ? high : low)
};
type_add_dim(tmp, r);
}
else {
tree_t n_elems = sem_make_int(tree_assocs(t) - 1);
range_t r = {
.kind = index_r.kind,
.left = index_r.left,
.right = call_builtin("add", index_type, n_elems,
index_r.left, NULL)
};
type_add_dim(tmp, r);
}
composite_type = tmp;
}
// All elements must be of the composite base type if this is
// a one-dimensional array otherwise construct an array type
// with n-1 dimensions.
type_t elem_type = NULL;
if (type_dims(composite_type) == 1)
elem_type = type_elem(base_type);
else {
elem_type = type_new(T_CARRAY);
type_set_ident(elem_type, type_ident(composite_type));
type_set_elem(elem_type, type_elem(base_type));
for (unsigned i = 1; i < type_dims(composite_type); i++)
type_add_dim(elem_type, type_dim(composite_type, i));
}
for (unsigned i = 0; i < tree_assocs(t); i++) {
assoc_t a = tree_assoc(t, i);
switch (a.kind) {
case A_RANGE:
if (!sem_check_range(&a.range))
return false;
tree_change_assoc(t, i, a);
break;
case A_NAMED:
if (!sem_check(a.name)) // TODO: constrained by index type
return false;
break;
default:
break;
}
if (!sem_check_constrained(a.value, elem_type))
return false;
if (!type_eq(elem_type, tree_type(a.value)))
sem_error(a.value, "type of element %s does not match base "
"type of aggregate %s",
istr(type_ident(tree_type(a.value))),
istr(type_ident(elem_type)));
}
// If a named choice is not locally static then it must be the
// only element
for (unsigned i = 0; i < tree_assocs(t); i++) {
assoc_t a = tree_assoc(t, i);
if (a.kind == A_NAMED && !sem_locally_static(a.name)) {
if (tree_assocs(t) != 1)
sem_error(a.name, "non-locally static choice must be "
"only choice");
}
}
tree_set_type(t, composite_type);
return true;
}
static bool sem_check_ref(tree_t t)
{
tree_t decl;
int n = 0;
do {
if ((decl = scope_find_nth(tree_ident(t), n++))) {
type_t type = tree_type(decl);
if (type_set_member(type))
break;
else if (type_kind(type) == T_FUNC
&& type_params(type) == 0
&& type_set_member(type_result(type)))
// Zero-argument function of correct type
break;
else if (!scope_can_overload(decl))
break;
}
} while (decl != NULL);
if (decl == NULL)
sem_error(t, (n == 1 ? "undefined identifier %s"
: "no suitable overload for identifier %s"),
istr(tree_ident(t)));
switch (tree_kind(decl)) {
case T_VAR_DECL:
case T_SIGNAL_DECL:
case T_PORT_DECL:
case T_CONST_DECL:
case T_ENUM_LIT:
case T_ALIAS:
case T_TYPE_DECL:
tree_set_type(t, tree_type(decl));
break;
case T_FUNC_DECL:
case T_FUNC_BODY:
tree_change_kind(t, T_FCALL);
tree_set_type(t, type_result(tree_type(decl)));
break;
default:
sem_error(t, "invalid use of %s", istr(tree_ident(t)));
}
tree_set_ref(t, decl);
return true;
}
static bool sem_check_array_ref(tree_t t)
{
tree_t value = tree_value(t);
if (!sem_check(value))
return false;
type_t type = tree_type(tree_value(t));
if (!type_is_array(type))
sem_error(t, "invalid array reference");
unsigned nindex = (type_kind(type) == T_UARRAY
? type_index_constrs(type)
: type_dims(type));
if (tree_params(t) != nindex)
sem_error(t, "array %s has %d dimensions but %d indices given",
istr(tree_ident(value)), nindex, tree_params(t));
bool ok = true;
for (unsigned i = 0; i < tree_params(t); i++) {
param_t p = tree_param(t, i);
if (p.kind != P_POS)
sem_error(t, "only scalar references supported");
type_t expect = sem_index_type(type);
ok = sem_check_constrained(p.value, expect) && ok;
if (ok && !type_eq(expect, tree_type(p.value)))
sem_error(p.value, "type of index %s does not match type of "
"array dimension %s",
istr(type_ident(tree_type(p.value))),
istr(type_ident(expect)));
}
tree_set_type(t, type_elem(type));
return ok;
}
static bool sem_check_array_slice(tree_t t)
{
if (!sem_check(tree_value(t)))
return false;
type_t array_type = tree_type(tree_value(t));
if (!type_is_array(array_type))
sem_error(t, "type of slice prefix is not an array");
type_set_push();
type_set_add(sem_std_type("INTEGER"));
range_t r = tree_range(t);
bool ok = sem_check_range(&r);
tree_set_range(t, r);
type_set_pop();
if (!ok)
return false;
bool wrong_dir =
(type_kind(array_type) != T_UARRAY)
&& (r.kind != type_dim(array_type, 0).kind)
&& (type_dim(array_type, 0).kind != RANGE_DYN);
if (wrong_dir)
sem_error(t, "range direction of slice does not match prefix");
type_t slice_type = type_new(T_SUBTYPE);
type_set_ident(slice_type, type_ident(array_type));
type_set_base(slice_type, array_type);
type_add_dim(slice_type, tree_range(t));
tree_set_type(t, slice_type);
return true;
}
static bool sem_check_attr_ref(tree_t t)
{
tree_t decl = scope_find(tree_ident(t));
if (decl == NULL)
sem_error(t, "undefined identifier %s", istr(tree_ident(t)));
if (icmp(tree_ident2(t), "range"))
sem_error(t, "range expression not allowed here");
tree_t a = tree_attr_tree(decl, tree_ident2(t));
if (a == NULL)
sem_error(t, "%s has no attribute %s",
istr(tree_ident(t)), istr(tree_ident2(t)));
if (tree_kind(a) == T_FUNC_DECL) {
type_t ftype = tree_type(a);
tree_set_type(t, type_result(ftype));
if (tree_params(t) == 0) {
// For an expression X'A add X as the final parameter
tree_t ref = sem_make_ref(decl);
tree_set_loc(ref, tree_loc(t));
param_t p = { .kind = P_POS, .value = ref };
tree_add_param(t, p);
}
if (tree_params(t) != type_params(ftype))
sem_error(t, "expected %d parameters for attribute %s "
"but have %d", type_params(ftype),
istr(tree_ident2(t)), tree_params(t));
for (unsigned i = 0; i < tree_params(t); i++) {
param_t p = tree_param(t, i);
if (p.kind != P_POS)
sem_error(t, "only positional arguments supported here");
type_t expect_type = type_param(ftype, i);
if (!sem_check_constrained(p.value, expect_type))
return false;
if (!type_eq(tree_type(p.value), expect_type))
sem_error(t, "expected type %s for attribute %s",
type_pp(expect_type), istr(tree_ident2(t)));
}
tree_set_ref(t, a);
}
else {
tree_set_value(t, a);
tree_set_type(t, tree_type(a));
tree_set_ref(t, decl);
}
return true;
}
static bool sem_check_qualified(tree_t t)
{
tree_t decl = scope_find(tree_ident(t));
if (tree_kind(decl) != T_TYPE_DECL)
sem_error(t, "%s is not a type name", istr(tree_ident(t)));
type_set_force(tree_type(decl));
tree_set_type(t, tree_type(decl));
return sem_check(tree_value(t));
}
static bool sem_check_map(tree_t t, tree_t unit,
tree_formals_t tree_Fs, tree_formal_t tree_F,
tree_actuals_t tree_As, tree_actual_t tree_A)
{
// Check there is an actual for each formal port or generic
const unsigned nformals = tree_Fs(unit);
bool ok = true;
struct {
tree_t decl;
bool have;
} formals[nformals];
for (unsigned i = 0; i < nformals; i++) {
formals[i].decl = tree_F(unit, i);
formals[i].have = false;
}
for (unsigned i = 0; i < tree_As(t); i++) {
param_t p = tree_A(t, i);
tree_t decl = NULL;
switch (p.kind) {
case P_POS:
if (p.pos >= nformals)
sem_error(p.value, "too many positional actuals");
if (formals[p.pos].have)
sem_error(p.value, "formal %s already has an actual",
istr(tree_ident(formals[p.pos].decl)));
formals[p.pos].have = true;
decl = formals[p.pos].decl;
break;
case P_NAMED:
for (unsigned i = 0; i < nformals; i++) {
if (tree_ident(formals[i].decl) == p.name) {
if (formals[i].have)
sem_error(p.value, "formal %s already has an actual",
istr(tree_ident(formals[i].decl)));
formals[i].have = true;
decl = formals[i].decl;
break;
}
}
if (decl == NULL)
sem_error(p.value, "%s has no formal %s",
istr(tree_ident(unit)), istr(p.name));
break;
case P_RANGE:
sem_error(p.value, "ranges cannot be used here");
}
ok = sem_check_constrained(p.value, tree_type(decl)) && ok;
}
for (unsigned i = 0; i < nformals; i++) {
if (!formals[i].have && !tree_has_value(formals[i].decl))
sem_error(t, "missing actual for formal %s",
istr(tree_ident(formals[i].decl)));
}
return ok;
}
static bool sem_check_instance(tree_t t)
{
// Find the referenced design unit
tree_t unit = lib_get(lib_work(), tree_ident2(t));
if (unit == NULL)
sem_error(t, "cannot find unit %s", istr(tree_ident2(t)));
if (tree_kind(unit) == T_ARCH) {
unit = lib_get(lib_work(), ident_until(tree_ident2(t), '-'));
if (unit == NULL)
sem_error(t, "no entity corresponding to architecture %s",
istr(tree_ident2(t)));
}
tree_set_ref(t, unit);
return sem_check_map(t, unit, tree_ports, tree_port,
tree_params, tree_param)
&& sem_check_map(t, unit, tree_generics, tree_generic,
tree_genmaps, tree_genmap);
}
static bool sem_check_if(tree_t t)
{
type_t std_bool = sem_std_type("BOOLEAN");
tree_t value = tree_value(t);
if (!sem_check_constrained(value, std_bool))
return false;
if (!type_eq(tree_type(value), std_bool))
sem_error(value, "type of test must be %s but is %s",
istr(type_ident(std_bool)),
istr(type_ident(tree_type(value))));
bool ok = true;
for (unsigned i = 0; i < tree_stmts(t); i++)
ok = sem_check(tree_stmt(t, i)) && ok;
for (unsigned i = 0; i < tree_else_stmts(t); i++)
ok = sem_check(tree_else_stmt(t, i)) && ok;
return ok;
}
static bool sem_locally_static(tree_t t)
{
// Rules for locally static expressions are in LRM 93 7.4.1
type_t type = tree_type(t);
tree_kind_t kind = tree_kind(t);
// Any literal other than of type time
if (kind == T_LITERAL) {
type_t std_time = sem_std_type("TIME");
return !type_eq(type, std_time);
}
else if ((kind == T_REF) && (tree_kind(tree_ref(t)) == T_ENUM_LIT))
return true;
// A constant reference with a locally static value
if ((kind == T_REF) && (tree_kind(tree_ref(t)) == T_CONST_DECL))
return sem_locally_static(tree_value(tree_ref(t)));
// An alias of a locally static name
if (kind == T_ALIAS)
return sem_locally_static(tree_value(t));
// A function call of an implicit operator with locally static actuals
if (kind == T_FCALL) {
tree_t decl = tree_ref(t);
if (tree_attr_str(decl, builtin_i) == NULL)
return false;
bool all_static = true;
for (unsigned i = 0; i < tree_params(t); i++) {
param_t p = tree_param(t, i);
all_static = all_static && sem_locally_static(p.value);
}
return all_static;
}
// TODO: clauses e, f, and g re. attributes
// A qualified expression whose operand is locally static
if (kind == T_QUALIFIED)
return sem_locally_static(tree_value(t));
// A type conversion whose expression is locally static
if (kind == T_TYPE_CONV)
return sem_locally_static(tree_value(t));
// Aggregates must have locally static range and all elements
// must have locally static values
if (kind == T_AGGREGATE) {
range_t r = type_dim(type, 0);
if (r.kind != RANGE_TO && r.kind != RANGE_DOWNTO)
return false;
if (!sem_locally_static(r.left) || !sem_locally_static(r.right))
return false;
for (unsigned i = 0; i < tree_assocs(t); i++) {
assoc_t a = tree_assoc(t, i);
if ((a.kind == A_NAMED) && !sem_locally_static(a.name))
return false;
if (!sem_locally_static(a.value))
return false;
}
return true;
}
return false;
}
static bool sem_check_case(tree_t t)
{
tree_t test = tree_value(t);
if (!sem_check(test))
return false;
type_t type = tree_type(test);
bool ok = true;
for (unsigned i = 0; i < tree_assocs(t); i++) {
assoc_t a = tree_assoc(t, i);
switch (a.kind) {
case A_OTHERS:
if (i != tree_assocs(t) - 1)
sem_error(t, "others choice must appear last");
// TODO: check type elements not covered by other choices
break;
case A_NAMED:
ok = sem_check_constrained(a.name, type) && ok;
if (ok) {
if (!type_eq(tree_type(a.name), type))
sem_error(a.name, "case choice must have type %s",
istr(type_ident(type)));
if (!sem_locally_static(a.name))
sem_error(a.name, "case choice must be locally static");
}
break;
default:
assert(false);
}
ok = sem_check(a.value) && ok;
}
return ok;
}
static bool sem_check_return(tree_t t)
{
if (top_scope->subprog == NULL)
sem_error(t, "return statement not allowed outside subprogram");
if (tree_has_value(t)) {
if (tree_kind(top_scope->subprog) == T_PROC_BODY)
sem_error(t, "cannot return a value from a procedure");
type_t expect = type_result(tree_type(top_scope->subprog));
if (!sem_check_constrained(tree_value(t), expect))
return false;
if (!type_eq(tree_type(tree_value(t)), expect))
sem_error(t, "expected return type %s", type_pp(expect));
}
return true;
}
static bool sem_check_while(tree_t t)
{
type_t std_bool = sem_std_type("BOOLEAN");
tree_t value = tree_value(t);
if (!sem_check_constrained(value, std_bool))
return false;
if (!type_eq(tree_type(value), std_bool))
sem_error(value, "type of loop condition must be %s but is %s",
istr(type_ident(std_bool)),
istr(type_ident(tree_type(value))));
bool ok = true;
for (unsigned i = 0; i < tree_stmts(t); i++)
ok = sem_check(tree_stmt(t, i)) && ok;
return ok;
}
static bool sem_check_for(tree_t t)
{
range_t r = tree_range(t);
if (!sem_check_range(&r))
return false;
tree_set_range(t, r);
tree_t idecl = tree_new(T_VAR_DECL);
tree_set_ident(idecl, tree_ident2(t));
tree_set_loc(idecl, tree_loc(t));
tree_set_type(idecl, tree_type(r.left));
tree_add_decl(t, idecl);
scope_push(NULL);
scope_insert(idecl);
bool ok = true;
for (unsigned i = 0; i < tree_stmts(t); i++)
ok = sem_check(tree_stmt(t, i)) && ok;
scope_pop();
return ok;
}
static bool sem_check_block(tree_t t)
{
scope_push(NULL);
bool ok = true;
for (unsigned i = 0; i < tree_decls(t); i++)
ok = sem_check(tree_decl(t, i)) && ok;
for (unsigned i = 0; i < tree_stmts(t); i++)
ok = sem_check(tree_stmt(t, i)) && ok;
scope_pop();
return ok;
}
static bool sem_check_exit(tree_t t)
{
if (tree_has_value(t)) {
tree_t value = tree_value(t);
if (!sem_check(value))
return false;
type_t std_bool = sem_std_type("BOOLEAN");
if (!type_eq(tree_type(value), std_bool))
sem_error(value, "type of exit condition must be %s but is %s",
istr(type_ident(std_bool)),
istr(type_ident(tree_type(value))));
}
return true;
}
static bool sem_check_select(tree_t t)
{
if (!sem_check(tree_value(t)))
return false;
type_t value_type = tree_type(tree_value(t));
for (unsigned i = 0; i < tree_assocs(t); i++) {
assoc_t a = tree_assoc(t, i);
if (a.kind == A_NAMED) {
if (!sem_check_constrained(a.name, value_type))
return false;
else if (!type_eq(tree_type(a.name), value_type))
sem_error(a.name, "choice must have type %s", type_pp(value_type));
else if (!sem_locally_static(a.name))
sem_error(a.name, "choice must be locally static");
}
if (!sem_check(a.value))
return false;
}
return true;
}
static bool sem_check_attr_decl(tree_t t)
{
type_t type = tree_type(t);
if (!sem_check_type(t, &type))
return false;
tree_set_type(t, type);
scope_apply_prefix(t);
return scope_insert(t);
}
static bool sem_check_attr_spec(tree_t t)
{
tree_t attr_decl = scope_find(tree_ident(t));
if (attr_decl == NULL)
sem_error(t, "undefined attribute %s", istr(tree_ident(t)));
tree_t obj_decl = scope_find(tree_ident2(t));
if (obj_decl == NULL)
sem_error(t, "undefined identifier %s", istr(tree_ident2(t)));
if (tree_kind(attr_decl) != T_ATTR_DECL)
sem_error(t, "name %s is not an attribute declaration",
istr(tree_ident(t)));
type_t type = tree_type(attr_decl);
tree_t value = tree_value(t);
if (!sem_check_constrained(value, type))
return false;
if (!type_eq(type, tree_type(value)))
sem_error(t, "expected attribute type %s", type_pp(type));
tree_add_attr_tree(obj_decl, tree_ident(t), value);
return true;
}
static bool sem_check_if_generate(tree_t t)
{
type_t std_bool = sem_std_type("BOOLEAN");
tree_t value = tree_value(t);
if (!sem_check_constrained(value, std_bool))
return false;
if (!type_eq(tree_type(value), std_bool))
sem_error(value, "condition of generate statement must be BOOLEAN");
scope_push(NULL);
bool ok = true;
for (unsigned i = 0; i < tree_decls(t); i++)
ok = sem_check(tree_decl(t, i)) && ok;
if (ok) {
for (unsigned i = 0; i < tree_stmts(t); i++)
ok = sem_check(tree_stmt(t, i)) && ok;
}
scope_pop();
return ok;
}
static bool sem_check_for_generate(tree_t t)
{
range_t r = tree_range(t);
if (!sem_check_range(&r))
return false;
tree_set_range(t, r);
tree_t idecl = tree_new(T_VAR_DECL);
tree_set_ident(idecl, tree_ident2(t));
tree_set_loc(idecl, tree_loc(t));
tree_set_type(idecl, tree_type(r.left));
tree_set_ref(t, idecl);
scope_push(NULL);
scope_insert(idecl);
bool ok = true;
for (unsigned i = 0; i < tree_decls(t); i++)
ok = sem_check(tree_decl(t, i)) && ok;
if (ok) {
for (unsigned i = 0; i < tree_stmts(t); i++)
ok = sem_check(tree_stmt(t, i)) && ok;
}
scope_pop();
return ok;
}
static void sem_intern_strings(void)
{
// Intern some commonly used strings
builtin_i = ident_new("builtin");
std_standard_i = ident_new("STD.STANDARD");
}
bool sem_check(tree_t t)
{
static bool have_interned = false;
if (!have_interned) {
sem_intern_strings();
have_interned = true;
}
switch (tree_kind(t)) {
case T_ARCH:
return sem_check_arch(t);
case T_PACKAGE:
return sem_check_package(t);
case T_ENTITY:
return sem_check_entity(t);
case T_TYPE_DECL:
return sem_check_type_decl(t);
case T_PORT_DECL:
return sem_check_port_decl(t);
case T_SIGNAL_DECL:
case T_VAR_DECL:
case T_CONST_DECL:
return sem_check_decl(t);
case T_PROCESS:
return sem_check_process(t);
case T_VAR_ASSIGN:
return sem_check_var_assign(t);
case T_SIGNAL_ASSIGN:
return sem_check_signal_assign(t);
case T_FCALL:
return sem_check_fcall(t);
case T_LITERAL:
return sem_check_literal(t);
case T_REF:
return sem_check_ref(t);
case T_WAIT:
return sem_check_wait(t);
case T_ASSERT:
return sem_check_assert(t);
case T_QUALIFIED:
return sem_check_qualified(t);
case T_FUNC_DECL:
return sem_check_func_decl(t);
case T_AGGREGATE:
return sem_check_aggregate(t);
case T_ATTR_REF:
return sem_check_attr_ref(t);
case T_ARRAY_REF:
return sem_check_array_ref(t);
case T_ARRAY_SLICE:
return sem_check_array_slice(t);
case T_INSTANCE:
return sem_check_instance(t);
case T_IF:
return sem_check_if(t);
case T_NULL:
return true;
case T_PACK_BODY:
return sem_check_package_body(t);
case T_FUNC_BODY:
return sem_check_func_body(t);
case T_RETURN:
return sem_check_return(t);
case T_CASSIGN:
return sem_check_cassign(t);
case T_WHILE:
return sem_check_while(t);
case T_ALIAS:
return sem_check_alias(t);
case T_FOR:
return sem_check_for(t);
case T_PROC_DECL:
return sem_check_proc_decl(t);
case T_PROC_BODY:
return sem_check_proc_body(t);
case T_BLOCK:
return sem_check_block(t);
case T_CASE:
return sem_check_case(t);
case T_EXIT:
return sem_check_exit(t);
case T_CONCAT:
return sem_check_concat(t);
case T_PCALL:
return sem_check_pcall(t);
case T_SELECT:
return sem_check_select(t);
case T_ATTR_SPEC:
return sem_check_attr_spec(t);
case T_ATTR_DECL:
return sem_check_attr_decl(t);
case T_COMPONENT:
return sem_check_component(t);
case T_IF_GENERATE:
return sem_check_if_generate(t);
case T_FOR_GENERATE:
return sem_check_for_generate(t);
default:
sem_error(t, "cannot check tree kind %d", tree_kind(t));
}
}
int sem_errors(void)
{
return errors;
}
Jump to Line
Something went wrong with that request. Please try again.