Browse Source

Add support for `Addressing_OptionalOkPtr`
Allowing for `i, ok := &x.(T);` (type assertions) and `v, ok := &m[k];` (map indexing)

gingerBill 4 years ago
parent
commit
795a5910cf
3 changed files with 237 additions and 126 deletions
  1. 28 19
      src/check_expr.cpp
  2. 191 90
      src/llvm_backend.cpp
  3. 18 17
      src/parser.hpp

+ 28 - 19
src/check_expr.cpp

@@ -1778,16 +1778,16 @@ bool check_is_not_addressable(CheckerContext *c, Operand *o) {
 		return false;
 		return false;
 	}
 	}
 
 
-	if (o->mode != Addressing_Variable) {
-		return true;
-	}
-
-	return false;
+	return o->mode != Addressing_Variable;
 }
 }
 
 
 void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) {
 void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) {
 	switch (op.kind) {
 	switch (op.kind) {
 	case Token_And: { // Pointer address
 	case Token_And: { // Pointer address
+		if (node->kind == Ast_TypeAssertion) {
+			gb_printf_err("%s\n", expr_to_string(node));
+		}
+
 		if (check_is_not_addressable(c, o)) {
 		if (check_is_not_addressable(c, o)) {
 			if (ast_node_expect(node, Ast_UnaryExpr)) {
 			if (ast_node_expect(node, Ast_UnaryExpr)) {
 				ast_node(ue, UnaryExpr, node);
 				ast_node(ue, UnaryExpr, node);
@@ -1818,8 +1818,19 @@ void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) {
 			o->mode = Addressing_Invalid;
 			o->mode = Addressing_Invalid;
 			return;
 			return;
 		}
 		}
-		o->mode = Addressing_Value;
+
 		o->type = alloc_type_pointer(o->type);
 		o->type = alloc_type_pointer(o->type);
+
+		switch (o->mode) {
+		case Addressing_OptionalOk:
+		case Addressing_MapIndex:
+			o->mode = Addressing_OptionalOkPtr;
+			break;
+		default:
+			o->mode = Addressing_Value;
+			break;
+		}
+
 		return;
 		return;
 	}
 	}
 	}
 	}
@@ -3892,7 +3903,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
 			operand->mode = Addressing_Value;
 			operand->mode = Addressing_Value;
 		} else if (entity->flags & EntityFlag_SoaPtrField) {
 		} else if (entity->flags & EntityFlag_SoaPtrField) {
 			operand->mode = Addressing_SoaVariable;
 			operand->mode = Addressing_SoaVariable;
-		} else if (operand->mode == Addressing_OptionalOk) {
+		} else if (operand->mode == Addressing_OptionalOk || operand->mode == Addressing_OptionalOkPtr) {
 			operand->mode = Addressing_Value;
 			operand->mode = Addressing_Value;
 		} else if (operand->mode == Addressing_SoaVariable) {
 		} else if (operand->mode == Addressing_SoaVariable) {
 			operand->mode = Addressing_Variable;
 			operand->mode = Addressing_Variable;
@@ -4015,7 +4026,7 @@ bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs,
 
 
 		if (o.type == nullptr || o.type->kind != Type_Tuple) {
 		if (o.type == nullptr || o.type->kind != Type_Tuple) {
 			if (lhs.count == 2 && rhs.count == 1 &&
 			if (lhs.count == 2 && rhs.count == 1 &&
-			    (o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk)) {
+			    (o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk || o.mode == Addressing_OptionalOkPtr)) {
 			    	bool do_normal = true;
 			    	bool do_normal = true;
 				Ast *expr = unparen_expr(o.expr);
 				Ast *expr = unparen_expr(o.expr);
 
 
@@ -4046,7 +4057,8 @@ bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs,
 					add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
 					add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
 				}
 				}
 
 
-				if (o.mode == Addressing_OptionalOk && expr->kind == Ast_TypeAssertion) {
+				if (expr->kind == Ast_TypeAssertion &&
+				    (o.mode == Addressing_OptionalOk || o.mode == Addressing_OptionalOkPtr)) {
 					// NOTE(bill): Used only for optimizations in the backend
 					// NOTE(bill): Used only for optimizations in the backend
 					if (is_blank_ident(lhs[0].expr)) {
 					if (is_blank_ident(lhs[0].expr)) {
 						expr->TypeAssertion.ignores[0] = true;
 						expr->TypeAssertion.ignores[0] = true;
@@ -4139,7 +4151,7 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count,
 
 
 		if (o.type == nullptr || o.type->kind != Type_Tuple) {
 		if (o.type == nullptr || o.type->kind != Type_Tuple) {
 			if (allow_ok && lhs_count == 2 && rhs.count == 1 &&
 			if (allow_ok && lhs_count == 2 && rhs.count == 1 &&
-			    (o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk)) {
+			    (o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk || o.mode == Addressing_OptionalOkPtr)) {
 				bool do_normal = true;
 				bool do_normal = true;
 				Ast *expr = unparen_expr(o.expr);
 				Ast *expr = unparen_expr(o.expr);
 
 
@@ -4170,7 +4182,8 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count,
 					add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
 					add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
 				}
 				}
 
 
-				if (o.mode == Addressing_OptionalOk && expr->kind == Ast_TypeAssertion) {
+				if (expr->kind == Ast_TypeAssertion &&
+				    (o.mode == Addressing_OptionalOk || o.mode == Addressing_OptionalOkPtr)) {
 					// NOTE(bill): Used only for optimizations in the backend
 					// NOTE(bill): Used only for optimizations in the backend
 					if (is_blank_ident(lhs[0]->token)) {
 					if (is_blank_ident(lhs[0]->token)) {
 						expr->TypeAssertion.ignores[0] = true;
 						expr->TypeAssertion.ignores[0] = true;
@@ -7505,15 +7518,11 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 		check_expr_base(c, o, ue->expr, th);
 		check_expr_base(c, o, ue->expr, th);
 		node->viral_state_flags |= ue->expr->viral_state_flags;
 		node->viral_state_flags |= ue->expr->viral_state_flags;
 
 
-		if (o->mode == Addressing_Invalid) {
-			o->expr = node;
-			return kind;
-		}
-		check_unary_expr(c, o, ue->op, node);
-		if (o->mode == Addressing_Invalid) {
-			o->expr = node;
-			return kind;
+		if (o->mode != Addressing_Invalid) {
+			check_unary_expr(c, o, ue->op, node);
 		}
 		}
+		o->expr = node;
+		return kind;
 	case_end;
 	case_end;
 
 
 
 

+ 191 - 90
src/llvm_backend.cpp

@@ -11830,6 +11830,194 @@ bool lb_is_expr_constant_zero(Ast *expr) {
 	return false;
 	return false;
 }
 }
 
 
+lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
+	ast_node(ue, UnaryExpr, expr);
+	auto tv = type_and_value_of_expr(expr);
+
+
+	Ast *ue_expr = unparen_expr(ue->expr);
+	if (ue_expr->kind == Ast_IndexExpr && tv.mode == Addressing_OptionalOkPtr && is_type_tuple(tv.type)) {
+		Type *tuple = tv.type;
+
+		Type *map_type = type_of_expr(ue_expr->IndexExpr.expr);
+		Type *ot = base_type(map_type);
+		Type *t = base_type(type_deref(ot));
+		bool deref = t != ot;
+		GB_ASSERT(t->kind == Type_Map);
+		ast_node(ie, IndexExpr, ue_expr);
+
+		lbValue map_val = lb_build_addr_ptr(p, ie->expr);
+		if (deref) {
+			map_val = lb_emit_load(p, map_val);
+		}
+
+		lbValue key = lb_build_expr(p, ie->index);
+		key = lb_emit_conv(p, key, t->Map.key);
+
+		Type *result_type = type_of_expr(expr);
+		lbAddr addr = lb_addr_map(map_val, key, t, alloc_type_pointer(t->Map.value));
+		lbValue ptr = lb_addr_get_ptr(p, addr);
+
+		lbValue ok = lb_emit_comp_against_nil(p, Token_NotEq, ptr);
+		ok = lb_emit_conv(p, ok, tuple->Tuple.variables[1]->type);
+
+		lbAddr res = lb_add_local_generated(p, tuple, false);
+		lbValue gep0 = lb_emit_struct_ep(p, res.addr, 0);
+		lbValue gep1 = lb_emit_struct_ep(p, res.addr, 1);
+		lb_emit_store(p, gep0, ptr);
+		lb_emit_store(p, gep1, ok);
+		return lb_addr_load(p, res);
+
+	} if (ue_expr->kind == Ast_CompoundLit) {
+		lbValue v = lb_build_expr(p, ue->expr);
+
+		Type *type = v.type;
+		lbAddr addr = {};
+		if (p->is_startup) {
+			addr = lb_add_global_generated(p->module, type, v);
+		} else {
+			addr = lb_add_local_generated(p, type, false);
+		}
+		lb_addr_store(p, addr, v);
+		return addr.addr;
+
+	} else if (ue_expr->kind == Ast_TypeAssertion) {
+		if (is_type_tuple(tv.type)) {
+			Type *tuple = tv.type;
+			Type *ptr_type = tuple->Tuple.variables[0]->type;
+			Type *ok_type = tuple->Tuple.variables[1]->type;
+
+			ast_node(ta, TypeAssertion, ue_expr);
+			TokenPos pos = ast_token(expr).pos;
+			Type *type = type_of_expr(ue_expr);
+			GB_ASSERT(!is_type_tuple(type));
+
+			lbValue e = lb_build_expr(p, ta->expr);
+			Type *t = type_deref(e.type);
+			if (is_type_union(t)) {
+				lbValue v = e;
+				if (!is_type_pointer(v.type)) {
+					v = lb_address_from_load_or_generate_local(p, v);
+				}
+				Type *src_type = type_deref(v.type);
+				Type *dst_type = type;
+
+				lbValue src_tag = {};
+				lbValue dst_tag = {};
+				if (is_type_union_maybe_pointer(src_type)) {
+					src_tag = lb_emit_comp_against_nil(p, Token_NotEq, v);
+					dst_tag = lb_const_bool(p->module, t_bool, true);
+				} else {
+					src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v));
+					dst_tag = lb_const_union_tag(p->module, src_type, dst_type);
+				}
+
+				lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag);
+
+				lbValue data_ptr = lb_emit_conv(p, v, ptr_type);
+				lbAddr res = lb_add_local_generated(p, tuple, true);
+				lbValue gep0 = lb_emit_struct_ep(p, res.addr, 0);
+				lbValue gep1 = lb_emit_struct_ep(p, res.addr, 1);
+				lb_emit_store(p, gep0, lb_emit_select(p, ok, data_ptr, lb_const_nil(p->module, ptr_type)));
+				lb_emit_store(p, gep1, lb_emit_conv(p, ok, ok_type));
+				return lb_addr_load(p, res);
+			} else if (is_type_any(t)) {
+				lbValue v = e;
+				if (is_type_pointer(v.type)) {
+					v = lb_emit_load(p, v);
+				}
+
+				lbValue data_ptr = lb_emit_conv(p, lb_emit_struct_ev(p, v, 0), ptr_type);
+				lbValue any_id = lb_emit_struct_ev(p, v, 1);
+				lbValue id = lb_typeid(p->module, type);
+
+				lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id);
+
+				lbAddr res = lb_add_local_generated(p, tuple, false);
+				lbValue gep0 = lb_emit_struct_ep(p, res.addr, 0);
+				lbValue gep1 = lb_emit_struct_ep(p, res.addr, 1);
+				lb_emit_store(p, gep0, lb_emit_select(p, ok, data_ptr, lb_const_nil(p->module, ptr_type)));
+				lb_emit_store(p, gep1, lb_emit_conv(p, ok, ok_type));
+				return lb_addr_load(p, res);
+			} else {
+				GB_PANIC("TODO(bill): type assertion %s", type_to_string(type));
+			}
+
+		} else {
+			GB_ASSERT(is_type_pointer(tv.type));
+
+			ast_node(ta, TypeAssertion, ue_expr);
+			TokenPos pos = ast_token(expr).pos;
+			Type *type = type_of_expr(ue_expr);
+			GB_ASSERT(!is_type_tuple(type));
+
+			lbValue e = lb_build_expr(p, ta->expr);
+			Type *t = type_deref(e.type);
+			if (is_type_union(t)) {
+				lbValue v = e;
+				if (!is_type_pointer(v.type)) {
+					v = lb_address_from_load_or_generate_local(p, v);
+				}
+				Type *src_type = type_deref(v.type);
+				Type *dst_type = type;
+
+				lbValue src_tag = {};
+				lbValue dst_tag = {};
+				if (is_type_union_maybe_pointer(src_type)) {
+					src_tag = lb_emit_comp_against_nil(p, Token_NotEq, v);
+					dst_tag = lb_const_bool(p->module, t_bool, true);
+				} else {
+					src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v));
+					dst_tag = lb_const_union_tag(p->module, src_type, dst_type);
+				}
+
+				lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag);
+				auto args = array_make<lbValue>(permanent_allocator(), 6);
+				args[0] = ok;
+
+				args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
+				args[2] = lb_const_int(p->module, t_i32, pos.line);
+				args[3] = lb_const_int(p->module, t_i32, pos.column);
+
+				args[4] = lb_typeid(p->module, src_type);
+				args[5] = lb_typeid(p->module, dst_type);
+				lb_emit_runtime_call(p, "type_assertion_check", args);
+
+				lbValue data_ptr = v;
+				return lb_emit_conv(p, data_ptr, tv.type);
+			} else if (is_type_any(t)) {
+				lbValue v = e;
+				if (is_type_pointer(v.type)) {
+					v = lb_emit_load(p, v);
+				}
+
+				lbValue data_ptr = lb_emit_struct_ev(p, v, 0);
+				lbValue any_id = lb_emit_struct_ev(p, v, 1);
+				lbValue id = lb_typeid(p->module, type);
+
+
+				lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id);
+				auto args = array_make<lbValue>(permanent_allocator(), 6);
+				args[0] = ok;
+
+				args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
+				args[2] = lb_const_int(p->module, t_i32, pos.line);
+				args[3] = lb_const_int(p->module, t_i32, pos.column);
+
+				args[4] = any_id;
+				args[5] = id;
+				lb_emit_runtime_call(p, "type_assertion_check", args);
+
+				return lb_emit_conv(p, data_ptr, tv.type);
+			} else {
+				GB_PANIC("TODO(bill): type assertion %s", type_to_string(type));
+			}
+		}
+	}
+
+	return lb_build_addr_ptr(p, ue->expr);
+}
+
 lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
 lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
 	lbModule *m = p->module;
 	lbModule *m = p->module;
 
 
@@ -12040,94 +12228,8 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
 
 
 	case_ast_node(ue, UnaryExpr, expr);
 	case_ast_node(ue, UnaryExpr, expr);
 		switch (ue->op.kind) {
 		switch (ue->op.kind) {
-		case Token_And: {
-			Ast *ue_expr = unparen_expr(ue->expr);
-			if (ue_expr->kind == Ast_CompoundLit) {
-				lbValue v = lb_build_expr(p, ue->expr);
-
-				Type *type = v.type;
-				lbAddr addr = {};
-				if (p->is_startup) {
-					addr = lb_add_global_generated(p->module, type, v);
-				} else {
-					addr = lb_add_local_generated(p, type, false);
-				}
-				lb_addr_store(p, addr, v);
-				return addr.addr;
-
-			} else if (ue_expr->kind == Ast_TypeAssertion) {
-				GB_ASSERT(is_type_pointer(tv.type));
-
-				ast_node(ta, TypeAssertion, ue_expr);
-				TokenPos pos = ast_token(expr).pos;
-				Type *type = type_of_expr(ue_expr);
-				GB_ASSERT(!is_type_tuple(type));
-
-				lbValue e = lb_build_expr(p, ta->expr);
-				Type *t = type_deref(e.type);
-				if (is_type_union(t)) {
-					lbValue v = e;
-					if (!is_type_pointer(v.type)) {
-						v = lb_address_from_load_or_generate_local(p, v);
-					}
-					Type *src_type = type_deref(v.type);
-					Type *dst_type = type;
-
-					lbValue src_tag = {};
-					lbValue dst_tag = {};
-					if (is_type_union_maybe_pointer(src_type)) {
-						src_tag = lb_emit_comp_against_nil(p, Token_NotEq, v);
-						dst_tag = lb_const_bool(p->module, t_bool, true);
-					} else {
-						src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v));
-						dst_tag = lb_const_union_tag(p->module, src_type, dst_type);
-					}
-
-					lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag);
-					auto args = array_make<lbValue>(permanent_allocator(), 6);
-					args[0] = ok;
-
-					args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
-					args[2] = lb_const_int(p->module, t_i32, pos.line);
-					args[3] = lb_const_int(p->module, t_i32, pos.column);
-
-					args[4] = lb_typeid(p->module, src_type);
-					args[5] = lb_typeid(p->module, dst_type);
-					lb_emit_runtime_call(p, "type_assertion_check", args);
-
-					lbValue data_ptr = v;
-					return lb_emit_conv(p, data_ptr, tv.type);
-				} else if (is_type_any(t)) {
-					lbValue v = e;
-					if (is_type_pointer(v.type)) {
-						v = lb_emit_load(p, v);
-					}
-
-					lbValue data_ptr = lb_emit_struct_ev(p, v, 0);
-					lbValue any_id = lb_emit_struct_ev(p, v, 1);
-					lbValue id = lb_typeid(p->module, type);
-
-
-					lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id);
-					auto args = array_make<lbValue>(permanent_allocator(), 6);
-					args[0] = ok;
-
-					args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
-					args[2] = lb_const_int(p->module, t_i32, pos.line);
-					args[3] = lb_const_int(p->module, t_i32, pos.column);
-
-					args[4] = any_id;
-					args[5] = id;
-					lb_emit_runtime_call(p, "type_assertion_check", args);
-
-					return lb_emit_conv(p, data_ptr, tv.type);
-				} else {
-					GB_PANIC("TODO(bill): type assertion %s", type_to_string(type));
-				}
-			}
-
-			return lb_build_addr_ptr(p, ue->expr);
-		}
+		case Token_And:
+			return lb_build_unary_and(p, expr);
 		default:
 		default:
 			{
 			{
 				lbValue v = lb_build_expr(p, ue->expr);
 				lbValue v = lb_build_expr(p, ue->expr);
@@ -12589,9 +12691,8 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
 
 
 	case_ast_node(ue, UnaryExpr, expr);
 	case_ast_node(ue, UnaryExpr, expr);
 		switch (ue->op.kind) {
 		switch (ue->op.kind) {
-		case Token_And: {
+		case Token_And:
 			return lb_build_addr(p, ue->expr);
 			return lb_build_addr(p, ue->expr);
-		}
 		default:
 		default:
 			GB_PANIC("Invalid unary expression for lb_build_addr");
 			GB_PANIC("Invalid unary expression for lb_build_addr");
 		}
 		}

+ 18 - 17
src/parser.hpp

@@ -7,23 +7,24 @@ struct AstFile;
 struct AstPackage;
 struct AstPackage;
 
 
 enum AddressingMode {
 enum AddressingMode {
-	Addressing_Invalid   = 0,    // invalid addressing mode
-	Addressing_NoValue   = 1,    // no value (void in C)
-	Addressing_Value     = 2,    // computed value (rvalue)
-	Addressing_Context   = 3,    // context value
-	Addressing_Variable  = 4,    // addressable variable (lvalue)
-	Addressing_Constant  = 5,    // constant
-	Addressing_Type      = 6,    // type
-	Addressing_Builtin   = 7,    // built-in procedure
-	Addressing_ProcGroup = 8,    // procedure group (overloaded procedure)
-	Addressing_MapIndex  = 9,    // map index expression -
-	                             // 	lhs: acts like a Variable
-	                             // 	rhs: acts like OptionalOk
-	Addressing_OptionalOk  = 10, // rhs: acts like a value with an optional boolean part (for existence check)
-	Addressing_SoaVariable = 11, // Struct-Of-Arrays indexed variable
-
-	Addressing_SwizzleValue    = 12, // Swizzle indexed value
-	Addressing_SwizzleVariable = 13, // Swizzle indexed variable
+	Addressing_Invalid   = 0,        // invalid addressing mode
+	Addressing_NoValue   = 1,        // no value (void in C)
+	Addressing_Value     = 2,        // computed value (rvalue)
+	Addressing_Context   = 3,        // context value
+	Addressing_Variable  = 4,        // addressable variable (lvalue)
+	Addressing_Constant  = 5,        // constant
+	Addressing_Type      = 6,        // type
+	Addressing_Builtin   = 7,        // built-in procedure
+	Addressing_ProcGroup = 8,        // procedure group (overloaded procedure)
+	Addressing_MapIndex  = 9,        // map index expression -
+	                                 //         lhs: acts like a Variable
+	                                 //         rhs: acts like OptionalOk
+	Addressing_OptionalOk    = 10,   // rhs: acts like a value with an optional boolean part (for existence check)
+	Addressing_OptionalOkPtr = 11,   // rhs: same as OptionalOk but the value is a pointer
+	Addressing_SoaVariable   = 12,   // Struct-Of-Arrays indexed variable
+
+	Addressing_SwizzleValue    = 13, // Swizzle indexed value
+	Addressing_SwizzleVariable = 14, // Swizzle indexed variable
 };
 };
 
 
 struct TypeAndValue {
 struct TypeAndValue {