use crate::types::TypeWidth;
use inkwell::IntPredicate;
use inkwell::values::{IntValue, PointerValue};
use sv_lang::Symbol;
use sv_lang::kinds::ExpressionKind;
use sv_sim_sim::state::EVENT_CELL_WIDTH;
use sv_sim_sim::state::event_pool::{DYNAMIC_ROWS, event_count, pool_key, pool_of_key, static_key};
use sv_sim_sim::symid::SymbolUid;
use crate::arrays::{Backing, EventElem};
use crate::error::LoweringError;
use crate::expr::{LowerCtx, LoweredValue, MemorySignalInfo};
use crate::ops::OpBuilder;
use crate::types::int_type;
use crate::value::PackedValue;
pub(crate) fn pool_info(cx: &LowerCtx<'_, '_, '_>) -> Option<MemorySignalInfo> {
cx.mem_signals.get(&pool_key()).copied()
}
pub(crate) fn hierarchical_event_var<'d>(e: &sv_lang::Expression<'d>) -> Option<Symbol<'d>> {
let mut e = *e;
loop {
if e.kind() == ExpressionKind::Conversion
&& let Some(inner) = e.conversion_operand()
{
e = inner;
} else if e.kind() == ExpressionKind::ElementSelect
&& let Some(base) = e.select_value()
{
e = base;
} else {
break;
}
}
(e.kind() == ExpressionKind::HierarchicalValue)
.then(|| e.referenced_symbol())
.flatten()
}
pub(crate) fn pool_for(
cx: &LowerCtx<'_, '_, '_>,
e: &sv_lang::Expression<'_>,
) -> Option<MemorySignalInfo> {
if let Some(var) = hierarchical_event_var(e)
&& let Some(p) = cx.mem_signals.get(&pool_of_key(&var.uid()))
{
return Some(*p);
}
pool_info(cx)
}
fn same_pool(
cx: &LowerCtx<'_, '_, '_>,
h: &EventElem<'_>,
pool: &MemorySignalInfo,
what: &str,
) -> Result<(), LoweringError> {
if h.info.slot == pool.slot {
return Ok(());
}
Err(LoweringError::unsupported_at(
&format!(
"{what}: the event handle belongs to the event pool of another instance (an event \
of one instance cannot be assigned, passed or stored by another, LRM 1800-2023 \
15.5.5)"
),
&cx.fn_sym,
))
}
fn static_entry(cx: &LowerCtx<'_, '_, '_>, sym: &Symbol<'_>) -> Option<MemorySignalInfo> {
cx.mem_signals.get(&static_key(&sym.uid())).copied()
}
pub(crate) fn static_array_backing(
cx: &LowerCtx<'_, '_, '_>,
sym: &Symbol<'_>,
) -> Option<MemorySignalInfo> {
static_entry(cx, sym)
}
fn i64c<'ctx>(cx: &LowerCtx<'ctx, '_, '_>, v: u64) -> IntValue<'ctx> {
cx.ctx.i64_type().const_int(v, false)
}
fn load_cell_low<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
pool: &MemorySignalInfo,
row: IntValue<'ctx>,
) -> Result<IntValue<'ctx>, LoweringError> {
let cell = crate::arrays::load_raw(cx, Backing::Memory(*pool), row, EVENT_CELL_WIDTH)?;
Ok(cx
.b
.build_int_truncate(cell, cx.ctx.i64_type(), "ev_cell_lo")
.unwrap())
}
fn store_cell_low<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
pool: &MemorySignalInfo,
row: IntValue<'ctx>,
value: IntValue<'ctx>,
) -> Result<(), LoweringError> {
let packed = int_type(cx.ctx, EVENT_CELL_WIDTH * 2);
let v = cx.b.build_int_z_extend(value, packed, "ev_cell_v").unwrap();
crate::arrays::store_raw(cx, Backing::Memory(*pool), row, v, EVENT_CELL_WIDTH)
}
fn op<'a, 'ctx>(cx: &'a LowerCtx<'ctx, '_, '_>) -> OpBuilder<'a, 'ctx> {
OpBuilder {
ctx: cx.ctx,
b: &cx.b,
}
}
pub(crate) fn frame_load<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
ptr: PointerValue<'ctx>,
) -> IntValue<'ctx> {
let op = op(cx);
let raw =
cx.b.build_load(op.packed_type(64, cx.repr), ptr, "ev_handle")
.unwrap()
.into_int_value();
op.lane_val(PackedValue {
llvm: raw,
width: 64,
repr: cx.repr,
signed: false,
})
}
pub(crate) fn frame_store<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
ptr: PointerValue<'ctx>,
id: IntValue<'ctx>,
) {
let op = op(cx);
let v = op.pack(id, op.i_n(64).const_zero(), 64, cx.repr, false);
cx.b.build_store(ptr, v.llvm).unwrap();
}
fn static_target<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
pool: &MemorySignalInfo,
own: IntValue<'ctx>,
) -> Result<IntValue<'ctx>, LoweringError> {
let i64t = cx.ctx.i64_type();
let row =
cx.b.build_int_add(own, i64c(cx, pool.lower_index as u64), "ev_hcell_row")
.unwrap();
let cell = load_cell_low(cx, pool, row)?;
let is_self =
cx.b.build_int_compare(IntPredicate::EQ, cell, i64t.const_zero(), "ev_is_self")
.unwrap();
let is_null =
cx.b.build_int_compare(IntPredicate::EQ, cell, i64t.const_all_ones(), "ev_is_null")
.unwrap();
let other =
cx.b.build_select(is_null, i64t.const_zero(), cell, "ev_other")
.unwrap()
.into_int_value();
Ok(cx
.b
.build_select(is_self, own, other, "ev_target")
.unwrap()
.into_int_value())
}
fn element<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
pool: MemorySignalInfo,
id: IntValue<'ctx>,
ok: IntValue<'ctx>,
) -> EventElem<'ctx> {
let i64t = cx.ctx.i64_type();
let non_null =
cx.b.build_int_compare(IntPredicate::NE, id, i64t.const_zero(), "ev_non_null")
.unwrap();
let valid = cx.b.build_and(ok, non_null, "ev_named").unwrap();
let row =
cx.b.build_select(valid, id, i64t.const_zero(), "ev_row")
.unwrap()
.into_int_value();
EventElem {
info: pool,
row,
valid,
}
}
pub(crate) fn resolve_handle<'ctx, 'd>(
cx: &LowerCtx<'ctx, 'd, '_>,
e: sv_lang::Expression<'d>,
) -> Result<Option<EventElem<'ctx>>, LoweringError> {
let mut e = e;
while e.kind() == ExpressionKind::Conversion
&& let Some(inner) = e.conversion_operand()
{
e = inner;
}
let Some(pool) = pool_for(cx, &e) else {
return Ok(None);
};
let all = cx.ctx.bool_type().const_all_ones();
if e.kind() == ExpressionKind::NullLiteral {
return Ok(Some(element(cx, pool, i64c(cx, 0), all)));
}
if !e.expr_type().is_some_and(|t| t.is_event()) {
return Ok(None);
}
let (id, ok) = if sv_sim_sim::symid::is_value_name(e.kind()) {
let sym = e
.referenced_symbol()
.ok_or_else(|| LoweringError::unsupported_at("an unresolved event name", &cx.fn_sym))?;
if let Some(st) = static_entry(cx, &sym) {
(
static_target(cx, &pool, i64c(cx, st.lower_index as u64))?,
all,
)
} else if let Some(&(ptr, 64, _)) = cx.locals.get(&sym.uid()) {
(frame_load(cx, ptr), all)
} else {
return Err(LoweringError::unsupported_at(
"an event that is neither an event of this module nor a local event handle \
(a hierarchical reference to an event, LRM 1800-2023 15.5, is unsupported \
where events are assigned, compared or passed)",
&sym,
));
}
} else if e.kind() == ExpressionKind::ElementSelect {
let Some((r, shape)) = crate::arrays::resolve_array_ref(cx, e)? else {
return Err(LoweringError::unsupported_at(
"an element of an event array that is not a module-level or local array",
&cx.fn_sym,
));
};
if !shape.dims.is_empty() {
return Err(LoweringError::unsupported_at(
"a sub-array of an event array used as an event",
&cx.fn_sym,
));
}
match r.backing {
Backing::Memory(info) => {
let own =
cx.b.build_int_add(i64c(cx, info.lower_index as u64), r.base, "ev_own")
.unwrap();
(static_target(cx, &pool, own)?, r.valid)
}
Backing::Local(base) => {
let p = crate::arrays::local_row_ptr(cx, base, r.base, 64);
(frame_load(cx, p), r.valid)
}
}
} else {
return Err(LoweringError::unsupported_at(
&format!(
"an event expression of kind {:?} (only event names, event array elements, \
and `null` are supported)",
e.kind()
),
&cx.fn_sym,
));
};
Ok(Some(element(cx, pool, id, ok)))
}
pub(crate) fn assign_handle<'ctx, 'd>(
cx: &LowerCtx<'ctx, 'd, '_>,
lhs: sv_lang::Expression<'d>,
rhs: &EventElem<'ctx>,
) -> Result<(), LoweringError> {
let pool = pool_for(cx, &lhs).expect("an event assignment resolved a handle");
same_pool(cx, rhs, &pool, "an event assignment")?;
let id = rhs.row;
if sv_sim_sim::symid::is_value_name(lhs.kind()) {
let sym = lhs
.referenced_symbol()
.ok_or_else(|| LoweringError::unsupported_at("an unresolved event name", &cx.fn_sym))?;
return assign_symbol(cx, &pool, &sym, id);
}
if lhs.kind() != ExpressionKind::ElementSelect {
return Err(LoweringError::unsupported_at(
"an event assignment target that is not an event, an event array element or a \
handle variable",
&cx.fn_sym,
));
}
let Some((r, shape)) = crate::arrays::resolve_array_ref(cx, lhs)? else {
return Err(LoweringError::unsupported_at(
"an element of an event array that is not a module-level or local array",
&cx.fn_sym,
));
};
if !shape.dims.is_empty() {
return Err(LoweringError::unsupported_at(
"an assignment to a sub-array of an event array",
&cx.fn_sym,
));
}
crate::arrays::when_valid(cx, r.valid, || match r.backing {
Backing::Memory(info) => {
let own =
cx.b.build_int_add(i64c(cx, info.lower_index as u64), r.base, "ev_own")
.unwrap();
store_static_handle(cx, &pool, own, id)
}
Backing::Local(base) => {
let p = crate::arrays::local_row_ptr(cx, base, r.base, 64);
frame_store(cx, p, id);
Ok(())
}
})
}
pub(crate) fn assign_symbol<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
pool: &MemorySignalInfo,
sym: &Symbol<'_>,
id: IntValue<'ctx>,
) -> Result<(), LoweringError> {
if let Some(st) = static_entry(cx, sym) {
return store_static_handle(cx, pool, i64c(cx, st.lower_index as u64), id);
}
if let Some(&(ptr, 64, _)) = cx.locals.get(&sym.uid()) {
frame_store(cx, ptr, id);
return Ok(());
}
Err(LoweringError::unsupported_at(
"an assignment to an event that is neither an event of this module nor a local event \
handle",
sym,
))
}
fn store_static_handle<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
pool: &MemorySignalInfo,
own: IntValue<'ctx>,
id: IntValue<'ctx>,
) -> Result<(), LoweringError> {
let i64t = cx.ctx.i64_type();
let is_null =
cx.b.build_int_compare(IntPredicate::EQ, id, i64t.const_zero(), "ev_assign_null")
.unwrap();
let cell =
cx.b.build_select(is_null, i64t.const_all_ones(), id, "ev_hcell")
.unwrap()
.into_int_value();
let row =
cx.b.build_int_add(own, i64c(cx, pool.lower_index as u64), "ev_hcell_row")
.unwrap();
store_cell_low(cx, pool, row, cell)
}
pub(crate) fn lower_event_assignment<'ctx, 'd>(
cx: &LowerCtx<'ctx, 'd, '_>,
lhs: sv_lang::Expression<'d>,
rhs: sv_lang::Expression<'d>,
) -> Result<LoweredValue<'ctx>, LoweringError> {
if let Some(v) = crate::class_events::lower_assignment(cx, lhs, rhs)? {
return Ok(v);
}
let h = resolve_handle(cx, rhs)?.ok_or_else(|| {
LoweringError::unsupported_at(
"an event assignment whose source is not an event expression",
&cx.fn_sym,
)
})?;
assign_handle(cx, lhs, &h)?;
let op = op(cx);
Ok(LoweredValue::Packed(op.pack(
cx.ctx.bool_type().const_zero(),
cx.ctx.bool_type().const_zero(),
1,
cx.repr,
false,
)))
}
pub(crate) fn lower_event_equality<'ctx, 'd>(
cx: &LowerCtx<'ctx, 'd, '_>,
e: sv_lang::Expression<'d>,
negate: bool,
) -> Result<Option<LoweredValue<'ctx>>, LoweringError> {
let (Some(l), Some(r)) = (e.left(), e.right()) else {
return Ok(None);
};
let is_event = |x: sv_lang::Expression<'d>| x.expr_type().is_some_and(|t| t.is_event());
if !is_event(l) && !is_event(r) {
return Ok(None);
}
if let Some(v) = crate::class_events::lower_equality(cx, l, r, negate)? {
return Ok(Some(v));
}
let (Some(a), Some(b)) = (resolve_handle(cx, l)?, resolve_handle(cx, r)?) else {
return Err(LoweringError::unsupported_at(
"an event comparison with an operand that is not an event expression",
&cx.fn_sym,
));
};
let mut same =
cx.b.build_int_compare(IntPredicate::EQ, a.row, b.row, "ev_same")
.unwrap();
if a.info.slot != b.info.slot {
let zero = i64c(cx, 0);
let a0 =
cx.b.build_int_compare(IntPredicate::EQ, a.row, zero, "ev_a0")
.unwrap();
let b0 =
cx.b.build_int_compare(IntPredicate::EQ, b.row, zero, "ev_b0")
.unwrap();
same = cx.b.build_and(a0, b0, "ev_both_null").unwrap();
}
if negate {
same = cx.b.build_not(same, "ev_differ").unwrap();
}
let op = op(cx);
Ok(Some(LoweredValue::Packed(op.pack(
same,
cx.ctx.bool_type().const_zero(),
1,
cx.repr,
false,
))))
}
pub(crate) fn lower_event_truth<'ctx, 'd>(
cx: &LowerCtx<'ctx, 'd, '_>,
e: sv_lang::Expression<'d>,
) -> Result<Option<LoweredValue<'ctx>>, LoweringError> {
let Some(operand) = e.conversion_operand() else {
return Ok(None);
};
if !operand.expr_type().is_some_and(|t| t.is_event())
|| e.expr_type().is_some_and(|t| t.is_event())
{
return Ok(None);
}
let non_null = if let Some(nn) = crate::class_events::lower_non_null(cx, operand)? {
nn
} else {
let Some(h) = resolve_handle(cx, operand)? else {
return Err(LoweringError::unsupported_at(
"an event used as a Boolean outside the event pool",
&cx.fn_sym,
));
};
cx.b.build_int_compare(IntPredicate::NE, h.row, i64c(cx, 0), "ev_nonnull")
.unwrap()
};
let to_w = e.expr_type().map_or(1, |t| t.value_width());
let op = op(cx);
let bit =
cx.b.build_int_z_extend_or_bit_cast(non_null, op.i_n(to_w), "ev_bool")
.unwrap();
Ok(Some(LoweredValue::Packed(op.pack(
bit,
op.i_n(to_w).const_zero(),
to_w,
cx.repr,
false,
))))
}
pub(crate) fn lower_event_condition<'ctx, 'd>(
cx: &LowerCtx<'ctx, 'd, '_>,
e: sv_lang::Expression<'d>,
) -> Result<Option<IntValue<'ctx>>, LoweringError> {
let mut inner = e;
while inner.kind() == ExpressionKind::Conversion
&& let Some(x) = inner.conversion_operand()
{
inner = x;
}
if !inner.expr_type().is_some_and(|t| t.is_event()) {
return Ok(None);
}
if let Some(nn) = crate::class_events::lower_non_null(cx, inner)? {
return Ok(Some(nn));
}
let Some(h) = resolve_handle(cx, inner)? else {
return Err(LoweringError::unsupported_at(
"an event used as a Boolean outside the event pool",
&cx.fn_sym,
));
};
Ok(Some(
cx.b.build_int_compare(IntPredicate::NE, h.row, i64c(cx, 0), "ev_nonnull")
.unwrap(),
))
}
fn alloc_event<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
pool: &MemorySignalInfo,
) -> Result<IntValue<'ctx>, LoweringError> {
let i64t = cx.ctx.i64_type();
let used = load_cell_low(cx, pool, i64c(cx, 0))?;
let cap = i64c(cx, u64::from(DYNAMIC_ROWS));
let full =
cx.b.build_int_compare(IntPredicate::UGE, used, cap, "ev_pool_full")
.unwrap();
crate::stmt_shared::fatal_if(
cx,
full,
sv_lang::SourceLoc::default(),
&format!(
"too many local events: more than {DYNAMIC_ROWS} automatic event declarations \
executed (the event pool of this module is exhausted)"
),
)?;
let n = used;
let next =
cx.b.build_int_add(n, i64t.const_int(1, false), "ev_alloc_next")
.unwrap();
store_cell_low(cx, pool, i64c(cx, 0), next)?;
let base = i64c(cx, 2 * pool.lower_index as u64 + 1);
Ok(cx.b.build_int_add(base, n, "ev_alloc_id").unwrap())
}
pub(crate) fn is_pool_event_var(cx: &LowerCtx<'_, '_, '_>, var: &Symbol<'_>) -> bool {
pool_info(cx).is_some() && var.value_type().as_ref().and_then(event_count).is_some()
}
pub(crate) fn init_event_var<'d>(
cx: &LowerCtx<'_, 'd, '_>,
var: &Symbol<'d>,
) -> Result<(), LoweringError> {
let pool = pool_info(cx).expect("an event declaration of a body with an event pool");
let ty = var.value_type().expect("an event variable has a type");
let count = event_count(&ty).expect("an event variable holds events");
let scalar = !ty.is_unpacked_array();
if let Some(st) = static_entry(cx, var) {
if let Some(init) = var.initializer() {
if !scalar {
return Err(LoweringError::unsupported_at(
"an initializer on an event array",
var,
));
}
let h = resolve_handle(cx, init)?.ok_or_else(|| {
LoweringError::unsupported_at("an event initializer that is not an event", var)
})?;
store_static_handle(cx, &pool, i64c(cx, st.lower_index as u64), h.row)?;
}
return Ok(());
}
let &(ptr, _, _) = cx
.locals
.get(&var.uid())
.ok_or_else(|| LoweringError::unsupported_at("a local event has no frame slot", var))?;
if scalar {
let id = match var.initializer() {
Some(init) => {
resolve_handle(cx, init)?
.ok_or_else(|| {
LoweringError::unsupported_at(
"an event initializer that is not an event",
var,
)
})?
.row
}
None => alloc_event(cx, &pool)?,
};
frame_store(cx, ptr, id);
return Ok(());
}
if var.initializer().is_some() {
return Err(LoweringError::unsupported_at(
"an initializer on an event array",
var,
));
}
let unit = cx.ctx.bool_type().const_zero();
crate::arrays::row_loop(cx, u64::from(count), unit, |k, acc| {
let id = alloc_event(cx, &pool)?;
let p = crate::arrays::local_row_ptr(cx, ptr, k, 64);
frame_store(cx, p, id);
Ok(acc)
})?;
Ok(())
}
fn copy_event_array<'ctx, 'd>(
cx: &LowerCtx<'ctx, 'd, '_>,
pool: &MemorySignalInfo,
formal: &Symbol<'d>,
formal_ptr: PointerValue<'ctx>,
actual: sv_lang::Expression<'d>,
to_formal: bool,
) -> Result<(), LoweringError> {
let count = formal
.value_type()
.as_ref()
.and_then(event_count)
.expect("an event array formal holds events");
let Some((r, _)) = crate::arrays::array_target(cx, actual)? else {
return Err(LoweringError::unsupported_at(
"an event array argument whose actual is not a module-level or local event array",
formal,
));
};
if pool_for(cx, &actual).is_some_and(|p| p.slot != pool.slot) {
return Err(LoweringError::unsupported_at(
"an event array argument whose actual is an event array of another instance",
formal,
));
}
let i64t = cx.ctx.i64_type();
let unit = cx.ctx.bool_type().const_zero();
crate::arrays::row_loop(cx, u64::from(count), unit, |k, acc| {
let row = cx.b.build_int_add(r.base, k, "ev_arg_row").unwrap();
let formal_row = crate::arrays::local_row_ptr(cx, formal_ptr, k, 64);
if to_formal {
let id = match r.backing {
Backing::Memory(info) => {
let own =
cx.b.build_int_add(i64c(cx, info.lower_index as u64), row, "ev_own")
.unwrap();
static_target(cx, pool, own)?
}
Backing::Local(base) => {
frame_load(cx, crate::arrays::local_row_ptr(cx, base, row, 64))
}
};
let id =
cx.b.build_select(r.valid, id, i64t.const_zero(), "ev_arg_id")
.unwrap()
.into_int_value();
frame_store(cx, formal_row, id);
} else {
let id = frame_load(cx, formal_row);
crate::arrays::when_valid(cx, r.valid, || match r.backing {
Backing::Memory(info) => {
let own =
cx.b.build_int_add(i64c(cx, info.lower_index as u64), row, "ev_own")
.unwrap();
store_static_handle(cx, pool, own, id)
}
Backing::Local(base) => {
frame_store(cx, crate::arrays::local_row_ptr(cx, base, row, 64), id);
Ok(())
}
})?;
}
Ok(acc)
})?;
Ok(())
}
pub(crate) fn bind_event_formal<'ctx, 'd>(
cx: &LowerCtx<'ctx, 'd, '_>,
formal: &Symbol<'d>,
actual: sv_lang::Expression<'d>,
) -> Result<(), LoweringError> {
let Some(pool) = pool_info(cx) else {
return Err(LoweringError::unsupported_at(
"an event formal in a module with no event pool (internal)",
formal,
));
};
if formal.value_type().is_some_and(|t| t.is_unpacked_array()) {
if let Some(&(ptr, _, _)) = cx.locals.get(&formal.uid()) {
return copy_event_array(cx, &pool, formal, ptr, actual, true);
}
return Err(LoweringError::unsupported_at(
"an event array as a task argument",
formal,
));
}
let h = resolve_handle(cx, actual)?.ok_or_else(|| {
LoweringError::unsupported_at(
"an event argument whose actual is not an event expression",
formal,
)
})?;
same_pool(cx, &h, &pool, "an event argument")?;
assign_symbol(cx, &pool, formal, h.row)
}
pub(crate) fn is_event_formal(formal: &Symbol<'_>) -> bool {
formal.value_type().as_ref().and_then(event_count).is_some()
}
pub(crate) fn copy_out_formal<'ctx, 'd>(
cx: &LowerCtx<'ctx, 'd, '_>,
formal: &Symbol<'d>,
actual: sv_lang::Expression<'d>,
) -> Result<bool, LoweringError> {
if !formal.value_type().is_some_and(|t| t.is_event()) && !is_event_formal(formal) {
return Ok(false);
}
if formal.value_type().is_some_and(|t| t.is_unpacked_array()) {
if let (Some(pool), Some(&(ptr, _, _))) = (pool_info(cx), cx.locals.get(&formal.uid())) {
copy_event_array(cx, &pool, formal, ptr, actual, false)?;
return Ok(true);
}
return Err(LoweringError::unsupported_at(
"an event array as a task argument",
formal,
));
}
let id = if let Some(&(ptr, 64, _)) = cx.locals.get(&formal.uid()) {
frame_load(cx, ptr)
} else if let (Some(pool), Some(st)) = (pool_info(cx), static_entry(cx, formal)) {
static_target(cx, &pool, i64c(cx, st.lower_index as u64))?
} else {
return Err(LoweringError::unsupported_at(
"an event formal has no frame handle (internal)",
formal,
));
};
let class_formal = crate::class_events::is_class_method_local(formal);
if class_formal != crate::class_events::is_class_event(actual) {
return Err(LoweringError::unsupported_at(
"an event argument whose formal and actual are not both class events or both \
module events (a module event cannot be passed back as a class event, LRM \
1800-2023 15.5)",
formal,
));
}
if class_formal {
crate::class_events::store_id(cx, actual, id)?;
return Ok(true);
}
let Some(pool) = pool_info(cx) else {
return Err(LoweringError::unsupported_at(
"an event formal in a module with no event pool (internal)",
formal,
));
};
let all = cx.ctx.bool_type().const_all_ones();
assign_handle(cx, actual, &element(cx, pool, id, all))?;
Ok(true)
}