Browse Source

Fix give expressions

Ginger Bill 8 years ago
parent
commit
0c6775ca14
7 changed files with 187 additions and 133 deletions
  1. 4 5
      code/demo.odin
  2. 133 96
      src/checker/expr.c
  3. 12 7
      src/checker/stmt.c
  4. 3 0
      src/checker/types.c
  5. 35 19
      src/parser.c
  6. 0 2
      src/ssa.c
  7. 0 4
      src/tokenizer.c

+ 4 - 5
code/demo.odin

@@ -12,11 +12,10 @@ import {
 }
 }
 
 
 proc main() {
 proc main() {
-	var cond = true;
-	var msg = if cond {
-		give "hello";
+	var x = if false {
+		give 123;
 	} else {
 	} else {
-		give "goodbye";
+		give 321;
 	};
 	};
-	fmt.println(msg);
+	fmt.println(x);
 }
 }

+ 133 - 96
src/checker/expr.c

@@ -3657,34 +3657,32 @@ bool check_set_index_data(Operand *o, Type *t, i64 *max_count) {
 }
 }
 
 
 
 
-bool check_is_giving(AstNode *node, AstNode **give_expr);
-
-bool check_giving_list(AstNodeArray stmts, AstNode **give_expr) {
-	// Iterate backwards
-	for (isize n = stmts.count-1; n >= 0; n--) {
-		AstNode *stmt = stmts.e[n];
-		if (stmt->kind != AstNode_EmptyStmt) {
-			return check_is_giving(stmt, give_expr);
-		}
-	}
-
-	if (give_expr) *give_expr = NULL;
-	return false;
-}
-
 bool check_is_giving(AstNode *node, AstNode **give_expr) {
 bool check_is_giving(AstNode *node, AstNode **give_expr) {
 	switch (node->kind) {
 	switch (node->kind) {
 	case_ast_node(es, ExprStmt, node);
 	case_ast_node(es, ExprStmt, node);
-		return check_is_giving(es->expr, give_expr);
+		if (es->expr->kind == AstNode_GiveExpr) {
+			if (give_expr) *give_expr = es->expr;
+			return true;
+		}
 	case_end;
 	case_end;
 
 
-	case_ast_node(ye, GiveExpr, node);
+	case_ast_node(ge, GiveExpr, node);
 		if (give_expr) *give_expr = node;
 		if (give_expr) *give_expr = node;
 		return true;
 		return true;
 	case_end;
 	case_end;
 
 
 	case_ast_node(be, BlockExpr, node);
 	case_ast_node(be, BlockExpr, node);
-		return check_giving_list(be->stmts, give_expr);
+		// Iterate backwards
+		for (isize n = be->stmts.count-1; n >= 0; n--) {
+			AstNode *stmt = be->stmts.e[n];
+			if (stmt->kind == AstNode_EmptyStmt) {
+				continue;
+			}
+			if (stmt->kind == AstNode_ExprStmt && stmt->ExprStmt.expr->kind == AstNode_GiveExpr) {
+				if (give_expr) *give_expr = stmt->ExprStmt.expr;
+				return true;
+			}
+		}
 	case_end;
 	case_end;
 	}
 	}
 
 
@@ -3742,6 +3740,79 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 		o->type = proc_type;
 		o->type = proc_type;
 	case_end;
 	case_end;
 
 
+	case_ast_node(ge, GiveExpr, node);
+		if (c->proc_stack.count == 0) {
+			error_node(node, "A give expression is only allowed within a procedure");
+			goto error;
+		}
+
+		if (ge->results.count == 0) {
+			error_node(node, "`give` has no results");
+			goto error;
+		}
+
+		gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
+
+		Array(Operand) operands;
+		array_init_reserve(&operands, c->tmp_allocator, 2*ge->results.count);
+
+		for_array(i, ge->results) {
+			AstNode *rhs = ge->results.e[i];
+			Operand o = {0};
+			check_multi_expr(c, &o, rhs);
+			if (!is_operand_value(o)) {
+				error_node(rhs, "Expected a value for a `give`");
+				continue;
+			}
+			if (o.type->kind != Type_Tuple) {
+				array_add(&operands, o);
+			} else {
+				TypeTuple *tuple = &o.type->Tuple;
+				for (isize j = 0; j < tuple->variable_count; j++) {
+					o.type = tuple->variables[j]->type;
+					array_add(&operands, o);
+				}
+			}
+		}
+
+		if (operands.count == 0) {
+			error_node(node, "`give` has no value");
+			gb_temp_arena_memory_end(tmp);
+			goto error;
+		} else if (operands.count == 1) {
+			Operand operand = operands.e[0];
+			if (type_hint != NULL) {
+				convert_to_typed(c, &operand, type_hint, 0);
+			}
+			o->type = default_type(operand.type);
+			o->mode = Addressing_Value;
+		} else {
+			Type *tuple = make_type_tuple(c->allocator);
+
+			Entity **variables = gb_alloc_array(c->allocator, Entity *, operands.count);
+			isize variable_index = 0;
+			for_array(i, operands) {
+				Operand operand = operands.e[i];
+				// Type *type = default_type(operand.type);
+				Type *type = operand.type;
+				switch (operand.mode) {
+				case Addressing_Constant:
+					variables[variable_index++] = make_entity_constant(c->allocator, NULL, empty_token, type, operand.value);
+					break;
+				default:
+					variables[variable_index++] = make_entity_param(c->allocator, NULL, empty_token, type, false);
+					break;
+				}
+			}
+			tuple->Tuple.variables = variables;
+			tuple->Tuple.variable_count = operands.count;
+
+			o->type = tuple;
+			o->mode = Addressing_Value;
+		}
+		gb_temp_arena_memory_end(tmp);
+	case_end;
+
 	case_ast_node(be, BlockExpr, node);
 	case_ast_node(be, BlockExpr, node);
 		if (c->proc_stack.count == 0) {
 		if (c->proc_stack.count == 0) {
 			error_node(node, "A block expression is only allowed within a procedure");
 			error_node(node, "A block expression is only allowed within a procedure");
@@ -3755,26 +3826,34 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 			be->stmts.count--;
 			be->stmts.count--;
 		}
 		}
 
 
-		check_open_scope(c, node);
-		check_stmt_list(c, be->stmts, Stmt_GiveAllowed);
-		check_close_scope(c);
-
-
 		if (be->stmts.count == 0) {
 		if (be->stmts.count == 0) {
 			error_node(node, "Empty block expression");
 			error_node(node, "Empty block expression");
 			goto error;
 			goto error;
 		}
 		}
+
+		check_open_scope(c, node);
+		check_stmt_list(c, be->stmts, Stmt_GiveAllowed);
+		check_close_scope(c);
+
 		AstNode *give_node = NULL;
 		AstNode *give_node = NULL;
 		if (!check_is_giving(node, &give_node) || give_node == NULL) {
 		if (!check_is_giving(node, &give_node) || give_node == NULL) {
-			error_node(node, "Missing give statement at");
+			error_node(node, "Missing give statement at end of block expression");
 			goto error;
 			goto error;
 		}
 		}
 
 
 		GB_ASSERT(give_node != NULL && give_node->kind == AstNode_GiveExpr);
 		GB_ASSERT(give_node != NULL && give_node->kind == AstNode_GiveExpr);
 		be->give_node = give_node;
 		be->give_node = give_node;
 
 
-		o->type = type_of_expr(&c->info, give_node);
-		o->mode = Addressing_Value;
+		Type *type = type_of_expr(&c->info, give_node);
+		if (type == NULL) {
+			goto error;
+		} else if (type == t_invalid) {
+			o->type = t_invalid;
+			o->mode = Addressing_Invalid;
+		} else {
+			o->type = type;
+			o->mode = Addressing_Value;
+		}
 	case_end;
 	case_end;
 
 
 	case_ast_node(ie, IfExpr, node);
 	case_ast_node(ie, IfExpr, node);
@@ -3795,17 +3874,20 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 			error_node(ie->cond, "Non-boolean condition in if expression");
 			error_node(ie->cond, "Non-boolean condition in if expression");
 		}
 		}
 
 
+
+		Operand x = {Addressing_Invalid};
+		Operand y = {Addressing_Invalid};
 		Type *if_type = NULL;
 		Type *if_type = NULL;
 		Type *else_type = NULL;
 		Type *else_type = NULL;
-		check_expr(c, &operand, ie->body);
-		if_type = operand.type;
+		check_expr(c, &x, ie->body);
+		if_type = x.type;
 
 
 		if (ie->else_expr != NULL) {
 		if (ie->else_expr != NULL) {
 			switch (ie->else_expr->kind) {
 			switch (ie->else_expr->kind) {
 			case AstNode_IfExpr:
 			case AstNode_IfExpr:
 			case AstNode_BlockExpr:
 			case AstNode_BlockExpr:
-				check_expr(c, &operand, ie->else_expr);
-				else_type = operand.type;
+				check_expr(c, &y, ie->else_expr);
+				else_type = y.type;
 				break;
 				break;
 			default:
 			default:
 				error_node(ie->else_expr, "Invalid else expression in if expression");
 				error_node(ie->else_expr, "Invalid else expression in if expression");
@@ -3819,84 +3901,32 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 
 
 		check_close_scope(c);
 		check_close_scope(c);
 
 
-		GB_ASSERT(if_type != NULL &&
-		          else_type != NULL);
-
-		if (!are_types_identical(if_type, else_type)) {
-			gbString its = type_to_string(if_type);
-			gbString ets = type_to_string(else_type);
-			error_node(node, "if expression type and else expression type do not match, %s != %s", its, ets);
-			gb_string_free(ets);
-			gb_string_free(its);
+		if (if_type == NULL || if_type == t_invalid ||
+		    else_type == NULL || else_type == t_invalid) {
 			goto error;
 			goto error;
 		}
 		}
 
 
-
-		o->type = if_type;
-		o->mode = Addressing_Value;
-	case_end;
-
-	case_ast_node(ye, GiveExpr, node);
-		if (c->proc_stack.count == 0) {
-			error_node(node, "A give expression is only allowed within a procedure");
+		convert_to_typed(c, &x, y.type, 0);
+		if (x.mode == Addressing_Invalid) {
 			goto error;
 			goto error;
 		}
 		}
-
-		if (ye->results.count == 0) {
-			error_node(node, "No give values");
+		convert_to_typed(c, &y, x.type, 0);
+		if (y.mode == Addressing_Invalid) {
+			x.mode = Addressing_Invalid;
 			goto error;
 			goto error;
 		}
 		}
 
 
-		gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
-
-		Array(Operand) operands;
-		array_init_reserve(&operands, c->tmp_allocator, 2*ye->results.count);
-
-		for_array(i, ye->results) {
-			AstNode *rhs = ye->results.e[i];
-			Operand o = {0};
-			check_multi_expr(c, &o, rhs);
-			if (!is_operand_value(o)) {
-				error_node(rhs, "Expected a value for a `give`");
-				continue;
-			}
-			if (o.type->kind != Type_Tuple) {
-				array_add(&operands, o);
-			} else {
-				TypeTuple *tuple = &o.type->Tuple;
-				for (isize j = 0; j < tuple->variable_count; j++) {
-					o.type = tuple->variables[j]->type;
-					array_add(&operands, o);
-				}
-			}
-		}
-
-		Type *give_type = NULL;
 
 
-		if (operands.count == 0) {
-			error_node(node, "`give` has no type");
-			gb_temp_arena_memory_end(tmp);
+		if (!are_types_identical(if_type, else_type)) {
+			gbString its = type_to_string(if_type);
+			gbString ets = type_to_string(else_type);
+			error_node(node, "Mismatched types in if expression, %s vs %s", its, ets);
+			gb_string_free(ets);
+			gb_string_free(its);
 			goto error;
 			goto error;
-		} else if (operands.count == 1) {
-			give_type = default_type(operands.e[0].type);
-		} else {
-			Type *tuple = make_type_tuple(c->allocator);
-
-			Entity **variables = gb_alloc_array(c->allocator, Entity *, operands.count);
-			isize variable_index = 0;
-			for_array(i, operands) {
-				Type *type = default_type(operands.e[i].type);
-				variables[variable_index++] = make_entity_param(c->allocator, NULL, empty_token, type, false);
-			}
-			tuple->Tuple.variables = variables;
-			tuple->Tuple.variable_count = operands.count;
-
-			give_type = tuple;
 		}
 		}
-		gb_temp_arena_memory_end(tmp);
-
 
 
-		o->type = give_type;
+		o->type = if_type;
 		o->mode = Addressing_Value;
 		o->mode = Addressing_Value;
 	case_end;
 	case_end;
 
 
@@ -4491,6 +4521,13 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
 		str = gb_string_appendc(str, "}");
 		str = gb_string_appendc(str, "}");
 	case_end;
 	case_end;
 
 
+	case_ast_node(be, BlockExpr, node);
+		str = gb_string_appendc(str, "block expression");
+	case_end;
+	case_ast_node(ie, IfExpr, node);
+		str = gb_string_appendc(str, "if expression");
+	case_end;
+
 	case_ast_node(te, TagExpr, node);
 	case_ast_node(te, TagExpr, node);
 		str = gb_string_appendc(str, "#");
 		str = gb_string_appendc(str, "#");
 		str = string_append_token(str, te->name);
 		str = string_append_token(str, te->name);

+ 12 - 7
src/checker/stmt.c

@@ -6,7 +6,7 @@ void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
 	check_scope_decls(c, stmts, 1.2*stmts.count, NULL);
 	check_scope_decls(c, stmts, 1.2*stmts.count, NULL);
 
 
 	bool ft_ok = (flags & Stmt_FallthroughAllowed) != 0;
 	bool ft_ok = (flags & Stmt_FallthroughAllowed) != 0;
-	u32 f = flags & (~Stmt_FallthroughAllowed);
+	flags &= ~Stmt_FallthroughAllowed;
 
 
 	isize max = stmts.count;
 	isize max = stmts.count;
 	for (isize i = stmts.count-1; i >= 0; i--) {
 	for (isize i = stmts.count-1; i >= 0; i--) {
@@ -20,16 +20,21 @@ void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
 		if (n->kind == AstNode_EmptyStmt) {
 		if (n->kind == AstNode_EmptyStmt) {
 			continue;
 			continue;
 		}
 		}
-		u32 new_flags = f;
+		u32 new_flags = flags;
 		if (ft_ok && i+1 == max) {
 		if (ft_ok && i+1 == max) {
 			new_flags |= Stmt_FallthroughAllowed;
 			new_flags |= Stmt_FallthroughAllowed;
 		}
 		}
 
 
-		if (n->kind == AstNode_ReturnStmt && i+1 < max) {
-			error_node(n, "Statements after this `return` are never executed");
-		} else if (n->kind == AstNode_ExprStmt && i+1 < max) {
-			if (n->ExprStmt.expr->kind == AstNode_GiveExpr) {
-				error_node(n, "A `give` must be the last statement in a block");
+		if (i+1 < max) {
+			switch (n->kind) {
+			case AstNode_ReturnStmt:
+				error_node(n, "Statements after this `return` are never executed");
+				break;
+			case AstNode_ExprStmt:
+				if (n->ExprStmt.expr->kind == AstNode_GiveExpr) {
+					error_node(n, "A `give` must be the last statement in a block");
+				}
+				break;
 			}
 			}
 		}
 		}
 
 

+ 3 - 0
src/checker/types.c

@@ -763,6 +763,9 @@ bool are_types_identical(Type *x, Type *y) {
 
 
 
 
 Type *default_type(Type *type) {
 Type *default_type(Type *type) {
+	if (type == NULL) {
+		return t_invalid;
+	}
 	if (type->kind == Type_Basic) {
 	if (type->kind == Type_Basic) {
 		switch (type->Basic.kind) {
 		switch (type->Basic.kind) {
 		case Basic_UntypedBool:    return t_bool;
 		case Basic_UntypedBool:    return t_bool;

+ 35 - 19
src/parser.c

@@ -1275,27 +1275,36 @@ void expect_semicolon(AstFile *f, AstNode *s) {
 		return;
 		return;
 	}
 	}
 
 
-	if (s != NULL && prev_token.pos.line != f->curr_token.pos.line) {
-		switch (s->kind) {
-		case AstNode_ProcDecl:
-			return;
-		case AstNode_GenericDecl:
-			if (s->GenericDecl.close.kind == Token_CloseBrace) {
+	if (s != NULL) {
+		if (prev_token.pos.line != f->curr_token.pos.line) {
+			switch (s->kind) {
+			case AstNode_ProcDecl:
 				return;
 				return;
-			} else if (s->GenericDecl.token.kind == Token_type) {
+			case AstNode_GenericDecl:
+				if (s->GenericDecl.close.kind == Token_CloseBrace) {
+					return;
+				} else if (s->GenericDecl.token.kind == Token_type) {
+					if (f->prev_token.kind == Token_CloseBrace) {
+						return;
+					}
+				}
+				break;
+
+			case AstNode_TypeSpec:
 				if (f->prev_token.kind == Token_CloseBrace) {
 				if (f->prev_token.kind == Token_CloseBrace) {
 					return;
 					return;
 				}
 				}
+				break;
 			}
 			}
-			break;
-
-		case AstNode_TypeSpec:
-			if (f->prev_token.kind == Token_CloseBrace) {
-				return;
+		} else {
+			switch (s->kind) {
+			case AstNode_GiveExpr:
+				if (f->curr_token.kind == Token_CloseBrace) {
+					return;
+				}
+				break;
 			}
 			}
-			break;
 		}
 		}
-
 		syntax_error(prev_token, "Expected `;` after %.*s, got %.*s",
 		syntax_error(prev_token, "Expected `;` after %.*s, got %.*s",
 		             LIT(ast_node_strings[s->kind]), LIT(token_strings[prev_token.kind]));
 		             LIT(ast_node_strings[s->kind]), LIT(token_strings[prev_token.kind]));
 	} else {
 	} else {
@@ -2752,10 +2761,11 @@ AstNode *parse_return_stmt(AstFile *f) {
 	}
 	}
 
 
 	Token token = expect_token(f, Token_return);
 	Token token = expect_token(f, Token_return);
-	AstNodeArray results = make_ast_node_array(f);
-
+	AstNodeArray results;
 	if (f->curr_token.kind != Token_Semicolon && f->curr_token.kind != Token_CloseBrace) {
 	if (f->curr_token.kind != Token_Semicolon && f->curr_token.kind != Token_CloseBrace) {
 		results = parse_rhs_expr_list(f);
 		results = parse_rhs_expr_list(f);
+	} else {
+		results = make_ast_node_array(f);
 	}
 	}
 
 
 	expect_semicolon(f, results.e[0]);
 	expect_semicolon(f, results.e[0]);
@@ -2774,9 +2784,15 @@ AstNode *parse_give_stmt(AstFile *f) {
 	}
 	}
 
 
 	Token token = expect_token(f, Token_give);
 	Token token = expect_token(f, Token_give);
-	AstNodeArray results = parse_rhs_expr_list(f);
-	expect_semicolon(f, results.e[0]);
-	return make_expr_stmt(f, make_give_expr(f, token, results));
+	AstNodeArray results;
+	if (f->curr_token.kind != Token_Semicolon && f->curr_token.kind != Token_CloseBrace) {
+		results = parse_rhs_expr_list(f);
+	} else {
+		results = make_ast_node_array(f);
+	}
+	AstNode *ge = make_give_expr(f, token, results);
+	expect_semicolon(f, ge);
+	return make_expr_stmt(f, ge);
 }
 }
 
 
 AstNode *parse_for_stmt(AstFile *f) {
 AstNode *parse_for_stmt(AstFile *f) {

+ 0 - 2
src/ssa.c

@@ -2665,7 +2665,6 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 		ssaValue *cond = ssa_build_cond(proc, ie->cond, then, else_);
 		ssaValue *cond = ssa_build_cond(proc, ie->cond, then, else_);
 		proc->curr_block = then;
 		proc->curr_block = then;
 
 
-
 		ssa_open_scope(proc);
 		ssa_open_scope(proc);
 		array_add(&edges, ssa_build_expr(proc, ie->body));
 		array_add(&edges, ssa_build_expr(proc, ie->body));
 		ssa_close_scope(proc, ssaDeferExit_Default, NULL);
 		ssa_close_scope(proc, ssaDeferExit_Default, NULL);
@@ -2721,7 +2720,6 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 				ssaValue *field = ssa_emit_struct_ep(proc, v, i);
 				ssaValue *field = ssa_emit_struct_ep(proc, v, i);
 				ssa_emit_store(proc, field, res);
 				ssa_emit_store(proc, field, res);
 			}
 			}
-
 			v = ssa_emit_load(proc, v);
 			v = ssa_emit_load(proc, v);
 
 
 			gb_temp_arena_memory_end(tmp);
 			gb_temp_arena_memory_end(tmp);

+ 0 - 4
src/tokenizer.c

@@ -180,10 +180,8 @@ void init_global_error_collector(void) {
 	gb_mutex_init(&global_error_collector.mutex);
 	gb_mutex_init(&global_error_collector.mutex);
 }
 }
 
 
-
 void warning_va(Token token, char *fmt, va_list va) {
 void warning_va(Token token, char *fmt, va_list va) {
 	gb_mutex_lock(&global_error_collector.mutex);
 	gb_mutex_lock(&global_error_collector.mutex);
-
 	global_error_collector.warning_count++;
 	global_error_collector.warning_count++;
 	// NOTE(bill): Duplicate error, skip it
 	// NOTE(bill): Duplicate error, skip it
 	if (!token_pos_eq(global_error_collector.prev, token.pos)) {
 	if (!token_pos_eq(global_error_collector.prev, token.pos)) {
@@ -198,7 +196,6 @@ void warning_va(Token token, char *fmt, va_list va) {
 
 
 void error_va(Token token, char *fmt, va_list va) {
 void error_va(Token token, char *fmt, va_list va) {
 	gb_mutex_lock(&global_error_collector.mutex);
 	gb_mutex_lock(&global_error_collector.mutex);
-
 	global_error_collector.count++;
 	global_error_collector.count++;
 	// NOTE(bill): Duplicate error, skip it
 	// NOTE(bill): Duplicate error, skip it
 	if (!token_pos_eq(global_error_collector.prev, token.pos)) {
 	if (!token_pos_eq(global_error_collector.prev, token.pos)) {
@@ -213,7 +210,6 @@ void error_va(Token token, char *fmt, va_list va) {
 
 
 void syntax_error_va(Token token, char *fmt, va_list va) {
 void syntax_error_va(Token token, char *fmt, va_list va) {
 	gb_mutex_lock(&global_error_collector.mutex);
 	gb_mutex_lock(&global_error_collector.mutex);
-
 	global_error_collector.count++;
 	global_error_collector.count++;
 	// NOTE(bill): Duplicate error, skip it
 	// NOTE(bill): Duplicate error, skip it
 	if (!token_pos_eq(global_error_collector.prev, token.pos)) {
 	if (!token_pos_eq(global_error_collector.prev, token.pos)) {