make type_allowed_in_extern more robust

Previously if the type parameter was a pointer, it would assert that the
size of the type was resolved. It used to be that the size of pointers was
always resolved, however with lazy values, pointers gained the
possibility of not having their size resolved.

Now, type_allowed_in_extern triggers the resolution of whether a pointer
is zero bits, and returns a possible error if the resolution fails.

This fixes a compiler assertion when building the
[zootdeck project](https://github.com/donpdonp/zootdeck). I do not have
a test case reduction for the issue.
master
Andrew Kelley 2019-09-23 15:02:38 -04:00
parent 29b82d20a4
commit 53ae03ebe9
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
3 changed files with 106 additions and 54 deletions

View File

@ -1538,7 +1538,8 @@ static Error emit_error_unless_type_allowed_in_packed_union(CodeGen *g, ZigType
return emit_error_unless_type_allowed_in_packed_container(g, type_entry, source_node, "union");
}
bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry) {
Error type_allowed_in_extern(CodeGen *g, ZigType *type_entry, bool *result) {
Error err;
switch (type_entry->id) {
case ZigTypeIdInvalid:
zig_unreachable();
@ -1555,11 +1556,13 @@ bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry) {
case ZigTypeIdVoid:
case ZigTypeIdFnFrame:
case ZigTypeIdAnyFrame:
return false;
*result = false;
return ErrorNone;
case ZigTypeIdOpaque:
case ZigTypeIdUnreachable:
case ZigTypeIdBool:
return true;
*result = true;
return ErrorNone;
case ZigTypeIdInt:
switch (type_entry->data.integral.bit_count) {
case 8:
@ -1567,37 +1570,52 @@ bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry) {
case 32:
case 64:
case 128:
return true;
*result = true;
return ErrorNone;
default:
return false;
*result = false;
return ErrorNone;
}
case ZigTypeIdVector:
return type_allowed_in_extern(g, type_entry->data.vector.elem_type);
return type_allowed_in_extern(g, type_entry->data.vector.elem_type, result);
case ZigTypeIdFloat:
return true;
*result = true;
return ErrorNone;
case ZigTypeIdArray:
return type_allowed_in_extern(g, type_entry->data.array.child_type);
return type_allowed_in_extern(g, type_entry->data.array.child_type, result);
case ZigTypeIdFn:
return type_entry->data.fn.fn_type_id.cc == CallingConventionC ||
*result = type_entry->data.fn.fn_type_id.cc == CallingConventionC ||
type_entry->data.fn.fn_type_id.cc == CallingConventionStdcall;
return ErrorNone;
case ZigTypeIdPointer:
if (type_size(g, type_entry) == 0)
return false;
return true;
case ZigTypeIdStruct:
return type_entry->data.structure.layout == ContainerLayoutExtern || type_entry->data.structure.layout == ContainerLayoutPacked;
case ZigTypeIdOptional:
{
ZigType *child_type = type_entry->data.maybe.child_type;
if (child_type->id != ZigTypeIdPointer && child_type->id != ZigTypeIdFn) {
return false;
}
return type_allowed_in_extern(g, child_type);
if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown)))
return err;
if (!type_has_bits(type_entry)) {
*result = false;
return ErrorNone;
}
*result = true;
return ErrorNone;
case ZigTypeIdStruct:
*result = type_entry->data.structure.layout == ContainerLayoutExtern ||
type_entry->data.structure.layout == ContainerLayoutPacked;
return ErrorNone;
case ZigTypeIdOptional: {
ZigType *child_type = type_entry->data.maybe.child_type;
if (child_type->id != ZigTypeIdPointer && child_type->id != ZigTypeIdFn) {
*result = false;
return ErrorNone;
}
return type_allowed_in_extern(g, child_type, result);
}
case ZigTypeIdEnum:
return type_entry->data.enumeration.layout == ContainerLayoutExtern || type_entry->data.enumeration.layout == ContainerLayoutPacked;
*result = type_entry->data.enumeration.layout == ContainerLayoutExtern ||
type_entry->data.enumeration.layout == ContainerLayoutPacked;
return ErrorNone;
case ZigTypeIdUnion:
return type_entry->data.unionation.layout == ContainerLayoutExtern || type_entry->data.unionation.layout == ContainerLayoutPacked;
*result = type_entry->data.unionation.layout == ContainerLayoutExtern ||
type_entry->data.unionation.layout == ContainerLayoutPacked;
return ErrorNone;
}
zig_unreachable();
}
@ -1687,12 +1705,17 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc
}
}
if (!calling_convention_allows_zig_types(fn_type_id.cc) && !type_allowed_in_extern(g, type_entry)) {
add_node_error(g, param_node->data.param_decl.type,
buf_sprintf("parameter of type '%s' not allowed in function with calling convention '%s'",
buf_ptr(&type_entry->name),
calling_convention_name(fn_type_id.cc)));
return g->builtin_types.entry_invalid;
if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
bool ok_type;
if ((err = type_allowed_in_extern(g, type_entry, &ok_type)))
return g->builtin_types.entry_invalid;
if (!ok_type) {
add_node_error(g, param_node->data.param_decl.type,
buf_sprintf("parameter of type '%s' not allowed in function with calling convention '%s'",
buf_ptr(&type_entry->name),
calling_convention_name(fn_type_id.cc)));
return g->builtin_types.entry_invalid;
}
}
switch (type_entry->id) {
@ -1809,7 +1832,10 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc
{
if ((err = type_resolve(g, fn_type_id.return_type, ResolveStatusSizeKnown)))
return g->builtin_types.entry_invalid;
if (!type_allowed_in_extern(g, fn_type_id.return_type)) {
bool ok_type;
if ((err = type_allowed_in_extern(g, fn_type_id.return_type, &ok_type)))
return g->builtin_types.entry_invalid;
if (!ok_type) {
add_node_error(g, fn_proto->return_type,
buf_sprintf("return type '%s' not allowed in function with calling convention '%s'",
buf_ptr(&fn_type_id.return_type->name),
@ -2128,14 +2154,19 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
return err;
}
if (struct_type->data.structure.layout == ContainerLayoutExtern &&
!type_allowed_in_extern(g, field_type))
{
add_node_error(g, field->decl_node,
buf_sprintf("extern structs cannot contain fields of type '%s'",
buf_ptr(&field_type->name)));
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
if (struct_type->data.structure.layout == ContainerLayoutExtern) {
bool ok_type;
if ((err = type_allowed_in_extern(g, field_type, &ok_type))) {
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
if (!ok_type) {
add_node_error(g, field->decl_node,
buf_sprintf("extern structs cannot contain fields of type '%s'",
buf_ptr(&field_type->name)));
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
}
}
@ -2368,19 +2399,22 @@ static Error resolve_union_type(CodeGen *g, ZigType *union_type) {
return ErrorNone;
}
static bool type_is_valid_extern_enum_tag(CodeGen *g, ZigType *ty) {
static Error type_is_valid_extern_enum_tag(CodeGen *g, ZigType *ty, bool *result) {
// Only integer types are allowed by the C ABI
if(ty->id != ZigTypeIdInt)
return false;
if(ty->id != ZigTypeIdInt) {
*result = false;
return ErrorNone;
}
// According to the ANSI C standard the enumeration type should be either a
// signed char, a signed integer or an unsigned one. But GCC/Clang allow
// other integral types as a compiler extension so let's accomodate them
// aswell.
return type_allowed_in_extern(g, ty);
return type_allowed_in_extern(g, ty, result);
}
static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
Error err;
assert(enum_type->id == ZigTypeIdEnum);
if (enum_type->data.enumeration.resolve_status == ResolveStatusInvalid)
@ -2443,15 +2477,23 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
add_node_error(g, decl_node->data.container_decl.init_arg_expr,
buf_sprintf("expected integer, found '%s'", buf_ptr(&wanted_tag_int_type->name)));
} else if (enum_type->data.enumeration.layout == ContainerLayoutExtern &&
!type_is_valid_extern_enum_tag(g, wanted_tag_int_type)) {
enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
ErrorMsg *msg = add_node_error(g, decl_node->data.container_decl.init_arg_expr,
buf_sprintf("'%s' is not a valid tag type for an extern enum",
buf_ptr(&wanted_tag_int_type->name)));
add_error_note(g, msg, decl_node->data.container_decl.init_arg_expr,
buf_sprintf("any integral type of size 8, 16, 32, 64 or 128 bit is valid"));
} else {
if (enum_type->data.enumeration.layout == ContainerLayoutExtern) {
bool ok_type;
if ((err = type_is_valid_extern_enum_tag(g, wanted_tag_int_type, &ok_type))) {
enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
return err;
}
if (!ok_type) {
enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
ErrorMsg *msg = add_node_error(g, decl_node->data.container_decl.init_arg_expr,
buf_sprintf("'%s' is not a valid tag type for an extern enum",
buf_ptr(&wanted_tag_int_type->name)));
add_error_note(g, msg, decl_node->data.container_decl.init_arg_expr,
buf_sprintf("any integral type of size 8, 16, 32, 64 or 128 bit is valid"));
return ErrorNone;
}
}
tag_int_type = wanted_tag_int_type;
}
}

View File

@ -42,7 +42,7 @@ ZigType *get_any_frame_type(CodeGen *g, ZigType *result_type);
bool handle_is_ptr(ZigType *type_entry);
bool type_has_bits(ZigType *type_entry);
bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry);
Error type_allowed_in_extern(CodeGen *g, ZigType *type_entry, bool *result);
bool ptr_allows_addr_zero(ZigType *ptr_type);
bool type_is_nonnull_ptr(ZigType *type);

View File

@ -14837,6 +14837,8 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira,
}
static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructionExport *instruction) {
Error err;
IrInstruction *name = instruction->name->child;
Buf *symbol_name = ir_resolve_str(ira, name);
if (symbol_name == nullptr) {
@ -14933,8 +14935,12 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio
want_var_export = true;
}
break;
case ZigTypeIdArray:
if (!type_allowed_in_extern(ira->codegen, target->value.type->data.array.child_type)) {
case ZigTypeIdArray: {
bool ok_type;
if ((err = type_allowed_in_extern(ira->codegen, target->value.type->data.array.child_type, &ok_type)))
return ira->codegen->invalid_instruction;
if (!ok_type) {
ir_add_error(ira, target,
buf_sprintf("array element type '%s' not extern-compatible",
buf_ptr(&target->value.type->data.array.child_type->name)));
@ -14942,6 +14948,7 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio
want_var_export = true;
}
break;
}
case ZigTypeIdMetaType: {
ZigType *type_value = target->value.data.x_type;
switch (type_value->id) {
@ -26720,7 +26727,10 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) {
buf_create_from_str("unknown-length pointer to opaque"));
return ErrorSemanticAnalyzeFail;
} else if (lazy_ptr_type->ptr_len == PtrLenC) {
if (!type_allowed_in_extern(ira->codegen, elem_type)) {
bool ok_type;
if ((err = type_allowed_in_extern(ira->codegen, elem_type, &ok_type)))
return err;
if (!ok_type) {
ir_add_error(ira, lazy_ptr_type->elem_type,
buf_sprintf("C pointers cannot point to non-C-ABI-compatible type '%s'",
buf_ptr(&elem_type->name)));