Extend "var" to allow type inference on variables.

This commit is contained in:
Christoffer Lerno
2022-10-13 09:26:15 +02:00
committed by Christoffer Lerno
parent 5e184f04e7
commit 5d9a7ab0a6
8 changed files with 155 additions and 27 deletions

View File

@@ -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;
}
/**

View File

@@ -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))
{

View File

@@ -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;

View File

@@ -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);

View File

@@ -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
{

View File

@@ -1 +1 @@
#define COMPILER_VERSION "0.3.82"
#define COMPILER_VERSION "0.3.83"

View 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();
}

View 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();
}