Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 107 additions & 2 deletions crates/celox-sv-analyzer/src/ast/classes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,22 @@ impl Property {
}
}

/// A property of unpacked structure type, or a member of one that is itself
/// an unpacked structure. It is not laid out: its leaf members are
/// properties named by their member path (see `unpacked_structs`).
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) struct AggregateProperty {
pub name: String,
pub owner: String,
pub local: bool,
pub protected: bool,
pub r#type: Type,
pub initializer: Option<celox_sv_syntax::ExprId>,
/// How many properties precede it: a later declaration of the same
/// name hides an earlier one.
pub position: usize,
}

/// A static property of a class: one variable of the module that declares
/// the class, shared by all of its objects (IEEE 1800-2023 8.9).
#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -123,6 +139,8 @@ pub(super) struct Class {
pub properties: Vec<Property>,
/// How many of `properties` the bases declare.
pub base_properties: usize,
/// Properties of unpacked structure type of the class and its bases.
pub aggregates: Vec<AggregateProperty>,
/// Static properties of the class and its bases.
pub statics: Vec<StaticProperty>,
/// Methods visible in the class, overrides replacing inherited ones.
Expand Down Expand Up @@ -195,10 +213,42 @@ impl Class {
/// The property `name`; one the class declares hides one of a base
/// (IEEE 1800-2023 8.14).
pub fn property(&self, name: &str) -> Option<&Property> {
self.properties
let (index, property) = self
.properties
.iter()
.enumerate()
.rev()
.find(|(_, property)| property.name == name)?;
(!self.hidden(name, index + 1, index)).then_some(property)
}

/// The property or member of unpacked structure type `name`.
pub fn aggregate(&self, name: &str) -> Option<&AggregateProperty> {
let aggregate = self
.aggregates
.iter()
.rev()
.find(|property| property.name == name)
.find(|aggregate| aggregate.name == name)?;
(!self.hidden(name, aggregate.position, aggregate.position)).then_some(aggregate)
}

/// Whether a later declaration hides the property or member `name`: a
/// property from position `properties` on, or a property of unpacked
/// structure type after position `aggregates`, of that name or of a
/// structure that contains it (`p` for `p.a`).
fn hidden(&self, name: &str, properties: usize, aggregates: usize) -> bool {
let paths = name
.match_indices('.')
.map(|(end, _)| &name[..end])
.chain([name])
.collect::<Vec<_>>();
self.properties
.iter()
.enumerate()
.any(|(index, property)| index >= properties && paths.contains(&property.name.as_str()))
|| self.aggregates.iter().any(|aggregate| {
aggregate.position > aggregates && paths.contains(&aggregate.name.as_str())
})
}

pub fn static_property(&self, name: &str) -> Option<&StaticProperty> {
Expand Down Expand Up @@ -746,6 +796,10 @@ pub(super) fn class(
.map(|base| base.properties.clone())
.unwrap_or_default();
let base_properties = properties.len();
let mut aggregates = base
.as_ref()
.map(|base| base.aggregates.clone())
.unwrap_or_default();
let mut statics = base
.as_ref()
.map(|base| base.statics.clone())
Expand Down Expand Up @@ -782,6 +836,55 @@ pub(super) fn class(
return Err(unsupported("const dynamic container class property"));
}
let property_name = instances::normalize_identifier(&variable.name.spelling);
if let Some(aggregate) = ty.unpacked_struct() {
if property.qualifiers.is_static {
return Err(unsupported(
"static class property of unpacked structure type",
));
}
if property.qualifiers.rand || property.qualifiers.randc {
return Err(unsupported(
"random class property of unpacked structure type",
));
}
if property.declaration.constant {
return Err(unsupported(
"const class property of unpacked structure type",
));
}
for (aggregate_name, aggregate_type) in
unpacked_structs::aggregates(&property_name, &ty)
{
aggregates.push(AggregateProperty {
initializer: (aggregate_name == property_name)
.then_some(variable.value)
.flatten(),
name: aggregate_name,
owner: name.to_owned(),
local: property.qualifiers.local,
protected: property.qualifiers.protected,
r#type: aggregate_type,
position: properties.len(),
});
}
for leaf in aggregate.leaves() {
let field = field(&tree.state, &leaf.r#type, env, size)?;
size = field.offset + field.storage();
properties.push(Property {
name: leaf.name(&property_name),
owner: name.to_owned(),
constant: false,
local: property.qualifiers.local,
protected: property.qualifiers.protected,
r#type: leaf.r#type,
field,
rand: false,
randc: false,
initializer: None,
});
}
continue;
}
if property.qualifiers.is_static {
statics.retain(|known| known.name != property_name);
statics.push(StaticProperty {
Expand Down Expand Up @@ -946,6 +1049,7 @@ pub(super) fn class(
is_interface: false,
properties,
base_properties,
aggregates,
statics,
methods,
declarations: Vec::new(),
Expand Down Expand Up @@ -1066,6 +1170,7 @@ fn interface_class(
constraints: Vec::new(),
properties: Vec::new(),
base_properties: 0,
aggregates: Vec::new(),
statics: Vec::new(),
methods,
declarations,
Expand Down
10 changes: 6 additions & 4 deletions crates/celox-sv-analyzer/src/ast/expression_binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ pub(super) use direct::{
};
pub(super) use lvalues::{net_lvalue, variable_lvalue};
pub(super) use objects::{
Cast as ObjectCast, Receiver as ObjectReceiver, cast as object_cast,
Cast as ObjectCast, Receiver as ObjectReceiver,
aggregate_property as object_aggregate_property, cast as object_cast,
check_access as check_member_access, check_assignment as check_handle_assignment,
constraint_mode_call as object_constraint_mode_call, constructor as object_constructor,
method as object_method, named_class, native_class as object_native_class,
property as object_property, rand_mode_call as object_rand_mode_call,
receiver as object_receiver, static_property as object_static_property, this as object_this,
method as object_method, method_result as object_method_result, named_class,
native_class as object_native_class, property as object_property,
rand_mode_call as object_rand_mode_call, receiver as object_receiver,
static_property as object_static_property, this as object_this,
};
pub(super) use strings::{
call as string_call, method as string_method, native_string, string_value,
Expand Down
20 changes: 20 additions & 0 deletions crates/celox-sv-analyzer/src/ast/expression_binding/direct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,16 @@ pub(in crate::ast) fn bind_value(
values.push(value);
continue;
}
if let Some((aggregate, class, _)) =
objects::aggregate_property(tree, id, native, dimensions)?
{
values.push(objects::aggregate_load(
&aggregate,
&class,
&objects::this(),
)?);
continue;
}
if let Some(value) = objects::unqualified_property(tree, dimensions, &name)?
{
values.push(value);
Expand Down Expand Up @@ -935,6 +945,16 @@ pub(in crate::ast) fn bind_value(
);
continue;
}
ExprKind::Member { .. }
if objects::aggregate_property(tree, id, native, dimensions)?.is_some() =>
{
let (aggregate, class, receiver) =
objects::aggregate_property(tree, id, native, dimensions)?
.expect("checked by the guard");
let handle = objects::receiver(tree, receiver, native, dimensions)?;
values.push(objects::aggregate_load(&aggregate, &class, &handle)?);
continue;
}
ExprKind::Member { .. }
if objects::property(tree, id, native, dimensions)?.is_some() =>
{
Expand Down
Loading
Loading