Resolve type and size cast targets during elaboration

A SystemVerilog cast target can be either a type or a constant size
expression. Currently the parser commits to `PECastType` or `PECastSize`
based on how the target identifier is classified while parsing. This is too
early for identifiers whose meaning is only known after symbol lookup. As a
result, corner cases are handled incorrectly when parser-time classification
does not match the result of elaboration. For example, the parser can not
decide whether an identifier inherited from a base class is a type or a
constant size expression. Supporting the inherited lookup is separate, but
the cast target must remain unresolved until elaboration for that lookup to
be used.

Parse both forms through `expr_primary_or_typename` and represent them with
one `PECast`. Keep atomic types wrapped in `PETypename` and preserve named
targets as `PEIdent`.

Resolve the target during elaboration. First use `test_type()` to distinguish
a type target from a constant size expression. For a type target, use a
contextual `elaborate_type()` call to resolve the type. Diagnose dimensions
after a type identifier directly from `PEIdent::elaborate_type()` when it is
used as a cast target. A failed type elaboration returns `nullptr` and does
not fall back to interpreting the target as a size expression. Dimensions
contained in the named type remain valid.

Cache the resolved target information during width checking because ordinary
expression elaboration needs the same information. Tag the cache with the
`NetScope` and recompute it when the scope changes since type parameters can
give the same cast expression a different target type in each instance.
Typed elaboration can bypass width checking, so resolve into a local value
when no matching cache is available. This avoids modifying the parsed
expression from a const elaboration method while still avoiding repeated
diagnostics between the normal width checking and expression elaboration
phases.

Share the type and size conversion paths between width-based and typed
elaboration. Use the explicit cast target when constructing a dynamic array
instead of the enclosing expression type. Own the cast target and operand
with `std::unique_ptr`.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This commit is contained in:
Lars-Peter Clausen 2026-08-03 19:55:21 -07:00
parent 506eaa5415
commit bea0fdeb3c
5 changed files with 324 additions and 239 deletions

View File

@ -139,31 +139,12 @@ bool PEBinary::has_aa_term(Design*des, NetScope*scope) const
return left_->has_aa_term(des, scope) || right_->has_aa_term(des, scope); return left_->has_aa_term(des, scope) || right_->has_aa_term(des, scope);
} }
PECastSize::PECastSize(PExpr*si, PExpr*b) PECast::PECast(PExpr *target, PExpr *base)
: size_(si), base_(b) : target_(target), base_(base)
{ {
} }
PECastSize::~PECastSize() bool PECast::has_aa_term(Design *des, NetScope *scope) const
{
}
bool PECastSize::has_aa_term(Design *des, NetScope *scope) const
{
return base_->has_aa_term(des, scope);
}
PECastType::PECastType(data_type_t*t, PExpr*b)
: target_(t), base_(b)
{
target_type_ = nullptr;
}
PECastType::~PECastType()
{
}
bool PECastType::has_aa_term(Design *des, NetScope *scope) const
{ {
return base_->has_aa_term(des, scope); return base_->has_aa_term(des, scope);
} }

104
PExpr.h
View File

@ -51,6 +51,11 @@ class PExpr : public LineInfo {
// Mode values used by test_width() (see below for description). // Mode values used by test_width() (see below for description).
enum width_mode_t { SIZED, UNSIZED, EXPAND, LOSSLESS, UPSIZE }; enum width_mode_t { SIZED, UNSIZED, EXPAND, LOSSLESS, UPSIZE };
enum class type_elaboration_context_t {
DEFAULT,
CAST_TARGET
};
// Flag values that can be passed to elaborate_expr(). // Flag values that can be passed to elaborate_expr().
static const unsigned NO_FLAGS = 0x0; static const unsigned NO_FLAGS = 0x0;
static const unsigned NEED_CONST = 0x1; static const unsigned NEED_CONST = 0x1;
@ -132,8 +137,12 @@ class PExpr : public LineInfo {
// may cache lookup state for a subsequent elaborate_type() call. // may cache lookup state for a subsequent elaborate_type() call.
virtual bool test_type(Design *des, NetScope *scope); virtual bool test_type(Design *des, NetScope *scope);
// Elaborate this expression as a type. Return null on failure. // Elaborate this expression as a type in a specific context. Return
virtual ivl_type_t elaborate_type(Design *des, NetScope *scope) const; // null if the expression is not a type or elaboration fails.
virtual ivl_type_t elaborate_type(
Design *des, NetScope *scope,
type_elaboration_context_t context =
type_elaboration_context_t::DEFAULT) const;
// After the test_width method is complete, these methods // After the test_width method is complete, these methods
// return valid results. // return valid results.
@ -385,7 +394,10 @@ class PEIdent : public PExpr {
virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, virtual NetExpr*elaborate_expr(Design*des, NetScope*scope,
ivl_type_t type, unsigned flags) const override; ivl_type_t type, unsigned flags) const override;
bool test_type(Design *des, NetScope *scope) override; bool test_type(Design *des, NetScope *scope) override;
ivl_type_t elaborate_type(Design *des, NetScope *scope) const override; ivl_type_t elaborate_type(
Design *des, NetScope *scope,
type_elaboration_context_t context =
type_elaboration_context_t::DEFAULT) const override;
virtual NetExpr*elaborate_expr(Design*des, NetScope*, virtual NetExpr*elaborate_expr(Design*des, NetScope*,
unsigned expr_wid, unsigned expr_wid,
unsigned flags) const override; unsigned flags) const override;
@ -732,7 +744,10 @@ class PETypename : public PExpr {
virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, virtual NetExpr*elaborate_expr(Design*des, NetScope*scope,
ivl_type_t type, unsigned flags) const override; ivl_type_t type, unsigned flags) const override;
bool test_type(Design *des, NetScope *scope) override; bool test_type(Design *des, NetScope *scope) override;
ivl_type_t elaborate_type(Design *des, NetScope *scope) const override; ivl_type_t elaborate_type(
Design *des, NetScope *scope,
type_elaboration_context_t context =
type_elaboration_context_t::DEFAULT) const override;
private: private:
data_type_t*data_type_; data_type_t*data_type_;
@ -1041,57 +1056,64 @@ class PECallFunction : public PExpr {
unsigned test_width_chain_(Design*des, NetScope*scope, width_mode_t&mode); unsigned test_width_chain_(Design*des, NetScope*scope, width_mode_t&mode);
}; };
/* /* Support SystemVerilog size and type casts. */
* Support the SystemVerilog cast to size. class PECast : public PExpr {
*/
class PECastSize : public PExpr {
public: public:
explicit PECastSize(PExpr*size, PExpr*base); explicit PECast(PExpr *target, PExpr *base);
~PECastSize() override; ~PECast() override = default;
void dump(std::ostream &out) const override; void dump(std::ostream &out) const override;
virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, NetExpr *elaborate_expr(Design *des, NetScope *scope,
unsigned expr_wid, ivl_type_t type, unsigned int flags) const override;
unsigned flags) const override;
virtual bool has_aa_term(Design *des, NetScope *scope) const override; NetExpr *elaborate_expr(Design *des, NetScope *scope,
unsigned int expr_wid,
unsigned int flags) const override;
virtual unsigned test_width(Design*des, NetScope*scope, bool has_aa_term(Design *des, NetScope *scope) const override;
width_mode_t&mode) override;
unsigned int test_width(Design *des, NetScope *scope,
width_mode_t &mode) override;
private: private:
PExpr* size_; NetExpr *elaborate_size_cast_(Design *des, NetScope *scope,
PExpr* base_; unsigned int expr_wid,
}; unsigned int target_width,
bool signed_flag,
unsigned int flags) const;
NetExpr *elaborate_type_cast_(Design *des, NetScope *scope,
unsigned int expr_wid,
ivl_type_t target_type,
unsigned int target_width,
bool signed_flag,
unsigned int flags) const;
/* enum class target_kind_t {
* Support the SystemVerilog cast to a different type. ERROR,
*/ SIZE,
class PECastType : public PExpr { TYPE
};
public: struct target_info_t {
explicit PECastType(data_type_t*target, PExpr*base); target_kind_t kind = target_kind_t::ERROR;
~PECastType() override; ivl_type_t type = nullptr;
unsigned int width = 0;
};
void dump(std::ostream &out) const override; target_info_t resolve_target_(Design *des, NetScope *scope) const;
target_info_t target_for_scope_(Design *des, NetScope *scope) const;
virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, std::unique_ptr<PExpr> target_;
ivl_type_t type, unsigned flags) const override; std::unique_ptr<PExpr> base_;
virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, // Cast targets can depend on parameters in the instance scope. Width
unsigned expr_wid, unsigned flags) const override; // testing and expression elaboration run sequentially for one scope, so
// retain only the most recent result.
virtual bool has_aa_term(Design *des, NetScope *scope) const override; const NetScope *target_scope_ = nullptr;
target_info_t target_info_;
virtual unsigned test_width(Design*des, NetScope*scope, bool target_resolved_ = false;
width_mode_t&mode) override;
private:
data_type_t* target_;
ivl_type_t target_type_;
PExpr* base_;
}; };
/* /*

View File

@ -488,7 +488,8 @@ unsigned PExpr::test_width(Design*des, NetScope*, width_mode_t&)
return 1; return 1;
} }
ivl_type_t PExpr::elaborate_type(Design *, NetScope *) const ivl_type_t PExpr::elaborate_type(
Design *, NetScope *, type_elaboration_context_t) const
{ {
return nullptr; return nullptr;
} }
@ -4219,151 +4220,263 @@ NetExpr* PECallFunction::elaborate_expr_method_par_(Design*des, const NetScope*s
return 0; return 0;
} }
unsigned PECastSize::test_width(Design*des, NetScope*scope, width_mode_t&) struct cast_type_info_t {
ivl_variable_type_t expr_type;
unsigned int width;
};
static cast_type_info_t cast_type_info(ivl_type_t type)
{ {
ivl_assert(*this, size_); if (auto darray = dynamic_cast<const netdarray_t *>(type)) {
return { darray->element_base_type(),
static_cast<unsigned int>(darray->element_width()) };
}
if (auto string_type = dynamic_cast<const netstring_t *>(type)) {
return { string_type->base_type(), 8 };
}
return { type->base_type(),
static_cast<unsigned int>(type->packed_width()) };
}
static unsigned int evaluate_cast_size(Design *des, NetScope *scope,
PExpr *target, const LineInfo &loc)
{
unsigned int width = 0;
auto size_expr = elab_and_eval(des, scope, target, -1, true);
auto size_const = dynamic_cast<NetEConst *>(size_expr);
if (size_const && !size_const->value().is_negative())
width = size_const->value().as_ulong();
delete size_expr;
if (width == 0) {
cerr << loc.get_fileline() << ": error: Cast size expression "
<< "must be constant and greater than zero." << endl;
des->errors += 1;
}
return width;
}
static bool test_size_cast_base(Design *des, NetScope *scope, PExpr *base,
const LineInfo &loc)
{
PExpr::width_mode_t mode = PExpr::SIZED;
base->test_width(des, scope, mode);
if (type_is_vectorable(base->expr_type()))
return true;
cerr << loc.get_fileline() << ": error: Cast base expression "
<< "must be a vector type." << endl;
des->errors += 1;
return false;
}
PECast::target_info_t PECast::resolve_target_(Design *des,
NetScope *scope) const
{
target_info_t target_info;
if (target_->test_type(des, scope)) {
auto type = target_->elaborate_type(
des, scope, PExpr::type_elaboration_context_t::CAST_TARGET);
if (!type)
return target_info;
target_info.kind = target_kind_t::TYPE;
target_info.type = type;
return target_info;
}
target_info.width = evaluate_cast_size(
des, scope, target_.get(), *this);
if (target_info.width != 0)
target_info.kind = target_kind_t::SIZE;
return target_info;
}
PECast::target_info_t PECast::target_for_scope_(Design *des,
NetScope *scope) const
{
if (target_resolved_ && target_scope_ == scope)
return target_info_;
return resolve_target_(des, scope);
}
unsigned int PECast::test_width(Design *des, NetScope *scope, width_mode_t &)
{
ivl_assert(*this, target_);
ivl_assert(*this, base_); ivl_assert(*this, base_);
expr_width_ = 0; if (!target_resolved_ || target_scope_ != scope) {
target_info_ = resolve_target_(des, scope);
NetExpr*size_ex = elab_and_eval(des, scope, size_, -1, true); target_scope_ = scope;
const NetEConst*size_ce = dynamic_cast<NetEConst*>(size_ex); target_resolved_ = true;
if (size_ce && !size_ce->value().is_negative())
expr_width_ = size_ce->value().as_ulong();
delete size_ex;
if (expr_width_ == 0) {
cerr << get_fileline() << ": error: Cast size expression "
"must be constant and greater than zero." << endl;
des->errors += 1;
return 0;
} }
width_mode_t tmp_mode = PExpr::SIZED; const auto &target_info = target_info_;
base_->test_width(des, scope, tmp_mode); if (target_info.kind == target_kind_t::ERROR)
if (!type_is_vectorable(base_->expr_type())) {
cerr << get_fileline() << ": error: Cast base expression "
"must be a vector type." << endl;
des->errors += 1;
return 0; return 0;
if (target_info.kind == target_kind_t::TYPE) {
width_mode_t mode = PExpr::SIZED;
base_->test_width(des, scope, mode);
auto type_info = cast_type_info(target_info.type);
expr_type_ = type_info.expr_type;
expr_width_ = type_info.width;
min_width_ = expr_width_;
signed_flag_ = target_info.type->get_signed();
return expr_width_;
} }
expr_type_ = base_->expr_type(); ivl_assert(*this, target_info.kind == target_kind_t::SIZE);
min_width_ = expr_width_; expr_width_ = target_info.width;
if (!test_size_cast_base(des, scope, base_.get(), *this))
return 0;
expr_type_ = base_->expr_type();
min_width_ = expr_width_;
signed_flag_ = base_->has_sign(); signed_flag_ = base_->has_sign();
return expr_width_; return expr_width_;
} }
NetExpr* PECastSize::elaborate_expr(Design*des, NetScope*scope, NetExpr *PECast::elaborate_expr(Design *des, NetScope *scope, ivl_type_t,
unsigned expr_wid, unsigned flags) const unsigned int flags) const
{ {
flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag ivl_assert(*this, target_);
ivl_assert(*this, size_);
ivl_assert(*this, base_); ivl_assert(*this, base_);
auto target_info = target_for_scope_(des, scope);
if (target_info.kind == target_kind_t::ERROR)
return nullptr;
if (target_info.kind == target_kind_t::TYPE) {
width_mode_t mode = PExpr::SIZED;
base_->test_width(des, scope, mode);
auto type_info = cast_type_info(target_info.type);
return elaborate_type_cast_(des, scope, type_info.width,
target_info.type, type_info.width,
target_info.type->get_signed(),
flags);
}
ivl_assert(*this, target_info.kind == target_kind_t::SIZE);
return elaborate_size_cast_(des, scope, target_info.width,
target_info.width,
base_->has_sign(), flags);
}
NetExpr *PECast::elaborate_expr(Design *des, NetScope *scope,
unsigned int expr_wid,
unsigned int flags) const
{
ivl_assert(*this, target_);
ivl_assert(*this, base_);
auto target_info = target_for_scope_(des, scope);
if (target_info.kind == target_kind_t::ERROR)
return nullptr;
if (target_info.kind == target_kind_t::TYPE) {
auto type_info = cast_type_info(target_info.type);
return elaborate_type_cast_(des, scope, expr_wid, target_info.type,
type_info.width, signed_flag_,
flags);
}
ivl_assert(*this, target_info.kind == target_kind_t::SIZE);
return elaborate_size_cast_(des, scope, expr_wid, target_info.width,
signed_flag_, flags);
}
NetExpr *PECast::elaborate_size_cast_(Design *des, NetScope *scope,
unsigned int expr_wid,
unsigned int target_width,
bool signed_flag,
unsigned int flags) const
{
flags &= ~SYS_TASK_ARG;
// A cast behaves exactly like an assignment to a temporary variable, // A cast behaves exactly like an assignment to a temporary variable,
// so the temporary result size may affect the sub-expression width. // so the temporary result size may affect the sub-expression width.
unsigned cast_width = base_->expr_width(); unsigned int cast_width = base_->expr_width();
if (cast_width < expr_width_) if (cast_width < target_width)
cast_width = expr_width_; cast_width = target_width;
NetExpr*sub = base_->elaborate_expr(des, scope, cast_width, flags); auto sub = base_->elaborate_expr(des, scope, cast_width, flags);
if (sub == 0) if (!sub)
return 0; return nullptr;
// Perform the cast. The extension method (zero/sign), if needed, // Perform the cast. The extension method (zero/sign), if needed,
// depends on the type of the base expression. // depends on the type of the base expression.
NetExpr*tmp = cast_to_width(sub, expr_width_, base_->has_sign(), *this); auto tmp = cast_to_width(sub, target_width, base_->has_sign(), *this);
// Pad up to the expression width. The extension method (zero/sign) // Pad up to the expression width. The extension method (zero/sign)
// depends on the type of enclosing expression. // depends on the type of enclosing expression.
return pad_to_width(tmp, expr_wid, signed_flag_, *this); return pad_to_width(tmp, expr_wid, signed_flag, *this);
} }
unsigned PECastType::test_width(Design*des, NetScope*scope, width_mode_t&) NetExpr *PECast::elaborate_type_cast_(Design *des, NetScope *scope,
unsigned int expr_wid,
ivl_type_t target_type,
unsigned int target_width,
bool signed_flag,
unsigned int flags) const
{ {
target_type_ = target_->elaborate_type(des, scope); auto darray = dynamic_cast<const netdarray_t *>(target_type);
auto vector = darray
? dynamic_cast<const netvector_t *>(darray->element_type())
: nullptr;
if (vector) {
unsigned int use_width = base_->expr_width();
auto base_expr = base_->elaborate_expr(des, scope, use_width,
NO_FLAGS);
if (!base_expr)
return nullptr;
width_mode_t tmp_mode = PExpr::SIZED; ivl_assert(*this, vector->packed_width() > 0);
base_->test_width(des, scope, tmp_mode); ivl_assert(*this, base_expr->expr_width() > 0);
if (const netdarray_t*use_darray = dynamic_cast<const netdarray_t*>(target_type_)) { // Find the number of elements needed to contain the source value.
expr_type_ = use_darray->element_base_type(); int length = base_expr->expr_width() + vector->packed_width() - 1;
expr_width_ = use_darray->element_width(); if (base_expr->expr_width() >
static_cast<unsigned int>(vector->packed_width()))
length /= vector->packed_width();
else
length /= base_expr->expr_width();
} else if (const netstring_t*use_string = dynamic_cast<const netstring_t*>(target_type_)) { auto length_expr = new NetEConst(verinum(length));
expr_type_ = use_string->base_type(); return new NetENew(target_type, length_expr, base_expr);
expr_width_ = 8;
} else {
expr_type_ = target_type_->base_type();
expr_width_ = target_type_->packed_width();
} }
min_width_ = expr_width_;
signed_flag_ = target_type_->get_signed();
return expr_width_; flags &= ~SYS_TASK_ARG;
}
NetExpr* PECastType::elaborate_expr(Design*des, NetScope*scope,
ivl_type_t type, unsigned flags) const
{
const netdarray_t*darray = NULL;
const netvector_t*vector = NULL;
// Casting array of vectors to dynamic array type
if((darray = dynamic_cast<const netdarray_t*>(type)) &&
(vector = dynamic_cast<const netvector_t*>(darray->element_type()))) {
PExpr::width_mode_t mode = PExpr::SIZED;
unsigned use_wid = base_->test_width(des, scope, mode);
NetExpr*base = base_->elaborate_expr(des, scope, use_wid, NO_FLAGS);
ivl_assert(*this, vector->packed_width() > 0);
ivl_assert(*this, base->expr_width() > 0);
// Find rounded up length that can fit the whole casted array of vectors
int len = base->expr_width() + vector->packed_width() - 1;
if(base->expr_width() > (unsigned)vector->packed_width()) {
len /= vector->packed_width();
} else {
len /= base->expr_width();
}
// Number of words in the created dynamic array
NetEConst*len_expr = new NetEConst(verinum(len));
return new NetENew(type, len_expr, base);
}
// Fallback
return elaborate_expr(des, scope, (unsigned) 0, flags);
}
NetExpr* PECastType::elaborate_expr(Design*des, NetScope*scope,
unsigned expr_wid, unsigned flags) const
{
flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag
// A cast behaves exactly like an assignment to a temporary variable, // A cast behaves exactly like an assignment to a temporary variable,
// so the temporary result size may affect the sub-expression width. // so the temporary result size may affect the sub-expression width.
unsigned cast_width = base_->expr_width(); unsigned int cast_width = base_->expr_width();
if (type_is_vectorable(base_->expr_type()) && (cast_width < expr_width_)) if (type_is_vectorable(base_->expr_type()) &&
cast_width = expr_width_; cast_width < target_width)
cast_width = target_width;
NetExpr*sub = base_->elaborate_expr(des, scope, cast_width, flags); auto sub = base_->elaborate_expr(des, scope, cast_width, flags);
if (sub == 0) if (!sub)
return 0; return nullptr;
NetExpr*tmp = 0; NetExpr *tmp = nullptr;
if (dynamic_cast<const netreal_t*>(target_type_)) { if (dynamic_cast<const netreal_t *>(target_type)) {
switch (sub->expr_type()) { switch (sub->expr_type()) {
case IVL_VT_REAL: case IVL_VT_REAL:
return sub; return sub;
case IVL_VT_LOGIC: case IVL_VT_LOGIC:
case IVL_VT_BOOL: case IVL_VT_BOOL:
return cast_to_real(sub); return cast_to_real(sub);
default: default:
break; break;
} }
cerr << get_fileline() << " error: Expression of type `" cerr << get_fileline() << " error: Expression of type `"
@ -4371,40 +4484,42 @@ NetExpr* PECastType::elaborate_expr(Design*des, NetScope*scope,
<< endl; << endl;
des->errors++; des->errors++;
return nullptr; return nullptr;
} else if (dynamic_cast<const netstring_t*>(target_type_)) { }
if (dynamic_cast<const netstring_t *>(target_type)) {
if (base_->expr_type() == IVL_VT_STRING) if (base_->expr_type() == IVL_VT_STRING)
return sub; // no conversion return sub;
if (base_->expr_type() == IVL_VT_LOGIC || if (base_->expr_type() == IVL_VT_LOGIC ||
base_->expr_type() == IVL_VT_BOOL) base_->expr_type() == IVL_VT_BOOL)
return sub; // handled by the target as special cases return sub;
} else if (target_type_ && target_type_->packed()) { } else if (target_type->packed()) {
switch (target_type_->base_type()) { switch (target_type->base_type()) {
case IVL_VT_BOOL: case IVL_VT_BOOL:
tmp = cast_to_int2(sub, expr_width_); tmp = cast_to_int2(sub, target_width);
break; break;
case IVL_VT_LOGIC: case IVL_VT_LOGIC:
tmp = cast_to_int4(sub, expr_width_); tmp = cast_to_int4(sub, target_width);
break; break;
default: default:
break; break;
} }
} }
if (tmp) { if (tmp) {
if (tmp == sub) { if (tmp == sub) {
// We already had the correct base type, so we just need to // We already had the correct base type, so we just need to
// fix the size. Note that even if the size is already correct, // fix the size. Note that even if the size is already correct,
// we still need to isolate the sub-expression from changes in // we still need to isolate the sub-expression from changes in
// the signedness pushed down from the main expression. // the signedness pushed down from the main expression.
tmp = cast_to_width(sub, expr_width_, sub->has_sign(), *this); tmp = cast_to_width(sub, target_width, sub->has_sign(), *this);
} }
return pad_to_width(tmp, expr_wid, signed_flag_, *this, target_type_); return pad_to_width(tmp, expr_wid, signed_flag, *this, target_type);
} }
cerr << get_fileline() << ": sorry: This cast operation is not yet supported." << endl; cerr << get_fileline()
<< ": sorry: This cast operation is not yet supported." << endl;
des->errors += 1; des->errors += 1;
return 0; return nullptr;
} }
unsigned PECastSign::test_width(Design *des, NetScope *scope, width_mode_t &mode) unsigned PECastSign::test_width(Design *des, NetScope *scope, width_mode_t &mode)
@ -5078,7 +5193,9 @@ static bool elaborate_type_dimensions(Design *des, NetScope *scope,
return dimensions_ok; return dimensions_ok;
} }
ivl_type_t PEIdent::elaborate_type(Design *des, NetScope *scope) const ivl_type_t PEIdent::elaborate_type(
Design *des, NetScope *scope,
type_elaboration_context_t context) const
{ {
symbol_search_results search_results; symbol_search_results search_results;
NetScope *declaration_scope; NetScope *declaration_scope;
@ -5110,11 +5227,21 @@ ivl_type_t PEIdent::elaborate_type(Design *des, NetScope *scope) const
return nullptr; return nullptr;
} }
const auto &name = path_.name.front();
if (context == type_elaboration_context_t::CAST_TARGET &&
!name.index.empty()) {
cerr << get_fileline() << ": error: Dimensions after a type "
<< "identifier are not allowed in a cast target." << endl;
cerr << type_def->get_fileline()
<< ": : The type was declared here." << endl;
des->errors++;
return nullptr;
}
ivl_type_t base_type = type_def->elaborate_type(des, declaration_scope); ivl_type_t base_type = type_def->elaborate_type(des, declaration_scope);
if (!base_type) if (!base_type)
return nullptr; return nullptr;
const auto &name = path_.name.front();
netranges_t packed_dimensions; netranges_t packed_dimensions;
if (!elaborate_type_dimensions(des, scope, name.index, if (!elaborate_type_dimensions(des, scope, name.index,
packed_dimensions)) packed_dimensions))
@ -8023,7 +8150,8 @@ NetExpr* PETernary::elab_and_eval_alternative_(Design*des, NetScope*scope,
* A typename expression is only legal in very narrow cases. This is * A typename expression is only legal in very narrow cases. This is
* just a placeholder. * just a placeholder.
*/ */
ivl_type_t PETypename::elaborate_type(Design *des, NetScope *scope) const ivl_type_t PETypename::elaborate_type(
Design *des, NetScope *scope, type_elaboration_context_t) const
{ {
return data_type_->elaborate_type(des, scope); return data_type_->elaborate_type(des, scope);
} }

51
parse.y
View File

@ -1235,7 +1235,7 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type,
%type <data_type> data_type data_type_opt data_type_or_implicit data_type_or_implicit_or_void %type <data_type> data_type data_type_opt data_type_or_implicit data_type_or_implicit_or_void
%type <data_type> block_reg_data_type %type <data_type> block_reg_data_type
%type <data_type> implicit_type %type <data_type> implicit_type
%type <data_type> reg_prefixed_atomic_type simple_type_or_string let_formal_type %type <data_type> reg_prefixed_atomic_type let_formal_type
%type <data_type> packed_array_data_type atomic_type %type <data_type> packed_array_data_type atomic_type
%type <data_type> ps_type_identifier ps_type_identifier_dim %type <data_type> ps_type_identifier ps_type_identifier_dim
@ -2900,35 +2900,6 @@ simple_immediate_assertion_statement /* IEEE1800-2012 A.6.10 */
} }
; ;
simple_type_or_string /* IEEE1800-2005: A.2.2.1 */
: integer_vector_type
{ vector_type_t*tmp = new vector_type_t($1, false, 0);
FILE_NAME(tmp, @1);
$$ = tmp;
}
| non_integer_type
{ real_type_t*tmp = new real_type_t($1);
FILE_NAME(tmp, @1);
$$ = tmp;
}
| atom_type
{ atom_type_t*tmp = new atom_type_t($1, true);
FILE_NAME(tmp, @1);
$$ = tmp;
}
| K_time
{ atom_type_t*tmp = new atom_type_t(atom_type_t::TIME, false);
FILE_NAME(tmp, @1);
$$ = tmp;
}
| K_string
{ string_type_t*tmp = new string_type_t;
FILE_NAME(tmp, @1);
$$ = tmp;
}
| ps_type_identifier
;
/* Many places where statements are allowed can actually take a /* Many places where statements are allowed can actually take a
statement or a null statement marked with a naked semi-colon. */ statement or a null statement marked with a naked semi-colon. */
@ -4983,24 +4954,14 @@ expr_primary
/* Cast expressions are primaries */ /* Cast expressions are primaries */
| expr_primary '\'' '(' expression ')' | expr_primary_or_typename '\'' '(' expression ')'
{ PExpr*base = $4; { auto base = $4;
if (pform_requires_sv(@1, "Size cast")) { if (pform_requires_sv(@1, "Cast expression")) {
PECastSize*tmp = new PECastSize($1, base); auto tmp = new PECast($1, base);
FILE_NAME(tmp, @1);
$$ = tmp;
} else {
$$ = base;
}
}
| simple_type_or_string '\'' '(' expression ')'
{ PExpr*base = $4;
if (pform_requires_sv(@1, "Type cast")) {
PECastType*tmp = new PECastType($1, base);
FILE_NAME(tmp, @1); FILE_NAME(tmp, @1);
$$ = tmp; $$ = tmp;
} else { } else {
delete $1;
$$ = base; $$ = base;
} }
} }

View File

@ -431,16 +431,9 @@ void PECallFunction::dump(ostream &out) const
} }
} }
void PECastSize::dump(ostream &out) const void PECast::dump(ostream &out) const
{ {
out << *size_ << "'("; target_->dump(out);
base_->dump(out);
out << ")";
}
void PECastType::dump(ostream &out) const
{
target_->pform_dump(out, 0);
out << "'("; out << "'(";
base_->dump(out); base_->dump(out);
out << ")"; out << ")";
@ -533,7 +526,7 @@ void PETernary::dump(ostream&out) const
void PETypename::dump(ostream&fd) const void PETypename::dump(ostream&fd) const
{ {
fd << "<type>"; data_type_->pform_dump(fd, 0);
} }
void PEUnary::dump(ostream&out) const void PEUnary::dump(ostream&out) const