Skip to content

Repeat expressions #28701

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: mainnet
Choose a base branch
from
Open

Repeat expressions #28701

wants to merge 2 commits into from

Conversation

mohammadfawaz
Copy link
Collaborator

@mohammadfawaz mohammadfawaz commented Jun 20, 2025

Closes #28670
Also closes #28699

This PR adds repeat expression as in [3u32; 4] which is equivalent to [3u32, 3u32, 3u32, 3u32]. Similarly to array type, the length (or count) is an expression that should evaluate to a constant at some point.

Most change in this PR are fairly clear and straightforward. I still made the interpreter work with repeat expressions even though it's getting replaced soon.

@mohammadfawaz mohammadfawaz self-assigned this Jun 20, 2025
@mohammadfawaz mohammadfawaz force-pushed the mohammadfawaz/repeat_expr_2 branch 6 times, most recently from 3df9ec0 to f8fad05 Compare June 23, 2025 01:07
@mohammadfawaz mohammadfawaz marked this pull request as ready for review June 23, 2025 01:10
@mohammadfawaz mohammadfawaz requested review from d0cd and mikebenfield and removed request for d0cd June 23, 2025 01:10
@mohammadfawaz mohammadfawaz force-pushed the mohammadfawaz/repeat_expr_2 branch from f8fad05 to 10c000c Compare June 23, 2025 13:05
@mohammadfawaz mohammadfawaz force-pushed the mohammadfawaz/repeat_expr_2 branch from 10c000c to 4eb1bfb Compare June 23, 2025 13:08
Comment on lines 633 to 656
// Handle array with multiple elements: [expr1, expr2, ...] or single element with a trailing comma: [expr,]
if self.eat(&Token::Comma) {
let sep = Some(Token::Comma);
let mut elements = vec![first_expr];

while !self.check(&close) {
// Allow parser recovery by accepting optional expressions.
if let Some(elem) = self.parse_expression().map(Some)? {
elements.push(elem);
}

// If the separator is present, consume it; otherwise, exit loop.
if sep.as_ref().filter(|sep| !self.eat(sep)).is_some() {
break;
}
}

let span = open_span + self.expect(&close)?;
return Ok(ArrayExpression { elements, span, id: self.node_builder.next_id() }.into());
}

// Single-element array with no trailing comma: [expr]
let span = open_span + self.expect(&close)?;
Ok(ArrayExpression { elements: vec![first_expr], span, id: self.node_builder.next_id() }.into())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Handle array with multiple elements: [expr1, expr2, ...] or single element with a trailing comma: [expr,]
if self.eat(&Token::Comma) {
let sep = Some(Token::Comma);
let mut elements = vec![first_expr];
while !self.check(&close) {
// Allow parser recovery by accepting optional expressions.
if let Some(elem) = self.parse_expression().map(Some)? {
elements.push(elem);
}
// If the separator is present, consume it; otherwise, exit loop.
if sep.as_ref().filter(|sep| !self.eat(sep)).is_some() {
break;
}
}
let span = open_span + self.expect(&close)?;
return Ok(ArrayExpression { elements, span, id: self.node_builder.next_id() }.into());
}
// Single-element array with no trailing comma: [expr]
let span = open_span + self.expect(&close)?;
Ok(ArrayExpression { elements: vec![first_expr], span, id: self.node_builder.next_id() }.into())
// Handle array with multiple elements: [expr1, expr2, ...] or single element with or without trailing comma: [expr,]
let mut elements = vec![first_expr];
while self.eat(&Token::Comma) && !self.check(&close) {
elements.push(self.parse_expression()?);
}
let span = open_span + self.expect(&close)?;
Ok(ArrayExpression { elements, span, id: self.node_builder.next_id() }.into())

@@ -340,6 +342,30 @@ impl CodeGeneratingVisitor<'_> {
(member_access, String::new())
}

fn visit_repeat(&mut self, input: &RepeatExpression) -> (String, String) {
let (operand, mut operand_instructions) = self.visit_expression(&input.expr);
let count = input.count.as_u32().expect("repeat count should be known at this point");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only place unknown repeat counts are disallowed, so if I just have something like this:

    transition main(x : u32) -> u32 {
        let arr = [3u32; x];
        return arr[0u32];
    }

This expect will be triggered.

I think probably the right way forward is for ConstPropagationOutput to have a repeat_count_not_evaluated item just like it has array_length_not_evaluated and so on, and for the ConstPropagationAndUnrollingPass to check it when it hits a fixed point just as it does for the others. And we should have a test with something like the main function I wrote above that triggers this error.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh absolutely, I did that for array types but forgot to do it here. Thanks for catching this.

@mohammadfawaz
Copy link
Collaborator Author

@mikebenfield requested changes addressed in this commit: 1bb79ed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Testing] Add execution tests that use const generics and const expr array lengths [Feature] Allow initializing an array using a repeat expression
2 participants