Changeset - a2e4729a398d
[Not reviewed]
0 6 0
mh - 4 years ago 2021-12-21 14:09:23
contact@maxhenger.nl
Refactored variable resolving
6 files changed with 166 insertions and 128 deletions:
0 comments (0 inline, 0 general)
src/protocol/ast.rs
Show inline comments
 
@@ -1301,7 +1301,7 @@ pub struct BreakStatement {
 
    pub span: InputSpan, // of the "break" keyword
 
    pub label: Option<Identifier>,
 
    // Phase 2: linker
 
    pub target: Option<EndWhileStatementId>,
 
    pub target: EndWhileStatementId, // invalid if not yet set
 
}
 

	
 
#[derive(Debug, Clone)]
 
@@ -1311,7 +1311,7 @@ pub struct ContinueStatement {
 
    pub span: InputSpan, // of the "continue" keyword
 
    pub label: Option<Identifier>,
 
    // Phase 2: linker
 
    pub target: Option<WhileStatementId>,
 
    pub target: WhileStatementId, // invalid if not yet set
 
}
 

	
 
#[derive(Debug, Clone)]
 
@@ -1388,7 +1388,7 @@ pub struct GotoStatement {
 
    pub span: InputSpan, // of the "goto" keyword
 
    pub label: Identifier,
 
    // Phase 2: linker
 
    pub target: Option<LabeledStatementId>,
 
    pub target: LabeledStatementId, // invalid if not yet set
 
}
 

	
 
#[derive(Debug, Clone)]
src/protocol/ast_printer.rs
Show inline comments
 
@@ -494,7 +494,7 @@ impl ASTWriter {
 
                self.kv(indent2).with_s_key("Label")
 
                    .with_opt_identifier_val(stmt.label.as_ref());
 
                self.kv(indent2).with_s_key("Target")
 
                    .with_opt_disp_val(stmt.target.as_ref().map(|v| &v.0.index));
 
                    .with_disp_val(&stmt.target.0.index);
 
            },
 
            Statement::Continue(stmt) => {
 
                self.kv(indent).with_id(PREFIX_CONTINUE_STMT_ID, stmt.this.0.index)
 
@@ -502,7 +502,7 @@ impl ASTWriter {
 
                self.kv(indent2).with_s_key("Label")
 
                    .with_opt_identifier_val(stmt.label.as_ref());
 
                self.kv(indent2).with_s_key("Target")
 
                    .with_opt_disp_val(stmt.target.as_ref().map(|v| &v.0.index));
 
                    .with_disp_val(&stmt.target.0.index);
 
            },
 
            Statement::Synchronous(stmt) => {
 
                self.kv(indent).with_id(PREFIX_SYNC_STMT_ID, stmt.this.0.index)
 
@@ -569,7 +569,7 @@ impl ASTWriter {
 
                    .with_s_key("Goto");
 
                self.kv(indent2).with_s_key("Label").with_identifier_val(&stmt.label);
 
                self.kv(indent2).with_s_key("Target")
 
                    .with_opt_disp_val(stmt.target.as_ref().map(|v| &v.0.index));
 
                    .with_disp_val(&stmt.target.0.index);
 
            },
 
            Statement::New(stmt) => {
 
                self.kv(indent).with_id(PREFIX_NEW_STMT_ID, stmt.this.0.index)
src/protocol/eval/executor.rs
Show inline comments
 
@@ -896,12 +896,12 @@ impl Prompt {
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::Break(stmt) => {
 
                cur_frame.position = stmt.target.unwrap().upcast();
 
                cur_frame.position = stmt.target.upcast();
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::Continue(stmt) => {
 
                cur_frame.position = stmt.target.unwrap().upcast();
 
                cur_frame.position = stmt.target.upcast();
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
@@ -992,7 +992,7 @@ impl Prompt {
 
                return Ok(EvalContinuation::Stepping);
 
            },
 
            Statement::Goto(stmt) => {
 
                cur_frame.position = stmt.target.unwrap().upcast();
 
                cur_frame.position = stmt.target.upcast();
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
src/protocol/parser/pass_definitions.rs
Show inline comments
 
@@ -498,7 +498,7 @@ impl PassDefinitions {
 
        } else if next == TokenKind::OpenParen {
 
            // Same as above: memory statement or normal expression
 
            if let Some(memory_stmt_id) = self.maybe_consume_memory_statement_without_semicolon(module, iter, ctx)? {
 
                consume_token(&module.source, iter, TokenKind::SemiColon);
 
                consume_token(&module.source, iter, TokenKind::SemiColon)?;
 
                section.push(memory_stmt_id.upcast().upcast());
 
            } else {
 
                let id = self.consume_expression_statement(module, iter, ctx)?;
 
@@ -624,7 +624,7 @@ impl PassDefinitions {
 
            this,
 
            span: break_span,
 
            label,
 
            target: None,
 
            target: EndWhileStatementId::new_invalid(),
 
        }))
 
    }
 

	
 
@@ -643,7 +643,7 @@ impl PassDefinitions {
 
            this,
 
            span: continue_span,
 
            label,
 
            target: None
 
            target: WhileStatementId::new_invalid(),
 
        }))
 
    }
 

	
 
@@ -767,7 +767,7 @@ impl PassDefinitions {
 
            this,
 
            span: goto_span,
 
            label,
 
            target: None
 
            target: LabeledStatementId::new_invalid(),
 
        }))
 
    }
 

	
src/protocol/parser/pass_typing.rs
Show inline comments
 
@@ -1191,8 +1191,8 @@ impl Visitor for PassTyping {
 
            let guard_stmt_id = section[base_index    ];
 
            let block_stmt_id = section[base_index + 1];
 

	
 
            self.visit_stmt(ctx, guard_stmt_id);
 
            self.visit_stmt(ctx, block_stmt_id);
 
            self.visit_stmt(ctx, guard_stmt_id)?;
 
            self.visit_stmt(ctx, block_stmt_id)?;
 
        }
 
        section.forget();
 

	
src/protocol/parser/pass_validation_linking.rs
Show inline comments
 
@@ -69,6 +69,13 @@ impl DefinitionType {
 
    }
 
}
 

	
 
struct ControlFlowStatement {
 
    in_sync: SynchronousStatementId,
 
    in_while: WhileStatementId,
 
    in_scope: Scope,
 
    statement: StatementId, // of 'break', 'continue' or 'goto'
 
}
 

	
 
/// This particular visitor will go through the entire AST in a recursive manner
 
/// and check if all statements and expressions are legal (e.g. no "return"
 
/// statements in component definitions), and will link certain AST nodes to
 
@@ -79,6 +86,9 @@ impl DefinitionType {
 
/// the linking of function calls and component instantiations will be checked
 
/// and linked to the appropriate definitions, but the return types and/or
 
/// arguments will not be checked for validity.
 
///
 
/// The main idea is, because we're visiting nodes in a tree, to do as much as
 
/// we can while we have the memory in cache.
 
pub(crate) struct PassValidationLinking {
 
    // Traversal state, all valid IDs if inside a certain AST element. Otherwise
 
    // `id.is_invalid()` returns true.
 
@@ -103,6 +113,8 @@ pub(crate) struct PassValidationLinking {
 
    // Keeping track of relative positions and unique IDs.
 
    relative_pos_in_block: i32, // of statements: to determine when variables are visible
 
    next_expr_index: i32, // to arrive at a unique ID for all expressions within a definition
 
    // Control flow statements that require label resolving
 
    control_flow_stmts: Vec<ControlFlowStatement>,
 
    // Various temporary buffers for traversal. Essentially working around
 
    // Rust's borrowing rules since it cannot understand we're modifying AST
 
    // members but not the AST container.
 
@@ -129,6 +141,7 @@ impl PassValidationLinking {
 
            must_be_assignable: None,
 
            relative_pos_in_block: 0,
 
            next_expr_index: 0,
 
            control_flow_stmts: Vec::with_capacity(32),
 
            variable_buffer: ScopedBuffer::with_capacity(128),
 
            definition_buffer: ScopedBuffer::with_capacity(128),
 
            statement_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAPACITY),
 
@@ -139,6 +152,7 @@ impl PassValidationLinking {
 
    fn reset_state(&mut self) {
 
        self.in_sync = SynchronousStatementId::new_invalid();
 
        self.in_while = WhileStatementId::new_invalid();
 
        self.in_select_guard = SelectStatementId::new_invalid();
 
        self.in_test_expr = StatementId::new_invalid();
 
        self.in_binding_expr = BindingExpressionId::new_invalid();
 
        self.in_binding_expr_lhs = false;
 
@@ -148,7 +162,8 @@ impl PassValidationLinking {
 
        self.expr_parent = ExpressionParent::None;
 
        self.must_be_assignable = None;
 
        self.relative_pos_in_block = 0;
 
        self.next_expr_index = 0
 
        self.next_expr_index = 0;
 
        self.control_flow_stmts.clear();
 
    }
 
}
 

	
 
@@ -216,6 +231,7 @@ impl Visitor for PassValidationLinking {
 
        // to each of the locals in the procedure.
 
        ctx.heap[id].num_expressions_in_body = self.next_expr_index;
 
        self.visit_definition_and_assign_local_ids(ctx, id.upcast());
 
        self.resolve_pending_control_flow_targets(ctx)?;
 

	
 
        Ok(())
 
    }
 
@@ -246,6 +262,7 @@ impl Visitor for PassValidationLinking {
 
        // to each of the locals in the procedure.
 
        ctx.heap[id].num_expressions_in_body = self.next_expr_index;
 
        self.visit_definition_and_assign_local_ids(ctx, id.upcast());
 
        self.resolve_pending_control_flow_targets(ctx)?;
 

	
 
        Ok(())
 
    }
 
@@ -259,11 +276,11 @@ impl Visitor for PassValidationLinking {
 
    }
 

	
 
    fn visit_local_memory_stmt(&mut self, ctx: &mut Ctx, id: MemoryStatementId) -> VisitorResult {
 
        // Note that we're not adding the variable to its scope for lookups.
 
        // This is done in the `visit_statement_for_locals_labels_and_in_sync`
 
        // method.
 
        let stmt = &ctx.heap[id];
 
        let expr_id = stmt.initial_expr;
 
        let variable_id = stmt.variable;
 

	
 
        self.checked_add_local(ctx, self.cur_scope, self.relative_pos_in_block, variable_id)?;
 

	
 
        assign_and_replace_next_stmt!(self, ctx, id.upcast().upcast());
 
        debug_assert_eq!(self.expr_parent, ExpressionParent::None);
 
@@ -275,14 +292,24 @@ impl Visitor for PassValidationLinking {
 
    }
 

	
 
    fn visit_local_channel_stmt(&mut self, ctx: &mut Ctx, id: ChannelStatementId) -> VisitorResult {
 
        let stmt = &ctx.heap[id];
 
        let from_id = stmt.from;
 
        let to_id = stmt.to;
 

	
 
        self.checked_add_local(ctx, self.cur_scope, self.relative_pos_in_block, from_id)?;
 
        self.checked_add_local(ctx, self.cur_scope, self.relative_pos_in_block, to_id)?;
 

	
 
        assign_and_replace_next_stmt!(self, ctx, id.upcast().upcast());
 
        Ok(())
 
    }
 

	
 
    fn visit_labeled_stmt(&mut self, ctx: &mut Ctx, id: LabeledStatementId) -> VisitorResult {
 
        let body_id = ctx.heap[id].body;
 
        self.visit_stmt(ctx, body_id)?;
 
        let stmt = &ctx.heap[id];
 
        let body_id = stmt.body;
 

	
 
        self.checked_add_label(ctx, self.relative_pos_in_block, self.in_sync, id)?;
 

	
 
        self.visit_stmt(ctx, body_id)?;
 
        Ok(())
 
    }
 

	
 
@@ -354,33 +381,25 @@ impl Visitor for PassValidationLinking {
 
    }
 

	
 
    fn visit_break_stmt(&mut self, ctx: &mut Ctx, id: BreakStatementId) -> VisitorResult {
 
        // Resolve break target
 
        let target_end_while = {
 
            let stmt = &ctx.heap[id];
 
            let target_while_id = self.resolve_break_or_continue_target(ctx, stmt.span, &stmt.label)?;
 
            let target_while = &ctx.heap[target_while_id];
 
            debug_assert!(!target_while.end_while.is_invalid());
 

	
 
            target_while.end_while
 
        };
 

	
 
        self.control_flow_stmts.push(ControlFlowStatement{
 
            in_sync: self.in_sync,
 
            in_while: self.in_while,
 
            in_scope: self.cur_scope,
 
            statement: id.upcast()
 
        });
 
        assign_then_erase_next_stmt!(self, ctx, id.upcast());
 
        let stmt = &mut ctx.heap[id];
 
        stmt.target = Some(target_end_while);
 

	
 
        Ok(())
 
    }
 

	
 
    fn visit_continue_stmt(&mut self, ctx: &mut Ctx, id: ContinueStatementId) -> VisitorResult {
 
        // Resolve continue target
 
        let target_while_id = {
 
            let stmt = &ctx.heap[id];
 
            self.resolve_break_or_continue_target(ctx, stmt.span, &stmt.label)?
 
        };
 

	
 
        self.control_flow_stmts.push(ControlFlowStatement{
 
            in_sync: self.in_sync,
 
            in_while: self.in_while,
 
            in_scope: self.cur_scope,
 
            statement: id.upcast()
 
        });
 
        assign_then_erase_next_stmt!(self, ctx, id.upcast());
 
        let stmt = &mut ctx.heap[id];
 
        stmt.target = Some(target_while_id);
 

	
 
        Ok(())
 
    }
 
@@ -502,9 +521,10 @@ impl Visitor for PassValidationLinking {
 
            match &ctx.heap[guard_id] {
 
                Statement::Local(LocalStatement::Memory(stmt)) => {
 
                    debug_assert_eq!(ctx.heap[arm_block_id].as_block().this.upcast(), arm_block_id); // backwards way of saying arm_block_id is a BlockStatementId
 
                    let variable_id = stmt.variable;
 
                    let block_stmt = BlockStatementId(arm_block_id);
 
                    let block_scope =  Scope::Regular(block_stmt);
 
                    self.checked_at_single_scope_add_local(ctx, block_scope, -1, stmt.variable)?;
 
                    self.checked_at_single_scope_add_local(ctx, block_scope, -1, variable_id)?;
 
                },
 
                Statement::Expression(_) => {},
 
                _ => unreachable!(), // just to be sure the parser produced the expected AST
 
@@ -543,24 +563,30 @@ impl Visitor for PassValidationLinking {
 
    }
 

	
 
    fn visit_goto_stmt(&mut self, ctx: &mut Ctx, id: GotoStatementId) -> VisitorResult {
 
        let target_id = self.find_label(ctx, &ctx.heap[id].label)?;
 
        ctx.heap[id].target = Some(target_id);
 

	
 
        let target = &ctx.heap[target_id];
 
        if self.in_sync != target.in_sync {
 
            // We can only goto the current scope or outer scopes. Because
 
            // nested sync statements are not allowed we must be inside a sync
 
            // statement.
 
            debug_assert!(!self.in_sync.is_invalid());
 
            let goto_stmt = &ctx.heap[id];
 
            let sync_stmt = &ctx.heap[self.in_sync];
 
            return Err(
 
                ParseError::new_error_str_at_span(&ctx.module().source, goto_stmt.span, "goto may not escape the surrounding synchronous block")
 
                .with_info_str_at_span(&ctx.module().source, target.label.span, "this is the target of the goto statement")
 
                .with_info_str_at_span(&ctx.module().source, sync_stmt.span, "which will jump past this statement")
 
            );
 
        }
 

	
 
        // TODO: Remove this
 
        // let target_id = self.find_label(ctx, &ctx.heap[id].label)?;
 
        // ctx.heap[id].target = Some(target_id);
 
        //
 
        // let target = &ctx.heap[target_id];
 
        // if self.in_sync != target.in_sync {
 
        //     // We can only goto the current scope or outer scopes. Because
 
        //     // nested sync statements are not allowed we must be inside a sync
 
        //     // statement.
 
        //     debug_assert!(!self.in_sync.is_invalid());
 
        //     let goto_stmt = &ctx.heap[id];
 
        //     let sync_stmt = &ctx.heap[self.in_sync];
 
        //     return Err(
 
        //         ParseError::new_error_str_at_span(&ctx.module().source, goto_stmt.span, "goto may not escape the surrounding synchronous block")
 
        //         .with_info_str_at_span(&ctx.module().source, target.label.span, "this is the target of the goto statement")
 
        //         .with_info_str_at_span(&ctx.module().source, sync_stmt.span, "which will jump past this statement")
 
        //     );
 
        // }
 
        self.control_flow_stmts.push(ControlFlowStatement{
 
            in_sync: self.in_sync,
 
            in_while: self.in_while,
 
            in_scope: self.cur_scope,
 
            statement: id.upcast(),
 
        });
 
        assign_then_erase_next_stmt!(self, ctx, id.upcast());
 

	
 
        Ok(())
 
@@ -1166,13 +1192,7 @@ impl Visitor for PassValidationLinking {
 
            Method::Put => {
 
                expecting_primitive_def = true;
 
                expecting_wrapping_sync_stmt = true;
 
                if !self.in_select_guard.is_invalid() {
 
                    let call_span = call_expr.func_span;
 
                    return Err(ParseError::new_error_str_at_span(
 
                        &ctx.module().source, call_span,
 
                        "a call to 'put' may not occur in a select statement's guard"
 
                    ));
 
                }
 
                expecting_no_select_stmt = true;
 
            },
 
            Method::Fires => {
 
                expecting_primitive_def = true;
 
@@ -1226,6 +1246,16 @@ impl Visitor for PassValidationLinking {
 
            }
 
        }
 

	
 
        if expecting_no_select_stmt {
 
            if !self.in_select_guard.is_invalid() {
 
                let (call_span, func_name) = get_span_and_name(ctx, id);
 
                return Err(ParseError::new_error_at_span(
 
                    &ctx.module().source, call_span,
 
                    format!("a call to '{}' may not occur in a select statement's guard", func_name)
 
                ));
 
            }
 
        }
 

	
 
        if expecting_wrapping_new_stmt {
 
            if !self.expr_parent.is_new() {
 
                let call_span = call_expr.func_span;
 
@@ -1443,14 +1473,6 @@ impl PassValidationLinking {
 
        // Copy statement IDs into buffer
 
        let statement_section = self.statement_buffer.start_section_initialized(&body.statements);
 

	
 
        // Perform the breadth-first pass. Its main purpose is to find labeled
 
        // statements such that we can find the `goto`-targets immediately when
 
        // performing the depth pass
 
        for stmt_idx in 0..statement_section.len() {
 
            self.relative_pos_in_block = stmt_idx as i32;
 
            self.visit_statement_for_locals_labels_and_in_sync(ctx, self.relative_pos_in_block, statement_section[stmt_idx])?;
 
        }
 

	
 
        // Perform the depth-first traversal
 
        assign_and_replace_next_stmt!(self, ctx, id.upcast());
 
        for stmt_idx in 0..statement_section.len() {
 
@@ -1466,38 +1488,6 @@ impl PassValidationLinking {
 
        Ok(())
 
    }
 

	
 
    fn visit_statement_for_locals_labels_and_in_sync(&mut self, ctx: &mut Ctx, relative_pos: i32, id: StatementId) -> VisitorResult {
 
        let statement = &mut ctx.heap[id];
 
        match statement {
 
            Statement::Local(stmt) => {
 
                match stmt {
 
                    LocalStatement::Memory(local) => {
 
                        let variable_id = local.variable;
 
                        self.checked_add_local(ctx, relative_pos, variable_id)?;
 
                    },
 
                    LocalStatement::Channel(local) => {
 
                        let from_id = local.from;
 
                        let to_id = local.to;
 
                        self.checked_add_local(ctx, relative_pos, from_id)?;
 
                        self.checked_add_local(ctx, relative_pos, to_id)?;
 
                    }
 
                }
 
            }
 
            Statement::Labeled(stmt) => {
 
                let stmt_id = stmt.this;
 
                let body_id = stmt.body;
 
                self.checked_add_label(ctx, relative_pos, self.in_sync, stmt_id)?;
 
                self.visit_statement_for_locals_labels_and_in_sync(ctx, relative_pos, body_id)?;
 
            },
 
            Statement::While(stmt) => {
 
                stmt.in_sync = self.in_sync;
 
            },
 
            _ => {},
 
        }
 

	
 
        return Ok(())
 
    }
 

	
 
    fn visit_definition_and_assign_local_ids(&mut self, ctx: &mut Ctx, definition_id: DefinitionId) {
 
        let mut var_counter = 0;
 

	
 
@@ -1578,27 +1568,77 @@ impl PassValidationLinking {
 
        block_stmt.next_unique_id_in_scope = var_counter;
 
    }
 

	
 
    fn resolve_pending_control_flow_targets(&mut self, ctx: &mut Ctx) -> Result<(), ParseError> {
 
        for entry in &self.control_flow_stmts {
 
            let stmt = &ctx.heap[entry.statement];
 

	
 
            match stmt {
 
                Statement::Break(stmt) => {
 
                    let stmt_id = stmt.this;
 
                    let target_while_id = Self::resolve_break_or_continue_target(ctx, entry, stmt.span, &stmt.label)?;
 
                    let target_while_stmt = &ctx.heap[target_while_id];
 
                    let target_end_while_id = target_while_stmt.end_while;
 
                    debug_assert!(!target_end_while_id.is_invalid());
 

	
 
                    let break_stmt = &mut ctx.heap[stmt_id];
 
                    break_stmt.target = target_end_while_id;
 
                },
 
                Statement::Continue(stmt) => {
 
                    let stmt_id = stmt.this;
 
                    let target_while_id = Self::resolve_break_or_continue_target(ctx, entry, stmt.span, &stmt.label)?;
 

	
 
                    let continue_stmt = &mut ctx.heap[stmt_id];
 
                    continue_stmt.target = target_while_id;
 
                },
 
                Statement::Goto(stmt) => {
 
                    let stmt_id = stmt.this;
 
                    let target_id = Self::find_label(entry.in_scope, ctx, &stmt.label)?;
 
                    let target_stmt = &ctx.heap[target_id];
 
                    if entry.in_sync != target_stmt.in_sync {
 
                        // Nested sync not allowed. And goto can only go to
 
                        // outer scopes, so we must be escaping from a sync.
 
                        debug_assert!(target_stmt.in_sync.is_invalid());    // target not in sync
 
                        debug_assert!(!entry.in_sync.is_invalid()); // but the goto is in sync
 
                        let goto_stmt = &ctx.heap[stmt_id];
 
                        let sync_stmt = &ctx.heap[entry.in_sync];
 
                        return Err(
 
                            ParseError::new_error_str_at_span(&ctx.module().source, goto_stmt.span, "goto may not escape the surrounding synchronous block")
 
                            .with_info_str_at_span(&ctx.module().source, target_stmt.label.span, "this is the target of the goto statement")
 
                            .with_info_str_at_span(&ctx.module().source, sync_stmt.span, "which will jump past this statement")
 
                        );
 
                    }
 

	
 
                    let goto_stmt = &mut ctx.heap[stmt_id];
 
                    goto_stmt.target = target_id;
 
                },
 
                _ => unreachable!("cannot resolve control flow target for {:?}", stmt),
 
            }
 
        }
 

	
 
        return Ok(())
 
    }
 

	
 
    //--------------------------------------------------------------------------
 
    // Utilities
 
    //--------------------------------------------------------------------------
 

	
 
    /// Adds a local variable to the current scope. It will also annotate the
 
    /// `Local` in the AST with its relative position in the block.
 
    fn checked_add_local(&mut self, ctx: &mut Ctx, relative_pos: i32, id: VariableId) -> Result<(), ParseError> {
 
        debug_assert!(self.cur_scope.is_block());
 
    fn checked_add_local(&mut self, ctx: &mut Ctx, target_scope: Scope, relative_pos: i32, id: VariableId) -> Result<(), ParseError> {
 
        debug_assert!(target_scope.is_block());
 
        let local = &ctx.heap[id];
 
        let mut scope = &self.cur_scope;
 
        println!("DEBUG: Adding local '{}' at relative_pos {} in scope {:?}", local.identifier.value.as_str(), relative_pos, self.cur_scope);
 

	
 
        let mut scope = target_scope;
 
        loop {
 
            // We immediately go to the parent scope. We check the current scope
 
            // in the call at the end. Likewise for checking the symbol table.
 
            let block = &ctx.heap[scope.to_block()];
 

	
 
            scope = &block.scope_node.parent;
 
            scope = block.scope_node.parent;
 
            if let Scope::Definition(definition_id) = scope {
 
                // At outer scope, check parameters of function/component
 
                for parameter_id in ctx.heap[*definition_id].parameters() {
 
                for parameter_id in ctx.heap[definition_id].parameters() {
 
                    let parameter = &ctx.heap[*parameter_id];
 
                    if local.identifier == parameter.identifier {
 
                        return Err(
 
@@ -1639,7 +1679,7 @@ impl PassValidationLinking {
 
        }
 

	
 
        // No collisions in any of the parent scope, attempt to add to scope
 
        self.checked_at_single_scope_add_local(ctx, self.cur_scope, relative_pos, id)
 
        self.checked_at_single_scope_add_local(ctx, target_scope, relative_pos, id)
 
    }
 

	
 
    /// Adds a local variable to the specified scope. Will check the specified
 
@@ -1785,10 +1825,9 @@ impl PassValidationLinking {
 
    /// Finds a particular labeled statement by its identifier. Once found it
 
    /// will make sure that the target label does not skip over any variable
 
    /// declarations within the scope in which the label was found.
 
    fn find_label(&self, ctx: &Ctx, identifier: &Identifier) -> Result<LabeledStatementId, ParseError> {
 
        debug_assert!(self.cur_scope.is_block());
 
    fn find_label(mut scope: Scope, ctx: &Ctx, identifier: &Identifier) -> Result<LabeledStatementId, ParseError> {
 
        debug_assert!(scope.is_block());
 

	
 
        let mut scope = &self.cur_scope;
 
        loop {
 
            debug_assert!(scope.is_block(), "scope is not a block");
 
            let relative_scope_pos = ctx.heap[scope.to_block()].relative_pos_in_parent;
 
@@ -1815,7 +1854,7 @@ impl PassValidationLinking {
 
                }
 
            }
 

	
 
            scope = &block.scope_node.parent;
 
            scope = block.scope_node.parent;
 
            if !scope.is_block() {
 
                return Err(ParseError::new_error_str_at_span(
 
                    &ctx.module().source, identifier.span, "could not find this label"
 
@@ -1827,8 +1866,7 @@ impl PassValidationLinking {
 

	
 
    /// This function will check if the provided while statement ID has a block
 
    /// statement that is one of our current parents.
 
    fn has_parent_while_scope(&self, ctx: &Ctx, id: WhileStatementId) -> bool {
 
        let mut scope = &self.cur_scope;
 
    fn has_parent_while_scope(mut scope: Scope, ctx: &Ctx, id: WhileStatementId) -> bool {
 
        let while_stmt = &ctx.heap[id];
 
        loop {
 
            debug_assert!(scope.is_block());
 
@@ -1838,7 +1876,7 @@ impl PassValidationLinking {
 
            }
 

	
 
            let block = &ctx.heap[block];
 
            scope = &block.scope_node.parent;
 
            scope = block.scope_node.parent;
 
            if !scope.is_block() {
 
                return false;
 
            }
 
@@ -1851,17 +1889,17 @@ impl PassValidationLinking {
 
    /// ID will be returned, otherwise a parsing error is constructed.
 
    /// The provided input position should be the position of the break/continue
 
    /// statement.
 
    fn resolve_break_or_continue_target(&self, ctx: &Ctx, span: InputSpan, label: &Option<Identifier>) -> Result<WhileStatementId, ParseError> {
 
    fn resolve_break_or_continue_target(ctx: &Ctx, control_flow: &ControlFlowStatement, span: InputSpan, label: &Option<Identifier>) -> Result<WhileStatementId, ParseError> {
 
        let target = match label {
 
            Some(label) => {
 
                let target_id = self.find_label(ctx, label)?;
 
                let target_id = Self::find_label(control_flow.in_scope, ctx, label)?;
 

	
 
                // Make sure break target is a while statement
 
                let target = &ctx.heap[target_id];
 
                if let Statement::While(target_stmt) = &ctx.heap[target.body] {
 
                    // Even though we have a target while statement, the break might not be
 
                    // present underneath this particular labeled while statement
 
                    if !self.has_parent_while_scope(ctx, target_stmt.this) {
 
                    if !Self::has_parent_while_scope(control_flow.in_scope, ctx, target_stmt.this) {
 
                        return Err(ParseError::new_error_str_at_span(
 
                            &ctx.module().source, label.span, "break statement is not nested under the target label's while statement"
 
                        ).with_info_str_at_span(
 
@@ -1881,13 +1919,13 @@ impl PassValidationLinking {
 
            None => {
 
                // Use the enclosing while statement, the break must be
 
                // nested within that while statement
 
                if self.in_while.is_invalid() {
 
                if control_flow.in_while.is_invalid() {
 
                    return Err(ParseError::new_error_str_at_span(
 
                        &ctx.module().source, span, "Break statement is not nested under a while loop"
 
                    ));
 
                }
 

	
 
                self.in_while
 
                control_flow.in_while
 
            }
 
        };
 

	
 
@@ -1895,11 +1933,11 @@ impl PassValidationLinking {
 
        // make sure we will not break out of a synchronous block
 
        {
 
            let target_while = &ctx.heap[target];
 
            if target_while.in_sync != self.in_sync {
 
            if target_while.in_sync != control_flow.in_sync {
 
                // Break is nested under while statement, so can only escape a
 
                // sync block if the sync is nested inside the while statement.
 
                debug_assert!(!self.in_sync.is_invalid());
 
                let sync_stmt = &ctx.heap[self.in_sync];
 
                debug_assert!(!control_flow.in_sync.is_invalid());
 
                let sync_stmt = &ctx.heap[control_flow.in_sync];
 
                return Err(
 
                    ParseError::new_error_str_at_span(&ctx.module().source, span, "break may not escape the surrounding synchronous block")
 
                        .with_info_str_at_span(&ctx.module().source, target_while.span, "the break escapes out of this loop")
0 comments (0 inline, 0 general)