From 7f8553441446d7f8528e546617267642aed931c2 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Thu, 5 Jun 2025 00:37:16 +0200 Subject: [PATCH] - Implicitly convert from constant typeid to Type in `$Type` assignment, and `$assignable`. - Make $Type parameters accept constant typeid values. --- lib/std/atomic.c3 | 10 ++-- lib/std/core/types.c3 | 14 ++--- lib/std/io/formatter.c3 | 2 +- lib/std/io/stream.c3 | 2 +- lib/std/sort/countingsort.c3 | 2 +- lib/std/sort/insertionsort.c3 | 2 +- lib/std/sort/quicksort.c3 | 2 +- lib/std/sort/sorted.c3 | 2 +- releasenotes.md | 2 + src/compiler/compiler_internal.h | 8 ++- src/compiler/copying.c | 2 +- src/compiler/parse_expr.c | 2 +- src/compiler/sema_decls.c | 7 ++- src/compiler/sema_expr.c | 52 ++++++++++++++++--- src/compiler/sema_types.c | 12 ++--- .../compile_time_introspection/tag_1343.c3t | 2 +- 16 files changed, 87 insertions(+), 36 deletions(-) diff --git a/lib/std/atomic.c3 b/lib/std/atomic.c3 index 5687da955..1f9ab1833 100644 --- a/lib/std/atomic.c3 +++ b/lib/std/atomic.c3 @@ -175,7 +175,7 @@ macro bool is_native_atomic_type($Type) $case BOOL: return true; $case DISTINCT: - return is_native_atomic_type($typefrom($Type.inner)); + return is_native_atomic_type($Type.inner); $default: return false; $endswitch @@ -240,7 +240,7 @@ macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT) $load_ordering = AtomicOrdering.SEQ_CONSISTENT; $endif - var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr))); + var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr)); $StorageType* storage_ptr = ($StorageType*)ptr; @@ -280,7 +280,7 @@ macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT) $load_ordering = AtomicOrdering.SEQ_CONSISTENT; $endif - var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr))); + var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr)); $StorageType* storage_ptr = ($StorageType*)ptr; @@ -372,7 +372,7 @@ macro fetch_shift_right(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT) $load_ordering = AtomicOrdering.SEQ_CONSISTENT; $endif - var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr))); + var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr)); $StorageType* storage_ptr = ($StorageType*)ptr; @@ -414,7 +414,7 @@ macro fetch_shift_left(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT) $load_ordering = AtomicOrdering.SEQ_CONSISTENT; $endif - var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr))); + var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr)); $StorageType* storage_ptr = ($StorageType*)ptr; diff --git a/lib/std/core/types.c3 b/lib/std/core/types.c3 index f92281a7a..b87e166e9 100644 --- a/lib/std/core/types.c3 +++ b/lib/std/core/types.c3 @@ -98,7 +98,7 @@ macro bool is_numerical($Type) { var $kind = $Type.kindof; $if $kind == TypeKind.DISTINCT: - return is_numerical($typefrom($Type.inner)); + return is_numerical($Type.inner); $else return $kind == TypeKind.SIGNED_INT || $kind == TypeKind.UNSIGNED_INT || $kind == TypeKind.FLOAT || $kind == TypeKind.VECTOR; @@ -135,7 +135,7 @@ macro bool is_signed($Type) @const $case FLOAT: return true; $case VECTOR: - return is_signed($typefrom($Type.inner)); + return is_signed($Type.inner); $default: return false; $endswitch @@ -150,7 +150,7 @@ macro bool is_unsigned($Type) @const $case UNSIGNED_INT: return true; $case VECTOR: - return is_unsigned($typefrom($Type.inner)); + return is_unsigned($Type.inner); $default: return false; $endswitch @@ -159,7 +159,7 @@ macro bool is_unsigned($Type) @const macro typeid flat_type($Type) @const { $if $Type.kindof == DISTINCT: - return flat_type($typefrom($Type.inner)); + return flat_type($Type.inner); $else return $Type.typeid; $endif @@ -168,7 +168,7 @@ macro typeid flat_type($Type) @const macro TypeKind flat_kind($Type) @const { $if $Type.kindof == DISTINCT: - return flat_type($typefrom($Type.inner)); + return flat_type($Type.inner); $else return $Type.kindof; $endif @@ -204,7 +204,7 @@ macro bool is_underlying_int($Type) @const $case UNSIGNED_INT: return true; $case DISTINCT: - return is_underlying_int($typefrom($Type.inner)); + return is_underlying_int($Type.inner); $default: return false; $endswitch @@ -232,7 +232,7 @@ macro bool is_vector($Type) @const macro typeid inner_type($Type) @const { $if $Type.kindof == TypeKind.DISTINCT: - return inner_type($typefrom($Type.inner)); + return inner_type($Type.inner); $else return $Type.typeid; $endif diff --git a/lib/std/io/formatter.c3 b/lib/std/io/formatter.c3 index 1117f7b66..050c2dede 100644 --- a/lib/std/io/formatter.c3 +++ b/lib/std/io/formatter.c3 @@ -41,7 +41,7 @@ macro usz? struct_to_format(value, Formatter* f, bool $force_dump) total += f.printf("%s: ", $member.nameof)!; $endif $if ($force_dump &&& $member.typeid.kindof == STRUCT) ||| - is_struct_with_default_print($typefrom($member.typeid)): + is_struct_with_default_print($member.typeid): total += struct_to_format($member.get(value), f, $force_dump)!; $else total += f.printf("%s", $member.get(value))!; diff --git a/lib/std/io/stream.c3 b/lib/std/io/stream.c3 index 708302e54..b0116f3a0 100644 --- a/lib/std/io/stream.c3 +++ b/lib/std/io/stream.c3 @@ -191,7 +191,7 @@ const char[*] MAX_VARS @private = { [2] = 3, [4] = 5, [8] = 10 }; *> macro usz? read_varint(stream, x_ptr) { - var $Type = $typefrom($typeof(x_ptr).inner); + var $Type = $typeof(x_ptr).inner; const MAX = MAX_VARS[$Type.sizeof]; $Type x; uint shift; diff --git a/lib/std/sort/countingsort.c3 b/lib/std/sort/countingsort.c3 index e2d23090c..6d2559937 100644 --- a/lib/std/sort/countingsort.c3 +++ b/lib/std/sort/countingsort.c3 @@ -32,7 +32,7 @@ alias Indexs @private = char[256]; alias ElementType = $typeof((Type){}[0]); const bool NO_KEY_FN @private = types::is_same(KeyFn, EmptySlot); -const bool KEY_BY_VALUE @private = NO_KEY_FN ||| $assignable((Type){}[0], $typefrom(KeyFn.paramsof[0].type)); +const bool KEY_BY_VALUE @private = NO_KEY_FN ||| $assignable((Type){}[0], KeyFn.paramsof[0].type); const bool LIST_HAS_REF @private = $defined(&(Type){}[0]); alias KeyFnReturnType @if(!NO_KEY_FN) = $typefrom(KeyFn.returns) ; diff --git a/lib/std/sort/insertionsort.c3 b/lib/std/sort/insertionsort.c3 index dbf4bd94a..29107ec9b 100644 --- a/lib/std/sort/insertionsort.c3 +++ b/lib/std/sort/insertionsort.c3 @@ -25,7 +25,7 @@ fn void isort(Type list, usz low, usz high, CmpFn comp, Context context) { var $has_cmp = @is_valid_macro_slot(comp); var $has_context = @is_valid_macro_slot(context); - var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom(CmpFn.paramsof[0].type)); + var $cmp_by_value = $has_cmp &&& $assignable(list[0], CmpFn.paramsof[0].type); var $has_get_ref = $defined(&list[0]); for (usz i = low; i < high; ++i) { diff --git a/lib/std/sort/quicksort.c3 b/lib/std/sort/quicksort.c3 index dc558a1c6..34c5d4b29 100644 --- a/lib/std/sort/quicksort.c3 +++ b/lib/std/sort/quicksort.c3 @@ -115,7 +115,7 @@ macro @partition(Type list, isz l, isz h, CmpFn cmp, Context context) { var $has_cmp = @is_valid_macro_slot(cmp); var $has_context = @is_valid_macro_slot(context); - var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom(CmpFn.paramsof[0].type)); + var $cmp_by_value = $has_cmp &&& $assignable(list[0], CmpFn.paramsof[0].type); ElementType pivot = list[l]; while (l < h) diff --git a/lib/std/sort/sorted.c3 b/lib/std/sort/sorted.c3 index d1d24405c..d57c2c971 100644 --- a/lib/std/sort/sorted.c3 +++ b/lib/std/sort/sorted.c3 @@ -39,7 +39,7 @@ macro int @sort_cmp(list, pos, cmp, ctx) @local { var $has_cmp = @is_valid_macro_slot(cmp); var $has_context = @is_valid_macro_slot(ctx); - var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom($typeof(cmp).paramsof[0].type)); + var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typeof(cmp).paramsof[0].type); var a = list[pos]; var b = list[pos+1]; diff --git a/releasenotes.md b/releasenotes.md index 248c14526..69914bb21 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -6,6 +6,8 @@ - `$typefrom` now also accepts a constant string, and so works like `$evaltype`. - `$evaltype` is deprecated in favour of `$typefrom`. - `-0xFF` will now be a signed integer. +- Implicitly convert from constant typeid to Type in `$Type` assignment, and `$assignable`. +- Make $Type parameters accept constant typeid values. ### Fixes - `-2147483648`, MIN literals work correctly. diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index ee35f1ce0..81fa8c16f 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1089,7 +1089,7 @@ typedef struct { bool is_assign; ExprId expr; - TypeInfoId type; + ExprId type; } ExprCastable; typedef struct @@ -4077,6 +4077,12 @@ INLINE bool expr_is_const_float(Expr *expr) return expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_FLOAT; } +INLINE bool expr_is_const_typeid(Expr *expr) +{ + ASSERT(expr->resolve_status == RESOLVE_DONE); + return expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_TYPEID; +} + INLINE bool expr_is_const_member(Expr *expr) { ASSERT(expr->resolve_status == RESOLVE_DONE); diff --git a/src/compiler/copying.c b/src/compiler/copying.c index 78ba9808b..ca38a8b27 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -471,7 +471,7 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr) UNREACHABLE case EXPR_CT_CASTABLE: MACRO_COPY_EXPRID(expr->castable_expr.expr); - MACRO_COPY_TYPEID(expr->castable_expr.type); + MACRO_COPY_EXPRID(expr->castable_expr.type); return expr; case EXPR_CT_EVAL: case EXPR_CT_IS_CONST: diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index ff76ea241..651a64763 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -1247,7 +1247,7 @@ static Expr *parse_ct_castable(ParseContext *c, Expr *left) CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); ASSIGN_EXPRID_OR_RET(expr->castable_expr.expr, parse_expr(c), poisoned_expr); CONSUME_OR_RET(TOKEN_COMMA, poisoned_expr); - ASSIGN_TYPEID_OR_RET(expr->castable_expr.type, parse_type(c), poisoned_expr); + ASSIGN_EXPRID_OR_RET(expr->castable_expr.type, parse_expr(c), poisoned_expr); CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); RANGE_EXTEND_PREV(expr); return expr; diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index bc104e12a..aa39444e5 100755 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -4322,8 +4322,13 @@ bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl) // Try to fold any constant into an lvalue. if (!sema_analyse_expr_value(context, init)) goto FAIL; + if (init->expr_kind == EXPR_TYPEINFO) + { + Type *type = init->type_expr->type; + expr_rewrite_const_typeid(init, type); + } // If this isn't a type, it's an error. - if (init->expr_kind != EXPR_TYPEINFO) + if (!expr_is_const_typeid(init)) { SEMA_ERROR(decl->var.init_expr, "Expected a type assigned to %s.", decl->name); goto FAIL; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index e63df4dba..a5c2dbe09 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1364,7 +1364,13 @@ static bool sema_analyse_parameter(SemaContext *context, Expr *arg, Decl *param, case VARDECL_PARAM_CT_TYPE: // $Foo if (!sema_analyse_expr_value(context, arg)) return false; - if (arg->expr_kind != EXPR_TYPEINFO) + + if (arg->expr_kind == EXPR_TYPEINFO) + { + assert(arg->type_expr->resolve_status == RESOLVE_DONE); + expr_rewrite_const_typeid(arg, arg->type_expr->type); + } + if (!sema_cast_const(arg) || !expr_is_const_typeid(arg)) { RETURN_SEMA_FUNC_ERROR(definition, arg, "A type, like 'int' or 'double' was expected for the parameter '%s'.", param->name); } @@ -6158,7 +6164,11 @@ static bool sema_expr_analyse_ct_type_identifier_assign(SemaContext *context, Ex } if (!sema_analyse_expr_value(context, right)) return false; - if (right->expr_kind != EXPR_TYPEINFO) RETURN_SEMA_ERROR(right, "Expected a type here."); + if (right->expr_kind == EXPR_TYPEINFO) + { + expr_rewrite_const_typeid(right, right->type_expr->type); + } + if (!expr_is_const_typeid(right)) RETURN_SEMA_ERROR(right, "Expected a type or constant typeid here."); Decl *decl = sema_find_symbol(context, info->unresolved.name); if (!decl) RETURN_SEMA_ERROR(info, "'%s' is not defined in this scope yet.", info->unresolved.name); @@ -9313,8 +9323,8 @@ INLINE bool lambda_parameter_match(Decl **ct_lambda_params, Decl *candidate) { case VARDECL_LOCAL_CT_TYPE: case VARDECL_PARAM_CT_TYPE: - if (ct_param->var.init_expr->type_expr->type->canonical != - param->var.init_expr->type_expr->type->canonical) + if (ct_param->var.init_expr->const_expr.typeid->canonical != + param->var.init_expr->const_expr.typeid->canonical) return false; break; case VARDECL_LOCAL_CT: @@ -9931,11 +9941,39 @@ static inline bool sema_expr_analyse_ct_arg(SemaContext *context, Type *infer_ty static inline bool sema_expr_analyse_castable(SemaContext *context, Expr *expr) { ASSERT_SPAN(expr, expr->resolve_status == RESOLVE_RUNNING); - TypeInfo *type_info = type_infoptr(expr->castable_expr.type); + Expr *type_expr = exprptr(expr->castable_expr.type); bool in_no_eval = context->call_env.in_no_eval; context->call_env.in_no_eval = true; - if (!sema_resolve_type_info(context, type_info, RESOLVE_TYPE_ALLOW_INFER)) goto FAILED; - Type *type = type_info->type; + Type *type; + if (type_expr->expr_kind == EXPR_TYPEINFO) + { + TypeInfo *type_info = type_expr->type_expr; + if (!sema_resolve_type_info(context, type_info, RESOLVE_TYPE_ALLOW_INFER)) goto FAILED; + type = type_info->type; + } + else + { + if (!sema_analyse_expr_value(context, type_expr)) goto FAILED; + switch (type_expr->expr_kind) + { + case EXPR_TYPEINFO: + ASSERT_SPAN(expr, type_expr->type_expr->resolve_status == RESOLVE_DONE); + type = type_expr->type_expr->type; + break; + case EXPR_CONST: + if (type_expr->const_expr.const_kind == CONST_TYPEID) + { + type = type_expr->const_expr.typeid; + break; + } + type = NULL; + break; + default: + type = NULL; + break; + } + } + if (!type) RETURN_SEMA_ERROR(type_expr, "Expected a type or constant typeid here."); Expr *inner = exprptr(expr->castable_expr.expr); if (!sema_analyse_inferred_expr(context, type, inner)) goto FAILED; bool ok = may_cast(context, inner, type, !expr->castable_expr.is_assign, true); diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index 78656c0ac..287e9e642 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -255,14 +255,14 @@ static bool sema_resolve_type_identifier(SemaContext *context, TypeInfo *type_in if (decl->var.kind == VARDECL_PARAM_CT_TYPE || decl->var.kind == VARDECL_LOCAL_CT_TYPE) { decl->var.is_read = true; - if (!decl->var.init_expr) + Expr *init_expr = decl->var.init_expr; + if (!init_expr) { - SEMA_ERROR(type_info, "You need to assign a type to '%s' before using it.", decl->name); - return false; + RETURN_SEMA_ERROR(type_info, "You need to assign a type to '%s' before using it.", decl->name); } - ASSERT(decl->var.init_expr->expr_kind == EXPR_TYPEINFO); - ASSERT(decl->var.init_expr->resolve_status == RESOLVE_DONE); - *type_info = *decl->var.init_expr->type_expr; + ASSERT_SPAN(init_expr, expr_is_const_typeid(init_expr)); + ASSERT_SPAN(init_expr, init_expr->resolve_status == RESOLVE_DONE); + type_info->type = init_expr->const_expr.typeid; return true; } FALLTHROUGH; diff --git a/test/test_suite/compile_time_introspection/tag_1343.c3t b/test/test_suite/compile_time_introspection/tag_1343.c3t index 97d892a5f..bef982d52 100644 --- a/test/test_suite/compile_time_introspection/tag_1343.c3t +++ b/test/test_suite/compile_time_introspection/tag_1343.c3t @@ -4,7 +4,7 @@ fn void Bar.xyz(&self) @tag("footag", 123) {} macro void Foo.tags(&self, other) { // inner to remove pointer - var $Type = $typefrom($typeof(other).inner); + var $Type = $typeof(other).inner; var $methodcount = $Type.methodsof.len; $for var $i = 0; $i < $methodcount; $i++: var $MethodType1 = $typeof($Type.$eval($Type.methodsof[$i]));