Type capture in macros

This commit is contained in:
Christoffer Lerno
2025-08-26 02:45:52 +02:00
parent 1634217fc4
commit 88096e0556
12 changed files with 319 additions and 116 deletions

View File

@@ -6,10 +6,10 @@ import std::collections::pair, std::io;
@param [in] array
@param [in] element
@require @typekind(array) == SLICE || @typekind(array) == ARRAY
@require @typematch(array[0], element) : "array and element must have the same type"
@require $Arr.kindof == SLICE || $Arr.kindof == ARRAY
@require $Arr.inner == $Type : "array and element must have the same type"
*>
macro bool contains(array, element)
macro bool contains($Arr array, $Type element)
{
foreach (&item : array)
{

View File

@@ -91,10 +91,10 @@ bitstruct UInt128LE : uint128 @littleendian
@require @is_array_or_slice_of_char(bytes) : "argument must be an array, a pointer to an array or a slice of char"
@require is_bitorder($Type) : "type must be a bitorder integer"
*>
macro read(bytes, $Type)
macro read($Val bytes, $Type)
{
char[] s;
$switch @typekind(bytes):
$switch $Val.kindof:
$case POINTER:
s = (*bytes)[:$Type.sizeof];
$default:

View File

@@ -623,11 +623,11 @@ macro uint hash_array(array_ptr) @local
}
<*
@require @typekind(vec) == VECTOR
@require $Vec.typeof == VECTOR
*>
macro uint hash_vec(vec) @local
macro uint hash_vec($Vec vec) @local
{
var $len = $sizeof(vec.len * $typeof(vec).inner.sizeof);
var $len = $sizeof(vec.len * $Vec.inner.sizeof);
$if $len > 16:
return (uint)komi::hash(((char*)&&vec)[:$len]);

View File

@@ -232,11 +232,10 @@ macro usz? read_varint(stream, x_ptr)
}
<*
@require @is_outstream(stream)
@require @typekind(x).is_int()
@require $Type.kindof.is_int()
*>
macro usz? write_varint(stream, x)
macro usz? write_varint(stream, $Type x)
{
var $Type = $typeof(x);
const MAX = MAX_VARS[$Type.sizeof];
char[MAX] buffer @noinit;
usz i;

View File

@@ -69,14 +69,14 @@ enum RoundingMode : int
faultdef OVERFLOW, MATRIX_INVERSE_DOESNT_EXIST;
<*
@require types::is_numerical($typeof(x)) : `The input must be a numerical value or numerical vector`
@require types::is_numerical($Num) : `The input must be a numerical value or numerical vector`
*>
macro deg_to_rad(x) => x * PI / 180;
macro deg_to_rad($Num x) => x * PI / 180;
<*
@require types::is_numerical($typeof(x)) : `The input must be a numerical value or numerical vector`
@require types::is_numerical($Num) : `The input must be a numerical value or numerical vector`
*>
macro abs(x) => $$abs(x);
macro abs($Num x) => $$abs(x);
<*
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
@@ -103,13 +103,12 @@ macro is_approx_rel(x, y, eps)
<*
@require values::@is_int(x) : `The input must be an integer`
*>
macro sign(x)
macro $Num sign($Num x)
{
var $Type = $typeof(x);
$if $Type.kindof == UNSIGNED_INT:
return ($Type)(x > 0);
$if $Num.kindof == UNSIGNED_INT:
return ($Num)(x > 0);
$else
return ($Type)(x > 0) - ($Type)(x < 0);
return ($Num)(x > 0) - ($Num)(x < 0);
$endif
}
@@ -117,9 +116,9 @@ macro sign(x)
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
@require values::@is_int(y) || values::@is_float(y) : "Expected an integer or floating point value"
*>
macro atan2(x, y)
macro atan2($Num1 x, $Num2 y)
{
$if @typeis(x, float) && @typeis(y, float):
$if $Num1 == float || $Num2 == float:
return _atan2f(x, y);
$else
return _atan2(x, y);
@@ -132,12 +131,12 @@ macro atan2(x, y)
@require @typematch(sinp, cosp) : "Expected sinp and cosp to have the same type"
@require $defined(*sinp = x) : "Expected x and sinp/cosp to have the same type"
*>
macro sincos_ref(x, sinp, cosp)
macro void sincos_ref($Num x, $Num* sinp, $Num* cosp)
{
$if @typeis(sinp, float*.typeid):
return _sincosf(x, sinp, cosp);
$if $Num == float:
_sincosf(x, sinp, cosp);
$else
return _sincos(x, sinp, cosp);
_sincos(x, sinp, cosp);
$endif
}
@@ -147,9 +146,9 @@ macro sincos_ref(x, sinp, cosp)
@param x : `the angle in radians`
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
*>
macro sincos(x)
macro sincos($Num x)
{
$if @typeis(x, float):
$if $Num == float:
float[<2>] v @noinit;
_sincosf(x, &v[0], &v[1]);
$else
@@ -162,9 +161,9 @@ macro sincos(x)
<*
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
*>
macro atan(x)
macro atan($Num x)
{
$if @typeis(x, float):
$if $Num == float:
return _atanf(x);
$else
return _atan(x);
@@ -174,9 +173,9 @@ macro atan(x)
<*
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
*>
macro atanh(x)
macro atanh($Num x)
{
$if @typeis(x, float):
$if $Num == float:
return _atanhf(x);
$else
return _atanh(x);
@@ -186,9 +185,9 @@ macro atanh(x)
<*
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
*>
macro acos(x)
macro acos($Num x)
{
$if @typeis(x, float):
$if $Num == float:
return _acosf(x);
$else
return _acos(x);
@@ -198,9 +197,9 @@ macro acos(x)
<*
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
*>
macro acosh(x)
macro acosh($Num x)
{
$if @typeis(x, float):
$if $Num == float:
return _acoshf(x);
$else
return _acosh(x);
@@ -210,9 +209,9 @@ macro acosh(x)
<*
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
*>
macro asin(x)
macro asin($Num x)
{
$if @typeis(x, float):
$if $Num == float:
return _asinf(x);
$else
return _asin(x);
@@ -222,9 +221,9 @@ macro asin(x)
<*
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
*>
macro asinh(x)
macro asinh($Num x)
{
$if @typeis(x, float):
$if $Num == float:
return _asinhf(x);
$else
return _asinh(x);
@@ -1111,23 +1110,23 @@ fn float _frexpf(float x, int* e)
}
}
macro overflow_add_helper(x, y) @local
macro $Num overflow_add_helper($Num x, $Num y) @local
{
$typeof(x) res @noinit;
$Num res @noinit;
if ($$overflow_add(x, y, &res)) return OVERFLOW?;
return res;
}
macro overflow_sub_helper(x, y) @local
macro $Num overflow_sub_helper($Num x, $Num y) @local
{
$typeof(x) res @noinit;
$Num res @noinit;
if ($$overflow_sub(x, y, &res)) return OVERFLOW?;
return res;
}
macro overflow_mul_helper(x, y) @local
macro $Num overflow_mul_helper($Num x, $Num y) @local
{
$typeof(x) res @noinit;
$Num res @noinit;
if ($$overflow_mul(x, y, &res)) return OVERFLOW?;
return res;
}
@@ -1135,29 +1134,23 @@ macro overflow_mul_helper(x, y) @local
<*
@param [&out] out : "Where the result of the addition is stored"
@return "Whether the addition resulted in an integer overflow"
@require @typematch(a, b) : "a and b must be the same type"
@require values::@is_flat_intlike(a) &&& values::@is_flat_intlike(b) : "a and b must both be integer or integer vector based"
@require $defined(*out) &&& @typematch(*out, a) : "out must be a pointer of the same type as a and b"
*>
macro bool overflow_add(a, b, out) => $$overflow_add(a, b, out);
*>
macro bool overflow_add($Num a, $Num b, $Num* out) => $$overflow_add(a, b, out);
<*
@param [&out] out : "Where the result of the subtraction is stored"
@return "Whether the subtraction resulted in an integer overflow"
@require @typematch(a, b) : "a and b must be the same type"
@require values::@is_flat_intlike(a) &&& values::@is_flat_intlike(b) : "a and b must both be integer or integer vector based"
@require $defined(*out) &&& @typematch(*out, a) : "out must be a pointer of the same type as a and b"
*>
macro bool overflow_sub(a, b, out) => $$overflow_sub(a, b, out);
*>
macro bool overflow_sub($Num a, $Num b, $Num* out) => $$overflow_sub(a, b, out);
<*
@param [&out] out : "Where the result of the multiplication is stored"
@return "Whether the multiplication resulted in an integer overflow"
@require @typematch(a, b) : "a and b must be the same type"
@require values::@is_flat_intlike(a) &&& values::@is_flat_intlike(b) : "a and b must both be integer or integer vector based"
@require $defined(*out) &&& @typematch(*out, a) : "out must be a pointer of the same type as a and b"
*>
macro bool overflow_mul(a, b, out) => $$overflow_mul(a, b, out);
macro bool overflow_mul($Num a, $Num b, $Num* out) => $$overflow_mul(a, b, out);
<*
@require types::is_vector($Type) || ($Type.kindof == ARRAY &&& types::is_numerical($typefrom($Type.inner)))
@@ -1171,10 +1164,9 @@ macro iota($Type)
return $val;
}
macro mul_div_helper(val, mul, div) @private
macro $Num mul_div_helper($Num val, $Num mul, $Num div) @private
{
var $Type = $typeof(val);
return ($Type)(($Type)mul * (val / ($Type)div) + ($Type)mul * (val % ($Type)div) / ($Type)div);
return mul * (val / div) + mul * (val % div) / div;
}
macro char char.muldiv(self, char mul, char div) => mul_div_helper(self, mul, div);
macro ichar ichar.muldiv(self, ichar mul, ichar div) => mul_div_helper(self, mul, div);
@@ -1194,49 +1186,49 @@ macro bool @is_same_vector_or_scalar(#vector_value, #vector_or_scalar) @private
@require @is_same_vector_or_scalar(self, mul) : `mul must be a vector of the same type as self, or be an integer scalar`
@require @is_same_vector_or_scalar(self, div) : `div must be a vector of the same type as self, or be an integer scalar`
*>
macro char[<*>] char[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
macro char[<*>] char[<*>].muldiv(self, mul, div) => mul_div_helper(self, ($typeof(self))mul, ($typeof(self))div);
<*
@require @is_same_vector_or_scalar(self, mul) : `mul must be a vector of the same type as self, or be an integer scalar`
@require @is_same_vector_or_scalar(self, div) : `div must be a vector of the same type as self, or be an integer scalar`
*>
macro ichar[<*>] ichar[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
macro ichar[<*>] ichar[<*>].muldiv(self, mul, div) => mul_div_helper(self, ($typeof(self))mul, ($typeof(self))div);
<*
@require @is_same_vector_or_scalar(self, mul) : `mul must be a vector of the same type as self, or be an integer scalar`
@require @is_same_vector_or_scalar(self, div) : `div must be a vector of the same type as self, or be an integer scalar`
*>
macro short[<*>] short[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
macro short[<*>] short[<*>].muldiv(self, mul, div) => mul_div_helper(self, ($typeof(self))mul, ($typeof(self))div);
<*
@require @is_same_vector_or_scalar(self, mul) : `mul must be a vector of the same type as self, or be an integer scalar`
@require @is_same_vector_or_scalar(self, div) : `div must be a vector of the same type as self, or be an integer scalar`
*>
macro ushort[<*>] ushort[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
macro ushort[<*>] ushort[<*>].muldiv(self, mul, div) => mul_div_helper(self, ($typeof(self))mul, ($typeof(self))div);
<*
@require @is_same_vector_or_scalar(self, mul) : `mul must be a vector of the same type as self, or be an integer scalar`
@require @is_same_vector_or_scalar(self, div) : `div must be a vector of the same type as self, or be an integer scalar`
*>
macro int[<*>] int[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
macro int[<*>] int[<*>].muldiv(self, mul, div) => mul_div_helper(self, ($typeof(self))mul, ($typeof(self))div);
<*
@require @is_same_vector_or_scalar(self, mul) : `mul must be a vector of the same type as self, or be an integer scalar`
@require @is_same_vector_or_scalar(self, div) : `div must be a vector of the same type as self, or be an integer scalar`
*>
macro uint[<*>] uint[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
macro uint[<*>] uint[<*>].muldiv(self, mul, div) => mul_div_helper(self, ($typeof(self))mul, ($typeof(self))div);
<*
@require @is_same_vector_or_scalar(self, mul) : `mul must be a vector of the same type as self, or be an integer scalar`
@require @is_same_vector_or_scalar(self, div) : `div must be a vector of the same type as self, or be an integer scalar`
*>
macro long[<*>] long[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
macro long[<*>] long[<*>].muldiv(self, mul, div) => mul_div_helper(self, ($typeof(self))mul, ($typeof(self))div);
<*
@require @is_same_vector_or_scalar(self, mul) : `mul must be a vector of the same type as self, or be an integer scalar`
@require @is_same_vector_or_scalar(self, div) : `div must be a vector of the same type as self, or be an integer scalar`
*>
macro ulong[<*>] ulong[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
macro ulong[<*>] ulong[<*>].muldiv(self, mul, div) => mul_div_helper(self, ($typeof(self))mul, ($typeof(self))div);
<*
@require types::is_int($typeof(a)) : `The input must be an integer`

View File

@@ -458,6 +458,7 @@ typedef struct VarDecl_
bool is_written : 1;
bool is_addr : 1;
bool self_addr : 1;
bool is_typecapture : 1;
bool is_threadlocal : 1;
bool no_init : 1;
bool no_alias : 1;
@@ -537,6 +538,7 @@ struct Signature_
bool is_macro : 1;
bool is_at_macro : 1;
bool is_safemacro : 1;
bool is_capture_return : 1;
Variadic variadic : 3;
CallABI abi : 8;
unsigned vararg_index;
@@ -3528,6 +3530,7 @@ INLINE Decl *decl_flatten(Decl *decl)
return decl;
}
static inline DeclKind decl_from_token(TokenType type)
{
if (type == TOKEN_STRUCT) return DECL_STRUCT;

View File

@@ -1314,11 +1314,11 @@ typedef enum
{
TYPE_COMPRESSED_NONE = 0,
TYPE_COMPRESSED_PTR = 1,
TYPE_COMPRESSED_SUB = 2,
TYPE_COMPRESSED_SUBPTR = 3,
TYPE_COMPRESSED_SLICE = 2,
TYPE_COMPRESSED_SLICEPTR = 3,
TYPE_COMPRESSED_PTRPTR = 4,
TYPE_COMPRESSED_PTRSUB = 5,
TYPE_COMPRESSED_SUBSUB = 6,
TYPE_COMPRESSED_PTRSLICE = 5,
TYPE_COMPRESSED_SLICESLICE = 6,
} TypeInfoCompressedKind;
typedef enum

View File

@@ -187,19 +187,19 @@ void print_type(FILE *file, TypeInfo *type)
case TYPE_COMPRESSED_PTR:
fputs("*", file);
break;
case TYPE_COMPRESSED_SUB:
case TYPE_COMPRESSED_SLICE:
fputs("[]", file);
break;
case TYPE_COMPRESSED_SUBPTR:
case TYPE_COMPRESSED_SLICEPTR:
fputs("[]*", file);
break;
case TYPE_COMPRESSED_PTRPTR:
fputs("**", file);
break;
case TYPE_COMPRESSED_PTRSUB:
case TYPE_COMPRESSED_PTRSLICE:
fputs("*[]", file);
break;
case TYPE_COMPRESSED_SUBSUB:
case TYPE_COMPRESSED_SLICESLICE:
fputs("[][]", file);
break;
}

View File

@@ -561,13 +561,13 @@ static inline TypeInfo *parse_array_type_index(ParseContext *c, TypeInfo *type)
switch (type->subtype)
{
case TYPE_COMPRESSED_NONE:
type->subtype = TYPE_COMPRESSED_SUB;
type->subtype = TYPE_COMPRESSED_SLICE;
break;
case TYPE_COMPRESSED_PTR:
type->subtype = TYPE_COMPRESSED_PTRSUB;
type->subtype = TYPE_COMPRESSED_PTRSLICE;
break;
case TYPE_COMPRESSED_SUB:
type->subtype = TYPE_COMPRESSED_SUBSUB;
case TYPE_COMPRESSED_SLICE:
type->subtype = TYPE_COMPRESSED_SLICESLICE;
break;
default:
goto DIRECT_SLICE;
@@ -653,8 +653,8 @@ static inline TypeInfo *parse_type_with_base_maybe_generic(ParseContext *c, Type
case TYPE_COMPRESSED_PTR:
type_info->subtype = TYPE_COMPRESSED_PTRPTR;
break;
case TYPE_COMPRESSED_SUB:
type_info->subtype = TYPE_COMPRESSED_SUBPTR;
case TYPE_COMPRESSED_SLICE:
type_info->subtype = TYPE_COMPRESSED_SLICEPTR;
break;
default:
{
@@ -1451,13 +1451,25 @@ static bool parse_next_is_typed_parameter(ParseContext *c, ParameterParseKind pa
case TOKEN_CT_VATYPE:
return parse_kind == PARAM_PARSE_LAMBDA || parse_kind == PARAM_PARSE_CALL;
case TOKEN_CT_TYPE_IDENT:
if (parse_kind == PARAM_PARSE_LAMBDA) return true;
if (parse_kind != PARAM_PARSE_CALL) return false;
switch (parse_kind)
{
case PARAM_PARSE_MACRO:
case PARAM_PARSE_CALL:
break;
case PARAM_PARSE_FUNC:
case PARAM_PARSE_BODY:
case PARAM_PARSE_ATTR:
return false;
case PARAM_PARSE_LAMBDA:
return true;
}
switch (peek(c))
{
case TOKEN_IDENT:
case TOKEN_HASH_IDENT:
case TOKEN_CT_IDENT:
case TOKEN_LBRACKET:
case TOKEN_STAR:
return true;
default:
return false;
@@ -1517,8 +1529,10 @@ bool parse_parameters(ParseContext *c, Decl ***params_ref, Variadic *variadic, i
// Now we have the following possibilities: "foo", "Foo foo", "Foo... foo", "foo...", "Foo"
TypeInfo *type = NULL;
bool is_type_capture = false;
if (parse_next_is_typed_parameter(c, parse_kind))
{
is_type_capture = parse_kind == PARAM_PARSE_MACRO && tok_is(c, TOKEN_CT_TYPE_IDENT);
// Parse the type,
ASSIGN_TYPE_OR_RET(type, parse_optional_type(c), false);
ellipsis = try_consume(c, TOKEN_ELLIPSIS);
@@ -1669,6 +1683,7 @@ bool parse_parameters(ParseContext *c, Decl ***params_ref, Variadic *variadic, i
}
Decl *param = decl_new_var(name, span, type, param_kind);
param->var.type_info = type ? type_infoid(type) : 0;
if (is_type_capture) param->var.is_typecapture = true;
param->var.self_addr = ref;
if (!parse_attributes(c, &param->attributes, NULL, NULL, NULL)) return false;
if (!no_name)
@@ -2370,6 +2385,10 @@ static inline bool parse_func_macro_header(ParseContext *c, Decl *decl)
{
RETURN_PRINT_ERROR_HERE("This is a reserved keyword and can't be used as a macro name.");
}
if (is_macro && tok_is(c, TOKEN_CT_TYPE_IDENT))
{
decl->func_decl.signature.is_capture_return = true;
}
ASSIGN_TYPE_OR_RET(rtype, parse_optional_type(c), false);
// 4. We might have a type here, if so then we read it.

View File

@@ -1103,13 +1103,14 @@ static inline bool sema_analyse_signature(SemaContext *context, Signature *sig,
bool is_macro = sig->is_macro;
bool is_macro_at_name = sig->is_at_macro || sig->is_safemacro;
// Check return type
ASSERT(sig->rtype || sig->is_macro);
TypeInfoId sig_rtype = sig->is_capture_return ? 0 : sig->rtype;
ASSERT(sig_rtype || sig->is_macro);
Type *rtype = NULL;
int format_index = (int)sig->attrs.format - 1;
if (sig->rtype)
if (sig_rtype)
{
TypeInfo *rtype_info = type_infoptr(sig->rtype);
if (!sema_resolve_type_info(context, type_infoptr(sig->rtype),
TypeInfo *rtype_info = type_infoptr(sig_rtype);
if (!sema_resolve_type_info(context, rtype_info,
is_macro ? RESOLVE_TYPE_ALLOW_INFER
: RESOLVE_TYPE_DEFAULT)) return false;
rtype = rtype_info->type;
@@ -1272,7 +1273,7 @@ static inline bool sema_analyse_signature(SemaContext *context, Signature *sig,
param->unit = context->unit;
ASSERT(param->decl_kind == DECL_VAR);
VarDeclKind var_kind = param->var.kind;
TypeInfo *type_info = type_infoptrzero(param->var.type_info);
TypeInfo *type_info = param->var.is_typecapture ? NULL: type_infoptrzero(param->var.type_info);
if (type_info)
{
if (!sema_resolve_type_info(context, type_info,
@@ -1835,7 +1836,7 @@ static bool sema_analyse_operator_common(SemaContext *context, Decl *method, Typ
RETURN_SEMA_ERROR(method, "Not enough parameters, '%s' requires %u.", method->name, (unsigned)parameters);
}
if (!signature->rtype) RETURN_SEMA_ERROR(method, "The return value must be explicitly typed for '%s'.", method->name);
if (signature->is_capture_return || !signature->rtype) RETURN_SEMA_ERROR(method, "The return value must be explicitly typed for '%s'.", method->name);
FOREACH(Decl *, param, params)
{
@@ -2055,7 +2056,7 @@ static inline bool sema_analyse_operator_arithmetics(SemaContext *context, Decl
{
RETURN_SEMA_ERROR(method, "Not enough parameters, '%s' requires 2 parameters.", method->name);
}
if (!signature->rtype) RETURN_SEMA_ERROR(method, "The return value must be explicitly typed for '%s'.", method->name);
if (signature->is_capture_return || !signature->rtype) RETURN_SEMA_ERROR(method, "The return value must be explicitly typed for '%s'.", method->name);
TypeInfo *rtype = type_infoptr(signature->rtype);
if (IS_OPTIONAL(rtype))
{
@@ -2673,6 +2674,7 @@ static inline Decl *sema_find_interface_for_method(SemaContext *context, Canonic
*/
static inline bool sema_compare_method_with_interface(SemaContext *context, Decl *decl, Decl *implemented_method)
{
assert(decl->decl_kind == DECL_FUNC);
Signature interface_sig = implemented_method->func_decl.signature;
Signature this_sig = decl->func_decl.signature;
Type *any_rtype = typeget(interface_sig.rtype);

View File

@@ -157,7 +157,7 @@ static inline bool sema_call_analyse_func_invocation(SemaContext *context, Decl
Expr *struct_var,
bool optional, const char *name, bool *no_match_ref);
static inline bool sema_call_check_invalid_body_arguments(SemaContext *context, Expr *call, CalledDecl *callee);
static inline bool sema_call_evaluate_arguments(SemaContext *context, CalledDecl *callee, Expr *call, bool *optional, bool *no_match_ref);
static inline bool sema_call_evaluate_arguments(SemaContext *context, CalledDecl *callee, Expr *call, bool *optional, bool *no_match_ref, Decl **type_capture);
static inline bool sema_call_check_contract_param_match(SemaContext *context, Decl *param, Expr *expr);
static bool sema_call_analyse_body_expansion(SemaContext *macro_context, Expr *call);
static bool sema_slice_index_is_in_range(SemaContext *context, Type *type, Expr *index_expr, bool end_index, bool from_end, bool *remove_from_end, bool check_valid);
@@ -1497,13 +1497,166 @@ static inline bool sema_call_check_invalid_body_arguments(SemaContext *context,
#define RETURN_ERR_WITH_DEFINITION do { if (compiler.context.errors_found != errors) SEMA_NOTE(definition, "The definition is here."); return false; } while (0)
static Decl *sema_macro_capture_type(SemaContext *context, Decl *param, Expr *arg)
{
Type *param_type = arg->type;
TypeInfo *type = type_infoptr(param->var.type_info);
while (true)
{
param_type = param_type->canonical;
switch (type->subtype)
{
case TYPE_COMPRESSED_NONE:
break;
case TYPE_COMPRESSED_PTR:
if (param_type->type_kind != TYPE_POINTER) goto NO_MATCH;
param_type = param_type->pointer;
type->subtype = TYPE_COMPRESSED_NONE;
continue;
case TYPE_COMPRESSED_SLICE:
if (param_type->type_kind != TYPE_SLICE) goto NO_MATCH;
param_type = param_type->array.base;
type->subtype = TYPE_COMPRESSED_NONE;
continue;
case TYPE_COMPRESSED_SLICEPTR:
if (param_type->type_kind != TYPE_POINTER) goto NO_MATCH;
param_type = param_type->pointer;
type->subtype = TYPE_COMPRESSED_SLICE;
continue;
case TYPE_COMPRESSED_PTRPTR:
if (param_type->type_kind != TYPE_POINTER) goto NO_MATCH;
param_type = param_type->pointer;
type->subtype = TYPE_COMPRESSED_PTR;
continue;
case TYPE_COMPRESSED_PTRSLICE:
if (param_type->type_kind != TYPE_SLICE) goto NO_MATCH;
param_type = param_type->array.base;
type->subtype = TYPE_COMPRESSED_PTR;
continue;
case TYPE_COMPRESSED_SLICESLICE:
if (param_type->type_kind != TYPE_SLICE) goto NO_MATCH;
param_type = param_type->array.base;
type->subtype = TYPE_COMPRESSED_SLICE;
continue;
}
ASSERT(type->subtype == TYPE_COMPRESSED_NONE);
switch (type->kind)
{
case TYPE_INFO_POISON: return NULL;
case TYPE_INFO_TYPEOF:
case TYPE_INFO_VATYPE:
case TYPE_INFO_EVALTYPE:
case TYPE_INFO_TYPEFROM:
case TYPE_INFO_GENERIC:
case TYPE_INFO_IDENTIFIER: UNREACHABLE;
case TYPE_INFO_CT_IDENTIFIER: goto DO_MATCH;
case TYPE_INFO_ARRAY:
if (param_type->type_kind != TYPE_ARRAY) goto NO_MATCH;
goto LEN_CHECK;
case TYPE_INFO_VECTOR:
if (param_type->type_kind != TYPE_VECTOR) goto NO_MATCH;
LEN_CHECK:
{
ArraySize size;
if (!sema_resolve_array_like_len(context, type, &size)) return false;
if (param_type->array.len != size) goto NO_MATCH;
param_type = param_type->array.base;
type = type->array.base;
continue;
}
case TYPE_INFO_INFERRED_ARRAY:
if (param_type->type_kind != TYPE_ARRAY) goto NO_MATCH;
param_type = param_type->array.base;
type = type->array.base;
continue;
case TYPE_INFO_INFERRED_VECTOR:
if (param_type->type_kind != TYPE_VECTOR) goto NO_MATCH;
param_type = param_type->array.base;
type = type->array.base;
continue;
case TYPE_INFO_SLICE:
if (param_type->type_kind != TYPE_SLICE) goto NO_MATCH;
param_type = param_type->array.base;
type = type->array.base;
continue;
case TYPE_INFO_POINTER:
if (param_type->type_kind != TYPE_POINTER) goto NO_MATCH;
param_type = param_type->pointer;
type = type->pointer;
continue;
break;
}
break;
}
DO_MATCH:;
const char *name = type->unresolved.name;
Decl *new_param = decl_new_var(name, arg->span, NULL, VARDECL_PARAM_CT_TYPE);
new_param->var.init_expr = expr_new_const_typeid(param->span, param_type);
return new_param;
NO_MATCH:
RETURN_SEMA_ERROR(arg, "Type is not valid %s", type_quoted_error_string(arg->type));
return NULL;
}
static bool sema_patch_captured_type(TypeInfo *type, Decl **captured)
{
assert(type->resolve_status != RESOLVE_DONE);
if (!captured || !captured[0]) return false;
while (true)
{
switch (type->kind)
{
case TYPE_INFO_ARRAY:
case TYPE_INFO_VECTOR:
case TYPE_INFO_SLICE:
case TYPE_INFO_INFERRED_ARRAY:
case TYPE_INFO_INFERRED_VECTOR:
type = type->array.base;
continue;
case TYPE_INFO_POINTER:
type = type->pointer;
continue;
case TYPE_INFO_CT_IDENTIFIER:
{
Decl *d;
const char *name = type->unresolved.name;
while ((d = *captured) != NULL)
{
if (d->name == name)
{
type->kind = TYPE_INFO_TYPEFROM;
type->unresolved_type_expr = d->var.init_expr;
return true;
}
captured++;
}
return false;
}
default:
UNREACHABLE;
}
}
}
static bool sema_analyse_parameter(SemaContext *context, Expr *arg, Decl *param, Decl *definition, bool *optional_ref,
bool *no_match_ref, bool macro, bool is_method_target)
bool *no_match_ref, bool macro, bool is_method_target, Decl **type_capture)
{
VarDeclKind kind = param->var.kind;
Type *type = param->type;
// 16. Analyse a regular argument.
unsigned errors = compiler.context.errors_found;
bool try_capture = false;
if (type_capture && param->var.is_typecapture)
{
if (sema_patch_captured_type(type_infoptr(param->var.type_info), type_capture))
{
if (!sema_resolve_type_info(context, type_infoptr(param->var.type_info), RESOLVE_TYPE_ALLOW_INFER)) return false;
param->type = typeget(param->var.type_info);
}
else
{
try_capture = true;
}
}
Type *type = param->type;
switch (kind)
{
case VARDECL_PARAM:
@@ -1607,11 +1760,25 @@ static bool sema_analyse_parameter(SemaContext *context, Expr *arg, Decl *param,
{
param->type = type_no_optional(arg->type);
}
if (try_capture)
{
Decl *new_param = sema_macro_capture_type(context, param, arg);
if (!new_param) return false;
for (int i = 0; i < MAX_PARAMS; i++)
{
if (!type_capture[i])
{
type_capture[i] = new_param;
type_capture[i + 1] = NULL;
break;
}
}
}
return true;
}
INLINE bool sema_set_default_argument(SemaContext *context, CalledDecl *callee, Expr *call, Decl *param,
bool *no_match_ref, Expr **expr_ref, bool *optional)
bool *no_match_ref, Expr **expr_ref, bool *optional, Decl **type_capture)
{
Expr *init_expr = param->var.init_expr;
if (!init_expr) return true;
@@ -1627,7 +1794,7 @@ INLINE bool sema_set_default_argument(SemaContext *context, CalledDecl *callee,
: call->span.row;
new_context->original_module = context->original_module;
success = sema_analyse_parameter(new_context, arg, param, callee->definition, optional, no_match_ref,
callee->macro, false);
callee->macro, false, type_capture);
SCOPE_END;
sema_context_destroy(&default_context);
if (no_match_ref && *no_match_ref) return true;
@@ -1643,7 +1810,7 @@ INLINE bool sema_set_default_argument(SemaContext *context, CalledDecl *callee,
param->var.defaulted = true;
if (parameter_checked) return true;
return sema_analyse_parameter(context, arg, param, callee->definition, optional, no_match_ref,
callee->macro, false);
callee->macro, false, type_capture);
default:
break;
}
@@ -1655,7 +1822,7 @@ INLINE bool sema_set_default_argument(SemaContext *context, CalledDecl *callee,
*expr_ref = function_scope_arg;
if (parameter_checked) return true;
return sema_analyse_parameter(context, function_scope_arg, param, callee->definition, optional, no_match_ref,
callee->macro, false);
callee->macro, false, type_capture);
}
@@ -1738,7 +1905,7 @@ INLINE Type *sema_get_va_type(SemaContext *context, Expr *expr, Variadic variadi
}
INLINE bool sema_call_evaluate_arguments(SemaContext *context, CalledDecl *callee, Expr *call, bool *optional,
bool *no_match_ref)
bool *no_match_ref, Decl **type_capture)
{
// Check body arguments (for macro calls, or possibly broken
if (!sema_call_check_invalid_body_arguments(context, call, callee)) return false;
@@ -1896,7 +2063,7 @@ SPLAT_NORMAL:;
if (!sema_set_default_argument(context, callee, call,
params[j], no_match_ref,
&actual_args[j],
optional))
optional, type_capture))
{
return false;
}
@@ -1906,7 +2073,7 @@ SPLAT_NORMAL:;
actual_args[index] = arg->named_argument_expr.value;
if (!sema_analyse_parameter(context, actual_args[index], param, callee->definition, optional, no_match_ref,
callee->macro, false)) return false;
callee->macro, false, type_capture)) return false;
continue;
}
if (call->call_expr.va_is_splat)
@@ -1983,7 +2150,7 @@ SPLAT_NORMAL:;
vec_add(call->call_expr.varargs, arg);
continue;
}
if (!sema_analyse_parameter(context, arg, params[i], callee->definition, optional, no_match_ref, callee->macro, callee->struct_var && i == 0)) return false;
if (!sema_analyse_parameter(context, arg, params[i], callee->definition, optional, no_match_ref, callee->macro, callee->struct_var && i == 0, type_capture)) return false;
actual_args[i] = arg;
}
if (num_args) last = args[num_args - 1];
@@ -1996,7 +2163,7 @@ SPLAT_NORMAL:;
if (i == vaarg_index && variadic != VARIADIC_NONE) continue;
if (!sema_set_default_argument(context, callee, call, params[i], no_match_ref, &actual_args[i],
optional)) return false;
optional, type_capture)) return false;
}
for (int i = 0; i < func_param_count; i++)
{
@@ -2289,7 +2456,7 @@ static inline bool sema_call_analyse_func_invocation(SemaContext *context, Decl
if (sig->attrs.noreturn) expr->call_expr.no_return = true;
if (!sema_call_evaluate_arguments(context, &callee, expr, &optional, no_match_ref)) return false;
if (!sema_call_evaluate_arguments(context, &callee, expr, &optional, no_match_ref, NULL)) return false;
Type *rtype = type->function.prototype->rtype;
if (expr->call_expr.is_dynamic_dispatch)
@@ -2564,6 +2731,8 @@ static inline bool sema_expr_setup_call_analysis(SemaContext *context, CalledDec
return true;
}
bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *struct_var, Decl *decl,
bool call_var_optional, bool *no_match_ref)
{
@@ -2620,7 +2789,9 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
};
bool has_optional_arg = call_var_optional;
if (!sema_call_evaluate_arguments(context, &callee, call_expr, &has_optional_arg, no_match_ref)) return false;
Decl *captured_types[MAX_PARAMS + 1];
captured_types[0] = NULL;
if (!sema_call_evaluate_arguments(context, &callee, call_expr, &has_optional_arg, no_match_ref, captured_types)) return false;
unsigned vararg_index = sig->vararg_index;
Expr **args = call_expr->call_expr.arguments;
@@ -2743,7 +2914,18 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
context_change_scope_with_flags(context, SCOPE_NONE);
SemaContext macro_context;
Type *rtype = typeget(sig->rtype);
TypeInfo *rtype_info = type_infoptrzero(sig->rtype);
if (sig->is_capture_return)
{
rtype_info = copy_type_info_single(rtype_info);
if (!sema_patch_captured_type(rtype_info, captured_types))
{
SEMA_ERROR(rtype_info, "The return type could not be inferred from any captured types, are you sure it's a captured parameter?");
goto EXIT_FAIL;
}
if (!sema_resolve_type_info(context, rtype_info, RESOLVE_TYPE_DEFAULT)) goto EXIT_FAIL;
}
Type *rtype = rtype_info ? rtype_info->type : NULL;
bool optional_return = rtype && type_is_optional(rtype);
bool may_be_optional = !rtype || optional_return;
if (rtype) rtype = type_no_optional(rtype);
@@ -2758,6 +2940,12 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
goto EXIT_FAIL;
}
for (int idx = 0; idx < MAX_PARAMS; idx++)
{
if (!captured_types[idx]) break;
if (!sema_add_local(&macro_context, captured_types[idx])) return false;
}
AstId assert_first = 0;
AstId* next = &assert_first;
@@ -2769,7 +2957,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
if (param->var.init_expr)
{
Type *param_type = param->type;
if (param_type && param->var.type_info && (param->var.out_param || param->var.not_null))
if (param->var.type_info && (param->var.out_param || param->var.not_null))
{
param_type = type_flatten(param_type);
if (param_type->type_kind != TYPE_POINTER && param_type->type_kind != TYPE_SLICE && param_type->type_kind != TYPE_INTERFACE && param_type->type_kind != TYPE_ANY)
@@ -3028,7 +3216,7 @@ static bool sema_call_analyse_body_expansion(SemaContext *macro_context, Expr *c
{
Decl *param = params[i];
Expr *expr = args[i];
if (!sema_analyse_parameter(macro_context, expr, param, body_decl, &has_optional_arg, NULL, true, false))
if (!sema_analyse_parameter(macro_context, expr, param, body_decl, &has_optional_arg, NULL, true, false, NULL))
{
return false;
}

View File

@@ -499,12 +499,12 @@ static inline bool sema_resolve_type(SemaContext *context, TypeInfo *type_info,
{
case TYPE_COMPRESSED_NONE:
case TYPE_COMPRESSED_PTR:
case TYPE_COMPRESSED_SUBPTR:
case TYPE_COMPRESSED_SLICEPTR:
case TYPE_COMPRESSED_PTRPTR:
case TYPE_COMPRESSED_PTRSUB:
case TYPE_COMPRESSED_PTRSLICE:
break;
case TYPE_COMPRESSED_SUB:
case TYPE_COMPRESSED_SUBSUB:
case TYPE_COMPRESSED_SLICE:
case TYPE_COMPRESSED_SLICESLICE:
resolve_kind = resolve_kind & ~RESOLVE_TYPE_NO_CHECK_DISTINCT;
break;
}
@@ -562,20 +562,20 @@ APPEND_QUALIFIERS:
case TYPE_COMPRESSED_PTR:
type_info->type = type_get_ptr(type_info->type);
break;
case TYPE_COMPRESSED_SUB:
case TYPE_COMPRESSED_SLICE:
if (!sema_check_array_type(context, type_info, type_info->type, TYPE_INFO_SLICE, 0, &type_info->type)) return type_info_poison(type_info);
break;
case TYPE_COMPRESSED_SUBPTR:
case TYPE_COMPRESSED_SLICEPTR:
if (!sema_check_array_type(context, type_info, type_info->type, TYPE_INFO_SLICE, 0, &type_info->type)) return type_info_poison(type_info);
type_info->type = type_get_ptr(type_info->type);
break;
case TYPE_COMPRESSED_PTRPTR:
type_info->type = type_get_ptr(type_get_ptr(type_info->type));
break;
case TYPE_COMPRESSED_PTRSUB:
case TYPE_COMPRESSED_PTRSLICE:
type_info->type = type_get_slice(type_get_ptr(type_info->type));
break;
case TYPE_COMPRESSED_SUBSUB:
case TYPE_COMPRESSED_SLICESLICE:
if (!sema_check_array_type(context, type_info, type_info->type, TYPE_INFO_SLICE, 0, &type_info->type)) return type_info_poison(type_info);
type_info->type = type_get_slice(type_info->type);
break;