use inkwell::basic_block::BasicBlock;
use inkwell::values::IntValue;
use sv_lang::kinds::PatternKind;
use sv_lang::nodes::{
ParenthesizedExpressionSyntax, ParenthesizedPatternSyntax, StructurePatternMemberSyntax,
StructurePatternSyntax, TaggedPatternSyntax, TaggedUnionExpressionSyntax,
};
use sv_lang::{AstNode, Expression, Pattern, Symbol, Type};
use sv_sim_sim::state::storage;
use sv_sim_sim::symid::SymbolUid;
use crate::error::LoweringError;
use crate::expr::{LowerCtx, LoweredValue, lower_condition, lower_expr};
use crate::ops::{OpBuilder, is_definitely_true};
use crate::select::fit_width;
use crate::value::{PackedValue, ValueRepr};
fn err(cx: &LowerCtx<'_, '_, '_>, why: &str) -> LoweringError {
LoweringError::unsupported_at(why, &cx.fn_sym)
}
fn op<'a, 'ctx>(cx: &'a LowerCtx<'ctx, '_, '_>) -> OpBuilder<'a, 'ctx> {
OpBuilder {
ctx: cx.ctx,
b: &cx.b,
}
}
fn known<'ctx>(cx: &LowerCtx<'ctx, '_, '_>, value: u64, width: u32) -> PackedValue<'ctx> {
let o = op(cx);
o.pack(
o.i_n(width).const_int(value, false),
o.i_n(width).const_zero(),
width,
cx.repr,
false,
)
}
fn const_i1<'ctx>(cx: &LowerCtx<'ctx, '_, '_>, v: bool) -> IntValue<'ctx> {
cx.ctx.bool_type().const_int(u64::from(v), false)
}
fn tagged_expr_member(
cx: &LowerCtx<'_, '_, '_>,
e: Expression<'_>,
) -> Result<String, LoweringError> {
crate::classes::with_design_trees(|trees| {
for tree in trees {
let Some(mut node) = e.syntax(tree) else {
continue;
};
while let Some(paren) = ParenthesizedExpressionSyntax::cast(node) {
node = paren.expression().syntax();
}
if let Some(t) = TaggedUnionExpressionSyntax::cast(node)
&& let Some(m) = t.member()
{
return Some(m.text().to_string());
}
}
None
})
.ok_or_else(|| {
err(
cx,
"a tagged union expression whose member name cannot be recovered from the syntax \
tree",
)
})
}
enum Shape {
Tagged(String),
Structure(Vec<Option<String>>),
Other,
}
fn shape_of(cx: &LowerCtx<'_, '_, '_>, p: &Pattern<'_>) -> Result<Shape, LoweringError> {
let found = crate::classes::with_design_trees(|trees| {
for tree in trees {
let Some(mut node) = p.syntax(tree) else {
continue;
};
while let Some(paren) = ParenthesizedPatternSyntax::cast(node) {
node = paren.pattern().syntax();
}
if let Some(t) = TaggedPatternSyntax::cast(node) {
return t.member_name().map(|m| Shape::Tagged(m.text().to_string()));
}
if let Some(s) = StructurePatternSyntax::cast(node) {
let names = s
.members()
.iter()
.map(|m| match m {
StructurePatternMemberSyntax::NamedStructurePatternMember(n) => {
n.name().map(|t| Some(t.text().to_string()))
}
StructurePatternMemberSyntax::OrderedStructurePatternMember(_) => {
Some(None)
}
})
.collect::<Option<Vec<_>>>();
return names.map(Shape::Structure);
}
return Some(Shape::Other);
}
None
});
found.ok_or_else(|| {
err(
cx,
"a pattern whose syntax cannot be recovered from the design's syntax trees",
)
})
}
fn find_member<'d>(ty: &Type<'d>, name: &str) -> Option<(u32, Symbol<'d>)> {
ty.fields()
.enumerate()
.find(|(_, f)| f.name() == name)
.map(|(i, f)| (i as u32, f))
}
fn member_window(
cx: &LowerCtx<'_, '_, '_>,
ty: &Type<'_>,
member: &Symbol<'_>,
) -> Result<(u64, u64), LoweringError> {
if member.value_type().is_some_and(|t| t.is_void()) {
return Ok((0, 0));
}
let total = storage::storage_width(ty)
.ok_or_else(|| err(cx, "a tagged union or struct type with no stored form"))?;
let (off, w) = crate::select::field_window(Some(*ty), member).ok_or_else(|| {
err(
cx,
"a member that is not a slice of its aggregate's stored bits",
)
})?;
if off + w > total {
return Err(err(
cx,
"a member window outside its aggregate's stored bits",
));
}
if ty.is_tagged_union() && off != 0 {
return Err(err(
cx,
"a tagged union member that is not right-justified (LRM 7.3.2)",
));
}
Ok((off, w))
}
fn typed_value<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
ty: &Type<'_>,
bits: PackedValue<'ctx>,
) -> LoweredValue<'ctx> {
let mut bits = PackedValue {
signed: ty.is_signed(),
..bits
};
if !ty.is_four_state() && bits.repr == ValueRepr::FourState {
let o = op(cx);
let (val, unk) = (o.lane_val(bits), o.lane_unk(bits));
let val =
o.b.build_and(val, o.b.build_not(unk, "nu").unwrap(), "v2")
.unwrap();
bits = o.pack(
val,
o.i_n(bits.width).const_zero(),
bits.width,
bits.repr,
bits.signed,
);
}
if ty.is_floating() {
LoweredValue::Real(crate::real::bits_to_real(cx, bits))
} else if ty.is_string() {
LoweredValue::Str(crate::strings::from_storage(cx, bits))
} else {
LoweredValue::Packed(bits)
}
}
fn slice<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
bits: PackedValue<'ctx>,
off: u64,
w: u64,
) -> PackedValue<'ctx> {
let o = op(cx);
let lo = o.i_n(bits.width).const_int(off, false);
o.part_select4(bits, lo, cx.ctx.bool_type().const_zero(), w as u32)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum MatchMode {
Exact,
Z,
X,
}
fn compare<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
mode: MatchMode,
a: PackedValue<'ctx>,
b: PackedValue<'ctx>,
) -> IntValue<'ctx> {
let o = op(cx);
let r = match mode {
MatchMode::Exact => o.caseeq4(a, b),
MatchMode::Z => o.wildcard_match(a, b, false),
MatchMode::X => o.casex_match(a, b),
};
is_definitely_true(cx.ctx, &cx.b, r)
}
fn tag_matches<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
bits: PackedValue<'ctx>,
ty: &Type<'_>,
idx: u32,
mode: MatchMode,
) -> Result<Option<IntValue<'ctx>>, LoweringError> {
let tag_bits = storage::tagged_tag_bits(ty);
if tag_bits == 0 {
return Ok(None);
}
let (lo, w) = storage::tagged_tag_slice(ty)
.filter(|(lo, w)| lo + w == u64::from(bits.width))
.ok_or_else(|| {
err(
cx,
"a tagged union value whose width disagrees with its type",
)
})?;
let tag = slice(cx, bits, lo, w);
let want = known(cx, u64::from(idx), w as u32);
Ok(Some(compare(cx, mode, tag, want)))
}
pub(crate) fn lower_tagged_expr<'ctx, 'd>(
cx: &LowerCtx<'ctx, 'd, '_>,
e: Expression<'d>,
) -> Result<LoweredValue<'ctx>, LoweringError> {
let ty = e.expr_type().filter(Type::is_tagged_union).ok_or_else(|| {
err(
cx,
"a tagged union expression that is not of a tagged union type",
)
})?;
let total = storage::storage_width(&ty).ok_or_else(|| {
err(
cx,
"a tagged union type with no stored form (a class or collection member)",
)
})?;
let name = tagged_expr_member(cx, e)?;
let (idx, member) = find_member(&ty, &name)
.ok_or_else(|| err(cx, &format!("tagged union has no member `{name}`")))?;
let (_, w) = member_window(cx, &ty, &member)?;
let tag_bits = storage::tagged_tag_bits(&ty);
let o = op(cx);
let mut parts: Vec<PackedValue<'ctx>> = Vec::new();
if tag_bits > 0 {
parts.push(known(cx, u64::from(idx), tag_bits as u32));
}
let pad = total - tag_bits - w;
if pad > 0 {
parts.push(known(cx, 0, pad as u32));
}
if w > 0 {
let inner = e
.tagged_union_value()
.ok_or_else(|| err(cx, "a tagged union expression missing its member value"))?;
let member_val = if member
.value_type()
.is_some_and(|t| storage::holds_refcounted(&t))
{
crate::collections::owned(cx, inner)?
} else {
lower_expr(cx, inner)?
};
let v = crate::real::to_storage(
cx,
member_val,
w as u32,
"a tagged union member value that is not a plain value",
)?;
parts.push(fit_width(&o, v, w as u32));
}
if parts.is_empty() {
return Err(err(cx, "a tagged union value with no bits"));
}
let v = o.concat4(&parts);
debug_assert_eq!(u64::from(v.width), total);
Ok(LoweredValue::Packed(PackedValue { signed: false, ..v }))
}
pub(crate) fn read_member<'ctx, 'd>(
cx: &LowerCtx<'ctx, 'd, '_>,
e: Expression<'d>,
base_expr: Expression<'d>,
) -> Result<Option<LoweredValue<'ctx>>, LoweringError> {
let Some(base_ty) = base_expr.expr_type().filter(Type::is_tagged_union) else {
return Ok(None);
};
let member = e
.member_symbol()
.ok_or_else(|| err(cx, "member access missing member symbol"))?;
let mty = member
.value_type()
.ok_or_else(|| err(cx, "member access field missing a type"))?;
if mty.is_void() {
return Err(err(
cx,
"a read of a void tagged union member (it carries no value)",
));
}
let base = lower_expr(cx, base_expr)?
.into_packed("a tagged union that is not a plain bit vector", &cx.fn_sym)?;
let (off, w) = member_window(cx, &base_ty, &member)?;
if w == 0 || off + w > u64::from(base.width) {
return Err(err(cx, "a tagged union member outside its value's bits"));
}
let idx = member.field_index();
let bits = slice(cx, base, off, w);
let Some(hit) = tag_matches(cx, base, &base_ty, idx, MatchMode::Exact)? else {
return Ok(Some(typed_value(cx, &mty, bits)));
};
let o = op(cx);
let miss = o.not(hit);
crate::stmt_shared::error_if(
cx,
miss,
e.source_range().start,
&format!(
"tagged union member `{}` read while the value holds a different member",
member.name()
),
)?;
let x = o.pack(
o.i_n(w as u32).const_zero(),
o.i_n(w as u32).const_all_ones(),
w as u32,
cx.repr,
bits.signed,
);
let bits = o.select(miss, x, bits);
Ok(Some(typed_value(cx, &mty, bits)))
}
pub(crate) fn checked_member_window<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
base_ty: &Type<'_>,
base_value: PackedValue<'ctx>,
member: &Symbol<'_>,
skip: IntValue<'ctx>,
at: sv_lang::SourceLoc,
) -> Result<Option<IntValue<'ctx>>, LoweringError> {
if !base_ty.is_tagged_union() {
return Ok(None);
}
let Some(hit) = tag_matches(
cx,
base_value,
base_ty,
member.field_index(),
MatchMode::Exact,
)?
else {
return Ok(None);
};
let o = op(cx);
let miss = o.b.build_and(o.not(hit), o.not(skip), "tag_miss").unwrap();
crate::stmt_shared::error_if(
cx,
miss,
at,
&format!(
"tagged union member `{}` assigned while the value holds a different member",
member.name()
),
)?;
Ok(Some(miss))
}
#[derive(Clone, Copy)]
pub(crate) struct Subject<'ctx, 'd> {
pub bits: PackedValue<'ctx>,
pub ty: Type<'d>,
}
pub(crate) fn subject_of<'ctx, 'd>(
cx: &LowerCtx<'ctx, 'd, '_>,
e: Expression<'d>,
) -> Result<Subject<'ctx, 'd>, LoweringError> {
let ty = e
.expr_type()
.ok_or_else(|| err(cx, "a matched expression without a type"))?;
let width = storage::storage_width(&ty).ok_or_else(|| {
err(
cx,
"pattern matching a value with no fixed-width stored form",
)
})?;
let bits = crate::real::to_storage(
cx,
lower_expr(cx, e)?,
width as u32,
"a pattern-matched value that is not a plain bit vector",
)?;
if u64::from(bits.width) != width {
return Err(err(
cx,
"a pattern-matched value whose width disagrees with its type",
));
}
Ok(Subject { bits, ty })
}
enum Parts<'ctx, 'd> {
Leaf,
Tagged {
idx: u32,
child: Option<(Pattern<'d>, Subject<'ctx, 'd>)>,
},
Structure(Vec<(Pattern<'d>, Subject<'ctx, 'd>)>),
}
fn child_patterns<'d>(
cx: &LowerCtx<'_, 'd, '_>,
p: &Pattern<'d>,
) -> Result<Vec<Pattern<'d>>, LoweringError> {
p.children()
.into_iter()
.map(|n| {
n.as_pattern()
.ok_or_else(|| err(cx, "a pattern child that is not a pattern"))
})
.collect()
}
fn decompose<'ctx, 'd>(
cx: &LowerCtx<'ctx, 'd, '_>,
pat: &Pattern<'d>,
sub: &Subject<'ctx, 'd>,
) -> Result<Parts<'ctx, 'd>, LoweringError> {
match pat.kind() {
PatternKind::Wildcard | PatternKind::Variable | PatternKind::Constant => Ok(Parts::Leaf),
PatternKind::Tagged => {
let Shape::Tagged(name) = shape_of(cx, pat)? else {
return Err(err(cx, "a tagged pattern whose syntax is not `tagged M`"));
};
if !sub.ty.is_tagged_union() {
return Err(err(
cx,
"a tagged pattern matched against a non-tagged-union value",
));
}
let (idx, member) = find_member(&sub.ty, &name)
.ok_or_else(|| err(cx, &format!("tagged pattern names no member `{name}`")))?;
let kids = child_patterns(cx, pat)?;
let mty = member
.value_type()
.ok_or_else(|| err(cx, "a tagged union member without a type"))?;
let child = match (kids.len(), mty.is_void()) {
(0, _) => None,
(1, false) => {
let (off, w) = member_window(cx, &sub.ty, &member)?;
if w == 0 {
return Err(err(cx, "a nested pattern for a member with no bits"));
}
Some((
kids[0],
Subject {
bits: slice(cx, sub.bits, off, w),
ty: mty,
},
))
}
_ => {
return Err(err(
cx,
"a tagged pattern whose nested pattern disagrees with its member",
));
}
};
Ok(Parts::Tagged { idx, child })
}
PatternKind::Structure => {
let Shape::Structure(names) = shape_of(cx, pat)? else {
return Err(err(cx, "a structure pattern whose syntax is not `'{...}`"));
};
let kids = child_patterns(cx, pat)?;
if kids.len() != names.len() {
return Err(err(
cx,
"a structure pattern whose members disagree with its syntax",
));
}
if !(sub.ty.is_struct() || sub.ty.is_unpacked_struct()) {
return Err(err(
cx,
"a structure pattern matched against a non-struct value",
));
}
let fields: Vec<Symbol<'d>> = sub.ty.fields().collect();
let mut out = Vec::new();
for (i, (kid, name)) in kids.into_iter().zip(names).enumerate() {
let field = match name {
Some(n) => {
find_member(&sub.ty, &n)
.ok_or_else(|| {
err(cx, &format!("structure pattern names no field `{n}`"))
})?
.1
}
None => *fields.get(i).ok_or_else(|| {
err(
cx,
"a structure pattern with more members than the struct has",
)
})?,
};
if kid.kind() == PatternKind::Wildcard {
continue;
}
let fty = field
.value_type()
.ok_or_else(|| err(cx, "a struct field without a type"))?;
let (off, w) = member_window(cx, &sub.ty, &field)?;
if w == 0 {
return Err(err(cx, "a nested pattern for a field with no bits"));
}
out.push((
kid,
Subject {
bits: slice(cx, sub.bits, off, w),
ty: fty,
},
));
}
Ok(Parts::Structure(out))
}
other => Err(err(cx, &format!("pattern kind {other:?}"))),
}
}
pub(crate) fn pattern_test<'ctx, 'd>(
cx: &LowerCtx<'ctx, 'd, '_>,
pat: &Pattern<'d>,
sub: &Subject<'ctx, 'd>,
mode: MatchMode,
) -> Result<IntValue<'ctx>, LoweringError> {
let o = op(cx);
match decompose(cx, pat, sub)? {
Parts::Leaf => match pat.kind() {
PatternKind::Constant => {
if sub.ty.is_floating() {
return Err(err(cx, "a constant pattern matched against a real value"));
}
let value = pat
.value_expr()
.ok_or_else(|| err(cx, "a constant pattern without a value"))?;
let c = crate::real::to_storage(
cx,
lower_expr(cx, value)?,
sub.bits.width,
"a constant pattern that is not a plain value",
)?;
let c = fit_width(&o, c, sub.bits.width);
Ok(compare(cx, mode, sub.bits, c))
}
PatternKind::Wildcard | PatternKind::Variable => Ok(const_i1(cx, true)),
other => Err(err(cx, &format!("a leaf pattern of kind {other:?}"))),
},
Parts::Tagged { idx, child } => {
let mut ok = tag_matches(cx, sub.bits, &sub.ty, idx, mode)?;
if let Some((p, s)) = child {
let inner = pattern_test(cx, &p, &s, mode)?;
ok = Some(match ok {
Some(t) => cx.b.build_and(t, inner, "pm_and").unwrap(),
None => inner,
});
}
Ok(ok.unwrap_or_else(|| const_i1(cx, true)))
}
Parts::Structure(items) => {
let mut ok = const_i1(cx, true);
for (p, s) in items {
let t = pattern_test(cx, &p, &s, mode)?;
ok = cx.b.build_and(ok, t, "pm_and").unwrap();
}
Ok(ok)
}
}
}
pub(crate) fn pattern_bind<'ctx, 'd>(
cx: &LowerCtx<'ctx, 'd, '_>,
pat: &Pattern<'d>,
sub: &Subject<'ctx, 'd>,
) -> Result<(), LoweringError> {
match decompose(cx, pat, sub)? {
Parts::Leaf => {
if pat.kind() != PatternKind::Variable {
return Ok(());
}
let var = pat
.variable()
.ok_or_else(|| err(cx, "a variable pattern without its variable"))?;
if var.name().is_empty() {
return Ok(());
}
let (slot, width, _) = cx.locals.get(&var.uid()).copied().ok_or_else(|| {
LoweringError::unsupported_at(
"a pattern variable with no storage in this scope",
&var,
)
})?;
if width != sub.bits.width {
return Err(LoweringError::unsupported_at(
&format!(
"pattern variable of {width} bits bound to a {}-bit value (two \
pattern variables of one name in one scope with different types)",
sub.bits.width
),
&var,
));
}
let bits = crate::collections::retain_stored(cx, &sub.ty, sub.bits);
cx.b.build_store(slot, bits.llvm).unwrap();
crate::expr::write_through_store(cx, slot);
Ok(())
}
Parts::Tagged { child, .. } => match child {
Some((p, s)) => pattern_bind(cx, &p, &s),
None => Ok(()),
},
Parts::Structure(items) => {
for (p, s) in items {
pattern_bind(cx, &p, &s)?;
}
Ok(())
}
}
}
#[derive(Clone, Copy)]
pub(crate) enum SubjectSrc<'ctx, 'd> {
Expr(Expression<'d>),
Done(Subject<'ctx, 'd>),
}
#[derive(Clone, Copy)]
pub(crate) enum Clause<'ctx, 'd> {
Cond(Expression<'d>),
Match {
subject: SubjectSrc<'ctx, 'd>,
pat: Pattern<'d>,
},
}
pub(crate) fn clauses_of<'ctx, 'd>(
cx: &LowerCtx<'ctx, 'd, '_>,
parts: impl IntoIterator<Item = (Expression<'d>, Option<Pattern<'d>>)>,
) -> Result<Vec<Clause<'ctx, 'd>>, LoweringError> {
let out: Vec<_> = parts
.into_iter()
.map(|(e, p)| match p {
Some(pat) => Clause::Match {
subject: SubjectSrc::Expr(e),
pat,
},
None => Clause::Cond(e),
})
.collect();
if out.is_empty() {
return Err(err(cx, "a predicate with no clauses"));
}
Ok(out)
}
pub(crate) fn lower_clauses<'ctx, 'd>(
cx: &LowerCtx<'ctx, 'd, '_>,
clauses: &[Clause<'ctx, 'd>],
mode: MatchMode,
) -> Result<IntValue<'ctx>, LoweringError> {
let (_, f) = crate::stmt_shared::current_block_and_fn(cx)?;
let done = cx.ctx.append_basic_block(f, "pm_done");
let mut failed_from: Vec<BasicBlock<'ctx>> = Vec::new();
for clause in clauses {
let (ok, bind) = match clause {
Clause::Cond(e) => (lower_condition(cx, *e)?, None),
Clause::Match { subject, pat } => {
let sub = match subject {
SubjectSrc::Expr(e) => subject_of(cx, *e)?,
SubjectSrc::Done(s) => *s,
};
(pattern_test(cx, pat, &sub, mode)?, Some((*pat, sub)))
}
};
let here =
cx.b.get_insert_block()
.ok_or_else(|| err(cx, "a pattern clause outside a basic block"))?;
let next = cx.ctx.append_basic_block(f, "pm_next");
cx.b.build_conditional_branch(ok, next, done).unwrap();
failed_from.push(here);
cx.b.position_at_end(next);
if let Some((pat, sub)) = bind {
pattern_bind(cx, &pat, &sub)?;
}
}
let last =
cx.b.get_insert_block()
.ok_or_else(|| err(cx, "a pattern clause outside a basic block"))?;
cx.b.build_unconditional_branch(done).unwrap();
cx.b.position_at_end(done);
let phi = cx.b.build_phi(cx.ctx.bool_type(), "pm_ok").unwrap();
let no = const_i1(cx, false);
let yes = const_i1(cx, true);
for bb in &failed_from {
phi.add_incoming(&[(&no, *bb)]);
}
phi.add_incoming(&[(&yes, last)]);
Ok(phi.as_basic_value().into_int_value())
}
pub(crate) fn if_arms<'ctx, 'd>(
cx: &LowerCtx<'ctx, 'd, '_>,
links: &[sv_lang::Statement<'d>],
) -> Result<Vec<Vec<Clause<'ctx, 'd>>>, LoweringError> {
links
.iter()
.map(|l| clauses_of(cx, l.conditions().iter().map(|c| (c.expr(), c.pattern()))))
.collect()
}
pub(crate) fn case_expression<'d>(
cx: &LowerCtx<'_, 'd, '_>,
s: sv_lang::Statement<'d>,
) -> Result<Expression<'d>, LoweringError> {
sv_sim_sim::patterns::pattern_case_expr(s).ok_or_else(|| {
err(
cx,
"case ... matches: its case expression is not recoverable from the statement's \
semantic children",
)
})
}
pub(crate) fn case_arms<'ctx, 'd>(
cx: &LowerCtx<'ctx, 'd, '_>,
s: sv_lang::Statement<'d>,
) -> Result<(MatchMode, Vec<Vec<Clause<'ctx, 'd>>>), LoweringError> {
let sel = case_expression(cx, s)?;
let cond = s
.pattern_case_condition()
.ok_or_else(|| err(cx, "case ... matches without a case condition"))?;
let mode = mode_of(cx, cond)?;
let subject = subject_of(cx, sel)?;
let arms = s
.pattern_case_items()
.iter()
.map(|item| {
let mut clauses = vec![Clause::Match {
subject: SubjectSrc::Done(subject),
pat: item.pattern(),
}];
if let Some(filter) = item.filter() {
clauses.push(Clause::Cond(filter));
}
clauses
})
.collect();
Ok((mode, arms))
}
pub(crate) fn mode_of(
cx: &LowerCtx<'_, '_, '_>,
cond: sv_lang::CaseStatementCondition,
) -> Result<MatchMode, LoweringError> {
use sv_lang::CaseStatementCondition as C;
match cond {
C::Normal => Ok(MatchMode::Exact),
C::WildcardJustZ => Ok(MatchMode::Z),
C::WildcardXOrZ => Ok(MatchMode::X),
C::Inside => Err(err(cx, "`case ... inside` with `matches`")),
}
}
pub(crate) fn lower_pattern_ternary<'ctx, 'd>(
cx: &LowerCtx<'ctx, 'd, '_>,
e: Expression<'d>,
) -> Result<LoweredValue<'ctx>, LoweringError> {
let then_expr = e
.true_value()
.ok_or_else(|| err(cx, "ternary missing true-value arm"))?;
let else_expr = e
.false_value()
.ok_or_else(|| err(cx, "ternary missing false-value arm"))?;
let clauses = clauses_of(cx, e.conditions().iter().map(|c| (c.expr(), c.pattern())))?;
let ok = lower_clauses(cx, &clauses, MatchMode::Exact)?;
let (_, f) = crate::stmt_shared::current_block_and_fn(cx)?;
let then_blk = cx.ctx.append_basic_block(f, "ptern_then");
let else_blk = cx.ctx.append_basic_block(f, "ptern_else");
let merge = cx.ctx.append_basic_block(f, "ptern_merge");
cx.b.build_conditional_branch(ok, then_blk, else_blk)
.unwrap();
let arm = |blk: BasicBlock<'ctx>,
x: Expression<'d>|
-> Result<(LoweredValue<'ctx>, BasicBlock<'ctx>), LoweringError> {
cx.b.position_at_end(blk);
let v = lower_expr(cx, x)?;
let from =
cx.b.get_insert_block()
.ok_or_else(|| err(cx, "a ternary arm left no open basic block"))?;
Ok((v, from))
};
let (then_v, then_from) = arm(then_blk, then_expr)?;
cx.b.build_unconditional_branch(merge).unwrap();
let (else_v, else_from) = arm(else_blk, else_expr)?;
cx.b.build_unconditional_branch(merge).unwrap();
cx.b.position_at_end(merge);
match (then_v, else_v) {
(LoweredValue::Packed(a), LoweredValue::Packed(b)) => {
if a.width != b.width {
return Err(err(cx, "pattern ternary arms of different widths"));
}
let phi = cx.b.build_phi(a.llvm.get_type(), "ptern").unwrap();
phi.add_incoming(&[(&a.llvm, then_from), (&b.llvm, else_from)]);
Ok(LoweredValue::Packed(PackedValue {
llvm: phi.as_basic_value().into_int_value(),
..a
}))
}
(LoweredValue::Real(a), LoweredValue::Real(b)) => {
let (a, b) = (
crate::real::as_f64(cx, a.llvm),
crate::real::as_f64(cx, b.llvm),
);
let phi = cx.b.build_phi(cx.ctx.f64_type(), "ptern").unwrap();
phi.add_incoming(&[(&a, then_from), (&b, else_from)]);
Ok(LoweredValue::Real(crate::value::RealValue {
llvm: phi.as_basic_value().into_float_value(),
}))
}
(LoweredValue::Str(a), LoweredValue::Str(b)) => {
let phi = cx.b.build_phi(a.llvm.get_type(), "ptern").unwrap();
phi.add_incoming(&[(&a.llvm, then_from), (&b.llvm, else_from)]);
Ok(LoweredValue::Str(crate::strings::StrValue {
llvm: phi.as_basic_value().into_int_value(),
}))
}
_ => Err(err(
cx,
"a pattern-matching conditional whose arms are wide vectors or of different kinds",
)),
}
}
pub(crate) fn print_pattern<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
ty: &Type<'_>,
v: PackedValue<'ctx>,
text: &mut String,
leaves: &mut Vec<LoweredValue<'ctx>>,
) -> Result<(), LoweringError> {
let mut picked = crate::strings::literal(cx, b"'{invalid}");
for (idx, member) in ty
.fields()
.enumerate()
.collect::<Vec<_>>()
.into_iter()
.rev()
{
let (_, w) = member_window(cx, ty, &member)?;
let mut mtext = format!("'{{{}:", member.name());
let mut mleaves = Vec::new();
if w == 0 {
mtext.push_str("void");
} else {
let fty = member
.value_type()
.ok_or_else(|| err(cx, "a tagged union member with no type"))?;
crate::systask::aggregate_pattern(
cx,
&fty,
PackedValue {
signed: fty.is_signed(),
..slice(cx, v, 0, w)
},
&mut mtext,
&mut mleaves,
)?;
}
mtext.push('}');
let fmt = crate::systask::build_c_string_global(cx.ctx, cx.module, &mtext, "tagged_fmt");
let s = crate::strings::sformat_with(cx, fmt, mleaves)?;
picked = match tag_matches(cx, v, ty, idx as u32, MatchMode::Exact)? {
None => s,
Some(hit) => crate::strings::StrValue {
llvm: cx
.b
.build_select(hit, s.llvm, picked.llvm, "tagged_pick")
.unwrap()
.into_int_value(),
},
};
}
text.push_str("%s");
leaves.push(LoweredValue::Str(picked));
Ok(())
}