Browse Source

Undef value `---` (for setting a value to be uninitialized/undefined)

Ginger Bill 8 years ago
parent
commit
c642e326ce
8 changed files with 124 additions and 14 deletions
  1. 6 1
      src/check_decl.cpp
  2. 35 3
      src/check_expr.cpp
  3. 7 1
      src/checker.cpp
  4. 30 8
      src/ir.cpp
  5. 4 0
      src/ir_print.cpp
  6. 11 0
      src/parser.cpp
  7. 19 1
      src/tokenizer.cpp
  8. 12 0
      src/types.cpp

+ 6 - 1
src/check_decl.cpp

@@ -34,7 +34,12 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
 		Type *t = operand->type;
 		Type *t = operand->type;
 		if (is_type_untyped(t)) {
 		if (is_type_untyped(t)) {
 			if (t == t_invalid || is_type_untyped_nil(t)) {
 			if (t == t_invalid || is_type_untyped_nil(t)) {
-				error(e->token, "Use of untyped nil in %.*s", LIT(context_name));
+				error(e->token, "Invalid use of untyped nil in %.*s", LIT(context_name));
+				e->type = t_invalid;
+				return NULL;
+			}
+			if (t == t_invalid || is_type_untyped_undef(t)) {
+				error(e->token, "Invalid use of --- in %.*s", LIT(context_name));
 				e->type = t_invalid;
 				e->type = t_invalid;
 				return NULL;
 				return NULL;
 			}
 			}

+ 35 - 3
src/check_expr.cpp

@@ -166,6 +166,13 @@ i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) {
 	Type *src = base_type(s);
 	Type *src = base_type(s);
 	Type *dst = base_type(type);
 	Type *dst = base_type(type);
 
 
+	if (is_type_untyped_undef(src)) {
+		if (type_has_undef(dst)) {
+			return 1;
+		}
+		return -1;
+	}
+
 	if (is_type_untyped_nil(src)) {
 	if (is_type_untyped_nil(src)) {
 		if (type_has_nil(dst)) {
 		if (type_has_nil(dst)) {
 			return 1;
 			return 1;
@@ -328,6 +335,11 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n
 				operand->mode = Addressing_Invalid;
 				operand->mode = Addressing_Invalid;
 				return;
 				return;
 			}
 			}
+			if (type == NULL && is_type_untyped_undef(operand->type)) {
+				error(operand->expr, "Use of --- in %.*s", LIT(context_name));
+				operand->mode = Addressing_Invalid;
+				return;
+			}
 			target_type = default_type(operand->type);
 			target_type = default_type(operand->type);
 			if (type != NULL && !is_type_any(type)) {
 			if (type != NULL && !is_type_any(type)) {
 				GB_ASSERT_MSG(is_type_typed(target_type), "%s", type_to_string(type));
 				GB_ASSERT_MSG(is_type_typed(target_type), "%s", type_to_string(type));
@@ -3366,7 +3378,9 @@ void convert_to_typed(Checker *c, Operand *operand, Type *target_type, i32 level
 
 
 
 
 	default:
 	default:
-		if (!is_type_untyped_nil(operand->type) || !type_has_nil(target_type)) {
+		if (is_type_untyped_undef(operand->type) && type_has_undef(target_type)) {
+			target_type = t_untyped_undef;
+		} else if (!is_type_untyped_nil(operand->type) || !type_has_nil(target_type)) {
 			operand->mode = Addressing_Invalid;
 			operand->mode = Addressing_Invalid;
 			convert_untyped_error(c, operand, target_type);
 			convert_untyped_error(c, operand, target_type);
 			return;
 			return;
@@ -5043,6 +5057,9 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 		} else {
 		} else {
 			// NOTE(bill): Generate the procedure type for this generic instance
 			// NOTE(bill): Generate the procedure type for this generic instance
 			// TODO(bill): Clean this shit up!
 			// TODO(bill): Clean this shit up!
+
+			ProcedureInfo proc_info = {};
+
 			if (pt->is_generic) {
 			if (pt->is_generic) {
 				GB_ASSERT(entity != NULL);
 				GB_ASSERT(entity != NULL);
 				DeclInfo *old_decl = decl_info_of_entity(&c->info, entity);
 				DeclInfo *old_decl = decl_info_of_entity(&c->info, entity);
@@ -5091,8 +5108,13 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 					add_entity_and_decl_info(c, ident, gen_entity, d);
 					add_entity_and_decl_info(c, ident, gen_entity, d);
 					gen_entity->scope = entity->scope;
 					gen_entity->scope = entity->scope;
 					add_entity_use(c, ident, gen_entity);
 					add_entity_use(c, ident, gen_entity);
-					check_procedure_later(c, c->curr_ast_file, token, d, final_proc_type, pd->body, tags);
 
 
+					proc_info.file = c->curr_ast_file;
+					proc_info.token = token;
+					proc_info.decl  = d;
+					proc_info.type  = final_proc_type;
+					proc_info.body  = pd->body;
+					proc_info.tags  = tags;
 
 
 					if (found) {
 					if (found) {
 						array_add(found, gen_entity);
 						array_add(found, gen_entity);
@@ -5107,7 +5129,6 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 				GB_ASSERT(gen_entity != NULL);
 				GB_ASSERT(gen_entity != NULL);
 			}
 			}
 
 
-
 			TypeProc *pt = &final_proc_type->Proc;
 			TypeProc *pt = &final_proc_type->Proc;
 
 
 			GB_ASSERT(pt->params != NULL);
 			GB_ASSERT(pt->params != NULL);
@@ -5124,6 +5145,7 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 						continue;
 						continue;
 					} else if (o.mode != Addressing_Type) {
 					} else if (o.mode != Addressing_Type) {
 						error(o.expr, "Expected a type for the argument");
 						error(o.expr, "Expected a type for the argument");
+						err = CallArgumentError_WrongTypes;
 					}
 					}
 
 
 					if (are_types_identical(e->type, o.type)) {
 					if (are_types_identical(e->type, o.type)) {
@@ -5180,6 +5202,10 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 					score += s;
 					score += s;
 				}
 				}
 			}
 			}
+
+			if (gen_entity != NULL && err == CallArgumentError_None) {
+				check_procedure_later(c, proc_info);
+			}
 		}
 		}
 	}
 	}
 
 
@@ -5748,6 +5774,7 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t
 				return kind;
 				return kind;
 			}
 			}
 
 
+			init_preload(c);
 			o->mode = Addressing_Value;
 			o->mode = Addressing_Value;
 			o->type = t_context;
 			o->type = t_context;
 			break;
 			break;
@@ -5761,6 +5788,11 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t
 		check_ident(c, o, node, NULL, type_hint, false);
 		check_ident(c, o, node, NULL, type_hint, false);
 	case_end;
 	case_end;
 
 
+	case_ast_node(u, Undef, node);
+		o->mode = Addressing_Value;
+		o->type = t_untyped_undef;
+	case_end;
+
 
 
 	case_ast_node(bl, BasicLit, node);
 	case_ast_node(bl, BasicLit, node);
 		Type *t = t_invalid;
 		Type *t = t_invalid;

+ 7 - 1
src/checker.cpp

@@ -1168,6 +1168,9 @@ void add_type_info_type(Checker *c, Type *t) {
 	}
 	}
 }
 }
 
 
+void check_procedure_later(Checker *c, ProcedureInfo info) {
+	map_set(&c->procs, hash_decl_info(info.decl), info);
+}
 
 
 void check_procedure_later(Checker *c, AstFile *file, Token token, DeclInfo *decl, Type *type, AstNode *body, u64 tags) {
 void check_procedure_later(Checker *c, AstFile *file, Token token, DeclInfo *decl, Type *type, AstNode *body, u64 tags) {
 	ProcedureInfo info = {};
 	ProcedureInfo info = {};
@@ -1177,7 +1180,7 @@ void check_procedure_later(Checker *c, AstFile *file, Token token, DeclInfo *dec
 	info.type  = type;
 	info.type  = type;
 	info.body  = body;
 	info.body  = body;
 	info.tags  = tags;
 	info.tags  = tags;
-	map_set(&c->procs, hash_decl_info(decl), info);
+	check_procedure_later(c, info);
 }
 }
 
 
 void push_procedure(Checker *c, Type *type) {
 void push_procedure(Checker *c, Type *type) {
@@ -2252,6 +2255,9 @@ void check_parsed_files(Checker *c) {
 	// NOTE(bill): Nested procedures bodies will be added to this "queue"
 	// NOTE(bill): Nested procedures bodies will be added to this "queue"
 	for_array(i, c->procs.entries) {
 	for_array(i, c->procs.entries) {
 		ProcedureInfo *pi = &c->procs.entries[i].value;
 		ProcedureInfo *pi = &c->procs.entries[i].value;
+		if (pi->type == NULL) {
+			continue;
+		}
 		CheckerContext prev_context = c->context;
 		CheckerContext prev_context = c->context;
 		defer (c->context = prev_context);
 		defer (c->context = prev_context);
 
 

+ 30 - 8
src/ir.cpp

@@ -312,6 +312,7 @@ enum irValueKind {
 	irValue_Constant,
 	irValue_Constant,
 	irValue_ConstantSlice,
 	irValue_ConstantSlice,
 	irValue_Nil,
 	irValue_Nil,
+	irValue_Undef,
 	irValue_TypeName,
 	irValue_TypeName,
 	irValue_Global,
 	irValue_Global,
 	irValue_Param,
 	irValue_Param,
@@ -338,6 +339,10 @@ struct irValueNil {
 	Type *type;
 	Type *type;
 };
 };
 
 
+struct irValueUndef {
+	Type *type;
+};
+
 struct irValueTypeName {
 struct irValueTypeName {
 	Type * type;
 	Type * type;
 	String name;
 	String name;
@@ -380,6 +385,7 @@ struct irValue {
 		irValueConstant      Constant;
 		irValueConstant      Constant;
 		irValueConstantSlice ConstantSlice;
 		irValueConstantSlice ConstantSlice;
 		irValueNil           Nil;
 		irValueNil           Nil;
+		irValueUndef         Undef;
 		irValueTypeName      TypeName;
 		irValueTypeName      TypeName;
 		irValueGlobal        Global;
 		irValueGlobal        Global;
 		irValueParam         Param;
 		irValueParam         Param;
@@ -630,6 +636,8 @@ Type *ir_type(irValue *value) {
 		return value->ConstantSlice.type;
 		return value->ConstantSlice.type;
 	case irValue_Nil:
 	case irValue_Nil:
 		return value->Nil.type;
 		return value->Nil.type;
+	case irValue_Undef:
+		return value->Undef.type;
 	case irValue_TypeName:
 	case irValue_TypeName:
 		return value->TypeName.type;
 		return value->TypeName.type;
 	case irValue_Global:
 	case irValue_Global:
@@ -806,6 +814,13 @@ irValue *ir_value_nil(gbAllocator a, Type *type) {
 	return v;
 	return v;
 }
 }
 
 
+irValue *ir_value_undef(gbAllocator a, Type *type) {
+	irValue *v = ir_alloc_value(a, irValue_Undef);
+	v->Undef.type = type;
+	return v;
+}
+
+
 String ir_get_global_name(irModule *m, irValue *v) {
 String ir_get_global_name(irModule *m, irValue *v) {
 	if (v->kind != irValue_Global) {
 	if (v->kind != irValue_Global) {
 		return str_lit("");
 		return str_lit("");
@@ -1261,7 +1276,7 @@ irValue *ir_add_global_string_array(irModule *m, String string) {
 
 
 
 
 
 
-irValue *ir_add_local(irProcedure *proc, Entity *e, AstNode *expr) {
+irValue *ir_add_local(irProcedure *proc, Entity *e, AstNode *expr, bool zero_initialized) {
 	irBlock *b = proc->decl_block; // all variables must be in the first block
 	irBlock *b = proc->decl_block; // all variables must be in the first block
 	irValue *instr = ir_instr_local(proc, e, true);
 	irValue *instr = ir_instr_local(proc, e, true);
 	instr->Instr.parent = b;
 	instr->Instr.parent = b;
@@ -1269,9 +1284,9 @@ irValue *ir_add_local(irProcedure *proc, Entity *e, AstNode *expr) {
 	array_add(&b->locals, instr);
 	array_add(&b->locals, instr);
 	proc->local_count++;
 	proc->local_count++;
 
 
-	// if (zero_initialized) {
+	if (zero_initialized) {
 		ir_emit_zero_init(proc, instr);
 		ir_emit_zero_init(proc, instr);
-	// }
+	}
 
 
 	if (expr != NULL && proc->entity != NULL) {
 	if (expr != NULL && proc->entity != NULL) {
 		irDebugInfo *di = *map_get(&proc->module->debug_info, hash_entity(proc->entity));
 		irDebugInfo *di = *map_get(&proc->module->debug_info, hash_entity(proc->entity));
@@ -1302,7 +1317,7 @@ irValue *ir_add_local_for_identifier(irProcedure *proc, AstNode *name, bool zero
 				return *prev_value;
 				return *prev_value;
 			}
 			}
 		}
 		}
-		return ir_add_local(proc, e, name);
+		return ir_add_local(proc, e, name, zero_initialized);
 	}
 	}
 	return NULL;
 	return NULL;
 }
 }
@@ -1318,7 +1333,7 @@ irValue *ir_add_local_generated(irProcedure *proc, Type *type) {
 	                                 scope,
 	                                 scope,
 	                                 empty_token,
 	                                 empty_token,
 	                                 type, false);
 	                                 type, false);
-	return ir_add_local(proc, e, NULL);
+	return ir_add_local(proc, e, NULL, true);
 }
 }
 
 
 
 
@@ -1351,7 +1366,7 @@ irValue *ir_add_param(irProcedure *proc, Entity *e, AstNode *expr, Type *abi_typ
 
 
 	switch (p->kind) {
 	switch (p->kind) {
 	case irParamPass_Value: {
 	case irParamPass_Value: {
-		irValue *l = ir_add_local(proc, e, expr);
+		irValue *l = ir_add_local(proc, e, expr, false);
 		ir_emit_store(proc, l, v);
 		ir_emit_store(proc, l, v);
 		return v;
 		return v;
 	}
 	}
@@ -1360,7 +1375,7 @@ irValue *ir_add_param(irProcedure *proc, Entity *e, AstNode *expr, Type *abi_typ
 		return ir_emit_load(proc, v);
 		return ir_emit_load(proc, v);
 	}
 	}
 	case irParamPass_Integer: {
 	case irParamPass_Integer: {
-		irValue *l = ir_add_local(proc, e, expr);
+		irValue *l = ir_add_local(proc, e, expr, false);
 		irValue *iptr = ir_emit_conv(proc, l, make_type_pointer(proc->module->allocator, p->type));
 		irValue *iptr = ir_emit_conv(proc, l, make_type_pointer(proc->module->allocator, p->type));
 		ir_emit_store(proc, iptr, v);
 		ir_emit_store(proc, iptr, v);
 		return ir_emit_load(proc, l);
 		return ir_emit_load(proc, l);
@@ -2771,6 +2786,9 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) {
 	if (is_type_untyped_nil(src)) {
 	if (is_type_untyped_nil(src)) {
 		return ir_value_nil(proc->module->allocator, t);
 		return ir_value_nil(proc->module->allocator, t);
 	}
 	}
+	if (is_type_untyped_undef(src)) {
+		return ir_value_undef(proc->module->allocator, t);
+	}
 
 
 	if (value->kind == irValue_Constant) {
 	if (value->kind == irValue_Constant) {
 		if (is_type_any(dst)) {
 		if (is_type_any(dst)) {
@@ -4403,6 +4421,10 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 		return ir_addr_load(proc, ir_build_addr(proc, expr));
 		return ir_addr_load(proc, ir_build_addr(proc, expr));
 	case_end;
 	case_end;
 
 
+	case_ast_node(u, Undef, expr);
+		return ir_value_undef(proc->module->allocator, tv.type);
+	case_end;
+
 	case_ast_node(i, Ident, expr);
 	case_ast_node(i, Ident, expr);
 		Entity *e = entity_of_ident(proc->module->info, expr);
 		Entity *e = entity_of_ident(proc->module->info, expr);
 		if (e->kind == Entity_Builtin) {
 		if (e->kind == Entity_Builtin) {
@@ -5836,7 +5858,7 @@ void ir_build_range_interval(irProcedure *proc, AstNodeBinaryExpr *node, Type *v
 void ir_store_type_case_implicit(irProcedure *proc, AstNode *clause, irValue *value) {
 void ir_store_type_case_implicit(irProcedure *proc, AstNode *clause, irValue *value) {
 	Entity *e = implicit_entity_of_node(proc->module->info, clause);
 	Entity *e = implicit_entity_of_node(proc->module->info, clause);
 	GB_ASSERT(e != NULL);
 	GB_ASSERT(e != NULL);
-	irValue *x = ir_add_local(proc, e, NULL);
+	irValue *x = ir_add_local(proc, e, NULL, false);
 	ir_emit_store(proc, x, value);
 	ir_emit_store(proc, x, value);
 }
 }
 
 

+ 4 - 0
src/ir_print.cpp

@@ -730,6 +730,10 @@ void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hin
 		ir_fprintf(f, "zeroinitializer");
 		ir_fprintf(f, "zeroinitializer");
 		break;
 		break;
 
 
+	case irValue_Undef:
+		ir_fprintf(f, "undef");
+		break;
+
 	case irValue_TypeName:
 	case irValue_TypeName:
 		ir_print_encoded_local(f, value->TypeName.name);
 		ir_print_encoded_local(f, value->TypeName.name);
 		break;
 		break;

+ 11 - 0
src/parser.cpp

@@ -140,6 +140,7 @@ Array<AstNode *> make_ast_node_array(AstFile *f, isize init_capacity = 8) {
 #define AST_NODE_KINDS \
 #define AST_NODE_KINDS \
 	AST_NODE_KIND(Ident,          "identifier",      Token) \
 	AST_NODE_KIND(Ident,          "identifier",      Token) \
 	AST_NODE_KIND(Implicit,       "implicit",        Token) \
 	AST_NODE_KIND(Implicit,       "implicit",        Token) \
+	AST_NODE_KIND(Undef,          "undef",           Token) \
 	AST_NODE_KIND(BasicLit,       "basic literal",   Token) \
 	AST_NODE_KIND(BasicLit,       "basic literal",   Token) \
 	AST_NODE_KIND(BasicDirective, "basic directive", struct { \
 	AST_NODE_KIND(BasicDirective, "basic directive", struct { \
 		Token token; \
 		Token token; \
@@ -516,6 +517,7 @@ Token ast_node_token(AstNode *node) {
 	switch (node->kind) {
 	switch (node->kind) {
 	case AstNode_Ident:          return node->Ident;
 	case AstNode_Ident:          return node->Ident;
 	case AstNode_Implicit:       return node->Implicit;
 	case AstNode_Implicit:       return node->Implicit;
+	case AstNode_Undef:          return node->Undef;
 	case AstNode_BasicLit:       return node->BasicLit;
 	case AstNode_BasicLit:       return node->BasicLit;
 	case AstNode_BasicDirective: return node->BasicDirective.token;
 	case AstNode_BasicDirective: return node->BasicDirective.token;
 	case AstNode_ProcLit:        return ast_node_token(node->ProcLit.type);
 	case AstNode_ProcLit:        return ast_node_token(node->ProcLit.type);
@@ -635,6 +637,7 @@ AstNode *clone_ast_node(gbAllocator a, AstNode *node) {
 	case AstNode_Invalid:        break;
 	case AstNode_Invalid:        break;
 	case AstNode_Ident:          break;
 	case AstNode_Ident:          break;
 	case AstNode_Implicit:       break;
 	case AstNode_Implicit:       break;
+	case AstNode_Undef:          break;
 	case AstNode_BasicLit:       break;
 	case AstNode_BasicLit:       break;
 	case AstNode_BasicDirective: break;
 	case AstNode_BasicDirective: break;
 	case AstNode_Ellipsis:
 	case AstNode_Ellipsis:
@@ -1073,6 +1076,11 @@ AstNode *ast_implicit(AstFile *f, Token token) {
 	result->Implicit = token;
 	result->Implicit = token;
 	return result;
 	return result;
 }
 }
+AstNode *ast_undef(AstFile *f, Token token) {
+	AstNode *result = make_ast_node(f, AstNode_Undef);
+	result->Undef = token;
+	return result;
+}
 
 
 
 
 AstNode *ast_basic_lit(AstFile *f, Token basic_lit) {
 AstNode *ast_basic_lit(AstFile *f, Token basic_lit) {
@@ -2200,6 +2208,9 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
 	case Token_Ident:
 	case Token_Ident:
 		return parse_ident(f);
 		return parse_ident(f);
 
 
+	case Token_Undef:
+		return ast_undef(f, expect_token(f, Token_Undef));
+
 	case Token_context:
 	case Token_context:
 		return ast_implicit(f, expect_token(f, Token_context));
 		return ast_implicit(f, expect_token(f, Token_context));
 
 

+ 19 - 1
src/tokenizer.cpp

@@ -56,6 +56,7 @@ TOKEN_KIND(Token__AssignOpEnd,   "_AssignOpEnd"), \
 	TOKEN_KIND(Token_ArrowLeft,  "<-"), \
 	TOKEN_KIND(Token_ArrowLeft,  "<-"), \
 	TOKEN_KIND(Token_Inc,        "++"), \
 	TOKEN_KIND(Token_Inc,        "++"), \
 	TOKEN_KIND(Token_Dec,        "--"), \
 	TOKEN_KIND(Token_Dec,        "--"), \
+	TOKEN_KIND(Token_Undef,      "---"), \
 \
 \
 TOKEN_KIND(Token__ComparisonBegin, "_ComparisonBegin"), \
 TOKEN_KIND(Token__ComparisonBegin, "_ComparisonBegin"), \
 	TOKEN_KIND(Token_CmpEq, "=="), \
 	TOKEN_KIND(Token_CmpEq, "=="), \
@@ -916,7 +917,24 @@ Token tokenizer_get_token(Tokenizer *t) {
 		case '~': token.kind = token_kind_variant2(t, Token_Xor, Token_XorEq);                                        break;
 		case '~': token.kind = token_kind_variant2(t, Token_Xor, Token_XorEq);                                        break;
 		case '!': token.kind = token_kind_variant2(t, Token_Not, Token_NotEq);                                        break;
 		case '!': token.kind = token_kind_variant2(t, Token_Not, Token_NotEq);                                        break;
 		case '+': token.kind = token_kind_variant3(t, Token_Add, Token_AddEq, '+', Token_Inc);                        break;
 		case '+': token.kind = token_kind_variant3(t, Token_Add, Token_AddEq, '+', Token_Inc);                        break;
-		case '-': token.kind = token_kind_variant4(t, Token_Sub, Token_SubEq, '-', Token_Dec, '>', Token_ArrowRight); break;
+		case '-':
+			token.kind = Token_Sub;
+			if (t->curr_rune == '=') {
+				advance_to_next_rune(t);
+				token.kind = Token_SubEq;
+			} else if (t->curr_rune == '-') {
+				advance_to_next_rune(t);
+				token.kind = Token_Dec;
+				if (t->curr_rune == '-') {
+					advance_to_next_rune(t);
+					token.kind = Token_Undef;
+				}
+			} else if (t->curr_rune == '>') {
+				advance_to_next_rune(t);
+				token.kind = Token_ArrowRight;
+			}
+			break;
+
 		case '/': {
 		case '/': {
 			if (t->curr_rune == '/') {
 			if (t->curr_rune == '/') {
 				while (t->curr_rune != '\n' && t->curr_rune != GB_RUNE_EOF) {
 				while (t->curr_rune != '\n' && t->curr_rune != GB_RUNE_EOF) {

+ 12 - 0
src/types.cpp

@@ -38,6 +38,7 @@ enum BasicKind {
 	Basic_UntypedString,
 	Basic_UntypedString,
 	Basic_UntypedRune,
 	Basic_UntypedRune,
 	Basic_UntypedNil,
 	Basic_UntypedNil,
+	Basic_UntypedUndef,
 
 
 	Basic_COUNT,
 	Basic_COUNT,
 
 
@@ -267,6 +268,7 @@ gb_global Type basic_types[] = {
 	{Type_Basic, {Basic_UntypedString,     BasicFlag_String     | BasicFlag_Untyped,   0, STR_LIT("untyped string")}},
 	{Type_Basic, {Basic_UntypedString,     BasicFlag_String     | BasicFlag_Untyped,   0, STR_LIT("untyped string")}},
 	{Type_Basic, {Basic_UntypedRune,       BasicFlag_Integer    | BasicFlag_Untyped,   0, STR_LIT("untyped rune")}},
 	{Type_Basic, {Basic_UntypedRune,       BasicFlag_Integer    | BasicFlag_Untyped,   0, STR_LIT("untyped rune")}},
 	{Type_Basic, {Basic_UntypedNil,        BasicFlag_Untyped,                          0, STR_LIT("untyped nil")}},
 	{Type_Basic, {Basic_UntypedNil,        BasicFlag_Untyped,                          0, STR_LIT("untyped nil")}},
+	{Type_Basic, {Basic_UntypedUndef,      BasicFlag_Untyped,                          0, STR_LIT("untyped undefined")}},
 };
 };
 
 
 // gb_global Type basic_type_aliases[] = {
 // gb_global Type basic_type_aliases[] = {
@@ -311,6 +313,7 @@ gb_global Type *t_untyped_complex    = &basic_types[Basic_UntypedComplex];
 gb_global Type *t_untyped_string     = &basic_types[Basic_UntypedString];
 gb_global Type *t_untyped_string     = &basic_types[Basic_UntypedString];
 gb_global Type *t_untyped_rune       = &basic_types[Basic_UntypedRune];
 gb_global Type *t_untyped_rune       = &basic_types[Basic_UntypedRune];
 gb_global Type *t_untyped_nil        = &basic_types[Basic_UntypedNil];
 gb_global Type *t_untyped_nil        = &basic_types[Basic_UntypedNil];
+gb_global Type *t_untyped_undef      = &basic_types[Basic_UntypedUndef];
 
 
 
 
 gb_global Type *t_u8_ptr       = NULL;
 gb_global Type *t_u8_ptr       = NULL;
@@ -905,6 +908,10 @@ bool is_type_untyped_nil(Type *t) {
 	t = base_type(t);
 	t = base_type(t);
 	return (t->kind == Type_Basic && t->Basic.kind == Basic_UntypedNil);
 	return (t->kind == Type_Basic && t->Basic.kind == Basic_UntypedNil);
 }
 }
+bool is_type_untyped_undef(Type *t) {
+	t = base_type(t);
+	return (t->kind == Type_Basic && t->Basic.kind == Basic_UntypedUndef);
+}
 
 
 
 
 
 
@@ -1001,6 +1008,11 @@ bool is_type_generic(Type *t) {
 }
 }
 
 
 
 
+bool type_has_undef(Type *t) {
+	t = base_type(t);
+	return true;
+}
+
 bool type_has_nil(Type *t) {
 bool type_has_nil(Type *t) {
 	t = base_type(t);
 	t = base_type(t);
 	switch (t->kind) {
 	switch (t->kind) {