static bool tuple_access(pass_opt_t* opt, ast_t* ast) { // Left is a postfix expression, right is a lookup name. ast_t* left = ast_child(ast); ast_t* right = ast_sibling(left); ast_t* type = ast_type(left); if(is_typecheck_error(type)) return false; // Change the lookup name to an integer index. if(!make_tuple_index(&right)) { ast_error(opt->check.errors, right, "lookup on a tuple must take the form _X, where X is an integer"); return false; } // Make sure our index is in bounds. make_tuple_index automatically shifts // from one indexed to zero, so we have to use -1 and >= for our comparisons. size_t right_idx = (size_t)ast_int(right)->low; size_t tuple_size = ast_childcount(type); if (right_idx == (size_t)-1) { ast_error(opt->check.errors, right, "tuples are one indexed not zero indexed. Did you mean _1?"); return false; } else if (right_idx >= tuple_size) { ast_error(opt->check.errors, right, "tuple index " __zu " is out of " "valid range. Valid range is [1, " __zu "]", right_idx, tuple_size); return false; } type = ast_childidx(type, right_idx); assert(type != NULL); ast_setid(ast, TK_FLETREF); ast_settype(ast, type); ast_inheritflags(ast); return true; }
bool expr_tuple(ast_t* ast) { ast_t* child = ast_child(ast); ast_t* type; if(ast_sibling(child) == NULL) { type = ast_type(child); } else { type = ast_from(ast, TK_TUPLETYPE); while(child != NULL) { ast_t* c_type = ast_type(child); if(c_type == NULL) return false; if(is_control_type(c_type)) { ast_error(child, "a tuple can't contain a control flow expression"); return false; } if(is_type_literal(c_type)) { // At least one tuple member is literal, so whole tuple is ast_free(type); make_literal_type(ast); return true; } ast_append(type, c_type); child = ast_sibling(child); } } ast_settype(ast, type); ast_inheritflags(ast); return true; }
bool expr_cases(ast_t* ast) { assert(ast_id(ast) == TK_CASES); ast_t* the_case = ast_child(ast); if(the_case == NULL) { ast_error(ast, "match must have at least one case"); return false; } ast_t* type = NULL; size_t branch_count = 0; while(the_case != NULL) { AST_GET_CHILDREN(the_case, pattern, guard, body); ast_t* body_type = ast_type(body); if(!is_typecheck_error(body_type) && !is_control_type(body_type)) { type = control_type_add_branch(type, body); ast_inheritbranch(ast, the_case); branch_count++; } the_case = ast_sibling(the_case); } if(type == NULL) type = ast_from(ast, TK_CASES); ast_settype(ast, type); ast_inheritflags(ast); ast_consolidate_branches(ast, branch_count); return true; }
bool expr_recover(ast_t* ast) { AST_GET_CHILDREN(ast, cap, expr); ast_t* type = ast_type(expr); if(is_typecheck_error(type)) return false; ast_t* r_type = recover_type(type, ast_id(cap)); if(r_type == NULL) { ast_error(ast, "can't recover to this capability"); ast_error(expr, "expression type is %s", ast_print_type(type)); return false; } ast_settype(ast, r_type); ast_inheritflags(ast); // Push our symbol status to our parent scope. ast_inheritstatus(ast_parent(ast), ast); return true; }
bool expr_qualify(pass_opt_t* opt, ast_t** astp) { // Left is a postfix expression, right is a typeargs. ast_t* ast = *astp; AST_GET_CHILDREN(ast, left, right); ast_t* type = ast_type(left); assert(ast_id(right) == TK_TYPEARGS); if(is_typecheck_error(type)) return false; switch(ast_id(left)) { case TK_TYPEREF: { // Qualify the type. assert(ast_id(type) == TK_NOMINAL); // If the type isn't polymorphic or the type is already qualified, // sugar .apply(). ast_t* def = names_def(opt, type); ast_t* typeparams = ast_childidx(def, 1); if((ast_id(typeparams) == TK_NONE) || (ast_id(ast_childidx(type, 2)) != TK_NONE)) { if(!expr_nominal(opt, &type)) return false; break; } type = ast_dup(type); ast_t* typeargs = ast_childidx(type, 2); ast_replace(&typeargs, right); ast_settype(ast, type); ast_setid(ast, TK_TYPEREF); return expr_typeref(opt, astp); } case TK_NEWREF: case TK_NEWBEREF: case TK_BEREF: case TK_FUNREF: case TK_NEWAPP: case TK_BEAPP: case TK_FUNAPP: { // Qualify the function. assert(ast_id(type) == TK_FUNTYPE); ast_t* typeparams = ast_childidx(type, 1); if(!reify_defaults(typeparams, right, true, opt)) return false; if(!check_constraints(left, typeparams, right, true, opt)) return false; type = reify(type, typeparams, right, opt); typeparams = ast_childidx(type, 1); ast_replace(&typeparams, ast_from(typeparams, TK_NONE)); ast_settype(ast, type); ast_setid(ast, ast_id(left)); ast_inheritflags(ast); return true; } default: {} } // Sugar .apply() ast_t* dot = ast_from(left, TK_DOT); ast_add(dot, ast_from_string(left, "apply")); ast_swap(left, dot); ast_add(dot, left); if(!expr_dot(opt, &dot)) return false; return expr_qualify(opt, astp); }
ast_result_t pass_expr(ast_t** astp, pass_opt_t* options) { typecheck_t* t = &options->check; ast_t* ast = *astp; bool r = true; switch(ast_id(ast)) { case TK_NOMINAL: r = expr_nominal(options, astp); break; case TK_FVAR: case TK_FLET: case TK_PARAM: r = expr_field(options, ast); break; case TK_NEW: case TK_BE: case TK_FUN: r = expr_fun(options, ast); break; case TK_SEQ: r = expr_seq(ast); break; case TK_VAR: case TK_LET: r = expr_local(t, ast); break; case TK_BREAK: r = expr_break(t, ast); break; case TK_CONTINUE: r = expr_continue(t, ast); break; case TK_RETURN: r = expr_return(options, ast); break; case TK_IS: case TK_ISNT: r = expr_identity(options, ast); break; case TK_ASSIGN: r = expr_assign(options, ast); break; case TK_CONSUME: r = expr_consume(t, ast); break; case TK_RECOVER: r = expr_recover(ast); break; case TK_DOT: r = expr_dot(options, astp); break; case TK_TILDE: r = expr_tilde(options, astp); break; case TK_QUALIFY: r = expr_qualify(options, astp); break; case TK_CALL: r = expr_call(options, astp); break; case TK_IF: r = expr_if(options, ast); break; case TK_WHILE: r = expr_while(options, ast); break; case TK_REPEAT: r = expr_repeat(options, ast); break; case TK_TRY_NO_CHECK: case TK_TRY: r = expr_try(options, ast); break; case TK_MATCH: r = expr_match(options, ast); break; case TK_CASES: r = expr_cases(ast); break; case TK_CASE: r = expr_case(options, ast); break; case TK_TUPLE: r = expr_tuple(ast); break; case TK_ARRAY: r = expr_array(options, astp); break; case TK_REFERENCE: r = expr_reference(options, astp); break; case TK_THIS: r = expr_this(options, ast); break; case TK_TRUE: case TK_FALSE: r = expr_literal(options, ast, "Bool"); break; case TK_ERROR: r = expr_error(ast); break; case TK_COMPILER_INTRINSIC: r = expr_compiler_intrinsic(t, ast); break; case TK_POSITIONALARGS: case TK_NAMEDARGS: case TK_NAMEDARG: case TK_UPDATEARG: ast_inheritflags(ast); break; case TK_AMP: r = expr_addressof(options, ast); break; case TK_DONTCARE: r = expr_dontcare(ast); break; case TK_INT: // Integer literals can be integers or floats make_literal_type(ast); break; case TK_FLOAT: make_literal_type(ast); break; case TK_STRING: if(ast_id(ast_parent(ast)) == TK_PACKAGE) return AST_OK; r = expr_literal(options, ast, "String"); break; case TK_FFICALL: return expr_ffi(options, ast); default: {} } if(!r) { assert(get_error_count() > 0); return AST_ERROR; } // Can't use ast here, it might have changed symtab_t* symtab = ast_get_symtab(*astp); if(symtab != NULL && !symtab_check_all_defined(symtab)) return AST_ERROR; return AST_OK; }
bool expr_return(pass_opt_t* opt, ast_t* ast) { typecheck_t* t = &opt->check; if(t->frame->method_body == NULL) { ast_error(ast, "return must occur in a method body"); return false; } // return is always the last expression in a sequence assert(ast_sibling(ast) == NULL); if(ast_parent(ast) == t->frame->method_body) { ast_error(ast, "use return only to exit early from a method, not at the end"); return false; } ast_t* type = ast_childidx(t->frame->method, 4); ast_t* body = ast_child(ast); if(!coerce_literals(&body, type, opt)) return false; ast_t* body_type = ast_type(body); if(is_typecheck_error(body_type)) return false; if(is_control_type(body_type)) { ast_error(body, "return value cannot be a control statement"); return false; } bool ok = true; switch(ast_id(t->frame->method)) { case TK_NEW: if(!is_none(body_type)) { ast_error(ast, "return in a constructor must return None"); ok = false; } break; case TK_BE: if(!is_none(body_type)) { ast_error(ast, "return in a behaviour must return None"); ok = false; } break; default: { // The body type must be a subtype of the return type, and an alias of // the body type must be a subtype of an alias of the return type. ast_t* a_type = alias(type); ast_t* a_body_type = alias(body_type); if(!is_subtype(body_type, type) || !is_subtype(a_body_type, a_type)) { ast_t* last = ast_childlast(body); ast_error(last, "returned value isn't the return type"); ast_error(type, "function return type: %s", ast_print_type(type)); ast_error(body_type, "returned value type: %s", ast_print_type(body_type)); ok = false; } ast_free_unattached(a_type); ast_free_unattached(a_body_type); } } ast_settype(ast, ast_from(ast, TK_RETURN)); ast_inheritflags(ast); return ok; }
bool expr_seq(pass_opt_t* opt, ast_t* ast) { bool ok = true; // Any expression other than the last that is still literal is an error for(ast_t* p = ast_child(ast); ast_sibling(p) != NULL; p = ast_sibling(p)) { ast_t* p_type = ast_type(p); if(is_typecheck_error(p_type)) { ok = false; } else if(is_type_literal(p_type)) { ast_error(p, "Cannot infer type of unused literal"); ok = false; } } // We might already have a type due to a return expression. ast_t* type = ast_type(ast); ast_t* last = ast_childlast(ast); if((type != NULL) && !coerce_literals(&last, type, opt)) return false; // Type is unioned with the type of the last child. type = control_type_add_branch(type, last); ast_settype(ast, type); ast_inheritflags(ast); if(!ast_has_scope(ast)) return ok; ast_t* parent = ast_parent(ast); switch(ast_id(parent)) { case TK_TRY: case TK_TRY_NO_CHECK: { // Propagate consumes forward in a try expression. AST_GET_CHILDREN(parent, body, else_clause, then_clause); if(body == ast) { // Push our consumes, but not defines, to the else clause. ast_inheritbranch(else_clause, body); ast_consolidate_branches(else_clause, 2); } else if(else_clause == ast) { // Push our consumes, but not defines, to the then clause. This // includes the consumes from the body. ast_inheritbranch(then_clause, else_clause); ast_consolidate_branches(then_clause, 2); } } default: {} } return ok; }
bool expr_assign(pass_opt_t* opt, ast_t* ast) { // Left and right are swapped in the AST to make sure we type check the // right side before the left. Fetch them in the opposite order. assert(ast != NULL); AST_GET_CHILDREN(ast, right, left); ast_t* l_type = ast_type(left); if(!is_lvalue(&opt->check, left, is_result_needed(ast))) { ast_error(ast, "left side must be something that can be assigned to"); return false; } assert(l_type != NULL); if(!coerce_literals(&right, l_type, opt)) return false; ast_t* r_type = ast_type(right); if(is_typecheck_error(r_type)) return false; if(!infer_locals(left, r_type)) return false; // Inferring locals may have changed the left type. l_type = ast_type(left); // Assignment is based on the alias of the right hand side. ast_t* a_type = alias(r_type); if(!is_subtype(a_type, l_type)) { ast_error(ast, "right side must be a subtype of left side"); ast_error(a_type, "right side type: %s", ast_print_type(a_type)); ast_error(l_type, "left side type: %s", ast_print_type(l_type)); ast_free_unattached(a_type); return false; } if((ast_id(left) == TK_TUPLE) && (ast_id(a_type) != TK_TUPLETYPE)) { switch(ast_id(a_type)) { case TK_UNIONTYPE: ast_error(ast, "can't destructure a union using assignment, use pattern matching " "instead"); break; case TK_ISECTTYPE: ast_error(ast, "can't destructure an intersection using assignment, use pattern " "matching instead"); break; default: assert(0); break; } ast_error(a_type, "right side type: %s", ast_print_type(a_type)); ast_free_unattached(a_type); return false; } bool ok_safe = safe_to_write(left, a_type); if(!ok_safe) { if(ast_id(left) == TK_FVARREF && ast_child(left) != NULL && ast_id(ast_child(left)) == TK_THIS) { // We are writing to a field in this ast_t* fn = ast_nearest(left, TK_FUN); if(fn != NULL) { ast_t* iso = ast_child(fn); assert(iso != NULL); token_id iso_id = ast_id(iso); if(iso_id == TK_BOX || iso_id == TK_VAL || iso_id == TK_TAG) { ast_error(ast, "cannot write to a field in a %s function", lexer_print(iso_id)); ast_free_unattached(a_type); return false; } } } ast_error(ast, "not safe to write right side to left side"); ast_error(a_type, "right side type: %s", ast_print_type(a_type)); ast_free_unattached(a_type); return false; } ast_free_unattached(a_type); // If it's an embedded field, check for a constructor result. if(ast_id(left) == TK_EMBEDREF) { if((ast_id(right) != TK_CALL) || (ast_id(ast_childidx(right, 2)) != TK_NEWREF)) { ast_error(ast, "an embedded field must be assigned using a constructor"); return false; } } ast_settype(ast, consume_type(l_type, TK_NONE)); ast_inheritflags(ast); return true; }
bool expr_identity(pass_opt_t* opt, ast_t* ast) { ast_settype(ast, type_builtin(opt, ast, "Bool")); ast_inheritflags(ast); return true; }
static ast_result_t declared_ffi(pass_opt_t* opt, ast_t* call, ast_t* decl) { assert(call != NULL); assert(decl != NULL); assert(ast_id(decl) == TK_FFIDECL); AST_GET_CHILDREN(call, call_name, call_ret_typeargs, args, named_args, call_error); AST_GET_CHILDREN(decl, decl_name, decl_ret_typeargs, params, named_params, decl_error); // Check args vs params ast_t* param = ast_child(params); ast_t* arg = ast_child(args); while((arg != NULL) && (param != NULL) && ast_id(param) != TK_ELLIPSIS) { ast_t* p_type = ast_childidx(param, 1); if(!coerce_literals(&arg, p_type, opt)) return AST_ERROR; ast_t* a_type = ast_type(arg); errorframe_t info = NULL; if((a_type != NULL) && !void_star_param(p_type, a_type) && !is_subtype(a_type, p_type, &info, opt)) { errorframe_t frame = NULL; ast_error_frame(&frame, arg, "argument not a subtype of parameter"); ast_error_frame(&frame, param, "parameter type: %s", ast_print_type(p_type)); ast_error_frame(&frame, arg, "argument type: %s", ast_print_type(a_type)); errorframe_append(&frame, &info); errorframe_report(&frame, opt->check.errors); return AST_ERROR; } arg = ast_sibling(arg); param = ast_sibling(param); } if(arg != NULL && param == NULL) { ast_error(opt->check.errors, arg, "too many arguments"); return AST_ERROR; } if(param != NULL && ast_id(param) != TK_ELLIPSIS) { ast_error(opt->check.errors, named_args, "too few arguments"); return AST_ERROR; } for(; arg != NULL; arg = ast_sibling(arg)) { ast_t* a_type = ast_type(arg); if((a_type != NULL) && is_type_literal(a_type)) { ast_error(opt->check.errors, arg, "Cannot pass number literals as unchecked FFI arguments"); return AST_ERROR; } } // Check return types ast_t* call_ret_type = ast_child(call_ret_typeargs); ast_t* decl_ret_type = ast_child(decl_ret_typeargs); errorframe_t info = NULL; if((call_ret_type != NULL) && !is_eqtype(call_ret_type, decl_ret_type, &info, opt)) { errorframe_t frame = NULL; ast_error_frame(&frame, call_ret_type, "call return type does not match declaration"); errorframe_append(&frame, &info); errorframe_report(&frame, opt->check.errors); return AST_ERROR; } // Check partiality if((ast_id(decl_error) == TK_NONE) && (ast_id(call_error) != TK_NONE)) { ast_error(opt->check.errors, call_error, "call is partial but the declaration is not"); return AST_ERROR; } if((ast_id(decl_error) == TK_QUESTION) || (ast_id(call_error) == TK_QUESTION)) { ast_seterror(call); } // Store the declaration so that codegen can generate a non-variadic // signature for the FFI call. ast_setdata(call, decl); ast_settype(call, decl_ret_type); ast_inheritflags(call); return AST_OK; }
bool expr_return(pass_opt_t* opt, ast_t* ast) { typecheck_t* t = &opt->check; // return is always the last expression in a sequence assert(ast_sibling(ast) == NULL); if(ast_parent(ast) == t->frame->method_body) { ast_error(ast, "use return only to exit early from a method, not at the end"); return false; } ast_t* type = ast_childidx(t->frame->method, 4); ast_t* body = ast_child(ast); if(!coerce_literals(&body, type, opt)) return false; ast_t* body_type = ast_type(body); if(is_typecheck_error(body_type)) return false; if(is_control_type(body_type)) { ast_error(body, "return value cannot be a control statement"); return false; } bool ok = true; switch(ast_id(t->frame->method)) { case TK_NEW: if(is_this_incomplete(t, ast)) { ast_error(ast, "all fields must be defined before constructor returns"); ok = false; } break; case TK_BE: assert(is_none(body_type)); break; default: { // The body type must be a subtype of the return type, and an alias of // the body type must be a subtype of an alias of the return type. ast_t* a_type = alias(type); ast_t* a_body_type = alias(body_type); errorframe_t info = NULL; if(!is_subtype(body_type, type, &info) || !is_subtype(a_body_type, a_type, &info)) { errorframe_t frame = NULL; ast_t* last = ast_childlast(body); ast_error_frame(&frame, last, "returned value isn't the return type"); ast_error_frame(&frame, type, "function return type: %s", ast_print_type(type)); ast_error_frame(&frame, body_type, "returned value type: %s", ast_print_type(body_type)); errorframe_append(&frame, &info); errorframe_report(&frame); ok = false; } ast_free_unattached(a_type); ast_free_unattached(a_body_type); } } ast_settype(ast, ast_from(ast, TK_RETURN)); ast_inheritflags(ast); return ok; }
bool expr_if(pass_opt_t* opt, ast_t* ast) { ast_t* cond = ast_child(ast); ast_t* left = ast_sibling(cond); ast_t* right = ast_sibling(left); if(ast_id(ast) == TK_IF) { ast_t* cond_type = ast_type(cond); if(is_typecheck_error(cond_type)) return false; if(!is_bool(cond_type)) { ast_error(cond, "condition must be a Bool"); return false; } } ast_t* l_type = ast_type(left); ast_t* r_type = ast_type(right); if(is_typecheck_error(l_type) || is_typecheck_error(r_type)) return false; ast_t* type = NULL; size_t branch_count = 0; if(!is_control_type(l_type)) { type = control_type_add_branch(type, left); ast_inheritbranch(ast, left); branch_count++; } if(!is_control_type(r_type)) { type = control_type_add_branch(type, right); ast_inheritbranch(ast, right); branch_count++; } if(type == NULL) { if((ast_id(ast_parent(ast)) == TK_SEQ) && ast_sibling(ast) != NULL) { ast_error(ast_sibling(ast), "unreachable code"); return false; } type = ast_from(ast, TK_IF); } ast_settype(ast, type); ast_inheritflags(ast); ast_consolidate_branches(ast, branch_count); literal_unify_control(ast, opt); // Push our symbol status to our parent scope. ast_inheritstatus(ast_parent(ast), ast); if(ast_id(ast) == TK_IFDEF) return resolve_ifdef(opt, ast); return true; }
bool expr_qualify(pass_opt_t* opt, ast_t** astp) { // Left is a postfix expression, right is a typeargs. ast_t* ast = *astp; ast_t* left = ast_child(ast); ast_t* right = ast_sibling(left); ast_t* type = ast_type(left); assert(ast_id(right) == TK_TYPEARGS); if(is_typecheck_error(type)) return false; switch(ast_id(left)) { case TK_TYPEREF: { // Qualify the type. assert(ast_id(type) == TK_NOMINAL); if(ast_id(ast_childidx(type, 2)) != TK_NONE) { ast_error(ast, "can't qualify an already qualified type"); return false; } type = ast_dup(type); ast_t* typeargs = ast_childidx(type, 2); ast_replace(&typeargs, right); ast_settype(ast, type); ast_setid(ast, TK_TYPEREF); return expr_typeref(opt, astp); } case TK_NEWREF: case TK_NEWBEREF: case TK_BEREF: case TK_FUNREF: case TK_NEWAPP: case TK_BEAPP: case TK_FUNAPP: { // Qualify the function. assert(ast_id(type) == TK_FUNTYPE); ast_t* typeparams = ast_childidx(type, 1); if(!check_constraints(left, typeparams, right, true)) return false; type = reify(left, type, typeparams, right); typeparams = ast_childidx(type, 1); ast_replace(&typeparams, ast_from(typeparams, TK_NONE)); ast_settype(ast, type); ast_setid(ast, ast_id(left)); ast_inheritflags(ast); return true; } default: {} } assert(0); return false; }
bool expr_case(pass_opt_t* opt, ast_t* ast) { assert(opt != NULL); assert(ast_id(ast) == TK_CASE); AST_GET_CHILDREN(ast, pattern, guard, body); if((ast_id(pattern) == TK_NONE) && (ast_id(guard) == TK_NONE)) { ast_error(ast, "can't have a case with no conditions, use an else clause"); return false; } ast_t* cases = ast_parent(ast); ast_t* match = ast_parent(cases); ast_t* match_expr = ast_child(match); ast_t* match_type = ast_type(match_expr); if(is_control_type(match_type) || is_typecheck_error(match_type)) return false; if(!infer_pattern_type(pattern, match_type, opt)) return false; if(!is_valid_pattern(opt, pattern)) return false; ast_t* operand_type = alias(match_type); ast_t* pattern_type = ast_type(pattern); bool ok = true; switch(is_matchtype(operand_type, pattern_type)) { case MATCHTYPE_ACCEPT: break; case MATCHTYPE_REJECT: ast_error(pattern, "this pattern can never match"); ast_error(match_type, "match type: %s", ast_print_type(operand_type)); ast_error(pattern, "pattern type: %s", ast_print_type(pattern_type)); ok = false; break; case MATCHTYPE_DENY: ast_error(pattern, "this capture violates capabilities"); ast_error(match_type, "match type: %s", ast_print_type(operand_type)); ast_error(pattern, "pattern type: %s", ast_print_type(pattern_type)); ok = false; break; } if(ast_id(guard) != TK_NONE) { ast_t* guard_type = ast_type(guard); if(is_typecheck_error(guard_type)) { ok = false; } else if(!is_bool(guard_type)) { ast_error(guard, "guard must be a boolean expression"); ok = false; } } ast_free_unattached(operand_type); ast_inheritflags(ast); return ok; }
bool expr_match(pass_opt_t* opt, ast_t* ast) { assert(ast_id(ast) == TK_MATCH); AST_GET_CHILDREN(ast, expr, cases, else_clause); // A literal match expression should have been caught by the cases, but check // again to avoid an assert if we've missed a case ast_t* expr_type = ast_type(expr); if(is_typecheck_error(expr_type)) return false; if(is_type_literal(expr_type)) { ast_error(expr, "cannot infer type for literal match expression"); return false; } ast_t* cases_type = ast_type(cases); ast_t* else_type = ast_type(else_clause); if(is_typecheck_error(cases_type) || is_typecheck_error(else_type)) return false; ast_t* type = NULL; size_t branch_count = 0; if(!is_control_type(cases_type)) { type = control_type_add_branch(type, cases); ast_inheritbranch(ast, cases); branch_count++; } if(!is_control_type(else_type)) { type = control_type_add_branch(type, else_clause); ast_inheritbranch(ast, else_clause); branch_count++; } if(type == NULL) { if(ast_sibling(ast) != NULL) { ast_error(ast_sibling(ast), "unreachable code"); return false; } type = ast_from(ast, TK_MATCH); } ast_settype(ast, type); ast_inheritflags(ast); ast_consolidate_branches(ast, branch_count); literal_unify_control(ast, opt); // Push our symbol status to our parent scope. ast_inheritstatus(ast_parent(ast), ast); return true; }