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
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1360,7 +1360,9 @@ jobs:
name: modelica_models Compatibility Gate
runs-on: ubuntu-24.04
needs: [nix-build-msl]
timeout-minutes: 15
# Cachix is optional for PRs, so a cache miss must leave enough time to
# rebuild the shared MSL artifact bundle before compiling the corpus.
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
with:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1365,6 +1365,13 @@ impl<'a> LowerBuilder<'a> {
}
};

if component_reference_has_slice_subscript(comp) {
match self.lower_statement_or_stop(statement, scope, call_depth)? {
true => break,
false => continue,
}
}

let target = assignment_target(comp, &self.local_const_bindings)?;
if target.indices.is_some() || target.base != output.name {
match self.lower_statement_or_stop(statement, scope, call_depth)? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ mod projected_array;
#[path = "function_projection/projection_helpers.rs"]
mod projection_helpers;
mod projection_selection;
#[path = "function_projection/scope_initialization.rs"]
mod scope_initialization;
#[path = "function_projection/selected_output.rs"]
mod selected_output;
#[path = "function_projection/target_projection.rs"]
Expand All @@ -39,11 +41,12 @@ use dimension_helpers::{
assignment_projection_dims, binary_mul_dims, constructor_input_projection_dims,
copy_projection_dims, declared_dims, dimension_mismatch_error, elementwise_binary_dims,
exact_declared_function_output_dims, flat_index_from_indices, flatten_array_elements,
formal_actual_projection_dims, is_ignorable_projection_statement, is_same_plain_var_ref,
named_actual_span, named_argument_spans, projected_declared_output_dims,
projected_field_output_dims, projection_assignment_target, required_flat_index_to_subscripts,
reserve_projection_capacity, scalar_count_for_dims, selector_dims_from_indices,
single_field_path, sum_expressions, valid_product_dim,
formal_accepts_structured_actual, formal_actual_projection_dims,
is_ignorable_projection_statement, is_same_plain_var_ref, named_actual_span,
named_argument_spans, projected_declared_output_dims, projected_field_output_dims,
projection_assignment_target, required_flat_index_to_subscripts, reserve_projection_capacity,
scalar_count_for_dims, selector_dims_from_indices, single_field_path, sum_expressions,
valid_product_dim,
};
#[cfg(test)]
use entrypoints::take_selected_projected_output;
Expand All @@ -64,9 +67,9 @@ use projected_array::projected_array_expression;
use projection_helpers::{
ArrayProjectionValueCtx, IfStatementProjection, MatrixVectorProductDims,
ProjectionAssignmentSelector, ProjectionAssignmentTarget, ProjectionValueCtx,
ScalarSelectionCtx, SelectedAssignment, array_element_scalar_width,
inherited_projection_source_span, inherited_projection_span, matrix_column_child_flat_index,
matrix_column_operand_count, matrix_elements_are_row_literals,
ScalarSelectionCtx, ScopedSelectionValueCtx, ScopedSubscriptProjectionCtx, SelectedAssignment,
array_element_scalar_width, inherited_projection_source_span, inherited_projection_span,
matrix_column_child_flat_index, matrix_column_operand_count, matrix_elements_are_row_literals,
outputs_contain_unresolved_function_scope_refs, projection_actual_with_span,
projection_arg_or_context_span, projection_value_ctx,
};
Expand Down Expand Up @@ -393,59 +396,29 @@ impl<'a> FunctionProjectionAnalysis<'a> {
format!("function `{}` input `{}`", function.name, input.name),
actual.span().unwrap_or(actual_span),
)?;
if let Some(dims) = dims.filter(|dims| !dims.is_empty()) {
self.insert_input_scalar_projection(
input,
&actual,
dims,
&mut scope,
depth + 1,
actual_span,
)?;
match dims {
Some(dims) if !dims.is_empty() => {
self.insert_input_scalar_projection(
input,
&actual,
dims,
&mut scope,
depth + 1,
actual_span,
)?;
}
Some(dims) => {
scope.dims.insert(input.name.clone(), dims);
}
None if input.dims.is_empty() && !formal_accepts_structured_actual(input) => {
scope.dims.insert(input.name.clone(), Vec::new());
}
None => {}
}
}
Ok(Some(scope))
}

fn initialize_projected_declared_arrays(
&self,
function: &rumoca_core::Function,
scope: &mut FunctionProjectionScope,
depth: usize,
owner_span: rumoca_core::Span,
) -> Result<(), LowerError> {
for param in function.outputs.iter().chain(function.locals.iter()) {
if self.initialize_declared_default(param, scope, depth + 1, owner_span)? {
continue;
}
if param.dims.is_empty() || scope.scalars.contains_key(param.name.as_str()) {
continue;
}
let param_span = inherited_projection_span(param.span, owner_span);
let count = scalar_count_for_dims(
&param.dims,
"function declared array dimensions",
param_span,
)?;
let dims = copy_projection_dims(
&param.dims,
"projected declared array dimension count",
param_span,
)?;
scope.dims.insert(param.name.clone(), dims);
let mut scalars = projection_vec_with_capacity(
count,
"projected declared array scalar count",
param_span,
)?;
for _ in 0..count {
scalars.push(rumoca_core::Expression::Empty { span: param_span });
}
scope.scalars.insert(param.name.clone(), scalars);
}
Ok(())
}

fn initialize_declared_default(
&self,
param: &rumoca_core::FunctionParam,
Expand Down Expand Up @@ -644,7 +617,7 @@ impl<'a> FunctionProjectionAnalysis<'a> {
is_constructor: false,
span,
};
let call = self.substitute(&call, scope)?;
let call = self.substitute_for_call(&call, scope)?;
let projected = self
.function_call_outputs_with_owner(&call, depth + 1, span)?
.ok_or_else(|| {
Expand Down Expand Up @@ -786,8 +759,13 @@ impl<'a> FunctionProjectionAnalysis<'a> {
}
return Ok(());
}
let inferred_dims = self.expr_dims_with_owner(&value, scope, depth + 1, value_span)?;
let inferred_dims = self
.expr_dims_with_owner(&value, scope, depth + 1, value_span)?
.or_else(|| scope.dims.get(target.as_str()).cloned());
let dims = assignment_projection_dims(function, &target, inferred_dims, value_span)?;
if let Some(dims) = dims.as_ref().filter(|dims| dims.is_empty()) {
scope.dims.insert(target.clone(), dims.clone());
}
if let Some(dims) = dims.filter(|dims| !dims.is_empty()) {
let scalars = self
.project_value_scalars(&value, &dims, scope, depth + 1, value_span)
Expand Down Expand Up @@ -932,6 +910,12 @@ impl<'a> FunctionProjectionAnalysis<'a> {
span,
)
})?;
if !self
.subscripted_projection_dims(dims, subscripts, scope, depth + 1, span)?
.is_empty()
{
return self.substitute(value, scope);
}
return projected_scalar_selection(
ScalarSelectionCtx {
name: name.as_str(),
Expand Down Expand Up @@ -969,6 +953,12 @@ impl<'a> FunctionProjectionAnalysis<'a> {
*span,
)
})?;
if !self
.subscripted_projection_dims(dims, subscripts, scope, depth + 1, *span)?
.is_empty()
{
return Ok(value);
}
projected_scalar_selection(
ScalarSelectionCtx {
name: name.as_str(),
Expand Down Expand Up @@ -1427,7 +1417,28 @@ impl<'a> FunctionProjectionAnalysis<'a> {
expr: &rumoca_core::Expression,
scope: &FunctionProjectionScope,
) -> Result<rumoca_core::Expression, LowerError> {
let mut substituter = FunctionScopeSubstituter { scope, error: None };
let mut substituter = FunctionScopeSubstituter {
scope,
materialize_arrays: false,
error: None,
};
let expr = substituter.rewrite_expression(expr);
if let Some(error) = substituter.error {
return Err(error);
}
Ok(expr)
}

fn substitute_for_call(
&self,
expr: &rumoca_core::Expression,
scope: &FunctionProjectionScope,
) -> Result<rumoca_core::Expression, LowerError> {
let mut substituter = FunctionScopeSubstituter {
scope,
materialize_arrays: true,
error: None,
};
let expr = substituter.rewrite_expression(expr);
if let Some(error) = substituter.error {
return Err(error);
Expand Down Expand Up @@ -1463,7 +1474,7 @@ impl<'a> FunctionProjectionAnalysis<'a> {
depth: usize,
owner_span: rumoca_core::Span,
) -> Result<Option<Vec<rumoca_core::Expression>>, LowerError> {
let mut substituted = self.substitute(expr, scope)?;
let mut substituted = self.substitute_for_call(expr, scope)?;
if substituted.span().is_none() {
substituted = substituted.with_span(owner_span);
}
Expand Down Expand Up @@ -1506,6 +1517,21 @@ impl<'a> FunctionProjectionAnalysis<'a> {
if dims.is_empty() {
return Ok(Some(self.substitute(expr, scope)?));
}
if let Some((name, subscripts, selection_span)) = indexed_var_selection(expr)
&& scope.scalars.contains_key(name.as_str())
{
return self.project_scoped_selection_value(
name,
subscripts,
ScopedSelectionValueCtx {
result_dims: dims,
flat_index,
scope,
depth: depth + 1,
span: inherited_projection_span(selection_span, owner_span),
},
);
}
match expr {
rumoca_core::Expression::VarRef {
name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ pub(super) fn assignment_projection_dims(
)),
}
}
(Some(value_dims), _) if value_dims.is_empty() => Ok(None),
(Some(value_dims), _) if value_dims.is_empty() => Ok(Some(value_dims)),
(Some(value_dims), _) => Ok(Some(value_dims)),
(None, Some(declared)) => Ok(Some(declared)),
(None, None) => Ok(None),
Expand Down Expand Up @@ -403,6 +403,7 @@ pub(super) fn projection_assignment_target(

pub(super) struct FunctionScopeSubstituter<'a> {
pub(super) scope: &'a FunctionProjectionScope,
pub(super) materialize_arrays: bool,
pub(super) error: Option<LowerError>,
}

Expand Down Expand Up @@ -451,6 +452,34 @@ impl FunctionScopeSubstituter<'_> {
}
}
}

fn projected_array_binding(
&mut self,
expr: &rumoca_core::Expression,
name: &rumoca_core::Reference,
span: rumoca_core::Span,
) -> rumoca_core::Expression {
if !self.materialize_arrays {
return expr.clone();
}
let Some(values) = self.scope.scalars.get(name.as_str()) else {
return expr.clone();
};
let Some(dims) = self.scope.dims.get(name.as_str()) else {
self.error = Some(LowerError::contract_violation(
format!(
"projected array `{}` has scalar values but no dimensions",
name.as_str()
),
span,
));
return expr.clone();
};
projected_array_expression(values, dims, span).unwrap_or_else(|error| {
self.error = Some(error);
expr.clone()
})
}
}

impl ExpressionRewriter for FunctionScopeSubstituter<'_> {
Expand All @@ -469,6 +498,9 @@ impl ExpressionRewriter for FunctionScopeSubstituter<'_> {
if !subscripts.is_empty() {
return self.walk_expression(expr);
}
if self.scope.scalars.contains_key(name.as_str()) {
return self.projected_array_binding(expr, name, *span);
}
if let Some(expr) = self.scope.full.get(name.as_str()) {
return expr.clone().with_span(*span);
}
Expand Down Expand Up @@ -501,6 +533,25 @@ impl ExpressionRewriter for FunctionScopeSubstituter<'_> {
}
self.walk_expression(expr)
}

fn walk_function_call_expression(
&mut self,
name: &rumoca_core::Reference,
args: &[rumoca_core::Expression],
is_constructor: bool,
span: rumoca_core::Span,
) -> rumoca_core::Expression {
let previous = self.materialize_arrays;
self.materialize_arrays = true;
let args = self.rewrite_expressions(args);
self.materialize_arrays = previous;
rumoca_core::Expression::FunctionCall {
name: name.clone(),
args,
is_constructor,
span,
}
}
}

pub(super) fn scalar_count_for_dims(
Expand Down
Loading
Loading