Browse Source

C-style `c_vararg`s (Not heavily tested)

Ginger Bill 8 years ago
parent
commit
c3b510c2d9
7 changed files with 202 additions and 55 deletions
  1. 6 3
      src/check_decl.cpp
  2. 39 1
      src/check_expr.cpp
  3. 3 0
      src/entity.cpp
  4. 33 4
      src/ir.cpp
  5. 95 43
      src/ir_print.cpp
  6. 18 3
      src/parser.cpp
  7. 8 1
      src/types.cpp

+ 6 - 3
src/check_decl.cpp

@@ -293,13 +293,16 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
 	}
 	}
 
 
 	if (is_foreign && is_export) {
 	if (is_foreign && is_export) {
-		error_node(pd->type, "You cannot apply both `foreign` and `export` to a procedure");
+		error_node(pd->type, "A foreign procedure cannot have an `export` tag");
 	}
 	}
 
 
 
 
 	if (pd->body != NULL) {
 	if (pd->body != NULL) {
 		if (is_foreign) {
 		if (is_foreign) {
-			error_node(pd->body, "A procedure tagged as `foreign` cannot have a body");
+			error_node(pd->body, "A foreign procedure cannot have a body");
+		}
+		if (proc_type->Proc.c_vararg) {
+			error_node(pd->body, "A procedure with a `#c_vararg` field cannot have a body");
 		}
 		}
 
 
 		d->scope = c->context.scope;
 		d->scope = c->context.scope;
@@ -360,7 +363,7 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
 			Type *other_type = base_type(f->type);
 			Type *other_type = base_type(f->type);
 			if (!are_signatures_similar_enough(this_type, other_type)) {
 			if (!are_signatures_similar_enough(this_type, other_type)) {
 				error_node(d->proc_decl,
 				error_node(d->proc_decl,
-						   "Redeclaration of #foreign procedure `%.*s` with different type signatures\n"
+						   "Redeclaration of foreign procedure `%.*s` with different type signatures\n"
 						   "\tat %.*s(%td:%td)",
 						   "\tat %.*s(%td:%td)",
 						   LIT(name), LIT(pos.file), pos.line, pos.column);
 						   LIT(name), LIT(pos.file), pos.line, pos.column);
 			}
 			}

+ 39 - 1
src/check_expr.cpp

@@ -1049,6 +1049,7 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
 	}
 	}
 
 
 	bool is_variadic = false;
 	bool is_variadic = false;
+	bool is_c_vararg = false;
 	Entity **variables = gb_alloc_array(c->allocator, Entity *, variable_count);
 	Entity **variables = gb_alloc_array(c->allocator, Entity *, variable_count);
 	isize variable_index = 0;
 	isize variable_index = 0;
 	for_array(i, params) {
 	for_array(i, params) {
@@ -1114,10 +1115,19 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
 
 
 		if (p->flags&FieldFlag_no_alias) {
 		if (p->flags&FieldFlag_no_alias) {
 			if (!is_type_pointer(type)) {
 			if (!is_type_pointer(type)) {
-				error_node(params[i], "`no_alias` can only be applied to fields of pointer type");
+				error_node(params[i], "`#no_alias` can only be applied to fields of pointer type");
 				p->flags &= ~FieldFlag_no_alias; // Remove the flag
 				p->flags &= ~FieldFlag_no_alias; // Remove the flag
 			}
 			}
 		}
 		}
+		if (p->flags&FieldFlag_c_vararg) {
+			if (p->type == NULL ||
+			    p->type->kind != AstNode_Ellipsis) {
+				error_node(params[i], "`#c_vararg` can only be applied to variadic type fields");
+				p->flags &= ~FieldFlag_c_vararg; // Remove the flag
+			} else {
+				is_c_vararg = true;
+			}
+		}
 
 
 		for_array(j, p->names) {
 		for_array(j, p->names) {
 			AstNode *name = p->names[j];
 			AstNode *name = p->names[j];
@@ -1145,6 +1155,9 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
 		Entity *end = variables[variable_count-1];
 		Entity *end = variables[variable_count-1];
 		end->type = make_type_slice(c->allocator, end->type);
 		end->type = make_type_slice(c->allocator, end->type);
 		end->flags |= EntityFlag_Ellipsis;
 		end->flags |= EntityFlag_Ellipsis;
+		if (is_c_vararg) {
+			end->flags |= EntityFlag_CVarArg;
+		}
 	}
 	}
 
 
 	Type *tuple = make_type_tuple(c->allocator);
 	Type *tuple = make_type_tuple(c->allocator);
@@ -1409,6 +1422,19 @@ void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) {
 	type->Proc.variadic           = variadic;
 	type->Proc.variadic           = variadic;
 	type->Proc.calling_convention = pt->calling_convention;
 	type->Proc.calling_convention = pt->calling_convention;
 
 
+	if (param_count > 0) {
+		Entity *end = params->Tuple.variables[param_count-1];
+		if (end->flags&EntityFlag_CVarArg) {
+			if (pt->calling_convention == ProcCC_Odin) {
+				error(end->token, "Odin calling convention does not support #c_vararg");
+			} else if (pt->calling_convention == ProcCC_Fast) {
+				error(end->token, "Fast calling convention does not support #c_vararg");
+			} else {
+				type->Proc.c_vararg = true;
+			}
+		}
+	}
+
 
 
 	type->Proc.abi_compat_params = gb_alloc_array(c->allocator, Type *, param_count);
 	type->Proc.abi_compat_params = gb_alloc_array(c->allocator, Type *, param_count);
 	for (isize i = 0; i < param_count; i++) {
 	for (isize i = 0; i < param_count; i++) {
@@ -4865,6 +4891,15 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 		if (score_) *score_ = score;
 		if (score_) *score_ = score;
 		return CallArgumentError_NonVariadicExpand;
 		return CallArgumentError_NonVariadicExpand;
 	}
 	}
+	if (vari_expand && proc_type->Proc.c_vararg) {
+		if (show_error) {
+			error(ce->ellipsis,
+			      "Cannot use `..` in call to a `#c_vararg` variadic procedure: `%.*s`",
+			      LIT(ce->proc->Ident.string));
+		}
+		if (score_) *score_ = score;
+		return CallArgumentError_NonVariadicExpand;
+	}
 
 
 	if (operands.count == 0 && param_count_excluding_defaults == 0) {
 	if (operands.count == 0 && param_count_excluding_defaults == 0) {
 		if (score_) *score_ = score;
 		if (score_) *score_ = score;
@@ -6566,6 +6601,9 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
 		if (f->flags&FieldFlag_no_alias) {
 		if (f->flags&FieldFlag_no_alias) {
 			str = gb_string_appendc(str, "#no_alias ");
 			str = gb_string_appendc(str, "#no_alias ");
 		}
 		}
+		if (f->flags&FieldFlag_c_vararg) {
+			str = gb_string_appendc(str, "#c_vararg ");
+		}
 
 
 		for_array(i, f->names) {
 		for_array(i, f->names) {
 			AstNode *name = f->names[i];
 			AstNode *name = f->names[i];

+ 3 - 0
src/entity.cpp

@@ -43,6 +43,9 @@ enum EntityFlag {
 	EntityFlag_Value         = 1<<9,
 	EntityFlag_Value         = 1<<9,
 	EntityFlag_Sret          = 1<<10,
 	EntityFlag_Sret          = 1<<10,
 	EntityFlag_BitFieldValue = 1<<11,
 	EntityFlag_BitFieldValue = 1<<11,
+
+	EntityFlag_CVarArg       = 1<<20,
+
 };
 };
 
 
 // Zero value means the overloading process is not yet done
 // Zero value means the overloading process is not yet done

+ 33 - 4
src/ir.cpp

@@ -1477,7 +1477,11 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, irValue **args, isize arg_
 	Type *results = pt->Proc.results;
 	Type *results = pt->Proc.results;
 
 
 	isize param_count = pt->Proc.param_count;
 	isize param_count = pt->Proc.param_count;
-	GB_ASSERT(param_count == arg_count);
+	if (pt->Proc.c_vararg) {
+		GB_ASSERT(param_count-1 <= arg_count);
+	} else {
+		GB_ASSERT(param_count == arg_count);
+	}
 	for (isize i = 0; i < param_count; i++) {
 	for (isize i = 0; i < param_count; i++) {
 		Type *original_type = pt->Proc.params->Tuple.variables[i]->type;
 		Type *original_type = pt->Proc.params->Tuple.variables[i]->type;
 		Type *new_type = pt->Proc.abi_compat_params[i];
 		Type *new_type = pt->Proc.abi_compat_params[i];
@@ -4649,6 +4653,7 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 		irValue **args = gb_alloc_array(proc->module->allocator, irValue *, gb_max(type->param_count, arg_count));
 		irValue **args = gb_alloc_array(proc->module->allocator, irValue *, gb_max(type->param_count, arg_count));
 		bool variadic = type->variadic;
 		bool variadic = type->variadic;
 		bool vari_expand = ce->ellipsis.pos.line != 0;
 		bool vari_expand = ce->ellipsis.pos.line != 0;
+		bool is_c_vararg = type->c_vararg;
 
 
 		for_array(i, ce->args) {
 		for_array(i, ce->args) {
 			irValue *a = ir_build_expr(proc, ce->args[i]);
 			irValue *a = ir_build_expr(proc, ce->args[i]);
@@ -4683,7 +4688,26 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 		}
 		}
 
 
 
 
-		if (variadic) {
+		if (is_c_vararg) {
+			GB_ASSERT(variadic);
+			GB_ASSERT(!vari_expand);
+			isize i = 0;
+			for (; i < type->param_count-1; i++) {
+				args[i] = ir_emit_conv(proc, args[i], pt->variables[i]->type);
+			}
+			Type *variadic_type = pt->variables[i]->type;
+			GB_ASSERT(is_type_slice(variadic_type));
+			variadic_type = base_type(variadic_type)->Slice.elem;
+			if (!is_type_any(variadic_type)) {
+				for (; i < arg_count; i++) {
+					args[i] = ir_emit_conv(proc, args[i], variadic_type);
+				}
+			} else {
+				for (; i < arg_count; i++) {
+					args[i] = ir_emit_conv(proc, args[i], default_type(ir_type(args[i])));
+				}
+			}
+		} else if (variadic) {
 			isize i = 0;
 			isize i = 0;
 			for (; i < type->param_count-1; i++) {
 			for (; i < type->param_count-1; i++) {
 				args[i] = ir_emit_conv(proc, args[i], pt->variables[i]->type);
 				args[i] = ir_emit_conv(proc, args[i], pt->variables[i]->type);
@@ -4702,7 +4726,12 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 			}
 			}
 		}
 		}
 
 
-		if (variadic && !vari_expand) {
+		i64 final_count = type->param_count;
+		if (is_c_vararg) {
+			final_count = arg_count;
+		}
+
+		if (variadic && !vari_expand && !is_c_vararg) {
 			ir_emit_comment(proc, str_lit("variadic call argument generation"));
 			ir_emit_comment(proc, str_lit("variadic call argument generation"));
 			gbAllocator allocator = proc->module->allocator;
 			gbAllocator allocator = proc->module->allocator;
 			Type *slice_type = pt->variables[type->param_count-1]->type;
 			Type *slice_type = pt->variables[type->param_count-1]->type;
@@ -4727,7 +4756,7 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 			args[arg_count-1] = ir_emit_load(proc, slice);
 			args[arg_count-1] = ir_emit_load(proc, slice);
 		}
 		}
 
 
-		return ir_emit_call(proc, value, args, type->param_count);
+		return ir_emit_call(proc, value, args, final_count);
 	case_end;
 	case_end;
 
 
 	case_ast_node(se, SliceExpr, expr);
 	case_ast_node(se, SliceExpr, expr);

+ 95 - 43
src/ir_print.cpp

@@ -161,10 +161,11 @@ void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) {
 			isize count = rt->Tuple.variable_count;
 			isize count = rt->Tuple.variable_count;
 			ir_fprintf(f, "{");
 			ir_fprintf(f, "{");
 			for (isize i = 0; i < count; i++) {
 			for (isize i = 0; i < count; i++) {
+				Entity *e = rt->Tuple.variables[i];
 				if (i > 0) {
 				if (i > 0) {
 					ir_fprintf(f, ", ");
 					ir_fprintf(f, ", ");
 				}
 				}
-				ir_print_type(f, m, rt->Tuple.variables[i]->type);
+				ir_print_type(f, m, e->type);
 			}
 			}
 			ir_fprintf(f, "}");
 			ir_fprintf(f, "}");
 		}
 		}
@@ -172,6 +173,35 @@ void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) {
 }
 }
 
 
 
 
+void ir_print_proc_type_without_pointer(irFileBuffer *f, irModule *m, Type *t) {
+	i64 word_bits = 8*build_context.word_size;
+	t = base_type(t);
+	GB_ASSERT(is_type_proc(t));
+
+	isize param_count = t->Proc.param_count;
+	isize result_count = t->Proc.result_count;
+	ir_print_proc_results(f, m, t);
+	ir_fprintf(f, " (");
+	if (t->Proc.return_by_pointer) {
+		ir_print_type(f, m, reduce_tuple_to_single_type(t->Proc.results));
+		ir_fprintf(f, "* sret noalias ");
+		if (param_count > 0) {
+			ir_fprintf(f, ", ");
+		}
+	}
+	for (isize i = 0; i < param_count; i++) {
+		if (i > 0) {
+			ir_fprintf(f, ", ");
+		}
+		if (i+1 == param_count && t->Proc.c_vararg) {
+			ir_fprintf(f, "...");
+		} else {
+			ir_print_type(f, m, t->Proc.abi_compat_params[i]);
+		}
+	}
+	ir_fprintf(f, ")");
+}
+
 void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
 void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
 	i64 word_bits = 8*build_context.word_size;
 	i64 word_bits = 8*build_context.word_size;
 	GB_ASSERT_NOT_NULL(t);
 	GB_ASSERT_NOT_NULL(t);
@@ -328,24 +358,8 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
 		}
 		}
 		return;
 		return;
 	case Type_Proc: {
 	case Type_Proc: {
-		isize param_count = t->Proc.param_count;
-		isize result_count = t->Proc.result_count;
-		ir_print_proc_results(f, m, t);
-		ir_fprintf(f, " (");
-		if (t->Proc.return_by_pointer) {
-			ir_print_type(f, m, reduce_tuple_to_single_type(t->Proc.results));
-			ir_fprintf(f, "* sret noalias ");
-			if (param_count > 0) {
-				ir_fprintf(f, ", ");
-			}
-		}
-		for (isize i = 0; i < param_count; i++) {
-			if (i > 0) {
-				ir_fprintf(f, ", ");
-			}
-			ir_print_type(f, m, t->Proc.abi_compat_params[i]);
-		}
-		ir_fprintf(f, ")*");
+		ir_print_proc_type_without_pointer(f, m, t);
+		ir_fprintf(f, "*");
 	} return;
 	} return;
 
 
 	case Type_Map: {
 	case Type_Map: {
@@ -1216,13 +1230,16 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 		irInstrCall *call = &instr->Call;
 		irInstrCall *call = &instr->Call;
 		Type *proc_type = base_type(ir_type(call->value));
 		Type *proc_type = base_type(ir_type(call->value));
 		GB_ASSERT(is_type_proc(proc_type));
 		GB_ASSERT(is_type_proc(proc_type));
+		bool is_c_vararg = proc_type->Proc.c_vararg;
 		Type *result_type = call->type;
 		Type *result_type = call->type;
 		if (result_type) {
 		if (result_type) {
 			ir_fprintf(f, "%%%d = ", value->index);
 			ir_fprintf(f, "%%%d = ", value->index);
 		}
 		}
 		ir_fprintf(f, "call ");
 		ir_fprintf(f, "call ");
 		ir_print_calling_convention(f, m, proc_type->Proc.calling_convention);
 		ir_print_calling_convention(f, m, proc_type->Proc.calling_convention);
-		if (result_type && !proc_type->Proc.return_by_pointer) {
+		if (is_c_vararg) {
+			ir_print_proc_type_without_pointer(f, m, proc_type);
+		} else if (result_type && !proc_type->Proc.return_by_pointer) {
 			ir_print_proc_results(f, m, proc_type);
 			ir_print_proc_results(f, m, proc_type);
 		} else {
 		} else {
 			ir_fprintf(f, "void");
 			ir_fprintf(f, "void");
@@ -1242,24 +1259,55 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 			}
 			}
 		}
 		}
 
 
+
 		if (call->arg_count > 0) {
 		if (call->arg_count > 0) {
 			Type *proc_type = base_type(ir_type(call->value));
 			Type *proc_type = base_type(ir_type(call->value));
 			GB_ASSERT(proc_type->kind == Type_Proc);
 			GB_ASSERT(proc_type->kind == Type_Proc);
 			TypeTuple *params = &proc_type->Proc.params->Tuple;
 			TypeTuple *params = &proc_type->Proc.params->Tuple;
-			for (isize i = 0; i < call->arg_count; i++) {
-				Entity *e = params->variables[i];
-				GB_ASSERT(e != NULL);
-				Type *t = proc_type->Proc.abi_compat_params[i];
-				if (i > 0) {
-					ir_fprintf(f, ", ");
+			if (proc_type->Proc.c_vararg) {
+				isize i = 0;
+				for (; i < params->variable_count-1; i++) {
+					Entity *e = params->variables[i];
+					GB_ASSERT(e != NULL);
+					Type *t = proc_type->Proc.abi_compat_params[i];
+					if (i > 0) {
+						ir_fprintf(f, ", ");
+					}
+					ir_print_type(f, m, t);
+					if (e->flags&EntityFlag_NoAlias) {
+						ir_fprintf(f, " noalias");
+					}
+					ir_fprintf(f, " ");
+					irValue *arg = call->args[i];
+					ir_print_value(f, m, arg, t);
 				}
 				}
-				ir_print_type(f, m, t);
-				if (e->flags&EntityFlag_NoAlias) {
-					ir_fprintf(f, " noalias");
+				for (; i < call->arg_count; i++) {
+					if (i > 0) {
+						ir_fprintf(f, ", ");
+					}
+
+					irValue *arg = call->args[i];
+					Type *t = ir_type(arg);
+					ir_print_type(f, m, t);
+					ir_fprintf(f, " ");
+					ir_print_value(f, m, arg, t);
+				}
+			} else {
+				for (isize i = 0; i < call->arg_count; i++) {
+					Entity *e = params->variables[i];
+					GB_ASSERT(e != NULL);
+					irValue *arg = call->args[i];
+					Type *t = proc_type->Proc.abi_compat_params[i];
+					if (i > 0) {
+						ir_fprintf(f, ", ");
+					}
+					ir_print_type(f, m, t);
+					if (e->flags&EntityFlag_NoAlias) {
+						ir_fprintf(f, " noalias");
+					}
+					ir_fprintf(f, " ");
+					ir_print_value(f, m, arg, t);
 				}
 				}
-				ir_fprintf(f, " ");
-				irValue *arg = call->args[i];
-				ir_print_value(f, m, arg, t);
 			}
 			}
 		}
 		}
 		ir_fprintf(f, ")\n");
 		ir_fprintf(f, ")\n");
@@ -1495,17 +1543,21 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
 			if (i > 0) {
 			if (i > 0) {
 				ir_fprintf(f, ", ");
 				ir_fprintf(f, ", ");
 			}
 			}
-			ir_print_type(f, m, abi_type);
-			if (e->flags&EntityFlag_NoAlias) {
-				ir_fprintf(f, " noalias");
-			}
-			if (proc->body != NULL) {
-				if (e->token.string != "" &&
-				    e->token.string != "_") {
-					ir_fprintf(f, " ");
-					ir_print_encoded_local(f, e->token.string);
-				} else {
-					ir_fprintf(f, " %%_.param_%td", i);
+			if (i+1 == params->variable_count && proc_type->c_vararg) {
+					ir_fprintf(f, " ...");
+			} else {
+				ir_print_type(f, m, abi_type);
+				if (e->flags&EntityFlag_NoAlias) {
+					ir_fprintf(f, " noalias");
+				}
+				if (proc->body != NULL) {
+					if (e->token.string != "" &&
+					    e->token.string != "_") {
+						ir_fprintf(f, " ");
+						ir_print_encoded_local(f, e->token.string);
+					} else {
+						ir_fprintf(f, " %%_.param_%td", i);
+					}
 				}
 				}
 			}
 			}
 		}
 		}

+ 18 - 3
src/parser.cpp

@@ -65,6 +65,7 @@ enum ProcTag {
 	ProcTag_bounds_check    = 1<<0,
 	ProcTag_bounds_check    = 1<<0,
 	ProcTag_no_bounds_check = 1<<1,
 	ProcTag_no_bounds_check = 1<<1,
 
 
+
 	ProcTag_require_results = 1<<4,
 	ProcTag_require_results = 1<<4,
 
 
 	ProcTag_foreign         = 1<<10,
 	ProcTag_foreign         = 1<<10,
@@ -72,6 +73,7 @@ enum ProcTag {
 	ProcTag_link_name       = 1<<12,
 	ProcTag_link_name       = 1<<12,
 	ProcTag_inline          = 1<<13,
 	ProcTag_inline          = 1<<13,
 	ProcTag_no_inline       = 1<<14,
 	ProcTag_no_inline       = 1<<14,
+
 	// ProcTag_dll_import      = 1<<15,
 	// ProcTag_dll_import      = 1<<15,
 	// ProcTag_dll_export      = 1<<16,
 	// ProcTag_dll_export      = 1<<16,
 };
 };
@@ -100,8 +102,9 @@ enum FieldFlag {
 	FieldFlag_ellipsis  = 1<<0,
 	FieldFlag_ellipsis  = 1<<0,
 	FieldFlag_using     = 1<<1,
 	FieldFlag_using     = 1<<1,
 	FieldFlag_no_alias  = 1<<2,
 	FieldFlag_no_alias  = 1<<2,
+	FieldFlag_c_vararg  = 1<<3,
 
 
-	FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias,
+	FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg,
 };
 };
 
 
 enum StmtAllowFlag {
 enum StmtAllowFlag {
@@ -2999,7 +3002,7 @@ AstNode *parse_proc_type(AstFile *f, Token proc_token, String *link_name_) {
 
 
 	parse_proc_tags(f, &tags, &link_name, &cc);
 	parse_proc_tags(f, &tags, &link_name, &cc);
 
 
-	if (link_name_)       *link_name_       = link_name;
+	if (link_name_) *link_name_ = link_name;
 
 
 	return ast_proc_type(f, proc_token, params, results, tags, cc);
 	return ast_proc_type(f, proc_token, params, results, tags, cc);
 }
 }
@@ -3030,6 +3033,7 @@ enum FieldPrefixKind {
 
 
 	FieldPrefix_Using,
 	FieldPrefix_Using,
 	FieldPrefix_NoAlias,
 	FieldPrefix_NoAlias,
+	FieldPrefix_CVarArg,
 };
 };
 
 
 FieldPrefixKind is_token_field_prefix(AstFile *f) {
 FieldPrefixKind is_token_field_prefix(AstFile *f) {
@@ -3047,6 +3051,9 @@ FieldPrefixKind is_token_field_prefix(AstFile *f) {
 			if (f->curr_token.string == "no_alias") {
 			if (f->curr_token.string == "no_alias") {
 				return FieldPrefix_NoAlias;
 				return FieldPrefix_NoAlias;
 			}
 			}
+			if (f->curr_token.string == "c_vararg") {
+				return FieldPrefix_CVarArg;
+			}
 			break;
 			break;
 		}
 		}
 	} break;
 	} break;
@@ -3058,6 +3065,7 @@ FieldPrefixKind is_token_field_prefix(AstFile *f) {
 u32 parse_field_prefixes(AstFile *f) {
 u32 parse_field_prefixes(AstFile *f) {
 	i32 using_count    = 0;
 	i32 using_count    = 0;
 	i32 no_alias_count = 0;
 	i32 no_alias_count = 0;
+	i32 c_vararg_count = 0;
 
 
 	for (;;) {
 	for (;;) {
 		FieldPrefixKind kind = is_token_field_prefix(f);
 		FieldPrefixKind kind = is_token_field_prefix(f);
@@ -3067,15 +3075,18 @@ u32 parse_field_prefixes(AstFile *f) {
 		switch (kind) {
 		switch (kind) {
 		case FieldPrefix_Using:     using_count    += 1; next_token(f); break;
 		case FieldPrefix_Using:     using_count    += 1; next_token(f); break;
 		case FieldPrefix_NoAlias:   no_alias_count += 1; next_token(f); break;
 		case FieldPrefix_NoAlias:   no_alias_count += 1; next_token(f); break;
+		case FieldPrefix_CVarArg:   c_vararg_count += 1; next_token(f); break;
 		}
 		}
 	}
 	}
 	if (using_count     > 1) syntax_error(f->curr_token, "Multiple `using`     in this field list");
 	if (using_count     > 1) syntax_error(f->curr_token, "Multiple `using`     in this field list");
 	if (no_alias_count  > 1) syntax_error(f->curr_token, "Multiple `#no_alias` in this field list");
 	if (no_alias_count  > 1) syntax_error(f->curr_token, "Multiple `#no_alias` in this field list");
+	if (c_vararg_count  > 1) syntax_error(f->curr_token, "Multiple `#c_vararg` in this field list");
 
 
 
 
 	u32 field_flags = 0;
 	u32 field_flags = 0;
 	if (using_count     > 0) field_flags |= FieldFlag_using;
 	if (using_count     > 0) field_flags |= FieldFlag_using;
 	if (no_alias_count  > 0) field_flags |= FieldFlag_no_alias;
 	if (no_alias_count  > 0) field_flags |= FieldFlag_no_alias;
+	if (c_vararg_count  > 0) field_flags |= FieldFlag_c_vararg;
 	return field_flags;
 	return field_flags;
 }
 }
 
 
@@ -3090,9 +3101,13 @@ u32 check_field_prefixes(AstFile *f, isize name_count, u32 allowed_flags, u32 se
 		set_flags &= ~FieldFlag_using;
 		set_flags &= ~FieldFlag_using;
 	}
 	}
 	if ((allowed_flags&FieldFlag_no_alias) == 0 && (set_flags&FieldFlag_no_alias)) {
 	if ((allowed_flags&FieldFlag_no_alias) == 0 && (set_flags&FieldFlag_no_alias)) {
-		syntax_error(f->curr_token, "`no_alias` is not allowed within this field list");
+		syntax_error(f->curr_token, "`#no_alias` is not allowed within this field list");
 		set_flags &= ~FieldFlag_no_alias;
 		set_flags &= ~FieldFlag_no_alias;
 	}
 	}
+	if ((allowed_flags&FieldFlag_c_vararg) == 0 && (set_flags&FieldFlag_c_vararg)) {
+		syntax_error(f->curr_token, "`#c_vararg` is not allowed within this field list");
+		set_flags &= ~FieldFlag_c_vararg;
+	}
 	return set_flags;
 	return set_flags;
 }
 }
 
 

+ 8 - 1
src/types.cpp

@@ -142,6 +142,7 @@ struct TypeRecord {
 		Type * abi_compat_result_type;                    \
 		Type * abi_compat_result_type;                    \
 		bool   variadic;                                  \
 		bool   variadic;                                  \
 		bool   require_results;                           \
 		bool   require_results;                           \
+		bool   c_vararg;                                  \
 		ProcCallingConvention calling_convention;         \
 		ProcCallingConvention calling_convention;         \
 	})                                                    \
 	})                                                    \
 	TYPE_KIND(Map, struct {                               \
 	TYPE_KIND(Map, struct {                               \
@@ -1070,7 +1071,9 @@ bool are_types_identical(Type *x, Type *y) {
 		if (y->kind == Type_Tuple) {
 		if (y->kind == Type_Tuple) {
 			if (x->Tuple.variable_count == y->Tuple.variable_count) {
 			if (x->Tuple.variable_count == y->Tuple.variable_count) {
 				for (isize i = 0; i < x->Tuple.variable_count; i++) {
 				for (isize i = 0; i < x->Tuple.variable_count; i++) {
-					if (!are_types_identical(x->Tuple.variables[i]->type, y->Tuple.variables[i]->type)) {
+					Entity *xe = x->Tuple.variables[i];
+					Entity *ye = y->Tuple.variables[i];
+					if (!are_types_identical(xe->type, ye->type)) {
 						return false;
 						return false;
 					}
 					}
 				}
 				}
@@ -1082,6 +1085,7 @@ bool are_types_identical(Type *x, Type *y) {
 	case Type_Proc:
 	case Type_Proc:
 		if (y->kind == Type_Proc) {
 		if (y->kind == Type_Proc) {
 			return x->Proc.calling_convention == y->Proc.calling_convention &&
 			return x->Proc.calling_convention == y->Proc.calling_convention &&
+			       x->Proc.c_vararg == y->Proc.c_vararg &&
 			       x->Proc.variadic == y->Proc.variadic &&
 			       x->Proc.variadic == y->Proc.variadic &&
 			       are_types_identical(x->Proc.params, y->Proc.params) &&
 			       are_types_identical(x->Proc.params, y->Proc.params) &&
 			       are_types_identical(x->Proc.results, y->Proc.results);
 			       are_types_identical(x->Proc.results, y->Proc.results);
@@ -2292,6 +2296,9 @@ gbString write_type_to_string(gbString str, Type *type) {
 					if (i > 0) {
 					if (i > 0) {
 						str = gb_string_appendc(str, ", ");
 						str = gb_string_appendc(str, ", ");
 					}
 					}
+					if (var->flags&EntityFlag_CVarArg) {
+						str = gb_string_appendc(str, "#c_vararg ");
+					}
 					if (var->flags&EntityFlag_Ellipsis) {
 					if (var->flags&EntityFlag_Ellipsis) {
 						Type *slice = base_type(var->type);
 						Type *slice = base_type(var->type);
 						str = gb_string_appendc(str, "..");
 						str = gb_string_appendc(str, "..");