From 3d2d46186751c67c4239479bcbe4908dff61ecd4 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Tue, 4 Jul 2017 23:52:00 +0100 Subject: [PATCH] Replace many built-in procedures with user-level procedures --- core/_preload.odin | 230 ++++++++++++++++++++++++++++++++++++--------- core/math.odin | 63 ++++++------- core/types.odin | 5 + src/check_expr.cpp | 14 +-- src/checker.cpp | 38 +++----- src/ir.cpp | 15 +-- src/types.cpp | 3 + 7 files changed, 248 insertions(+), 120 deletions(-) diff --git a/core/_preload.odin b/core/_preload.odin index 70532fa4e..32c01635c 100644 --- a/core/_preload.odin +++ b/core/_preload.odin @@ -5,6 +5,7 @@ import ( "fmt.odin"; "utf8.odin"; "raw.odin"; + "types.odin"; ) // Naming Conventions: // In general, PascalCase for types and snake_case for values @@ -117,6 +118,38 @@ __argv__: ^^u8; __argc__: i32; +__INITIAL_MAP_CAP :: 16; + +__MapKey :: struct #ordered { + hash: u128, + str: string, +} + +__MapFindResult :: struct #ordered { + hash_index: int, + entry_prev: int, + entry_index: int, +} + +__MapEntryHeader :: struct #ordered { + key: __MapKey, + next: int, +/* + value: Value_Type, +*/ +} + +__MapHeader :: struct #ordered { + m: ^raw.DynamicMap, + is_key_string: bool, + entry_size: int, + entry_align: int, + value_offset: int, + value_size: int, +} + + + type_info_base :: proc(info: ^TypeInfo) -> ^TypeInfo { if info == nil do return nil; @@ -256,6 +289,14 @@ resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_AL } +copy :: proc(dst, src: []$T) -> int #cc_contextless { + n := max(0, min(len(dst), len(src))); + if n > 0 do __mem_copy(&dst[0], &src[0], n*size_of(T)); + return n; +} + + + append :: proc(array: ^[]$T, args: ..T) -> int { if array == nil { return 0; @@ -277,36 +318,148 @@ append :: proc(array: ^[]$T, args: ..T) -> int { return slice.len; } -append :: proc(array_: ^[dynamic]$T, args: ..T) -> int { - array := ^raw.DynamicArray(array_); +append :: proc(array: ^[dynamic]$T, args: ..T) -> int { + if array == nil { + return 0; + } + a := ^raw.DynamicArray(array); arg_len := len(args); if arg_len <= 0 { - return array.len; + return a.len; } ok := true; - if array.cap <= array.len+arg_len { - cap := 2 * array.cap + max(8, arg_len); - ok = __dynamic_array_reserve(array, size_of(T), align_of(T), cap); + if a.cap <= a.len+arg_len { + cap := 2 * a.cap + max(8, arg_len); + ok = __dynamic_array_reserve(a, size_of(T), align_of(T), cap); } // TODO(bill): Better error handling for failed reservation - if !ok do return array.len; + if !ok do return a.len; - data := ^T(array.data); + data := ^T(a.data); assert(data != nil); - __mem_copy(data + array.len, &args[0], size_of(T) * arg_len); - array.len += arg_len; - return array.len; + __mem_copy(data + a.len, &args[0], size_of(T) * arg_len); + a.len += arg_len; + return a.len; } -copy :: proc(dst, src: []$T) -> int #cc_contextless { - n := max(0, min(len(dst), len(src))); - if n > 0 do __mem_copy(&dst[0], &src[0], n*size_of(T)); - return n; +pop :: proc(array: ^[]$T) -> T { + res: T; + if array do return res; + assert(len(array) > 0); + res = array[len(array)-1]; + ^raw.Slice(array).len -= 1; + return res; } +pop :: proc(array: ^[dynamic]$T) -> T { + res: T; + if array do return res; + assert(len(array) > 0); + res = array[len(array)-1]; + ^raw.DynamicArray(array).len -= 1; + return res; +} + +clear :: proc(slice: ^[]$T) #cc_contextless #inline { + if slice != nil do ^raw.Slice(slice).len = 0; +} +clear :: proc(array: ^[dynamic]$T) #cc_contextless #inline { + if array != nil do ^raw.DynamicArray(array).len = 0; +} +clear :: proc(m: ^map[$K]$V) #cc_contextless #inline { + if m == nil do return; + raw_map := ^raw.DynamicMap(array); + hashes := ^raw.DynamicArray(&raw_map.hashes); + entries := ^raw.DynamicArray(&raw_map.entries); + hashes.len = 0; + entries.len = 0; +} + +reserve :: proc(array: ^[dynamic]$T, capacity: int) -> bool { + if array == nil do return false; + a := ^raw.DynamicArray(array); + + if capacity <= a.cap do return true; + + // __check_context(); + if a.allocator.procedure == nil { + a.allocator = context.allocator; + } + assert(a.allocator.procedure != nil); + + old_size := a.cap * size_of(T); + new_size := capacity * size_of(T); + allocator := a.allocator; + + new_data := allocator.procedure(allocator.data, AllocatorMode.Resize, new_size, align_of(T), a.data, old_size, 0); + if new_data == nil do return false; + + a.data = new_data; + a.cap = capacity; + return true; +} + + +__get_map_header :: proc(m: ^map[$K]$V) -> __MapHeader { + header := __MapHeader{m = ^raw.DynamicMap(m)}; + Entry :: struct { + key: __MapKey, + next: int, + value: V, + } + + header.is_key_string = types.is_string(type_info(K)); + header.entry_size = size_of(Entry); + header.entry_align = align_of(Entry); + header.value_offset = offset_of(Entry, value); + header.value_size = size_of(V); + return header; +} + +__get_map_key :: proc(key: $K) -> __MapKey { + map_key: __MapKey; + ti := type_info(K); + match { + case types.is_integer(ti): + match 8*size_of(key) { + case 8: map_key.hash = u128( ^u8(&key)^); + case 16: map_key.hash = u128( ^u16(&key)^); + case 32: map_key.hash = u128( ^u32(&key)^); + case 64: map_key.hash = u128( ^u64(&key)^); + case 128: map_key.hash = u128(^u128(&key)^); + } + case types.is_rune(ti): + map_key.hash = u128(^rune(&key)^); + case types.is_pointer(ti): + map_key.hash = u128(uint(^rawptr(&key)^)); + case types.is_float(ti): + match 8*size_of(key) { + case 32: map_key.hash = u128(^u32(&key)^); + case 64: map_key.hash = u128(^u64(&key)^); + case: panic("Unhandled float size"); + } + case types.is_string(ti): + str := ^string(&key)^; + map_key.hash = __default_hash_string(str); + map_key.str = str; + case: + panic("Unhandled map key type"); + } + return map_key; +} + +reserve :: proc(m: ^map[$K]$V, capacity: int) { + if m != nil do __dynamic_map_reserve(__get_map_header(m), capacity); +} + +delete :: proc(m: ^map[$K]$V, key: K) { + if m != nil do __dynamic_map_delete(__get_map_header(m), __get_map_key(key)); +} + + new :: proc(T: type) -> ^T #inline do return ^T(alloc(size_of(T), align_of(T))); @@ -315,6 +468,23 @@ free :: proc(slice: []$T) do free_ptr(^raw.Slice(&slice).data); free :: proc(str: string) do free_ptr(^raw.String(&str).data); free :: proc(ptr: rawptr) do free_ptr(ptr); +slice_to_bytes :: proc(slice: []$T) -> []u8 { + s := ^raw.Slice(&slice); + s.len *= size_of(T); + s.cap *= size_of(T); + return ^[]u8(s)^; +} + +slice_ptr :: proc(ptr: ^$T, len: int) -> []T { + assert(0 <= len); + s := raw.Slice{ptr, len, len}; + return ^[]T(&s)^; +} +slice_ptr :: proc(ptr: ^$T, len, cap: int) -> []T { + assert(0 <= len && len <= cap); + s := raw.Slice{ptr, len, cap}; + return ^[]T(&s)^; +} @@ -648,36 +818,6 @@ __default_hash :: proc(data: []u8) -> u128 { } __default_hash_string :: proc(s: string) -> u128 do return __default_hash([]u8(s)); -__INITIAL_MAP_CAP :: 16; - -__MapKey :: struct #ordered { - hash: u128, - str: string, -} - -__MapFindResult :: struct #ordered { - hash_index: int, - entry_prev: int, - entry_index: int, -} - -__MapEntryHeader :: struct #ordered { - key: __MapKey, - next: int, -/* - value: Value_Type, -*/ -} - -__MapHeader :: struct #ordered { - m: ^raw.DynamicMap, - is_key_string: bool, - entry_size: int, - entry_align: int, - value_offset: int, - value_size: int, -} - __dynamic_map_reserve :: proc(using header: __MapHeader, cap: int) { __dynamic_array_reserve(&m.hashes, size_of(int), align_of(int), cap); __dynamic_array_reserve(&m.entries, entry_size, entry_align, cap); diff --git a/core/math.odin b/core/math.odin index d9e4e7670..8fdd51cbd 100644 --- a/core/math.odin +++ b/core/math.odin @@ -44,18 +44,18 @@ foreign __llvm_core { fmuladd :: proc(a, b, c: f64) -> f64 #link_name "llvm.fmuladd.f64" ---; } -tan :: proc(θ: f32) -> f32 #inline { return sin(θ)/cos(θ); } -tan :: proc(θ: f64) -> f64 #inline { return sin(θ)/cos(θ); } +tan :: proc(θ: f32) -> f32 #inline do return sin(θ)/cos(θ); +tan :: proc(θ: f64) -> f64 #inline do return sin(θ)/cos(θ); -lerp :: proc(a, b, t: f32) -> (x: f32) { return a*(1-t) + b*t; } -lerp :: proc(a, b, t: f64) -> (x: f64) { return a*(1-t) + b*t; } -unlerp :: proc(a, b, x: f32) -> (t: f32) { return (x-a)/(b-a); } -unlerp :: proc(a, b, x: f64) -> (t: f64) { return (x-a)/(b-a); } +lerp :: proc(a, b, t: f32) -> (x: f32) do return a*(1-t) + b*t; +lerp :: proc(a, b, t: f64) -> (x: f64) do return a*(1-t) + b*t; +unlerp :: proc(a, b, x: f32) -> (t: f32) do return (x-a)/(b-a); +unlerp :: proc(a, b, x: f64) -> (t: f64) do return (x-a)/(b-a); -sign :: proc(x: f32) -> f32 { return x >= 0 ? +1 : -1; } -sign :: proc(x: f64) -> f64 { return x >= 0 ? +1 : -1; } +sign :: proc(x: f32) -> f32 do return x >= 0 ? +1 : -1; +sign :: proc(x: f64) -> f64 do return x >= 0 ? +1 : -1; @@ -75,17 +75,17 @@ copy_sign :: proc(x, y: f64) -> f64 { return transmute(f64, ix); } -round :: proc(x: f32) -> f32 { return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); } -round :: proc(x: f64) -> f64 { return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); } +round :: proc(x: f32) -> f32 do return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); +round :: proc(x: f64) -> f64 do return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); -floor :: proc(x: f32) -> f32 { return x >= 0 ? f32(i64(x)) : f32(i64(x-0.5)); } // TODO: Get accurate versions -floor :: proc(x: f64) -> f64 { return x >= 0 ? f64(i64(x)) : f64(i64(x-0.5)); } // TODO: Get accurate versions +floor :: proc(x: f32) -> f32 do return x >= 0 ? f32(i64(x)) : f32(i64(x-0.5)); // TODO: Get accurate versions +floor :: proc(x: f64) -> f64 do return x >= 0 ? f64(i64(x)) : f64(i64(x-0.5)); // TODO: Get accurate versions -ceil :: proc(x: f32) -> f32 { return x < 0 ? f32(i64(x)) : f32(i64(x+1)); } // TODO: Get accurate versions -ceil :: proc(x: f64) -> f64 { return x < 0 ? f64(i64(x)) : f64(i64(x+1)); } // TODO: Get accurate versions +ceil :: proc(x: f32) -> f32 do return x < 0 ? f32(i64(x)) : f32(i64(x+1)); // TODO: Get accurate versions +ceil :: proc(x: f64) -> f64 do return x < 0 ? f64(i64(x)) : f64(i64(x+1)); // TODO: Get accurate versions -remainder :: proc(x, y: f32) -> f32 { return x - round(x/y) * y; } -remainder :: proc(x, y: f64) -> f64 { return x - round(x/y) * y; } +remainder :: proc(x, y: f32) -> f32 do return x - round(x/y) * y; +remainder :: proc(x, y: f64) -> f64 do return x - round(x/y) * y; mod :: proc(x, y: f32) -> f32 { result: f32; @@ -107,8 +107,8 @@ mod :: proc(x, y: f64) -> f64 { } -to_radians :: proc(degrees: f32) -> f32 { return degrees * TAU / 360; } -to_degrees :: proc(radians: f32) -> f32 { return radians * 360 / TAU; } +to_radians :: proc(degrees: f32) -> f32 do return degrees * TAU / 360; +to_degrees :: proc(radians: f32) -> f32 do return radians * 360 / TAU; @@ -123,36 +123,27 @@ cross :: proc(x, y: Vec3) -> Vec3 { } -mag :: proc(v: Vec2) -> f32 { return sqrt(dot(v, v)); } -mag :: proc(v: Vec3) -> f32 { return sqrt(dot(v, v)); } -mag :: proc(v: Vec4) -> f32 { return sqrt(dot(v, v)); } +mag :: proc(v: Vec2) -> f32 do return sqrt(dot(v, v)); +mag :: proc(v: Vec3) -> f32 do return sqrt(dot(v, v)); +mag :: proc(v: Vec4) -> f32 do return sqrt(dot(v, v)); -norm :: proc(v: Vec2) -> Vec2 { return v / mag(v); } -norm :: proc(v: Vec3) -> Vec3 { return v / mag(v); } -norm :: proc(v: Vec4) -> Vec4 { return v / mag(v); } +norm :: proc(v: Vec2) -> Vec2 do return v / mag(v); +norm :: proc(v: Vec3) -> Vec3 do return v / mag(v); +norm :: proc(v: Vec4) -> Vec4 do return v / mag(v); norm0 :: proc(v: Vec2) -> Vec2 { m := mag(v); - if m == 0 { - return 0; - } - return v / m; + return m == 0 ? 0 : v/m; } norm0 :: proc(v: Vec3) -> Vec3 { m := mag(v); - if m == 0 { - return 0; - } - return v / m; + return m == 0 ? 0 : v/m; } norm0 :: proc(v: Vec4) -> Vec4 { m := mag(v); - if m == 0 { - return 0; - } - return v / m; + return m == 0 ? 0 : v/m; } diff --git a/core/types.odin b/core/types.odin index 3bffd4456..df1866c32 100644 --- a/core/types.odin +++ b/core/types.odin @@ -11,6 +11,11 @@ is_integer :: proc(info: ^TypeInfo) -> bool { _, ok := type_info_base(info).(^TypeInfo.Integer); return ok; } +is_rune :: proc(info: ^TypeInfo) -> bool { + if info == nil do return false; + _, ok := type_info_base(info).(^TypeInfo.Rune); + return ok; +} is_float :: proc(info: ^TypeInfo) -> bool { if info == nil do return false; _, ok := type_info_base(info).(^TypeInfo.Float); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 586979f90..10ff78609 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4217,7 +4217,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id operand->type = type; } break; - #if 1 + #if 0 case BuiltinProc_free: { // proc free(^Type) // proc free([]Type) @@ -4250,6 +4250,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id #endif + #if 0 case BuiltinProc_reserve: { // proc reserve([dynamic]Type, count: int) { // proc reserve(map[Key]Type, count: int) { @@ -4276,7 +4277,8 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id operand->type = NULL; operand->mode = Addressing_NoValue; } break; - + #endif + #if 0 case BuiltinProc_clear: { Type *type = operand->type; bool is_pointer = is_type_pointer(type); @@ -4291,7 +4293,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id operand->type = NULL; operand->mode = Addressing_NoValue; } break; - + #endif #if 0 case BuiltinProc_append: { // proc append([dynamic]Type, item: ..Type) @@ -4341,7 +4343,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id operand->type = t_int; } break; #endif - + #if 0 case BuiltinProc_delete: { // proc delete(map[Key]Value, key: Key) Type *type = operand->type; @@ -4372,6 +4374,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id operand->mode = Addressing_NoValue; } break; + #endif case BuiltinProc_size_of: { @@ -4701,7 +4704,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id } break; - #if 1 + #if 0 case BuiltinProc_slice_ptr: { // proc slice_ptr(a: ^T, len: int) -> []T // proc slice_ptr(a: ^T, len, cap: int) -> []T @@ -4759,7 +4762,6 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id operand->mode = Addressing_Value; } break; #endif - case BuiltinProc_expand_to_tuple: { Type *type = base_type(operand->type); if (!is_type_struct(type) & diff --git a/src/checker.cpp b/src/checker.cpp index 6ea755d38..149435ed2 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -29,12 +29,12 @@ enum BuiltinProcId { // BuiltinProc_new, BuiltinProc_make, - BuiltinProc_free, + // BuiltinProc_free, - BuiltinProc_reserve, - BuiltinProc_clear, + // BuiltinProc_reserve, + // BuiltinProc_clear, // BuiltinProc_append, - BuiltinProc_delete, + // BuiltinProc_delete, BuiltinProc_size_of, BuiltinProc_align_of, @@ -51,8 +51,8 @@ enum BuiltinProcId { BuiltinProc_imag, BuiltinProc_conj, - BuiltinProc_slice_ptr, - BuiltinProc_slice_to_bytes, + // BuiltinProc_slice_ptr, + // BuiltinProc_slice_to_bytes, BuiltinProc_expand_to_tuple, @@ -61,12 +61,6 @@ enum BuiltinProcId { BuiltinProc_abs, BuiltinProc_clamp, -/* BuiltinProc_sqrt, - BuiltinProc_sin, - BuiltinProc_cos, - BuiltinProc_tan, - BuiltinProc_pow, */ - BuiltinProc_transmute, BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures @@ -81,12 +75,12 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { // {STR_LIT("new"), 1, false, Expr_Expr}, {STR_LIT("make"), 1, true, Expr_Expr}, - {STR_LIT("free"), 1, false, Expr_Stmt}, + // {STR_LIT("free"), 1, false, Expr_Stmt}, - {STR_LIT("reserve"), 2, false, Expr_Stmt}, - {STR_LIT("clear"), 1, false, Expr_Stmt}, + // {STR_LIT("reserve"), 2, false, Expr_Stmt}, + // {STR_LIT("clear"), 1, false, Expr_Stmt}, // {STR_LIT("append"), 1, true, Expr_Expr}, - {STR_LIT("delete"), 2, false, Expr_Stmt}, + // {STR_LIT("delete"), 2, false, Expr_Stmt}, {STR_LIT("size_of"), 1, false, Expr_Expr}, {STR_LIT("align_of"), 1, false, Expr_Expr}, @@ -103,8 +97,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("imag"), 1, false, Expr_Expr}, {STR_LIT("conj"), 1, false, Expr_Expr}, - {STR_LIT("slice_ptr"), 2, true, Expr_Expr}, - {STR_LIT("slice_to_bytes"), 1, false, Expr_Stmt}, + // {STR_LIT("slice_ptr"), 2, true, Expr_Expr}, + // {STR_LIT("slice_to_bytes"), 1, false, Expr_Expr}, {STR_LIT("expand_to_tuple"), 1, false, Expr_Expr}, @@ -113,14 +107,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("abs"), 1, false, Expr_Expr}, {STR_LIT("clamp"), 3, false, Expr_Expr}, -/* - {STR_LIT("__sqrt"), 1, false, Expr_Expr}, - {STR_LIT("__sin"), 1, false, Expr_Expr}, - {STR_LIT("__cos"), 1, false, Expr_Expr}, - {STR_LIT("__tan"), 1, false, Expr_Expr}, - {STR_LIT("__pow"), 2, false, Expr_Expr}, - */ - {STR_LIT("transmute"), 2, false, Expr_Expr}, {STR_LIT(""), 0, true, Expr_Expr}, // DIRECTIVE diff --git a/src/ir.cpp b/src/ir.cpp index 1484980cc..930132cce 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3922,7 +3922,7 @@ irValue *ir_build_builtin_proc(irProcedure *proc, AstNode *expr, TypeAndValue tv } } break; - #if 1 + #if 0 case BuiltinProc_free: { ir_emit_comment(proc, str_lit("free")); @@ -3997,7 +3997,7 @@ irValue *ir_build_builtin_proc(irProcedure *proc, AstNode *expr, TypeAndValue tv return ir_emit_global_call(proc, "free_ptr", args, 1); } break; #endif - + #if 0 case BuiltinProc_reserve: { ir_emit_comment(proc, str_lit("reserve")); gbAllocator a = proc->module->allocator; @@ -4032,7 +4032,8 @@ irValue *ir_build_builtin_proc(irProcedure *proc, AstNode *expr, TypeAndValue tv GB_PANIC("Unknown type for `reserve`"); } } break; - + #endif + #if 0 case BuiltinProc_clear: { ir_emit_comment(proc, str_lit("clear")); Type *original_type = type_of_expr(proc->module->info, ce->args[0]); @@ -4058,7 +4059,7 @@ irValue *ir_build_builtin_proc(irProcedure *proc, AstNode *expr, TypeAndValue tv } return NULL; } break; - + #endif #if 0 case BuiltinProc_append: { ir_emit_comment(proc, str_lit("append")); @@ -4169,7 +4170,7 @@ irValue *ir_build_builtin_proc(irProcedure *proc, AstNode *expr, TypeAndValue tv return ir_emit_global_call(proc, "__dynamic_array_append", daa_args, 5); } break; #endif - + #if 0 case BuiltinProc_delete: { ir_emit_comment(proc, str_lit("delete")); irValue *map = ir_build_expr(proc, ce->args[0]); @@ -4186,7 +4187,7 @@ irValue *ir_build_builtin_proc(irProcedure *proc, AstNode *expr, TypeAndValue tv args[1] = ir_gen_map_key(proc, key, key_type); return ir_emit_global_call(proc, "__dynamic_map_delete", args, 2); } break; - + #endif case BuiltinProc_swizzle: { ir_emit_comment(proc, str_lit("swizzle.begin")); @@ -4260,7 +4261,7 @@ irValue *ir_build_builtin_proc(irProcedure *proc, AstNode *expr, TypeAndValue tv return ir_emit_load(proc, res); } break; - #if 1 + #if 0 case BuiltinProc_slice_ptr: { ir_emit_comment(proc, str_lit("slice_ptr")); irValue *ptr = ir_build_expr(proc, ce->args[0]); diff --git a/src/types.cpp b/src/types.cpp index 98aa1e73e..294e17cd8 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -919,6 +919,9 @@ bool is_type_untyped_undef(Type *t) { bool is_type_valid_for_keys(Type *t) { t = core_type(t); + if (t->kind == Type_Generic) { + return true; + } if (is_type_untyped(t)) { return false; }