From 88096e055676f0f9ebbcba7780bf62d469c00039 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Tue, 26 Aug 2025 02:45:52 +0200 Subject: [PATCH] Type capture in macros --- lib/std/core/array.c3 | 6 +- lib/std/core/bitorder.c3 | 4 +- lib/std/core/builtin.c3 | 6 +- lib/std/io/stream.c3 | 5 +- lib/std/math/math.c3 | 106 +++++++-------- src/compiler/compiler_internal.h | 3 + src/compiler/enums.h | 8 +- src/compiler/json_output.c | 8 +- src/compiler/parse_global.c | 35 +++-- src/compiler/sema_decls.c | 16 ++- src/compiler/sema_expr.c | 222 ++++++++++++++++++++++++++++--- src/compiler/sema_types.c | 16 +-- 12 files changed, 319 insertions(+), 116 deletions(-) diff --git a/lib/std/core/array.c3 b/lib/std/core/array.c3 index 8f4ef1786..ae807df2e 100644 --- a/lib/std/core/array.c3 +++ b/lib/std/core/array.c3 @@ -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) { diff --git a/lib/std/core/bitorder.c3 b/lib/std/core/bitorder.c3 index 34b9457ea..2da28c820 100644 --- a/lib/std/core/bitorder.c3 +++ b/lib/std/core/bitorder.c3 @@ -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: diff --git a/lib/std/core/builtin.c3 b/lib/std/core/builtin.c3 index aa3eeb5fc..e84e063b3 100644 --- a/lib/std/core/builtin.c3 +++ b/lib/std/core/builtin.c3 @@ -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]); diff --git a/lib/std/io/stream.c3 b/lib/std/io/stream.c3 index dd850548b..9e574525a 100644 --- a/lib/std/io/stream.c3 +++ b/lib/std/io/stream.c3 @@ -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; diff --git a/lib/std/math/math.c3 b/lib/std/math/math.c3 index db139fd5d..93c378fb1 100644 --- a/lib/std/math/math.c3 +++ b/lib/std/math/math.c3 @@ -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` diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index abfbfb801..91555089e 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -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; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 1141b3aa1..a0a1413a5 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -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 diff --git a/src/compiler/json_output.c b/src/compiler/json_output.c index a2675127e..38bc49ed3 100644 --- a/src/compiler/json_output.c +++ b/src/compiler/json_output.c @@ -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; } diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 0fe3d8e15..55cbd28c0 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -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, ¶m->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. diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index beddfaa1c..84423e412 100755 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -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); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index c159924a1..2c8acd2cc 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -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(¯o_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; } diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index 96c96408c..b0805a386 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -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;