diff --git a/releasenotes.md b/releasenotes.md index 543a4409a..b63c2d09c 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -3,6 +3,8 @@ ## 0.7.3 Change list ### Changes / improvements +- `$typefrom` now also accepts a constant string, and so works like `$evaltype`. +- `$evaltype` is deprecated in favour of `$typefrom`. ### Fixes diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 1992e3e61..ca1182c21 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -9975,7 +9975,7 @@ static inline bool sema_expr_resolve_ct_eval(SemaContext *context, Expr *expr) if (!result) return false; if (result->expr_kind == EXPR_TYPEINFO) { - RETURN_SEMA_ERROR(result, "Evaluation to a type requires the use of '$evaltype' rather than '$eval'."); + RETURN_SEMA_ERROR(result, "Evaluation to a type requires the use of '$typefrom' rather than '$eval'."); } expr_replace(expr, result); return true; diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index b9097c8e0..78656c0ac 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -11,7 +11,7 @@ static inline bool sema_resolve_type(SemaContext *context, TypeInfo *type_info, static bool sema_resolve_type_identifier(SemaContext *context, TypeInfo *type_info, ResolveTypeKind resolve_type_kind); INLINE bool sema_resolve_vatype(SemaContext *context, TypeInfo *type_info); INLINE bool sema_resolve_evaltype(SemaContext *context, TypeInfo *type_info, ResolveTypeKind resolve_kind); -INLINE bool sema_resolve_typefrom(SemaContext *context, TypeInfo *type_info); +INLINE bool sema_resolve_typefrom(SemaContext *context, TypeInfo *type_info, ResolveTypeKind resolve_kind); INLINE bool sema_resolve_typeof(SemaContext *context, TypeInfo *type_info); static int compare_function(Signature *sig, FunctionPrototype *proto); @@ -293,6 +293,7 @@ static bool sema_resolve_type_identifier(SemaContext *context, TypeInfo *type_in // $evaltype("Foo") INLINE bool sema_resolve_evaltype(SemaContext *context, TypeInfo *type_info, ResolveTypeKind resolve_kind) { + SEMA_DEPRECATED(type_info, "$evaltype is deprecated, use $typefrom instead."); Expr *expr = type_info->unresolved_type_expr; Expr *inner = sema_ct_eval_expr(context, true, expr, true); if (!inner) return type_info_poison(type_info); @@ -353,16 +354,69 @@ INLINE bool sema_resolve_typeof(SemaContext *context, TypeInfo *type_info) UNREACHABLE } -INLINE bool sema_resolve_typefrom(SemaContext *context, TypeInfo *type_info) +INLINE bool sema_resolve_typefrom(SemaContext *context, TypeInfo *type_info, ResolveTypeKind resolve_kind) { Expr *expr = type_info->unresolved_type_expr; if (!sema_analyse_expr(context, expr)) return false; - if (!sema_cast_const(expr) || expr->const_expr.const_kind != CONST_TYPEID) + if (!sema_cast_const(expr)) { - RETURN_SEMA_ERROR(expr, "Expected a constant typeid value."); + RETURN_SEMA_ERROR(expr, "Expected a constant value."); } - type_info->type = expr->const_expr.typeid; - return true; + switch (expr->const_expr.const_kind) + { + case CONST_TYPEID: + type_info->type = expr->const_expr.typeid; + return true; + case CONST_STRING: + break; + default: + RETURN_SEMA_ERROR(expr, "Expected a constant string or typeid value."); + } + + Path *path = NULL; + const char *interned_version = NULL; + const char *bytes = expr->const_expr.bytes.ptr; + ArraySize bytes_len = expr->const_expr.bytes.len; + TokenType token = sema_splitpathref(bytes, bytes_len, &path, &interned_version); + TypeInfo *info; + switch (token) + { + case TOKEN_IDENT: + if (slice_is_type(bytes, bytes_len)) + { + RETURN_SEMA_ERROR(expr, "'%.*s' could not be found, did you spell it right?", (int)bytes_len, bytes); + } + FALLTHROUGH; + case TOKEN_CONST_IDENT: + RETURN_SEMA_ERROR(expr, "Expected a type name, but the argument was \"%.*s\".", (int)bytes_len, bytes); + case TYPE_TOKENS: + type_info->type = type_from_token(token); + return true; + case TOKEN_TYPE_IDENT: + info = type_info_new(TYPE_INFO_IDENTIFIER, expr->span); + info->unresolved.name = interned_version; + info->unresolved.path = path; + info->resolve_status = RESOLVE_NOT_DONE; + break; + default: + RETURN_SEMA_ERROR(expr, "Only valid types can be resolved with $typefrom. You passed the string \"%.*s\", which cannot be resolved as a type.", (int)bytes_len, bytes); + } + if (!sema_resolve_type(context, info, resolve_kind)) return false; + switch (sema_resolve_storage_type(context, info->type)) + { + case STORAGE_ERROR: + return false; + case STORAGE_VOID: + case STORAGE_UNKNOWN: + case STORAGE_NORMAL: + type_info->type = info->type; + return true; + case STORAGE_WILDCARD: + RETURN_SEMA_ERROR(expr, "$typefrom failed to resolve \"%.*s\" to a definite type.", (int)bytes_len, bytes); + case STORAGE_COMPILE_TIME: + RETURN_SEMA_ERROR(expr, "$typefrom does not support compile-time types."); + } + UNREACHABLE } // $vatype(...) @@ -476,7 +530,7 @@ static inline bool sema_resolve_type(SemaContext *context, TypeInfo *type_info, if (!sema_resolve_typeof(context, type_info)) return type_info_poison(type_info); goto APPEND_QUALIFIERS; case TYPE_INFO_TYPEFROM: - if (!sema_resolve_typefrom(context, type_info)) return type_info_poison(type_info); + if (!sema_resolve_typefrom(context, type_info, resolve_kind)) return type_info_poison(type_info); goto APPEND_QUALIFIERS; case TYPE_INFO_INFERRED_VECTOR: case TYPE_INFO_INFERRED_ARRAY: diff --git a/src/utils/lib.h b/src/utils/lib.h index d1ea4f121..117ba34f3 100644 --- a/src/utils/lib.h +++ b/src/utils/lib.h @@ -151,6 +151,7 @@ const char *str_unescape(char *string); bool str_is_identifier(const char *string); bool str_eq(const char *str1, const char *str2); bool str_is_type(const char *string); +bool slice_is_type(const char *string, size_t); bool str_is_integer(const char *string); bool str_has_no_uppercase(const char *string); bool str_is_valid_module_name(const char *name); diff --git a/src/utils/stringutils.c b/src/utils/stringutils.c index f397a6ad9..e9c1c0953 100644 --- a/src/utils/stringutils.c +++ b/src/utils/stringutils.c @@ -103,6 +103,28 @@ bool str_is_type(const char *string) return found_lower; } +bool slice_is_type(const char *string, size_t len) +{ + const char *begin = scan_past_underscore(string); + if (!begin) return false; + const char *end = string + len; + if (begin == end) return false; + char c = begin++[0]; + if (!char_is_upper(c)) return false; + bool found_lower = false; + while (begin != end) + { + c = begin++[0]; + if (char_is_lower(c)) + { + found_lower = true; + continue; + } + if (!char_is_alphanum_(c)) return false; + } + return found_lower; +} + bool str_is_identifier(const char *string) { string = scan_past_underscore(string); diff --git a/test/test_suite/compile_time_introspection/defined.c3t b/test/test_suite/compile_time_introspection/defined.c3t index 79dbb0c6b..ed9bf1256 100644 --- a/test/test_suite/compile_time_introspection/defined.c3t +++ b/test/test_suite/compile_time_introspection/defined.c3t @@ -38,11 +38,11 @@ fn void main() x++; $counter++; $endif - $if $defined($evaltype("Foo").$eval("ab")): + $if $defined($typefrom("Foo").$eval("ab")): x++; $counter++; $endif - $if $defined($evaltype("mymodule::Foo").$eval("bob")): + $if $defined($typefrom("mymodule::Foo").$eval("bob")): x++; $counter++; $endif diff --git a/test/test_suite/compile_time_introspection/nameof.c3t b/test/test_suite/compile_time_introspection/nameof.c3t index 5246aa0e9..12b273d56 100644 --- a/test/test_suite/compile_time_introspection/nameof.c3t +++ b/test/test_suite/compile_time_introspection/nameof.c3t @@ -10,11 +10,11 @@ int b; fn void main() { printf("%s\n", Foo.qnameof); - printf("%s\n", $evaltype("Foo").qnameof); + printf("%s\n", $typefrom("Foo").qnameof); printf("%s\n", Foo.nameof); - printf("%s\n", $evaltype("mymodule::Foo").nameof); + printf("%s\n", $typefrom("mymodule::Foo").nameof); printf("%s\n", Foo.extnameof); - printf("%s\n", $evaltype("Foo").extnameof); + printf("%s\n", $typefrom("Foo").extnameof); printf("%s\n", $qnameof(b)); printf("%s\n", $qnameof($eval("b"))); diff --git a/test/test_suite/compile_time_introspection/nameof_err.c3 b/test/test_suite/compile_time_introspection/nameof_err.c3 index f04049945..685a364ad 100644 --- a/test/test_suite/compile_time_introspection/nameof_err.c3 +++ b/test/test_suite/compile_time_introspection/nameof_err.c3 @@ -11,10 +11,10 @@ fn void main2() fn void main3() { - $evaltype("int").extnameof; // #error: 'int' does not have a property or method 'extnameof' + $typefrom("int").extnameof; // #error: 'int' does not have a property or method 'extnameof' } fn void main4() { - $evaltype("foo::int").extnameof; // #error: Only valid types may be resolved with $evaltype. + $typefrom("foo::int").extnameof; // #error: Only valid types can be resolved with $typefrom } diff --git a/test/test_suite/compile_time_introspection/sizeof.c3t b/test/test_suite/compile_time_introspection/sizeof.c3t index eaf721234..6c63568a3 100644 --- a/test/test_suite/compile_time_introspection/sizeof.c3t +++ b/test/test_suite/compile_time_introspection/sizeof.c3t @@ -3,17 +3,17 @@ import bar; import bar::abc; long x = Baz.sizeof; -short y = $evaltype("Baz").sizeof; +short y = $typefrom("Baz").sizeof; int z = bar::Baz.sizeof; int w = bar::Baz.sizeof; int v = bar::abc::Foo.sizeof; int x1 = $sizeof(x); int y1 = $sizeof($eval("y")); int a = Baz.y.sizeof; -int b = $evaltype("Deep").a.$eval("b").sizeof; +int b = $typefrom("Deep").a.$eval("b").sizeof; int c = Deep.a.b.c.sizeof; int d = Deep[][100].sizeof; -int e = $evaltype("Deep")[][100]**[100][]*.sizeof; +int e = $typefrom("Deep")[][100]**[100][]*.sizeof; int a2 = Baz.y.sizeof; int a3 = Baz.y.sizeof; int a4 = Baz.y.sizeof; diff --git a/test/test_suite/compile_time_introspection/sizeof_errors.c3 b/test/test_suite/compile_time_introspection/sizeof_errors.c3 index b07e18623..d6b8a0cbd 100644 --- a/test/test_suite/compile_time_introspection/sizeof_errors.c3 +++ b/test/test_suite/compile_time_introspection/sizeof_errors.c3 @@ -8,7 +8,7 @@ fn void a() fn void b() { - int x = $evaltype("Bazz").sizeof; // #error: 'Bazz' could not be found, did you spell it rig + int x = $typefrom("Bazz").sizeof; // #error: 'Bazz' could not be found, did you spell it rig } @@ -19,17 +19,17 @@ fn void e() fn void f() { - int x = $evaltype("bar::Bazy").sizeof; // #error: 'bar::Bazy' could not be found + int x = $typefrom("bar::Bazy").sizeof; // #error: 'bar::Bazy' could not be found } fn void g() { - int x = $evaltype("bar::").sizeof; // #error: Only valid types may be resolved with $evaltype + int x = $typefrom("bar::").sizeof; // #error: Only valid types can be resolved with $typefrom } fn void k() { - int v = $evaltype("int").x.sizeof; // #error: 'x'. + int v = $typefrom("int").x.sizeof; // #error: 'x'. } fn void l()