bool is_lvalue(const exprt &expr) { if(expr.id()==ID_index) return is_lvalue(to_index_expr(expr).op0()); else if(expr.id()==ID_member) return is_lvalue(to_member_expr(expr).op0()); else if(expr.id()==ID_dereference) return true; else if(expr.id()==ID_symbol) return true; else return false; }
exprt make_va_list(const exprt &expr) { // we first strip any typecast if(expr.id()==ID_typecast) return make_va_list(to_typecast_expr(expr).op()); // if it's an address of an lvalue, we take that if(expr.id()==ID_address_of && expr.operands().size()==1 && is_lvalue(expr.op0())) return expr.op0(); return expr; }
bool is_xvalue() const noexcept { return !is_lvalue(); }
T copy_or_move() const { if(is_lvalue()) return *lvalue; else return std::move(*xvalue); }
static bool is_lvalue(typecheck_t* t, ast_t* ast, bool need_value) { switch(ast_id(ast)) { case TK_DONTCARE: // Can only assign to it if we don't need the value. return !need_value; case TK_VAR: case TK_LET: return assign_id(t, ast_child(ast), ast_id(ast) == TK_LET, need_value); case TK_VARREF: { ast_t* id = ast_child(ast); return assign_id(t, id, false, need_value); } case TK_LETREF: { ast_error(ast, "can't assign to a let local"); return false; } case TK_FVARREF: { AST_GET_CHILDREN(ast, left, right); if(ast_id(left) == TK_THIS) return assign_id(t, right, false, need_value); return true; } case TK_FLETREF: { AST_GET_CHILDREN(ast, left, right); if(ast_id(left) != TK_THIS) { ast_error(ast, "can't assign to a let field"); return false; } if(t->frame->loop_body != NULL) { ast_error(ast, "can't assign to a let field in a loop"); return false; } return assign_id(t, right, true, need_value); } case TK_EMBEDREF: { AST_GET_CHILDREN(ast, left, right); if(ast_id(left) != TK_THIS) { ast_error(ast, "can't assign to an embed field"); return false; } if(t->frame->loop_body != NULL) { ast_error(ast, "can't assign to an embed field in a loop"); return false; } return assign_id(t, right, true, need_value); } case TK_TUPLE: { // A tuple is an lvalue if every component expression is an lvalue. ast_t* child = ast_child(ast); while(child != NULL) { if(!is_lvalue(t, child, need_value)) return false; child = ast_sibling(child); } return true; } case TK_SEQ: { // A sequence is an lvalue if it has a single child that is an lvalue. // This is used because the components of a tuple are sequences. ast_t* child = ast_child(ast); if(ast_sibling(child) != NULL) return false; return is_lvalue(t, child, need_value); } default: {} } return false; }
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_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(l_type == NULL || !is_lvalue(opt, left, is_result_needed(ast))) { ast_error(opt->check.errors, ast, "left side must be something that can be assigned to"); return false; } 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(is_control_type(r_type)) { ast_error(opt->check.errors, ast, "the right hand side does not return a value"); return false; } if(!infer_locals(opt, 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); errorframe_t info = NULL; if(!is_subtype(a_type, l_type, &info, opt)) { errorframe_t frame = NULL; ast_error_frame(&frame, ast, "right side must be a subtype of left side"); errorframe_append(&frame, &info); errorframe_report(&frame, opt->check.errors); 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(opt->check.errors, ast, "can't destructure a union using assignment, use pattern matching " "instead"); break; case TK_ISECTTYPE: ast_error(opt->check.errors, ast, "can't destructure an intersection using assignment, use pattern " "matching instead"); break; default: assert(0); break; } 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(opt->check.errors, ast, "cannot write to a field in a %s function. If you are trying to " "change state in a function use fun ref", lexer_print(iso_id)); ast_free_unattached(a_type); return false; } } } ast_error(opt->check.errors, ast, "not safe to write right side to left side"); ast_error_continue(opt->check.errors, a_type, "right side type: %s", ast_print_type(a_type)); ast_free_unattached(a_type); return false; } ast_free_unattached(a_type); if(!check_embed_construction(opt, left, right)) return false; ast_settype(ast, consume_type(l_type, TK_NONE)); return true; }
static void emit_expr(struct q *q, const struct node *node) { switch (node->tok) { case '.': if (node->left->tok == TOK_IDENT) emit_byte(q, OP_PUSHNS); emit_expr(q, node->left); emit_expr(q, node->right); emit_byte(q, OP_GET); break; case '%': case '*': case '/': case '+': case '-': emit_expr(q, node->left); emit_expr(q, node->right); emit_byte(q, OP_MATH); emit_byte(q, node->tok); break; case '=': emit_expr(q, node->right); emit_expr(q, node->left); if (node->left->tok != TOK_IDENT && node->left->tok != '.' && node->left->tok != '=') die(q, "%s: %d: %s", serr, node->line_no, "invalid assignment"); emit_byte(q, OP_SET); break; case ',': emit_expr(q, node->left); emit_expr(q, node->right); die(q, "%s: %d: %s", serr, node->line_no, "use ';', not ','"); break; case '(': emit_function_call(q, node); break; case TOK_NIL: emit_byte(q, OP_PUSH); emit_byte(q, TYPE_NIL); break; case TOK_IDENT: /* * Identifier could be either rvalue or lvalue: * "foo.bar = baz = x.y;" * Lvalues: bar, baz * Rvalues: foo, x, y * Lvalue is a key, for namespace insert. * Rvalue is always a key for namespace lookup. */ if (node->parent == NULL || node->parent->tok != '.') emit_byte(q, OP_PUSHNS); emit_ident(q, node); if (!is_lvalue(node) && !(node->parent != NULL && node->parent->tok == '.' && is_lvalue(node->parent))) emit_byte(q, OP_GET); break; case TOK_STR: emit_string_constant(q, node); break; case TOK_NUM: emit_num(q, strtod(node->vec.ptr, NULL)); break; case '[': emit_byte(q, OP_NEWMAP); assert(node->right != NULL); if (node->right->tok != TOK_NIL) { emit_hash_definition(q, node->right, 0); emit_byte(q, OP_POP); } break; default: die(q, "%s: %d: %s [%d]", serr, node->line_no, "internal parser error", node->tok); break; } }