mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Extend "var" to allow type inference on variables.
This commit is contained in:
committed by
Christoffer Lerno
parent
5e184f04e7
commit
5d9a7ab0a6
@@ -28,16 +28,16 @@ fault VarCastResult
|
||||
**/
|
||||
macro void @scope(&variable; @body) @builtin
|
||||
{
|
||||
$typeof(variable) temp = variable;
|
||||
var temp = variable;
|
||||
defer variable = temp;
|
||||
@body();
|
||||
}
|
||||
|
||||
macro void @swap(&a, &b) @builtin
|
||||
{
|
||||
$typeof(a) temp = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
var temp = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,11 +95,11 @@ macro void unreachable($string = "Unreachable statement reached.") @builtin @nor
|
||||
|
||||
macro bitcast(expr, $Type) @builtin
|
||||
{
|
||||
var $size = (usz)($sizeof(expr));
|
||||
$assert($size == $Type.sizeof, "Cannot bitcast between types of different size.");
|
||||
$Type x = void;
|
||||
mem::copy(&x, &expr, $size, $Type.alignof, $alignof(expr));
|
||||
return x;
|
||||
var $size = (usz)($sizeof(expr));
|
||||
$assert($size == $Type.sizeof, "Cannot bitcast between types of different size.");
|
||||
$Type x = void;
|
||||
mem::copy(&x, &expr, $size, $Type.alignof, $alignof(expr));
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -80,7 +80,7 @@ macro min(x, ...) @builtin
|
||||
$if ($vacount == 1):
|
||||
return less(x, $vaarg(0)) ? x : $vaarg(0);
|
||||
$else:
|
||||
$typeof(x) result = x;
|
||||
var result = x;
|
||||
$for (var $i = 0; $i < $vacount; $i++):
|
||||
if (less($vaarg($i), result))
|
||||
{
|
||||
@@ -96,7 +96,7 @@ macro max(x, ...) @builtin
|
||||
$if ($vacount == 1):
|
||||
return greater(x, $vaarg(0)) ? x : $vaarg(0);
|
||||
$else:
|
||||
$typeof(x) result = x;
|
||||
var result = x;
|
||||
$for (var $i = 0; $i < $vacount; $i++):
|
||||
if (greater($vaarg($i), result))
|
||||
{
|
||||
|
||||
@@ -2309,6 +2309,7 @@ INLINE Type *type_from_inferred(Type *flattened, Type *element_type, unsigned co
|
||||
INLINE bool type_len_is_inferred(Type *type);
|
||||
INLINE bool type_is_substruct(Type *type);
|
||||
INLINE Type *type_flatten_for_bitstruct(Type *type);
|
||||
INLINE const char *type_invalid_storage_type_name(Type *type);
|
||||
INLINE bool type_is_float(Type *type);
|
||||
INLINE bool type_is_optional(Type *type);
|
||||
INLINE bool type_is_optional_type(Type *type);
|
||||
@@ -2559,6 +2560,21 @@ INLINE bool type_is_float(Type *type)
|
||||
return kind >= TYPE_FLOAT_FIRST && kind <= TYPE_FLOAT_LAST;
|
||||
}
|
||||
|
||||
INLINE const char *type_invalid_storage_type_name(Type *type)
|
||||
{
|
||||
switch (type->type_kind)
|
||||
{
|
||||
case TYPE_MEMBER:
|
||||
return "a member reference";
|
||||
case TYPE_UNTYPED_LIST:
|
||||
return "an untyped list";
|
||||
case TYPE_TYPEINFO:
|
||||
return "a type";
|
||||
default:
|
||||
UNREACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
INLINE bool type_is_invalid_storage_type(Type *type)
|
||||
{
|
||||
if (!type) return false;
|
||||
|
||||
@@ -860,6 +860,20 @@ Decl *parse_var_decl(ParseContext *c)
|
||||
Decl *decl;
|
||||
switch (c->tok)
|
||||
{
|
||||
case TOKEN_CONST_IDENT:
|
||||
SEMA_ERROR_HERE("Constants must be declared using 'const' not 'var'.");
|
||||
return poisoned_decl;
|
||||
case TOKEN_IDENT:
|
||||
decl = DECL_VAR_NEW(NULL, VARDECL_LOCAL, VISIBLE_LOCAL);
|
||||
advance(c);
|
||||
if (!tok_is(c, TOKEN_EQ))
|
||||
{
|
||||
SEMA_ERROR_HERE("'var' must always have an initial value, or the type cannot be inferred.");
|
||||
return false;
|
||||
}
|
||||
advance_and_verify(c, TOKEN_EQ);
|
||||
ASSIGN_EXPR_OR_RET(decl->var.init_expr, parse_expr(c), poisoned_decl);
|
||||
break;
|
||||
case TOKEN_CT_IDENT:
|
||||
decl = DECL_VAR_NEW(NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL);
|
||||
advance(c);
|
||||
|
||||
@@ -2341,11 +2341,11 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local)
|
||||
|
||||
// We expect a constant to actually be parsed correctly so that it has a value, so
|
||||
// this should always be true.
|
||||
assert(decl->var.type_info || decl->var.kind == VARDECL_CONST);
|
||||
assert(decl->var.type_info || decl->var.init_expr);
|
||||
|
||||
bool is_global = decl->var.kind == VARDECL_GLOBAL || !local;
|
||||
|
||||
if (!sema_analyse_attributes_for_var(context, decl)) return false;
|
||||
if (!sema_analyse_attributes_for_var(context, decl)) return decl_poison(decl);
|
||||
|
||||
if (is_global)
|
||||
{
|
||||
@@ -2358,42 +2358,59 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local)
|
||||
if (context->current_macro)
|
||||
{
|
||||
SEMA_ERROR(decl, "Macros with declarations may not be used outside of functions.");
|
||||
return false;
|
||||
return decl_poison(decl);
|
||||
}
|
||||
SEMA_ERROR(decl, "Variable declarations may not be used outside of functions.");
|
||||
return false;
|
||||
return decl_poison(decl);
|
||||
}
|
||||
// Add a local to the current context, will throw error on shadowing.
|
||||
if (!sema_add_local(context, decl)) return decl_poison(decl);
|
||||
}
|
||||
|
||||
// 1. Local or global constants: const int FOO = 123.
|
||||
if (decl->var.kind == VARDECL_CONST)
|
||||
if (!decl->var.type_info)
|
||||
{
|
||||
Expr *init_expr = decl->var.init_expr;
|
||||
// 1a. We require an init expression.
|
||||
if (!init_expr)
|
||||
{
|
||||
assert(decl->var.kind == VARDECL_CONST);
|
||||
SEMA_ERROR(decl, "Constants need to have an initial value.");
|
||||
return false;
|
||||
return decl_poison(decl);
|
||||
}
|
||||
if (decl->var.kind == VARDECL_LOCAL && !context->current_macro)
|
||||
{
|
||||
SEMA_ERROR(decl, "Defining a variable using 'var %s = ...' is only allowed inside a macro.", decl->name);
|
||||
return decl_poison(decl);
|
||||
}
|
||||
assert(!decl->var.no_init);
|
||||
if (!decl->var.type_info)
|
||||
{
|
||||
if (!sema_analyse_expr(context, init_expr)) return false;
|
||||
if (!sema_analyse_expr(context, init_expr)) return decl_poison(decl);
|
||||
if (is_global && !expr_is_constant_eval(init_expr, CONSTANT_EVAL_GLOBAL_INIT))
|
||||
{
|
||||
SEMA_ERROR(init_expr, "This expression cannot be evaluated at compile time.");
|
||||
return false;
|
||||
return decl_poison(decl);
|
||||
}
|
||||
decl->type = init_expr->type;
|
||||
if (type_is_invalid_storage_type(init_expr->type))
|
||||
{
|
||||
SEMA_ERROR(init_expr, "A value of type '%s' cannot be used as a constant.", type_quoted_error_string(init_expr->type));
|
||||
return false;
|
||||
if (init_expr->type == type_untypedlist)
|
||||
{
|
||||
SEMA_ERROR(init_expr, "The type of an untyped list cannot be inferred, you can try adding an explicit type to solve this.");
|
||||
}
|
||||
else if (decl->var.kind == VARDECL_CONST)
|
||||
{
|
||||
SEMA_ERROR(init_expr, "You cannot initialize a constant to %s, but you can assign the expression to a compile time variable.", type_invalid_storage_type_name(init_expr->type));
|
||||
}
|
||||
else
|
||||
{
|
||||
SEMA_ERROR(init_expr, "You can't store a compile time type in a variable.");
|
||||
}
|
||||
return decl_poison(decl);
|
||||
}
|
||||
if (!decl->alignment) decl->alignment = type_alloca_alignment(decl->type);
|
||||
if (!sema_analyse_decl_type(context, decl->type, init_expr->span)) return false;
|
||||
if (!sema_analyse_decl_type(context, decl->type, init_expr->span)) return decl_poison(decl);
|
||||
// Skip further evaluation.
|
||||
goto EXIT_OK;
|
||||
}
|
||||
@@ -2402,12 +2419,12 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local)
|
||||
if (!sema_resolve_type_info_maybe_inferred(context, decl->var.type_info, decl->var.init_expr != NULL)) return decl_poison(decl);
|
||||
|
||||
decl->type = decl->var.type_info->type;
|
||||
if (!sema_analyse_decl_type(context, decl->type, decl->var.type_info->span)) return false;
|
||||
if (!sema_analyse_decl_type(context, decl->type, decl->var.type_info->span)) return decl_poison(decl);
|
||||
bool is_static = decl->var.is_static;
|
||||
if (is_static && context->call_env.pure)
|
||||
{
|
||||
SEMA_ERROR(decl, "'@pure' functions may not have static variables.");
|
||||
return false;
|
||||
return decl_poison(decl);
|
||||
}
|
||||
if (is_static && !decl->has_extname)
|
||||
{
|
||||
@@ -2422,7 +2439,7 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local)
|
||||
if (!decl->var.init_expr && infer_len)
|
||||
{
|
||||
SEMA_ERROR(decl->var.type_info, "The length cannot be inferred without an initializer.");
|
||||
return false;
|
||||
return decl_poison(decl);
|
||||
}
|
||||
if (decl->var.init_expr)
|
||||
{
|
||||
@@ -2449,7 +2466,7 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local)
|
||||
if (type_is_len_inferred(init->type))
|
||||
{
|
||||
SEMA_ERROR(decl->var.type_info, "You cannot use [*] and [<*>] underlying types with initializers.");
|
||||
return false;
|
||||
return decl_poison(decl);
|
||||
}
|
||||
decl->type = cast_infer_len(decl->type, init->type);
|
||||
}
|
||||
@@ -2460,6 +2477,7 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local)
|
||||
if ((is_global || decl->var.is_static) && !expr_is_constant_eval(init_expr, CONSTANT_EVAL_GLOBAL_INIT))
|
||||
{
|
||||
SEMA_ERROR(init_expr, "The expression must be a constant value.");
|
||||
return decl_poison(decl);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define COMPILER_VERSION "0.3.82"
|
||||
#define COMPILER_VERSION "0.3.83"
|
||||
40
test/test_suite/assignment/var_variable.c3t
Normal file
40
test/test_suite/assignment/var_variable.c3t
Normal file
@@ -0,0 +1,40 @@
|
||||
module test;
|
||||
import std::io;
|
||||
|
||||
|
||||
macro @foo(;@body)
|
||||
{
|
||||
var i = 1.0;
|
||||
@body();
|
||||
}
|
||||
|
||||
fn void main()
|
||||
{
|
||||
@foo() { int j = 1; };
|
||||
@foo() { var j = 1.0; }; // #error: is only allowed inside
|
||||
}
|
||||
|
||||
fn void test()
|
||||
{
|
||||
var g = 1; // #error: is only allowed inside
|
||||
}
|
||||
|
||||
macro void test2m()
|
||||
{
|
||||
var h = { 1, 2 }; // #error: cannot be inferred
|
||||
}
|
||||
|
||||
fn void test2()
|
||||
{
|
||||
test2m();
|
||||
}
|
||||
struct Foo { int a; }
|
||||
macro void test3m()
|
||||
{
|
||||
var $foo = Foo.membersof;
|
||||
var h = $foo[0]; // #error: compile time type
|
||||
}
|
||||
fn void test3()
|
||||
{
|
||||
test3m();
|
||||
}
|
||||
40
test/test_suite2/assignment/var_variable.c3t
Normal file
40
test/test_suite2/assignment/var_variable.c3t
Normal file
@@ -0,0 +1,40 @@
|
||||
module test;
|
||||
import std::io;
|
||||
|
||||
|
||||
macro @foo(;@body)
|
||||
{
|
||||
var i = 1.0;
|
||||
@body();
|
||||
}
|
||||
|
||||
fn void main()
|
||||
{
|
||||
@foo() { int j = 1; };
|
||||
@foo() { var j = 1.0; }; // #error: is only allowed inside
|
||||
}
|
||||
|
||||
fn void test()
|
||||
{
|
||||
var g = 1; // #error: is only allowed inside
|
||||
}
|
||||
|
||||
macro void test2m()
|
||||
{
|
||||
var h = { 1, 2 }; // #error: cannot be inferred
|
||||
}
|
||||
|
||||
fn void test2()
|
||||
{
|
||||
test2m();
|
||||
}
|
||||
struct Foo { int a; }
|
||||
macro void test3m()
|
||||
{
|
||||
var $foo = Foo.membersof;
|
||||
var h = $foo[0]; // #error: compile time type
|
||||
}
|
||||
fn void test3()
|
||||
{
|
||||
test3m();
|
||||
}
|
||||
Reference in New Issue
Block a user