use super::aggregate::{Chain, RawStep, aggregate_chain};
use super::real::{real_kind, strip_conversions};
use super::*;
use crate::constraints::ir::{ArrayId, ArrayNode};
#[derive(Clone, Copy, Debug)]
enum Sel {
Index(i64),
Slice { lo: i64, hi: i64, asc: Option<bool> },
}
fn bad(what: impl Into<String>) -> BuildError {
BuildError::BadUnique { what: what.into() }
}
fn element_name(base: &str, idx: &[i64]) -> String {
let mut s = base.to_string();
for i in idx {
s.push_str(&format!("[{i}]"));
}
s
}
fn dimension<'d>(ty: Type<'d>, what: &str) -> Result<Option<(i64, i64, Type<'d>)>, BuildError> {
let c = ty.canonical();
if c.is_dynamically_sized_array() || c.is_queue() || c.is_associative_array() {
return Err(unsupported(
format!(
"`{what}`, whose size is only known at run time (a dynamic array, queue or \
associative array), as a `unique` member"
),
"C14",
));
}
if !c.is_unpacked_array() {
return Ok(None);
}
let r = c.fixed_range();
let elem = c.element_type().ok_or_else(|| BuildError::Missing {
what: format!("element type of the array `{what}`"),
})?;
Ok(Some((i64::from(r.left), i64::from(r.right), elem)))
}
fn expand<'d>(
ty: Type<'d>,
sels: &[Sel],
name: &str,
prefix: &mut Vec<i64>,
out: &mut Vec<Vec<i64>>,
) -> Result<Type<'d>, BuildError> {
let Some((left, right, elem)) = dimension(ty, name)? else {
if !sels.is_empty() {
return Err(bad(format!(
"`{name}` is selected into below an unpacked array, which is a bit- or \
part-select and not a variable"
)));
}
out.push(prefix.clone());
return Ok(ty);
};
let desc = left >= right;
let (lower, upper) = (left.min(right), left.max(right));
let check = |i: i64| {
if (lower..=upper).contains(&i) {
Ok(())
} else {
Err(BuildError::OutOfRangeSelect {
what: format!("`{name}` index {i} outside [{left}:{right}]"),
})
}
};
let ordered = |lo: i64, hi: i64| -> Vec<i64> {
if desc {
(lo..=hi).rev().collect()
} else {
(lo..=hi).collect()
}
};
let indices = match sels.first() {
Some(Sel::Index(i)) => {
check(*i)?;
vec![*i]
}
Some(Sel::Slice { lo, hi, asc }) => {
check(*lo)?;
check(*hi)?;
if let Some(asc) = asc
&& lo != hi
&& *asc == desc
{
return Err(bad(format!(
"the slice of `{name}` is written against the array's declared \
direction [{left}:{right}]"
)));
}
ordered(*lo, *hi)
}
None => ordered(lower, upper),
};
let rest = sels.get(1..).unwrap_or(&[]);
let mut leaf = None;
for i in indices {
prefix.push(i);
let t = expand(elem, rest, name, prefix, out)?;
prefix.pop();
leaf = Some(t);
}
leaf.ok_or_else(|| BuildError::Missing {
what: format!("elements of the array `{name}`"),
})
}
struct Member<'d> {
root: Expression<'d>,
chain: Option<Chain<'d>>,
name: String,
leaf: Type<'d>,
paths: Vec<Vec<i64>>,
}
impl Builder<'_> {
fn unique_target<'d>(e: Expression<'d>) -> Result<(Expression<'d>, Vec<Sel>), BuildError> {
let e = strip_conversions(e);
match e.kind() {
ExpressionKind::NamedValue
| ExpressionKind::HierarchicalValue
| ExpressionKind::MemberAccess => Ok((e, Vec::new())),
ExpressionKind::ElementSelect | ExpressionKind::RangeSelect => {
let base =
e.select_value()
.map(strip_conversions)
.ok_or_else(|| BuildError::Missing {
what: "base of a select".into(),
})?;
let bt = base.expr_type().ok_or_else(|| BuildError::Missing {
what: "type of a select base".into(),
})?;
if dimension(bt, "the selected array")?.is_none() {
return Err(bad(
"a bit- or part-select of a packed value is not a variable",
));
}
let (root, mut sels) = Self::unique_target(base)?;
if matches!(sels.last(), Some(Sel::Slice { .. })) {
return Err(unsupported(
"a select of an array slice as a `unique` member",
"C14",
));
}
let run_time = || {
unsupported(
"a `unique` member selected by a run time index or bound (array \
elements are only variables at fixed indices)",
"C14",
)
};
let const_of = |x: Expression<'_>| x.constant_value().and_then(|c| c.as_i64());
if e.kind() == ExpressionKind::ElementSelect {
let i = e.selector().and_then(const_of).ok_or_else(run_time)?;
sels.push(Sel::Index(i));
return Ok((root, sels));
}
let kind = e
.range_selection_kind()
.ok_or_else(|| BuildError::Missing {
what: "kind of a range select".into(),
})?;
let (Some(l), Some(r)) = (e.range_left(), e.range_right()) else {
return Err(BuildError::Missing {
what: "bounds of a range select".into(),
});
};
let l = const_of(l).ok_or_else(run_time)?;
let r = const_of(r).ok_or_else(run_time)?;
sels.push(if kind == 0 {
Sel::Slice {
lo: l.min(r),
hi: l.max(r),
asc: Some(l <= r),
}
} else if r < 1 {
return Err(BuildError::Missing {
what: "constant width of an indexed part select".into(),
});
} else if kind == 1 {
Sel::Slice {
lo: l,
hi: l + r - 1,
asc: None,
}
} else {
Sel::Slice {
lo: l - r + 1,
hi: l,
asc: None,
}
});
Ok((root, sels))
}
other => Err(bad(format!("a `{other:?}` expression is not a variable"))),
}
}
fn unique_member<'d>(&self, item: Expression<'d>) -> Result<Member<'d>, BuildError> {
let (root, sels) = Self::unique_target(item)?;
let chain = aggregate_chain(root, &self.forced_roots);
let (sym, local) = match root.kind() {
ExpressionKind::MemberAccess => {
let base = Self::operand(root, "a member access")?;
let is_this = base.kind() == ExpressionKind::NamedValue
&& base.referenced_symbol().is_some_and(|s| s.name() == "this");
let member = root.member_symbol().ok_or_else(|| BuildError::Missing {
what: "member symbol of a member access".into(),
})?;
(member, is_this)
}
ExpressionKind::NamedValue | ExpressionKind::HierarchicalValue => (
root.referenced_symbol()
.ok_or_else(|| BuildError::Missing {
what: "symbol of a named value".into(),
})?,
true,
),
other => unreachable!(
"unique_target returns only named values and member accesses, got {other:?}"
),
};
let name = sym.name().to_string();
let ty = root.expr_type().ok_or_else(|| BuildError::Missing {
what: format!("type of `{name}`"),
})?;
let mut paths = Vec::new();
let leaf = expand(ty, &sels, &name, &mut Vec::new(), &mut paths)?;
let local = local || chain.is_some();
if !local && paths.iter().any(|p| !p.is_empty()) {
return Err(unsupported(
format!(
"array elements of `{name}`, read through an object handle, as `unique` members"
),
"C17 follow-up",
));
}
Ok(Member {
root,
chain,
name,
leaf,
paths,
})
}
fn unique_leaf(&mut self, m: &Member<'_>, path: &[i64]) -> Result<Expr, BuildError> {
let ex = if path.is_empty() {
self.expr(m.root)?
} else {
let mut chain = m.chain.clone().ok_or_else(|| BuildError::Missing {
what: format!("property symbol of `{}`", m.name),
})?;
chain.steps.extend(path.iter().map(|&i| RawStep::Const(i)));
let ty = ty_of(Some(m.leaf), &format!("`{}`", m.name))?;
self.aggregate_value(chain, ty)?
};
match &ex {
Expr::Var(id, _) if self.vars[*id as usize].randc => Err(bad(format!(
"`{}` is a `randc` variable, which may not appear in a `unique` group",
element_name(&m.name, path)
))),
Expr::Var(..) | Expr::State(..) => Ok(ex),
_ => Err(bad(format!(
"`{}` is not a variable (it folds to a constant)",
element_name(&m.name, path)
))),
}
}
fn dynamic_member<'d>(
&self,
item: Expression<'d>,
) -> Option<Result<(String, Type<'d>), BuildError>> {
let e = strip_conversions(item);
if !matches!(
e.kind(),
ExpressionKind::NamedValue
| ExpressionKind::HierarchicalValue
| ExpressionKind::MemberAccess
) {
return None;
}
let t = e.expr_type()?.canonical();
if !(t.is_dynamically_sized_array() || t.is_queue()) {
return None;
}
let name = e
.referenced_symbol()
.or_else(|| e.member_symbol())
.map(|s| s.name().to_string())
.unwrap_or_default();
let elem = t.element_type();
Some(
elem.map(|el| (name.clone(), el))
.ok_or_else(|| BuildError::Missing {
what: format!("element type of the array `{name}`"),
}),
)
}
pub(super) fn unique(&mut self, c: Constraint<'_>) -> Result<Vec<Expr>, BuildError> {
let mut group = Vec::new();
let mut dyns: Vec<(String, Type<'_>, ArrayId)> = Vec::new();
for k in c.children() {
let item = k.as_expression().ok_or_else(|| BuildError::Missing {
what: "expression member of a `unique` constraint".into(),
})?;
if let Some(t) = self.dynamic_member(item) {
let (name, leaf) = t?;
self.arrays_allowed("a dynamic array as a `unique` member")?;
dyns.push((name, leaf, self.array_of(item)?));
continue;
}
group.push(self.unique_member(item)?);
}
if dyns.is_empty() && group.iter().map(|m| m.paths.len()).sum::<usize>() < 2 {
return Ok(Vec::new());
}
for (name, leaf, _) in &dyns {
if real_kind(Some(*leaf)).is_some() {
return Err(BuildError::RealConstraint {
what: format!(
"`unique` over the real elements of `{name}`: the real-domain solver \
has no pairwise-distinct constraint (IEEE 1800-2023 18.5.4 allows \
real members)"
),
});
}
if !leaf.is_integral() {
return Err(bad(format!(
"the elements of `{name}` are not of an integral or real type (they are `{}`)",
leaf.to_sv_string()
)));
}
}
let mut members = Vec::new();
for m in &group {
if real_kind(Some(m.leaf)).is_some() {
return Err(BuildError::RealConstraint {
what: format!(
"`unique` over the real value `{}`: the real-domain solver has no \
pairwise-distinct constraint (IEEE 1800-2023 18.5.4 allows real members)",
m.name
),
});
}
if !m.leaf.is_integral() {
return Err(bad(format!(
"`{}` is not a variable of an integral or real type (it is `{}`)",
m.name,
m.leaf.to_sv_string()
)));
}
for p in &m.paths {
members.push(self.unique_leaf(m, p)?);
}
}
let leaves: Vec<(&str, Type<'_>)> = group
.iter()
.map(|m| (m.name.as_str(), m.leaf))
.chain(dyns.iter().map(|(n, l, _)| (n.as_str(), *l)))
.collect();
let (first_name, first_leaf) = leaves[0];
for &(name, leaf) in &leaves[1..] {
if !first_leaf.is_equivalent(&leaf) {
return Err(bad(format!(
"`{first_name}` (`{}`) and `{name}` (`{}`) are not of equivalent types \
(IEEE 1800-2023 6.22.1)",
first_leaf.to_sv_string(),
leaf.to_sv_string()
)));
}
}
if !dyns.is_empty() {
let arrays: Vec<ArrayId> = dyns.iter().map(|d| d.2).collect();
let elem = self.arrays[arrays[0] as usize].elem_ty;
if members.iter().any(|e| e.ty() != elem)
|| arrays
.iter()
.any(|&a| self.arrays[a as usize].elem_ty != elem)
{
return Err(bad(
"the members are not of equivalent types (their solved widths or signedness differ)",
));
}
return Ok(vec![Expr::Array(ArrayNode::Distinct {
scalars: members,
arrays,
})]);
}
let ty = members[0].ty();
if members.iter().any(|e| e.ty() != ty) {
return Err(bad(
"the members are not of equivalent types (their solved widths or signedness differ)",
));
}
Ok(vec![Expr::AllDistinct(members)])
}
}