mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
- $defined(#hash) will not check the internal expression, just that #hash exists.
- Added optional macro arguments using `macro foo(int x = ...)` which can be checked using `$defined(x)`. - Supplemental `roundeven` has a normal implementation.
This commit is contained in:
@@ -746,39 +746,39 @@ fn void* tmalloc(usz size, usz alignment = 0) @builtin @inline @nodiscard
|
|||||||
|
|
||||||
<*
|
<*
|
||||||
@param $Type : "The type to allocate"
|
@param $Type : "The type to allocate"
|
||||||
|
@param #init : "The optional initializer"
|
||||||
|
|
||||||
@require $vacount < 2 : "Too many arguments."
|
@require !$defined(#init) ||| $defined($Type a = #init) : "#init must be an initializer for the type"
|
||||||
@require $vacount == 0 ||| $defined($Type a = $vaexpr[0]) : "The second argument must be an initializer for the type"
|
|
||||||
@require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
|
@require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
|
||||||
@return "A pointer to data of type $Type."
|
@return "A pointer to data of type $Type."
|
||||||
*>
|
*>
|
||||||
macro new($Type, ...) @nodiscard
|
macro new($Type, #init = ...) @nodiscard @safemacro
|
||||||
{
|
{
|
||||||
$if $vacount == 0:
|
$if $defined(#init):
|
||||||
return ($Type*)calloc($Type.sizeof);
|
|
||||||
$else
|
|
||||||
$Type* val = malloc($Type.sizeof);
|
$Type* val = malloc($Type.sizeof);
|
||||||
*val = $vaexpr[0];
|
*val = #init;
|
||||||
return val;
|
return val;
|
||||||
|
$else
|
||||||
|
return ($Type*)calloc($Type.sizeof);
|
||||||
$endif
|
$endif
|
||||||
}
|
}
|
||||||
|
|
||||||
<*
|
<*
|
||||||
@param $Type : "The type to allocate"
|
@param $Type : "The type to allocate"
|
||||||
@param padding : "The padding to add after the allocation"
|
@param padding : "The padding to add after the allocation"
|
||||||
|
@param #init : "The optional initializer"
|
||||||
|
|
||||||
@require $vacount < 2 : "Too many arguments."
|
@require !$defined(#init) ||| $defined($Type a = #init) : "#init must be an initializer for the type"
|
||||||
@require $vacount == 0 ||| $defined($Type a = $vaexpr[0]) : "The second argument must be an initializer for the type"
|
|
||||||
@return "A pointer to data of type $Type."
|
@return "A pointer to data of type $Type."
|
||||||
*>
|
*>
|
||||||
macro new_with_padding($Type, usz padding, ...) @nodiscard
|
macro new_with_padding($Type, usz padding, #init = ...) @nodiscard @safemacro
|
||||||
{
|
{
|
||||||
$if $vacount == 0:
|
$if $defined(#init):
|
||||||
return ($Type*)calloc($Type.sizeof + padding);
|
|
||||||
$else
|
|
||||||
$Type* val = malloc($Type.sizeof + padding);
|
$Type* val = malloc($Type.sizeof + padding);
|
||||||
*val = $vaexpr[0];
|
*val = #init;
|
||||||
return val;
|
return val;
|
||||||
|
$else
|
||||||
|
return ($Type*)calloc($Type.sizeof + padding);
|
||||||
$endif
|
$endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -787,19 +787,19 @@ macro new_with_padding($Type, usz padding, ...) @nodiscard
|
|||||||
exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
|
exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
|
||||||
|
|
||||||
@param $Type : "The type to allocate"
|
@param $Type : "The type to allocate"
|
||||||
|
@param #init : "The optional initializer"
|
||||||
|
|
||||||
@require $vacount < 2 : "Too many arguments."
|
@require !$defined(#init) ||| $defined($Type a = #init) : "#init must be an initializer for the type"
|
||||||
@require $vacount == 0 ||| $defined($Type a = $vaexpr[0]) : "The second argument must be an initializer for the type"
|
|
||||||
@return "A pointer to data of type $Type with the proper alignment"
|
@return "A pointer to data of type $Type with the proper alignment"
|
||||||
*>
|
*>
|
||||||
macro new_aligned($Type, ...) @nodiscard
|
macro new_aligned($Type, #init = ...) @nodiscard @safemacro
|
||||||
{
|
{
|
||||||
$if $vacount == 0:
|
$if $defined(#init):
|
||||||
return ($Type*)calloc_aligned($Type.sizeof, $Type.alignof);
|
|
||||||
$else
|
|
||||||
$Type* val = malloc_aligned($Type.sizeof, $Type.alignof);
|
$Type* val = malloc_aligned($Type.sizeof, $Type.alignof);
|
||||||
*val = $vaexpr[0];
|
*val = #init;
|
||||||
return val;
|
return val;
|
||||||
|
$else
|
||||||
|
return ($Type*)calloc_aligned($Type.sizeof, $Type.alignof);
|
||||||
$endif
|
$endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -841,38 +841,38 @@ macro alloc_aligned($Type) @nodiscard
|
|||||||
|
|
||||||
<*
|
<*
|
||||||
@param $Type : "The type to allocate"
|
@param $Type : "The type to allocate"
|
||||||
|
@param #init : "The optional initializer"
|
||||||
|
|
||||||
@require $vacount < 2 : "Too many arguments."
|
@require !$defined(#init) ||| $defined($Type a = #init) : "#init must be an initializer for the type"
|
||||||
@require $vacount == 0 ||| $defined($Type a = $vaexpr[0]) : "The second argument must be an initializer for the type"
|
|
||||||
@return "A pointer to temporary data of type $Type."
|
@return "A pointer to temporary data of type $Type."
|
||||||
*>
|
*>
|
||||||
macro tnew($Type, ...) @nodiscard
|
macro tnew($Type, #init = ...) @nodiscard @safemacro
|
||||||
{
|
{
|
||||||
$if $vacount == 0:
|
$if $defined(#init):
|
||||||
return ($Type*)tcalloc($Type.sizeof, $Type.alignof) @inline;
|
|
||||||
$else
|
|
||||||
$Type* val = tmalloc($Type.sizeof, $Type.alignof) @inline;
|
$Type* val = tmalloc($Type.sizeof, $Type.alignof) @inline;
|
||||||
*val = $vaexpr[0];
|
*val = #init;
|
||||||
return val;
|
return val;
|
||||||
|
$else
|
||||||
|
return ($Type*)tcalloc($Type.sizeof, $Type.alignof) @inline;
|
||||||
$endif
|
$endif
|
||||||
}
|
}
|
||||||
|
|
||||||
<*
|
<*
|
||||||
@param $Type : "The type to allocate"
|
@param $Type : "The type to allocate"
|
||||||
@param padding : "The padding to add after the allocation"
|
@param padding : "The padding to add after the allocation"
|
||||||
|
@param #init : "The optional initializer"
|
||||||
|
|
||||||
@require $vacount < 2 : "Too many arguments."
|
@require !$defined(#init) ||| $defined($Type a = #init) : "#init must be an initializer for the type"
|
||||||
@require $vacount == 0 ||| $defined($Type a = $vaexpr[0]) : "The second argument must be an initializer for the type"
|
|
||||||
@return "A pointer to temporary data of type $Type with added padding at the end."
|
@return "A pointer to temporary data of type $Type with added padding at the end."
|
||||||
*>
|
*>
|
||||||
macro temp_with_padding($Type, usz padding, ...) @nodiscard
|
macro temp_with_padding($Type, usz padding, #init = ...) @nodiscard @safemacro
|
||||||
{
|
{
|
||||||
$if $vacount == 0:
|
$if $defined(#init):
|
||||||
return ($Type*)tcalloc($Type.sizeof + padding, $Type.alignof) @inline;
|
|
||||||
$else
|
|
||||||
$Type* val = tmalloc($Type.sizeof + padding, $Type.alignof) @inline;
|
$Type* val = tmalloc($Type.sizeof + padding, $Type.alignof) @inline;
|
||||||
*val = $vaexpr[0];
|
*val = #init;
|
||||||
return val;
|
return val;
|
||||||
|
$else
|
||||||
|
return ($Type*)tcalloc($Type.sizeof + padding, $Type.alignof) @inline;
|
||||||
$endif
|
$endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ fn float _roundf(float x) @extern("roundf") @weak @nostrip
|
|||||||
uint u = bitcast(x, uint);
|
uint u = bitcast(x, uint);
|
||||||
int e = (u >> 23) & 0xff;
|
int e = (u >> 23) & 0xff;
|
||||||
if (e >= 0x7f + 23) return x;
|
if (e >= 0x7f + 23) return x;
|
||||||
if (u >> 31) x = -x;
|
bool signed = u >> 31 != 0;
|
||||||
|
if (signed) x = -x;
|
||||||
if (e < 0x7f - 1)
|
if (e < 0x7f - 1)
|
||||||
{
|
{
|
||||||
force_eval_add(x, TOINTF);
|
force_eval_add(x, TOINTF);
|
||||||
@@ -47,7 +48,7 @@ fn float _roundf(float x) @extern("roundf") @weak @nostrip
|
|||||||
default:
|
default:
|
||||||
y = y + x;
|
y = y + x;
|
||||||
}
|
}
|
||||||
if (u >> 31) y = -y;
|
if (signed) y = -y;
|
||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,78 @@
|
|||||||
module std::math::math_rt;
|
module std::math::math_rt;
|
||||||
|
|
||||||
fn float __roundevenf(float f) @extern("roundevenf") @weak @nostrip
|
const double TOINT = 1 / math::DOUBLE_EPSILON;
|
||||||
|
const float TOINTF = (float)(1 / math::FLOAT_EPSILON);
|
||||||
|
macro force_eval_add(x, v)
|
||||||
{
|
{
|
||||||
// Slow implementation
|
$typeof(x) temp @noinit;
|
||||||
return math::round(f / 2) * 2;
|
@volatile_store(temp, x + v);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn double __roundeven(double d) @extern("roundeven") @weak @nostrip
|
fn double __roundeven(double x) @extern("roundeven") @weak @nostrip
|
||||||
{
|
{
|
||||||
// Slow implementation
|
ulong u = bitcast(x, ulong);
|
||||||
return math::round(d / 2) * 2;
|
int e = (int)((u >> 52) & 0x7ff);
|
||||||
|
if (e >= 0x3ff + 52) return x;
|
||||||
|
bool signed = u >> 63 != 0;
|
||||||
|
if (signed) x = -x;
|
||||||
|
if (e < 0x3ff - 1)
|
||||||
|
{
|
||||||
|
/* raise inexact if x!=0 */
|
||||||
|
force_eval_add(x, TOINT);
|
||||||
|
return 0 * x;
|
||||||
|
}
|
||||||
|
double y = (x + TOINT) - TOINT - x;
|
||||||
|
switch
|
||||||
|
{
|
||||||
|
case y > 0.5:
|
||||||
|
y = y + x - 1;
|
||||||
|
case y < -0.5:
|
||||||
|
y = y + x + 1;
|
||||||
|
case y == 0.5 || y == -0.5:
|
||||||
|
if (u & 1)
|
||||||
|
{
|
||||||
|
y = x + (y > 0 ? y + 1 : y - 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nextcase;
|
||||||
|
default:
|
||||||
|
y = y + x;
|
||||||
|
}
|
||||||
|
if (signed) y = -y;
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn float __roundevenf(float x) @extern("roundevenf") @weak @nostrip
|
||||||
|
{
|
||||||
|
uint u = bitcast(x, uint);
|
||||||
|
int e = (u >> 23) & 0xff;
|
||||||
|
if (e >= 0x7f + 23) return x;
|
||||||
|
bool signed = u >> 31 != 0;
|
||||||
|
if (signed) x = -x;
|
||||||
|
if (e < 0x7f - 1)
|
||||||
|
{
|
||||||
|
force_eval_add(x, TOINTF);
|
||||||
|
return 0 * x;
|
||||||
|
}
|
||||||
|
float y = (x + TOINTF) - TOINTF - x;
|
||||||
|
switch
|
||||||
|
{
|
||||||
|
case y > 0.5f:
|
||||||
|
y = y + x - 1;
|
||||||
|
case y < -0.5f:
|
||||||
|
y = y + x + 1;
|
||||||
|
case y == 0.5f || y == -0.5f:
|
||||||
|
if (u & 1)
|
||||||
|
{
|
||||||
|
y = x + (y > 0.0f ? y + 1.0f : y - 1.0f);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nextcase;
|
||||||
|
default:
|
||||||
|
y = y + x;
|
||||||
|
}
|
||||||
|
if (signed) y = -y;
|
||||||
|
return y;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn double __powidf2(double a, int b) @extern("__powidf2") @weak @nostrip
|
fn double __powidf2(double a, int b) @extern("__powidf2") @weak @nostrip
|
||||||
|
|||||||
@@ -33,6 +33,8 @@
|
|||||||
- Added `$kindof` compile time function.
|
- Added `$kindof` compile time function.
|
||||||
- Deprecated `@typekind` macro in favour of `$kindof`.
|
- Deprecated `@typekind` macro in favour of `$kindof`.
|
||||||
- Deprecated `@typeis` macro in favour of `$typeof(#foo) == int`.
|
- Deprecated `@typeis` macro in favour of `$typeof(#foo) == int`.
|
||||||
|
- `$defined(#hash)` will not check the internal expression, just that `#hash` exists.
|
||||||
|
- Added optional macro arguments using `macro foo(int x = ...)` which can be checked using `$defined(x)`.
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
- List.remove_at would incorrectly trigger ASAN.
|
- List.remove_at would incorrectly trigger ASAN.
|
||||||
@@ -104,6 +106,7 @@
|
|||||||
- Added `String.trim_charset`.
|
- Added `String.trim_charset`.
|
||||||
- Added array `@reduce`, `@filter`, `@any`, `@all`, `@sum`, `@product`, and `@indices_of` macros.
|
- Added array `@reduce`, `@filter`, `@any`, `@all`, `@sum`, `@product`, and `@indices_of` macros.
|
||||||
- `String.bformat` has reduced overhead.
|
- `String.bformat` has reduced overhead.
|
||||||
|
- Supplemental `roundeven` has a normal implementation.
|
||||||
|
|
||||||
## 0.7.4 Change list
|
## 0.7.4 Change list
|
||||||
|
|
||||||
|
|||||||
@@ -6218,6 +6218,7 @@ static inline void llvm_emit_macro_block(GenContext *c, BEValue *be_value, Expr
|
|||||||
{
|
{
|
||||||
// Skip vararg
|
// Skip vararg
|
||||||
if (!val) continue;
|
if (!val) continue;
|
||||||
|
if (val->var.no_init && val->var.defaulted) continue;
|
||||||
// In case we have a constant, we never do an emit. The value is already folded.
|
// In case we have a constant, we never do an emit. The value is already folded.
|
||||||
switch (val->var.kind)
|
switch (val->var.kind)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1679,7 +1679,19 @@ bool parse_parameters(ParseContext *c, Decl ***params_ref, Variadic *variadic, i
|
|||||||
{
|
{
|
||||||
if (try_consume(c, TOKEN_EQ))
|
if (try_consume(c, TOKEN_EQ))
|
||||||
{
|
{
|
||||||
if (!parse_decl_initializer(c, param)) return poisoned_decl;
|
if (try_consume(c, TOKEN_ELLIPSIS))
|
||||||
|
{
|
||||||
|
if (parse_kind != PARAM_PARSE_MACRO)
|
||||||
|
{
|
||||||
|
PRINT_ERROR_HERE("Optional arguments with '...' is only allowed as macro arguments.");
|
||||||
|
return poisoned_decl;
|
||||||
|
}
|
||||||
|
param->var.no_init = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!parse_decl_initializer(c, param)) return poisoned_decl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ellipsis)
|
if (ellipsis)
|
||||||
|
|||||||
@@ -1643,7 +1643,15 @@ INLINE bool sema_set_default_argument(SemaContext *context, CalledDecl *callee,
|
|||||||
bool *no_match_ref, Expr **expr_ref, bool *optional)
|
bool *no_match_ref, Expr **expr_ref, bool *optional)
|
||||||
{
|
{
|
||||||
Expr *init_expr = param->var.init_expr;
|
Expr *init_expr = param->var.init_expr;
|
||||||
if (!init_expr) return true;
|
if (!init_expr)
|
||||||
|
{
|
||||||
|
// Handle foo = ... macro init
|
||||||
|
if (param->var.no_init)
|
||||||
|
{
|
||||||
|
param->var.defaulted = true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
Expr *arg = copy_expr_single(init_expr);
|
Expr *arg = copy_expr_single(init_expr);
|
||||||
bool parameter_checked = false;
|
bool parameter_checked = false;
|
||||||
if (arg->resolve_status != RESOLVE_DONE)
|
if (arg->resolve_status != RESOLVE_DONE)
|
||||||
@@ -2031,8 +2039,9 @@ SPLAT_NORMAL:;
|
|||||||
{
|
{
|
||||||
if (i == vaarg_index) continue;
|
if (i == vaarg_index) continue;
|
||||||
if (actual_args[i]) continue;
|
if (actual_args[i]) continue;
|
||||||
// Argument missing, that's bad.
|
|
||||||
Decl *param = params[i];
|
Decl *param = params[i];
|
||||||
|
if (param->var.no_init) continue; // Macro empty args
|
||||||
|
// Argument missing, that's bad.
|
||||||
if (no_match_ref)
|
if (no_match_ref)
|
||||||
{
|
{
|
||||||
*no_match_ref = true;
|
*no_match_ref = true;
|
||||||
@@ -2118,6 +2127,7 @@ NEXT_FLAG:
|
|||||||
}
|
}
|
||||||
if (idx == vacount) goto TOO_FEW_ARGUMENTS;
|
if (idx == vacount) goto TOO_FEW_ARGUMENTS;
|
||||||
expr = vaargs[idx];
|
expr = vaargs[idx];
|
||||||
|
if (!expr) goto TOO_FEW_ARGUMENTS;
|
||||||
Type *type = sema_get_va_type(context, expr, variadic);
|
Type *type = sema_get_va_type(context, expr, variadic);
|
||||||
if (!type_ok(type)) return false;
|
if (!type_ok(type)) return false;
|
||||||
|
|
||||||
@@ -2132,6 +2142,7 @@ NEXT_FLAG:
|
|||||||
c = data[i];
|
c = data[i];
|
||||||
if (++idx == vacount) goto TOO_FEW_ARGUMENTS;
|
if (++idx == vacount) goto TOO_FEW_ARGUMENTS;
|
||||||
expr = vaargs[idx];
|
expr = vaargs[idx];
|
||||||
|
if (!expr) goto TOO_FEW_ARGUMENTS;
|
||||||
type = sema_get_va_type(context, expr, variadic);
|
type = sema_get_va_type(context, expr, variadic);
|
||||||
if (!type_ok(type)) return false;
|
if (!type_ok(type)) return false;
|
||||||
}
|
}
|
||||||
@@ -2157,6 +2168,7 @@ NEXT_FLAG:
|
|||||||
c = data[i];
|
c = data[i];
|
||||||
if (++idx == vacount) goto TOO_FEW_ARGUMENTS;
|
if (++idx == vacount) goto TOO_FEW_ARGUMENTS;
|
||||||
expr = vaargs[idx];
|
expr = vaargs[idx];
|
||||||
|
if (!expr) goto TOO_FEW_ARGUMENTS;
|
||||||
type = sema_get_va_type(context, expr, variadic);
|
type = sema_get_va_type(context, expr, variadic);
|
||||||
if (!type_ok(type)) return false;
|
if (!type_ok(type)) return false;
|
||||||
}
|
}
|
||||||
@@ -2675,6 +2687,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
param->var.init_expr = args[i];
|
param->var.init_expr = args[i];
|
||||||
|
if (!args[i]) continue;
|
||||||
// Lazy arguments doesn't affect optional arg.
|
// Lazy arguments doesn't affect optional arg.
|
||||||
if (param->var.kind == VARDECL_PARAM_EXPR) continue;
|
if (param->var.kind == VARDECL_PARAM_EXPR) continue;
|
||||||
has_optional_arg = has_optional_arg || IS_OPTIONAL(args[i]);
|
has_optional_arg = has_optional_arg || IS_OPTIONAL(args[i]);
|
||||||
@@ -2794,6 +2807,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
|
|||||||
{
|
{
|
||||||
// Skip raw vararg
|
// Skip raw vararg
|
||||||
if (!param) continue;
|
if (!param) continue;
|
||||||
|
if (param->var.no_init && param->var.defaulted) continue;
|
||||||
if (!sema_add_local(¯o_context, param)) goto EXIT_FAIL;
|
if (!sema_add_local(¯o_context, param)) goto EXIT_FAIL;
|
||||||
if (param->var.init_expr)
|
if (param->var.init_expr)
|
||||||
{
|
{
|
||||||
@@ -10345,6 +10359,13 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr
|
|||||||
success = decl != NULL;
|
success = decl != NULL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case EXPR_HASH_IDENT:
|
||||||
|
{
|
||||||
|
Decl *decl = sema_find_symbol(active_context, main_expr->hash_ident_expr.identifier);
|
||||||
|
if (!decl_ok(decl)) goto FAIL;
|
||||||
|
success = decl != NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case EXPR_COMPILER_CONST:
|
case EXPR_COMPILER_CONST:
|
||||||
success = sema_expr_analyse_compiler_const(active_context, main_expr, false);
|
success = sema_expr_analyse_compiler_const(active_context, main_expr, false);
|
||||||
break;
|
break;
|
||||||
@@ -10373,13 +10394,6 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr
|
|||||||
success = eval != NULL;
|
success = eval != NULL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EXPR_HASH_IDENT:
|
|
||||||
{
|
|
||||||
Decl *decl = sema_resolve_symbol(active_context, main_expr->hash_ident_expr.identifier, NULL, main_expr->span);
|
|
||||||
if (!decl) goto FAIL;
|
|
||||||
main_expr = copy_expr_single(decl->var.init_expr);
|
|
||||||
goto RETRY;
|
|
||||||
}
|
|
||||||
case EXPR_SUBSCRIPT:
|
case EXPR_SUBSCRIPT:
|
||||||
{
|
{
|
||||||
if (!sema_expr_analyse_subscript(active_context, main_expr, CHECK_VALUE, true))
|
if (!sema_expr_analyse_subscript(active_context, main_expr, CHECK_VALUE, true))
|
||||||
|
|||||||
Reference in New Issue
Block a user