Skip to content

Commit

Permalink
Type: add helpers for managing typeof and attributed types
Browse files Browse the repository at this point in the history
  • Loading branch information
ehaas committed Aug 21, 2024
1 parent fd6b8a3 commit 9c9a38f
Showing 1 changed file with 75 additions and 88 deletions.
163 changes: 75 additions & 88 deletions src/aro/Type.zig
Original file line number Diff line number Diff line change
Expand Up @@ -442,23 +442,47 @@ pub fn withAttributes(self: Type, allocator: std.mem.Allocator, attributes: []co
return Type{ .specifier = .attributed, .data = .{ .attributed = attributed_type }, .decayed = self.decayed };
}

pub fn isCallable(ty: Type) ?Type {
fn makeTypeFn(comptime predicate: anytype) @TypeOf(predicate) {
const S = struct {
fn func(ty: Type) @typeInfo(@TypeOf(predicate)).Fn.return_type.? {
return switch (ty.specifier) {
.typeof_type => @call(.auto, func, .{ty.data.sub_type.*}),
.typeof_expr => @call(.auto, func, .{ty.data.expr.ty}),
.attributed => @call(.auto, func, .{ty.data.attributed.base}),
else => @call(.auto, predicate, .{ty}),
};
}
};
return S.func;
}

fn makeTypeCompFn(comptime predicate: anytype) @TypeOf(predicate) {
const S = struct {
fn func(ty: Type, comp: *const Compilation) @typeInfo(@TypeOf(predicate)).Fn.return_type.? {
return switch (ty.specifier) {
.typeof_type => @call(.auto, func, .{ ty.data.sub_type.*, comp }),
.typeof_expr => @call(.auto, func, .{ ty.data.expr.ty, comp }),
.attributed => @call(.auto, func, .{ ty.data.attributed.base, comp }),
else => @call(.auto, predicate, .{ ty, comp }),
};
}
};
return S.func;
}

pub const isCallable = makeTypeFn(isCallableImpl);
fn isCallableImpl(ty: Type) ?Type {
return switch (ty.specifier) {
.func, .var_args_func, .old_style_func => ty,
.pointer => if (ty.data.sub_type.isFunc()) ty.data.sub_type.* else null,
.typeof_type => ty.data.sub_type.isCallable(),
.typeof_expr => ty.data.expr.ty.isCallable(),
.attributed => ty.data.attributed.base.isCallable(),
else => null,
};
}

pub fn isFunc(ty: Type) bool {
pub const isFunc = makeTypeFn(isFuncImpl);
fn isFuncImpl(ty: Type) bool {
return switch (ty.specifier) {
.func, .var_args_func, .old_style_func => true,
.typeof_type => ty.data.sub_type.isFunc(),
.typeof_expr => ty.data.expr.ty.isFunc(),
.attributed => ty.data.attributed.base.isFunc(),
else => false,
};
}
Expand Down Expand Up @@ -491,17 +515,15 @@ pub fn setIncompleteArrayLen(ty: *Type, len: u64) void {
}

/// Whether the type is promoted if used as a variadic argument or as an argument to a function with no prototype
fn undergoesDefaultArgPromotion(ty: Type, comp: *const Compilation) bool {
const undergoesDefaultArgPromotion = makeTypeCompFn(undergoesDefaultArgPromotionImpl);
fn undergoesDefaultArgPromotionImpl(ty: Type, comp: *const Compilation) bool {
return switch (ty.specifier) {
.bool => true,
.char, .uchar, .schar => true,
.short, .ushort => true,
.@"enum" => if (comp.langopts.emulate == .clang) ty.data.@"enum".isIncomplete() else false,
.float => true,

.typeof_type => ty.data.sub_type.undergoesDefaultArgPromotion(comp),
.typeof_expr => ty.data.expr.ty.undergoesDefaultArgPromotion(comp),
.attributed => ty.data.attributed.base.undergoesDefaultArgPromotion(comp),
else => false,
};
}
Expand Down Expand Up @@ -536,7 +558,8 @@ pub fn isPtr(ty: Type) bool {
};
}

pub fn isInt(ty: Type) bool {
pub const isInt = makeTypeFn(isIntImpl);
fn isIntImpl(ty: Type) bool {
return switch (ty.specifier) {
// zig fmt: off
.@"enum", .bool, .char, .schar, .uchar, .short, .ushort, .int, .uint, .long, .ulong,
Expand All @@ -545,27 +568,23 @@ pub fn isInt(ty: Type) bool {
.complex_long_long, .complex_ulong_long, .complex_int128, .complex_uint128,
.bit_int, .complex_bit_int => true,
// zig fmt: on
.typeof_type => ty.data.sub_type.isInt(),
.typeof_expr => ty.data.expr.ty.isInt(),
.attributed => ty.data.attributed.base.isInt(),
else => false,
};
}

pub fn isFloat(ty: Type) bool {
pub const isFloat = makeTypeFn(isFloatImpl);
fn isFloatImpl(ty: Type) bool {
return switch (ty.specifier) {
// zig fmt: off
.float, .double, .long_double, .complex_float, .complex_double, .complex_long_double,
.fp16, .float16, .float128, .complex_float128, .complex_float16 => true,
// zig fmt: on
.typeof_type => ty.data.sub_type.isFloat(),
.typeof_expr => ty.data.expr.ty.isFloat(),
.attributed => ty.data.attributed.base.isFloat(),
else => false,
};
}

pub fn isReal(ty: Type) bool {
pub const isReal = makeTypeFn(isRealImpl);
fn isRealImpl(ty: Type) bool {
return switch (ty.specifier) {
// zig fmt: off
.complex_float, .complex_double, .complex_long_double,
Expand All @@ -574,14 +593,12 @@ pub fn isReal(ty: Type) bool {
.complex_long_long, .complex_ulong_long, .complex_int128, .complex_uint128,
.complex_bit_int, .complex_float16 => false,
// zig fmt: on
.typeof_type => ty.data.sub_type.isReal(),
.typeof_expr => ty.data.expr.ty.isReal(),
.attributed => ty.data.attributed.base.isReal(),
else => true,
};
}

pub fn isComplex(ty: Type) bool {
pub const isComplex = makeTypeFn(isComplexImpl);
fn isComplexImpl(ty: Type) bool {
return switch (ty.specifier) {
// zig fmt: off
.complex_float, .complex_double, .complex_long_double,
Expand All @@ -590,19 +607,14 @@ pub fn isComplex(ty: Type) bool {
.complex_long_long, .complex_ulong_long, .complex_int128, .complex_uint128,
.complex_bit_int, .complex_float16 => true,
// zig fmt: on
.typeof_type => ty.data.sub_type.isComplex(),
.typeof_expr => ty.data.expr.ty.isComplex(),
.attributed => ty.data.attributed.base.isComplex(),
else => false,
};
}

pub fn isVoidStar(ty: Type) bool {
pub const isVoidStar = makeTypeFn(isVoidStarImpl);
fn isVoidStarImpl(ty: Type) bool {
return switch (ty.specifier) {
.pointer => ty.data.sub_type.specifier == .void,
.typeof_type => ty.data.sub_type.isVoidStar(),
.typeof_expr => ty.data.expr.ty.isVoidStar(),
.attributed => ty.data.attributed.base.isVoidStar(),
else => false,
};
}
Expand All @@ -627,52 +639,44 @@ pub fn isUnsignedInt(ty: Type, comp: *const Compilation) bool {
return ty.signedness(comp) == .unsigned;
}

pub fn signedness(ty: Type, comp: *const Compilation) std.builtin.Signedness {
pub const signedness = makeTypeCompFn(singednessImpl);
fn singednessImpl(ty: Type, comp: *const Compilation) std.builtin.Signedness {
return switch (ty.specifier) {
// zig fmt: off
.char, .complex_char => return comp.getCharSignedness(),
.uchar, .ushort, .uint, .ulong, .ulong_long, .uint128, .bool, .complex_uchar, .complex_ushort,
.complex_uint, .complex_ulong, .complex_ulong_long, .complex_uint128 => .unsigned,
// zig fmt: on
.bit_int, .complex_bit_int => ty.data.int.signedness,
.typeof_type => ty.data.sub_type.signedness(comp),
.typeof_expr => ty.data.expr.ty.signedness(comp),
.attributed => ty.data.attributed.base.signedness(comp),
else => .signed,
};
}

pub fn isEnumOrRecord(ty: Type) bool {
pub const isEnumOrRecord = makeTypeFn(isEnumOrRecordImpl);
fn isEnumOrRecordImpl(ty: Type) bool {
return switch (ty.specifier) {
.@"enum", .@"struct", .@"union" => true,
.typeof_type => ty.data.sub_type.isEnumOrRecord(),
.typeof_expr => ty.data.expr.ty.isEnumOrRecord(),
.attributed => ty.data.attributed.base.isEnumOrRecord(),
else => false,
};
}

pub fn isRecord(ty: Type) bool {
pub const isRecord = makeTypeFn(isRecordImpl);
fn isRecordImpl(ty: Type) bool {
return switch (ty.specifier) {
.@"struct", .@"union" => true,
.typeof_type => ty.data.sub_type.isRecord(),
.typeof_expr => ty.data.expr.ty.isRecord(),
.attributed => ty.data.attributed.base.isRecord(),
else => false,
};
}

pub fn isAnonymousRecord(ty: Type, comp: *const Compilation) bool {
pub const isAnonymousRecord = makeTypeCompFn(isAnonymousRecordImpl);
fn isAnonymousRecordImpl(ty: Type, comp: *const Compilation) bool {
return switch (ty.specifier) {
// anonymous records can be recognized by their names which are in
// the format "(anonymous TAG at path:line:col)".
.@"struct", .@"union" => {
const mapper = comp.string_interner.getSlowTypeMapper();
return mapper.lookup(ty.data.record.name)[0] == '(';
},
.typeof_type => ty.data.sub_type.isAnonymousRecord(comp),
.typeof_expr => ty.data.expr.ty.isAnonymousRecord(comp),
.attributed => ty.data.attributed.base.isAnonymousRecord(comp),
else => false,
};
}
Expand Down Expand Up @@ -701,23 +705,19 @@ pub fn elemType(ty: Type) Type {
};
}

pub fn returnType(ty: Type) Type {
pub const returnType = makeTypeFn(returnTypeImpl);
fn returnTypeImpl(ty: Type) Type {
return switch (ty.specifier) {
.func, .var_args_func, .old_style_func => ty.data.func.return_type,
.typeof_type => ty.data.sub_type.returnType(),
.typeof_expr => ty.data.expr.ty.returnType(),
.attributed => ty.data.attributed.base.returnType(),
.invalid => Type.invalid,
else => unreachable,
};
}

pub fn params(ty: Type) []Func.Param {
pub const params = makeTypeFn(paramsImpl);
fn paramsImpl(ty: Type) []Func.Param {
return switch (ty.specifier) {
.func, .var_args_func, .old_style_func => ty.data.func.params,
.typeof_type => ty.data.sub_type.params(),
.typeof_expr => ty.data.expr.ty.params(),
.attributed => ty.data.attributed.base.params(),
.invalid => &.{},
else => unreachable,
};
Expand All @@ -733,12 +733,10 @@ pub fn isInvalidFunc(ty: Type) bool {
return false;
}

pub fn arrayLen(ty: Type) ?u64 {
pub const arrayLen = makeTypeFn(arrayLenImpl);
fn arrayLenImpl(ty: Type) ?u64 {
return switch (ty.specifier) {
.array, .static_array => ty.data.array.len,
.typeof_type => ty.data.sub_type.arrayLen(),
.typeof_expr => ty.data.expr.ty.arrayLen(),
.attributed => ty.data.attributed.base.arrayLen(),
else => null,
};
}
Expand All @@ -765,11 +763,9 @@ pub fn getAttributes(ty: Type) []const Attribute {
};
}

pub fn getRecord(ty: Type) ?*const Type.Record {
pub const getRecord = makeTypeFn(getRecordImpl);
fn getRecordImpl(ty: Type) ?*const Type.Record {
return switch (ty.specifier) {
.attributed => ty.data.attributed.base.getRecord(),
.typeof_type => ty.data.sub_type.getRecord(),
.typeof_expr => ty.data.expr.ty.getRecord(),
.@"struct", .@"union" => ty.data.record,
else => null,
};
Expand Down Expand Up @@ -862,7 +858,8 @@ pub fn integerConversion(a: Type, b: Type, comp: *const Compilation) Type {
return if (a_real and b_real) target_ty else target_ty.makeComplex();
}

pub fn integerPromotion(ty: Type, comp: *Compilation) Type {
pub const integerPromotion = makeTypeCompFn(integerPromotionImpl);
fn integerPromotionImpl(ty: Type, comp: *const Compilation) Type {
var specifier = ty.specifier;
switch (specifier) {
.@"enum" => {
Expand All @@ -885,9 +882,6 @@ pub fn integerPromotion(ty: Type, comp: *Compilation) Type {
.complex_uint, .complex_long, .complex_ulong, .complex_long_long, .complex_ulong_long,
.complex_int128, .complex_uint128 => specifier,
// zig fmt: on
.typeof_type => return ty.data.sub_type.integerPromotion(comp),
.typeof_expr => return ty.data.expr.ty.integerPromotion(comp),
.attributed => return ty.data.attributed.base.integerPromotion(comp),
.invalid => .invalid,
else => unreachable, // _BitInt, or not an integer type
},
Expand Down Expand Up @@ -928,22 +922,17 @@ pub fn hasIncompleteSize(ty: Type) bool {
};
}

pub fn hasUnboundVLA(ty: Type) bool {
var cur = ty;
while (true) {
switch (cur.specifier) {
.unspecified_variable_len_array => return true,
.array,
.static_array,
.incomplete_array,
.variable_len_array,
=> cur = cur.elemType(),
.typeof_type => cur = cur.data.sub_type.*,
.typeof_expr => cur = cur.data.expr.ty,
.attributed => cur = cur.data.attributed.base,
else => return false,
}
}
pub const hasUnboundVLA = makeTypeFn(hasUnboundVLAImpl);
fn hasUnboundVLAImpl(ty: Type) bool {
return switch (ty.specifier) {
.unspecified_variable_len_array => true,
.array,
.static_array,
.incomplete_array,
.variable_len_array,
=> ty.elemType().hasUnboundVLA(),
else => false,
};
}

pub fn hasField(ty: Type, name: StringId) bool {
Expand Down Expand Up @@ -1027,12 +1016,10 @@ pub fn sizeof(ty: Type, comp: *const Compilation) ?u64 {
};
}

pub fn bitSizeof(ty: Type, comp: *const Compilation) ?u64 {
pub const bitSizeof = makeTypeCompFn(bitSizeofImpl);
fn bitSizeofImpl(ty: Type, comp: *const Compilation) ?u64 {
return switch (ty.specifier) {
.bool => if (comp.langopts.emulate == .msvc) @as(u64, 8) else 1,
.typeof_type => ty.data.sub_type.bitSizeof(comp),
.typeof_expr => ty.data.expr.ty.bitSizeof(comp),
.attributed => ty.data.attributed.base.bitSizeof(comp),
.bit_int => return ty.data.int.bits,
.long_double => comp.target.cTypeBitSize(.longdouble),
else => 8 * (ty.sizeof(comp) orelse return null),
Expand Down

0 comments on commit 9c9a38f

Please sign in to comment.