mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
$eval now also works with @foo, #foo, $Foo and $foo parameters #2114.
This commit is contained in:
@@ -6,12 +6,13 @@
|
|||||||
|
|
||||||
- `$typefrom` now also accepts a constant string, and so works like `$evaltype`.
|
- `$typefrom` now also accepts a constant string, and so works like `$evaltype`.
|
||||||
- `$evaltype` is deprecated in favour of `$typefrom`.
|
- `$evaltype` is deprecated in favour of `$typefrom`.
|
||||||
- `-0xFF` will now be a signed integer.
|
- Literal rules have changed, this makes `-0xFF` now a signed integer.
|
||||||
- Implicitly convert from constant typeid to Type in `$Type` assignment, and `$assignable`.
|
- Implicitly convert from constant typeid to Type in `$Type` assignment, and `$assignable`.
|
||||||
- Make $Type parameters accept constant typeid values.
|
- Make $Type parameters accept constant typeid values.
|
||||||
- Deprecate `foo.#bar`.
|
- Deprecate `foo.#bar`.
|
||||||
- Allow inference across `&&` #2172.
|
- Allow inference across `&&` #2172.
|
||||||
- Added support for custom file extensions in project.json targets.
|
- Added support for custom file extensions in project.json targets.
|
||||||
|
- `$eval` now also works with `@foo`, `#foo`, `$Foo` and `$foo` parameters #2114.
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
- `-2147483648`, MIN literals work correctly.
|
- `-2147483648`, MIN literals work correctly.
|
||||||
|
|||||||
@@ -340,15 +340,10 @@ Expr *sema_expr_analyse_ct_arg_index(SemaContext *context, Expr *index_expr, uns
|
|||||||
return context->macro_varargs[(size_t)index_val.i.low];
|
return context->macro_varargs[(size_t)index_val.i.low];
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr *sema_ct_eval_expr(SemaContext *context, bool is_type_eval, Expr *inner, bool report_missing)
|
Expr *sema_resolve_string_ident(SemaContext *context, Expr *inner, bool report_missing)
|
||||||
{
|
{
|
||||||
|
ASSERT_SPAN(inner, expr_is_const_string(inner));
|
||||||
Path *path = NULL;
|
Path *path = NULL;
|
||||||
if (!sema_analyse_ct_expr(context, inner)) return NULL;
|
|
||||||
if (!expr_is_const_string(inner))
|
|
||||||
{
|
|
||||||
SEMA_ERROR(inner, "'%s' expects a constant string as the argument.", is_type_eval ? "$evaltype" : "$eval");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
const char *interned_version = NULL;
|
const char *interned_version = NULL;
|
||||||
TokenType token = sema_splitpathref(inner->const_expr.bytes.ptr, inner->const_expr.bytes.len, &path, &interned_version);
|
TokenType token = sema_splitpathref(inner->const_expr.bytes.ptr, inner->const_expr.bytes.len, &path, &interned_version);
|
||||||
switch (token)
|
switch (token)
|
||||||
@@ -356,6 +351,19 @@ Expr *sema_ct_eval_expr(SemaContext *context, bool is_type_eval, Expr *inner, bo
|
|||||||
case TOKEN_CONST_IDENT:
|
case TOKEN_CONST_IDENT:
|
||||||
inner->unresolved_ident_expr.is_const = true;
|
inner->unresolved_ident_expr.is_const = true;
|
||||||
break;
|
break;
|
||||||
|
case TOKEN_HASH_IDENT:
|
||||||
|
if (path) goto NO_PATH;
|
||||||
|
inner->expr_kind = EXPR_HASH_IDENT;
|
||||||
|
inner->ct_ident_expr.identifier = interned_version;
|
||||||
|
inner->resolve_status = RESOLVE_NOT_DONE;
|
||||||
|
return inner;
|
||||||
|
case TOKEN_CT_IDENT:
|
||||||
|
if (path) goto NO_PATH;
|
||||||
|
inner->expr_kind = EXPR_CT_IDENT;
|
||||||
|
inner->ct_ident_expr.identifier = interned_version;
|
||||||
|
inner->resolve_status = RESOLVE_NOT_DONE;
|
||||||
|
return inner;
|
||||||
|
case TOKEN_AT_IDENT:
|
||||||
case TOKEN_IDENT:
|
case TOKEN_IDENT:
|
||||||
if (!interned_version)
|
if (!interned_version)
|
||||||
{
|
{
|
||||||
@@ -369,12 +377,16 @@ Expr *sema_ct_eval_expr(SemaContext *context, bool is_type_eval, Expr *inner, bo
|
|||||||
break;
|
break;
|
||||||
case TYPE_TOKENS:
|
case TYPE_TOKENS:
|
||||||
{
|
{
|
||||||
|
if (path) goto NO_PATH;
|
||||||
TypeInfo *info = type_info_new_base(type_from_token(token), inner->span);
|
TypeInfo *info = type_info_new_base(type_from_token(token), inner->span);
|
||||||
inner->expr_kind = EXPR_TYPEINFO;
|
inner->expr_kind = EXPR_TYPEINFO;
|
||||||
inner->resolve_status = RESOLVE_NOT_DONE;
|
inner->resolve_status = RESOLVE_NOT_DONE;
|
||||||
inner->type_expr = info;
|
inner->type_expr = info;
|
||||||
return inner;
|
return inner;
|
||||||
}
|
}
|
||||||
|
case TOKEN_CT_TYPE_IDENT:
|
||||||
|
if (path) goto NO_PATH;
|
||||||
|
FALLTHROUGH;
|
||||||
case TOKEN_TYPE_IDENT:
|
case TOKEN_TYPE_IDENT:
|
||||||
{
|
{
|
||||||
TypeInfo *info = type_info_new(TYPE_INFO_IDENTIFIER, inner->span);
|
TypeInfo *info = type_info_new(TYPE_INFO_IDENTIFIER, inner->span);
|
||||||
@@ -387,21 +399,31 @@ Expr *sema_ct_eval_expr(SemaContext *context, bool is_type_eval, Expr *inner, bo
|
|||||||
return inner;
|
return inner;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if (is_type_eval)
|
SEMA_ERROR(inner, "'%.*s' could not be resolved to a valid symbol.", (int)inner->const_expr.bytes.len, inner->const_expr.bytes.ptr);
|
||||||
{
|
return poisoned_expr;
|
||||||
SEMA_ERROR(inner, "Only valid types may be resolved with $evaltype.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SEMA_ERROR(inner, "Only plain function, variable and constant names may be resolved with $eval.");
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
inner->expr_kind = EXPR_UNRESOLVED_IDENTIFIER;
|
inner->expr_kind = EXPR_UNRESOLVED_IDENTIFIER;
|
||||||
inner->resolve_status = RESOLVE_NOT_DONE;
|
inner->resolve_status = RESOLVE_NOT_DONE;
|
||||||
inner->unresolved_ident_expr.ident = interned_version;
|
inner->unresolved_ident_expr.ident = interned_version;
|
||||||
inner->unresolved_ident_expr.path = path;
|
inner->unresolved_ident_expr.path = path;
|
||||||
return inner;
|
return inner;
|
||||||
|
NO_PATH:
|
||||||
|
if (report_missing)
|
||||||
|
{
|
||||||
|
SEMA_ERROR(inner, "Unexpected path in '%.*s'.", (int)inner->const_expr.bytes.len, inner->const_expr.bytes.ptr);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr *sema_ct_eval_expr(SemaContext *context, bool is_type_eval, Expr *inner, bool report_missing)
|
||||||
|
{
|
||||||
|
if (!sema_analyse_ct_expr(context, inner)) return NULL;
|
||||||
|
if (!expr_is_const_string(inner))
|
||||||
|
{
|
||||||
|
SEMA_ERROR(inner, "'%s' expects a constant string as the argument.", is_type_eval ? "$evaltype" : "$eval");
|
||||||
|
return poisoned_expr;
|
||||||
|
}
|
||||||
|
return sema_resolve_string_ident(context, inner, report_missing);
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr *expr_access_inline_member(Expr *parent, Decl *parent_decl)
|
Expr *expr_access_inline_member(Expr *parent, Decl *parent_decl)
|
||||||
@@ -4082,7 +4104,7 @@ RETRY:
|
|||||||
{
|
{
|
||||||
case EXPR_HASH_IDENT:
|
case EXPR_HASH_IDENT:
|
||||||
SEMA_DEPRECATED(child, "Using 'abc.#foo' access style is deprecated. Use 'abc.eval($foo)' instead.");
|
SEMA_DEPRECATED(child, "Using 'abc.#foo' access style is deprecated. Use 'abc.eval($foo)' instead.");
|
||||||
if (!sema_expr_fold_hash(context, child)) return false;
|
if (!sema_expr_fold_hash(context, child)) return NULL;
|
||||||
in_hash = true;
|
in_hash = true;
|
||||||
goto RETRY;
|
goto RETRY;
|
||||||
case EXPR_OTHER_CONTEXT:
|
case EXPR_OTHER_CONTEXT:
|
||||||
@@ -4109,6 +4131,7 @@ RETRY:
|
|||||||
ASSERT_SPAN(child, child->resolve_status != RESOLVE_DONE);
|
ASSERT_SPAN(child, child->resolve_status != RESOLVE_DONE);
|
||||||
// Only report missing if missing var is NULL
|
// Only report missing if missing var is NULL
|
||||||
Expr *result = sema_ct_eval_expr(context, false, child->inner_expr, missing == NULL);
|
Expr *result = sema_ct_eval_expr(context, false, child->inner_expr, missing == NULL);
|
||||||
|
if (!expr_ok(result)) return NULL;
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
if (missing) *missing = true;
|
if (missing) *missing = true;
|
||||||
@@ -9240,6 +9263,7 @@ RETRY:
|
|||||||
{
|
{
|
||||||
Expr *expr = type_info->unresolved_type_expr;
|
Expr *expr = type_info->unresolved_type_expr;
|
||||||
expr = sema_ct_eval_expr(context, true, expr, false);
|
expr = sema_ct_eval_expr(context, true, expr, false);
|
||||||
|
if (!expr_ok(expr)) return poisoned_type;
|
||||||
if (!expr) return NULL;
|
if (!expr) return NULL;
|
||||||
if (expr->expr_kind != EXPR_TYPEINFO)
|
if (expr->expr_kind != EXPR_TYPEINFO)
|
||||||
{
|
{
|
||||||
@@ -9691,8 +9715,12 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EXPR_CT_EVAL:
|
case EXPR_CT_EVAL:
|
||||||
success = sema_ct_eval_expr(active_context, "$eval", main_expr->inner_expr, false);
|
{
|
||||||
|
Expr *eval = sema_ct_eval_expr(active_context, "$eval", main_expr->inner_expr, false);
|
||||||
|
if (!expr_ok(eval)) return false;
|
||||||
|
success = eval != NULL;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case EXPR_HASH_IDENT:
|
case EXPR_HASH_IDENT:
|
||||||
{
|
{
|
||||||
Decl *decl = sema_resolve_symbol(active_context, main_expr->hash_ident_expr.identifier, NULL, main_expr->span);
|
Decl *decl = sema_resolve_symbol(active_context, main_expr->hash_ident_expr.identifier, NULL, main_expr->span);
|
||||||
@@ -10997,6 +11025,8 @@ TokenType sema_splitpathref(const char *string, ArraySize len, Path **path_ref,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (ch == '$' || ch == '@' || ch == '#') break; // $foo / @foo
|
||||||
|
|
||||||
return TOKEN_INVALID_TOKEN;
|
return TOKEN_INVALID_TOKEN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11016,7 +11046,20 @@ TokenType sema_splitpathref(const char *string, ArraySize len, Path **path_ref,
|
|||||||
}
|
}
|
||||||
if (len == 0) return TOKEN_INVALID_TOKEN;
|
if (len == 0) return TOKEN_INVALID_TOKEN;
|
||||||
uint32_t hash = FNV1_SEED;
|
uint32_t hash = FNV1_SEED;
|
||||||
for (size_t i = 0; i < len; i++)
|
size_t start = 0;
|
||||||
|
switch (string[0])
|
||||||
|
{
|
||||||
|
case '@':
|
||||||
|
case '$':
|
||||||
|
case '#':
|
||||||
|
hash = FNV1a(string[0], hash);
|
||||||
|
start = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = start; i < len; i++)
|
||||||
{
|
{
|
||||||
char c = string[i];
|
char c = string[i];
|
||||||
if (!char_is_alphanum_(c)) return TOKEN_INVALID_TOKEN;
|
if (!char_is_alphanum_(c)) return TOKEN_INVALID_TOKEN;
|
||||||
@@ -11030,10 +11073,12 @@ TokenType sema_splitpathref(const char *string, ArraySize len, Path **path_ref,
|
|||||||
case TOKEN_TYPE_IDENT:
|
case TOKEN_TYPE_IDENT:
|
||||||
case TOKEN_IDENT:
|
case TOKEN_IDENT:
|
||||||
case TOKEN_CONST_IDENT:
|
case TOKEN_CONST_IDENT:
|
||||||
return type;
|
case TOKEN_AT_IDENT:
|
||||||
|
case TOKEN_CT_TYPE_IDENT:
|
||||||
|
case TOKEN_CT_IDENT:
|
||||||
|
case TOKEN_HASH_IDENT:
|
||||||
case TYPE_TOKENS:
|
case TYPE_TOKENS:
|
||||||
if (!*path_ref) return type;
|
return type;
|
||||||
FALLTHROUGH;
|
|
||||||
default:
|
default:
|
||||||
*ident_ref = NULL;
|
*ident_ref = NULL;
|
||||||
return TOKEN_INVALID_TOKEN;
|
return TOKEN_INVALID_TOKEN;
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr);
|
|||||||
bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *struct_var, Decl *decl, bool call_var_optional, bool *no_match_ref);
|
bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *struct_var, Decl *decl, bool call_var_optional, bool *no_match_ref);
|
||||||
Expr *sema_expr_analyse_ct_arg_index(SemaContext *context, Expr *index_expr, unsigned *index_ref);
|
Expr *sema_expr_analyse_ct_arg_index(SemaContext *context, Expr *index_expr, unsigned *index_ref);
|
||||||
Expr *sema_ct_eval_expr(SemaContext *context, bool is_type_eval, Expr *inner, bool report_missing);
|
Expr *sema_ct_eval_expr(SemaContext *context, bool is_type_eval, Expr *inner, bool report_missing);
|
||||||
|
Expr *sema_resolve_string_ident(SemaContext *context, Expr *inner, bool report_missing);
|
||||||
bool sema_analyse_asm(SemaContext *context, AsmInlineBlock *block, Ast *asm_stmt);
|
bool sema_analyse_asm(SemaContext *context, AsmInlineBlock *block, Ast *asm_stmt);
|
||||||
|
|
||||||
bool sema_bit_assignment_check(SemaContext *context, Expr *right, Decl *member);
|
bool sema_bit_assignment_check(SemaContext *context, Expr *right, Decl *member);
|
||||||
|
|||||||
@@ -296,7 +296,7 @@ INLINE bool sema_resolve_evaltype(SemaContext *context, TypeInfo *type_info, Res
|
|||||||
SEMA_DEPRECATED(type_info, "$evaltype is deprecated, use $typefrom instead.");
|
SEMA_DEPRECATED(type_info, "$evaltype is deprecated, use $typefrom instead.");
|
||||||
Expr *expr = type_info->unresolved_type_expr;
|
Expr *expr = type_info->unresolved_type_expr;
|
||||||
Expr *inner = sema_ct_eval_expr(context, true, expr, true);
|
Expr *inner = sema_ct_eval_expr(context, true, expr, true);
|
||||||
if (!inner) return type_info_poison(type_info);
|
if (!inner || !expr_ok(inner)) return type_info_poison(type_info);
|
||||||
if (inner->expr_kind != EXPR_TYPEINFO)
|
if (inner->expr_kind != EXPR_TYPEINFO)
|
||||||
{
|
{
|
||||||
SEMA_ERROR(expr, "Only type names may be resolved with $evaltype.");
|
SEMA_ERROR(expr, "Only type names may be resolved with $evaltype.");
|
||||||
@@ -373,34 +373,15 @@ INLINE bool sema_resolve_typefrom(SemaContext *context, TypeInfo *type_info, Res
|
|||||||
RETURN_SEMA_ERROR(expr, "Expected a constant string or typeid value.");
|
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;
|
const char *bytes = expr->const_expr.bytes.ptr;
|
||||||
ArraySize bytes_len = expr->const_expr.bytes.len;
|
ArraySize len = expr->const_expr.bytes.len;
|
||||||
TokenType token = sema_splitpathref(bytes, bytes_len, &path, &interned_version);
|
Expr *typefrom = sema_resolve_string_ident(context, expr, true);
|
||||||
TypeInfo *info;
|
if (!typefrom || !expr_ok(typefrom)) return false;
|
||||||
switch (token)
|
if (typefrom->expr_kind != EXPR_TYPEINFO)
|
||||||
{
|
{
|
||||||
case TOKEN_IDENT:
|
RETURN_SEMA_ERROR(expr, "Expected a type, not a regular identifier '%.*s'.", (int)len, bytes);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
TypeInfo *info = typefrom->type_expr;
|
||||||
if (!sema_resolve_type(context, info, resolve_kind)) return false;
|
if (!sema_resolve_type(context, info, resolve_kind)) return false;
|
||||||
switch (sema_resolve_storage_type(context, info->type))
|
switch (sema_resolve_storage_type(context, info->type))
|
||||||
{
|
{
|
||||||
@@ -412,7 +393,7 @@ INLINE bool sema_resolve_typefrom(SemaContext *context, TypeInfo *type_info, Res
|
|||||||
type_info->type = info->type;
|
type_info->type = info->type;
|
||||||
return true;
|
return true;
|
||||||
case STORAGE_WILDCARD:
|
case STORAGE_WILDCARD:
|
||||||
RETURN_SEMA_ERROR(expr, "$typefrom failed to resolve \"%.*s\" to a definite type.", (int)bytes_len, bytes);
|
RETURN_SEMA_ERROR(expr, "$typefrom failed to resolve \"%.*s\" to a definite type.", (int)len, bytes);
|
||||||
case STORAGE_COMPILE_TIME:
|
case STORAGE_COMPILE_TIME:
|
||||||
RETURN_SEMA_ERROR(expr, "$typefrom does not support compile-time types.");
|
RETURN_SEMA_ERROR(expr, "$typefrom does not support compile-time types.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import std::io;
|
|||||||
|
|
||||||
fn void main()
|
fn void main()
|
||||||
{
|
{
|
||||||
$eval("foo()"); // #error: with $eval
|
$eval("foo()"); // #error: could not be resolved to a valid
|
||||||
}
|
}
|
||||||
fn void foo()
|
fn void foo()
|
||||||
{
|
{
|
||||||
|
|||||||
30
test/test_suite/compile_time/extended_eval.c3t
Normal file
30
test/test_suite/compile_time/extended_eval.c3t
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// #target: macos-x64
|
||||||
|
module test;
|
||||||
|
macro @abc(#a)
|
||||||
|
{
|
||||||
|
$eval("#a") = 1;
|
||||||
|
}
|
||||||
|
fn void main()
|
||||||
|
{
|
||||||
|
int $foo = 1;
|
||||||
|
$eval("$foo") = 2;
|
||||||
|
$typefrom("int") x;
|
||||||
|
var $Type = double;
|
||||||
|
$typefrom("$Type") y;
|
||||||
|
$eval("@abc")(x);
|
||||||
|
x = 0;
|
||||||
|
$eval("test::@abc")(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* #expect: test.ll
|
||||||
|
|
||||||
|
entry:
|
||||||
|
%x = alloca i32, align 4
|
||||||
|
%y = alloca double, align 8
|
||||||
|
store i32 0, ptr %x, align 4
|
||||||
|
store double 0.000000e+00, ptr %y, align 8
|
||||||
|
store i32 1, ptr %x, align 4
|
||||||
|
store i32 0, ptr %x, align 4
|
||||||
|
store i32 1, ptr %x, align 4
|
||||||
|
ret void
|
||||||
|
}
|
||||||
@@ -16,5 +16,5 @@ fn void main3()
|
|||||||
|
|
||||||
fn void main4()
|
fn void main4()
|
||||||
{
|
{
|
||||||
$typefrom("foo::int").extnameof; // #error: Only valid types can be resolved with $typefrom
|
$typefrom("foo::int").extnameof; // #error: Unexpected path in
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ fn void f()
|
|||||||
|
|
||||||
fn void g()
|
fn void g()
|
||||||
{
|
{
|
||||||
int x = $typefrom("bar::").sizeof; // #error: Only valid types can be resolved with $typefrom
|
int x = $typefrom("bar::").sizeof; // #error: could not be resolved to a valid symbol
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void k()
|
fn void k()
|
||||||
|
|||||||
Reference in New Issue
Block a user