From cb17cfff7d2ffab6850961549a91935a87e00067 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Tue, 26 Aug 2025 13:21:42 +0200 Subject: [PATCH] Deprecation of @assignable_to --- lib/std/collections/object.c3 | 2 +- lib/std/core/array.c3 | 2 +- lib/std/core/builtin.c3 | 10 +++++----- lib/std/core/mem.c3 | 12 ++++++------ lib/std/core/refcount.c3 | 6 +++--- lib/std/core/values.c3 | 2 +- lib/std/io/io.c3 | 2 +- lib/std/io/stream.c3 | 4 ++-- lib/std/math/math.c3 | 6 +++--- lib/std/math/random.c3 | 2 +- lib/std/sort/countingsort.c3 | 2 +- lib/std/sort/insertionsort.c3 | 2 +- lib/std/sort/quicksort.c3 | 2 +- lib/std/sort/sorted.c3 | 2 +- releasenotes.md | 1 + src/compiler/sema_expr.c | 2 +- test/unit/regression/castable_assignable.c3 | 16 ++++++++-------- test/unit/stdlib/math/math.c3 | 8 ++++++++ 18 files changed, 46 insertions(+), 37 deletions(-) diff --git a/lib/std/collections/object.c3 b/lib/std/collections/object.c3 index 1deac0b95..8443c4f21 100644 --- a/lib/std/collections/object.c3 +++ b/lib/std/collections/object.c3 @@ -203,7 +203,7 @@ macro Object* Object.object_from_value(&self, value) @private return value; $case $Type.typeid == void*.typeid: return &NULL_OBJECT; - $case @assignable_to(value, String): + $case $defined(String x = value): return new_string(value, self.allocator); $default: $error "Unsupported object type."; diff --git a/lib/std/core/array.c3 b/lib/std/core/array.c3 index 8f4ef1786..4ba4fca4f 100644 --- a/lib/std/core/array.c3 +++ b/lib/std/core/array.c3 @@ -497,7 +497,7 @@ macro @tzip(left, right, #operation = EMPTY_MACRO_SLOT, fill_with = EMPTY_MACRO_ @require @is_valid_list(left) : "Expected a valid list" @require @is_valid_list(right) : "Expected a valid list" @require find_len(right) >= find_len(left) : `Right side length must be >= the destination (left) side length; consider using a sub-array of data for the assignment.` - @require @assignable_to(#operation, $typefrom(@zip_into_fn(left, right))) : "The functor must use the same types as the `left` and `right` inputs, and return a value of the `left` type." + @require $defined($typefrom(@zip_into_fn(left, right)) x = #operation) : "The functor must use the same types as the `left` and `right` inputs, and return a value of the `left` type." *> macro @zip_into(left, right, #operation) { diff --git a/lib/std/core/builtin.c3 b/lib/std/core/builtin.c3 index aa3eeb5fc..64c9269c7 100644 --- a/lib/std/core/builtin.c3 +++ b/lib/std/core/builtin.c3 @@ -108,7 +108,7 @@ macro anycast(any v, $Type) @builtin return ($Type*)v.ptr; } -macro bool @assignable_to(#foo, $Type) @const @builtin => $defined(*&&($Type){} = #foo); +macro bool @assignable_to(#foo, $Type) @const @builtin @deprecated("use '$define($Type x = #foo)'") => $defined(*&&($Type){} = #foo); macro @addr(#val) @builtin { @@ -319,7 +319,7 @@ macro enum_by_name($Type, String enum_name) @builtin @param $Type : `The type of the enum` @require $Type.kindof == ENUM : `Only enums may be used` @require $defined($Type.#value) : `Expected '#value' to match an enum associated value` - @require @assignable_to(value, $typeof(($Type){}.#value)) : `Expected the value to match the type of the associated value` + @require $defined($typeof(($Type){}.#value) v = value) : `Expected the value to match the type of the associated value` @ensure @typeis(return, $Type) @return? NOT_FOUND *> @@ -372,7 +372,7 @@ macro bool @unlikely(bool #value, $probability = 1.0) @builtin <* @require values::@is_int(#value) || values::@is_bool(#value) - @require @assignable_to(expected, $typeof(#value)) + @require $defined($typeof(#value) v = expected) @require $probability >= 0 && $probability <= 1.0 *> macro @expect(#value, expected, $probability = 1.0) @builtin @@ -480,7 +480,7 @@ macro bool @ok(#expr) @builtin @require $defined(#v = #v) : "#v must be a variable" @require $defined(#expr!) : "Expected an optional expression" - @require @assignable_to(#expr!!, $typeof(#v)) : `Type of #expr must be an optional of #v's type` + @require $defined(#v = #expr!!) : `Type of #expr must be an optional of #v's type` *> macro void? @try(#v, #expr) @builtin @maydiscard { @@ -521,7 +521,7 @@ macro void? @try(#v, #expr) @builtin @maydiscard @require $defined(#v = #v) : "#v must be a variable" @require $defined(#expr!) : "Expected an optional expression" - @require @assignable_to(#expr!!, $typeof(#v)) : `Type of #expr must be an optional of #v's type` + @require $defined(#v = #expr!!) : `Type of #expr must be an optional of #v's type` @return "True if it was the expected fault, false if the variable was assigned, otherwise returns an optional." *> macro bool? @try_catch(#v, #expr, fault expected_fault) @builtin diff --git a/lib/std/core/mem.c3 b/lib/std/core/mem.c3 index de1911b06..44561b524 100644 --- a/lib/std/core/mem.c3 +++ b/lib/std/core/mem.c3 @@ -433,7 +433,7 @@ macro void set_inline(void* dst, char val, usz $len, usz $dst_align = 0, bool $i @require values::@inner_kind(b) == TypeKind.SLICE || values::@inner_kind(b) == TypeKind.POINTER @require values::@inner_kind(a) != TypeKind.SLICE || len == -1 @require values::@inner_kind(a) != TypeKind.POINTER || len > -1 - @require values::@assign_to(a, b) && values::@assign_to(b, a) + @require $defined(a = b, b = a) *> macro bool equals(a, b, isz len = -1, usz $align = 0) { @@ -740,7 +740,7 @@ fn void* tmalloc(usz size, usz alignment = 0) @builtin @inline @nodiscard <* @require $vacount < 2 : "Too many arguments." - @require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument 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" *> macro new($Type, ...) @nodiscard @@ -756,7 +756,7 @@ macro new($Type, ...) @nodiscard <* @require $vacount < 2 : "Too many arguments." - @require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument 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" *> macro new_with_padding($Type, usz padding, ...) @nodiscard @@ -774,7 +774,7 @@ macro new_with_padding($Type, usz padding, ...) @nodiscard Allocate using an aligned allocation. This is necessary for types with a default memory alignment exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned. @require $vacount < 2 : "Too many arguments." - @require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument 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" *> macro new_aligned($Type, ...) @nodiscard { @@ -814,7 +814,7 @@ macro alloc_aligned($Type) @nodiscard <* @require $vacount < 2 : "Too many arguments." - @require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument 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" *> macro tnew($Type, ...) @nodiscard { @@ -829,7 +829,7 @@ macro tnew($Type, ...) @nodiscard <* @require $vacount < 2 : "Too many arguments." - @require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument 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" *> macro temp_with_padding($Type, usz padding, ...) @nodiscard { diff --git a/lib/std/core/refcount.c3 b/lib/std/core/refcount.c3 index 136c07e8e..374707104 100644 --- a/lib/std/core/refcount.c3 +++ b/lib/std/core/refcount.c3 @@ -21,7 +21,7 @@ fn Ref wrap(Type* ptr, Allocator allocator = mem) } <* @require $vacount < 2 : "Too many arguments." - @require $vacount == 0 ||| @assignable_to($vaexpr[0], Type) : "The first argument must be an initializer for the type" + @require $vacount == 0 ||| $defined(Type a = $vaexpr[0]) : "The first argument must be an initializer for the type" *> macro Ref new(..., Allocator allocator = mem) { @@ -99,7 +99,7 @@ struct RefCounted } <* - @require @assignable_to(refcounted, RefCounted*) : "Expected a ref counted value" + @require $defined(RefCounted* c = refcounted) : "Expected a ref counted value" *> macro retain(refcounted) { @@ -112,7 +112,7 @@ macro retain(refcounted) } <* - @require @assignable_to(refcounted, RefCounted*) : "Expected a ref counted value" + @require $defined(RefCounted* c = refcounted) : "Expected a ref counted value" @require !$defined(refcounted.dealloc()) ||| @typeis(refcounted.dealloc(), void) : "Expected refcounted type to have a valid dealloc" *> diff --git a/lib/std/core/values.c3 b/lib/std/core/values.c3 index b926381c5..f0d53cc20 100644 --- a/lib/std/core/values.c3 +++ b/lib/std/core/values.c3 @@ -16,7 +16,7 @@ macro bool @is_promotable_to_floatlike(#value) @const => types::is_promotable_to macro bool @is_promotable_to_float(#value) @const => types::is_promotable_to_float($typeof(#value)); macro bool @is_vector(#value) @const => types::is_vector($typeof(#value)); macro bool @is_same_vector_type(#value1, #value2) @const => types::is_same_vector_type($typeof(#value1), $typeof(#value2)); -macro bool @assign_to(#value1, #value2) @const => @assignable_to(#value1, $typeof(#value2)); +macro bool @assign_to(#value1, #value2) @const @deprecated("use '$define(#value1 = #value2)'") => @assignable_to(#value1, $typeof(#value2)); macro bool @is_lvalue(#value) => $defined(#value = #value); macro bool @is_const(#foo) @const @builtin { diff --git a/lib/std/io/io.c3 b/lib/std/io/io.c3 index ed43598fb..5c0992d8e 100644 --- a/lib/std/io/io.c3 +++ b/lib/std/io/io.c3 @@ -160,7 +160,7 @@ macro usz? fprint(out, x) $case ZString: return out.write(x.str_view()); $case DString: return out.write(x.str_view()); $default: - $if @assignable_to(x, String): + $if $defined(String a = x): return out.write((String)x); $else $if is_struct_with_default_print($Type): diff --git a/lib/std/io/stream.c3 b/lib/std/io/stream.c3 index dd850548b..daae8a111 100644 --- a/lib/std/io/stream.c3 +++ b/lib/std/io/stream.c3 @@ -39,7 +39,7 @@ fn usz? available(InStream s) macro bool @is_instream(#expr) @const { - return @assignable_to(#expr, InStream); + return $defined(InStream i = #expr); } macro bool @is_not_instream_if_ptr(#expr) @const @@ -49,7 +49,7 @@ macro bool @is_not_instream_if_ptr(#expr) @const macro bool @is_outstream(#expr) @const { - return @assignable_to(#expr, OutStream); + return $defined(OutStream s = #expr); } macro bool @is_not_outstream_if_ptr(#expr) @const diff --git a/lib/std/math/math.c3 b/lib/std/math/math.c3 index db139fd5d..1109b0877 100644 --- a/lib/std/math/math.c3 +++ b/lib/std/math/math.c3 @@ -252,8 +252,8 @@ macro @ceil($input) @const => $$ceil($input); @return "lower if x < lower, upper if x > upper, otherwise return x." @require types::is_numerical($typeof(x)) : `The input must be a numerical value or numerical vector` - @require values::@assign_to(lower, x) : `The lower bound must be convertable to the value type.` - @require values::@assign_to(upper, x) : `The upper bound must be convertable to the value type.` + @require $defined(x = lower) : `The lower bound must be convertable to the value type.` + @require $defined(x = upper) : `The upper bound must be convertable to the value type.` *> macro clamp(x, lower, upper) => $$max(($typeof(x))lower, $$min(x, ($typeof(x))upper)); @@ -413,7 +413,7 @@ macro nearbyint(x) => $$nearbyint(x); <* @require values::@is_promotable_to_floatlike(x) : `The input must be a number or a float vector` - @require @assignable_to(exp, $typeof(values::promote_int(x))) || values::@is_int(exp) : `The input must be an integer, castable to the type of x` + @require $defined($typeof(values::promote_int(x)) v = exp) || values::@is_int(exp) : `The input must be an integer, castable to the type of x` *> macro pow(x, exp) { diff --git a/lib/std/math/random.c3 b/lib/std/math/random.c3 index fb1cf4114..43abacf1b 100644 --- a/lib/std/math/random.c3 +++ b/lib/std/math/random.c3 @@ -133,7 +133,7 @@ macro double next_double(random) } // True if the value is a Random. -macro bool is_random(random) => @assignable_to(random, Random); +macro bool is_random(random) => $defined(Random r = random); macro uint128 @long_to_int128(#function) => (uint128)#function << 64 + #function; macro ulong @int_to_long(#function) => (ulong)#function << 32 + #function; diff --git a/lib/std/sort/countingsort.c3 b/lib/std/sort/countingsort.c3 index 633e597f1..925b1efd1 100644 --- a/lib/std/sort/countingsort.c3 +++ b/lib/std/sort/countingsort.c3 @@ -32,7 +32,7 @@ alias Indexs @private = char[256]; alias ElementType = $typeof((Type){}[0]); const bool NO_KEY_FN @private = types::is_same(KeyFn, EmptySlot); -const bool KEY_BY_VALUE @private = NO_KEY_FN ||| @assignable_to((ElementType){}, KeyFn.paramsof[0].type); +const bool KEY_BY_VALUE @private = NO_KEY_FN ||| $defined($typefrom(KeyFn.paramsof[0].type) x = (ElementType){}); const bool LIST_HAS_REF @private = $defined(&(Type){}[0]); alias KeyFnReturnType @if(!NO_KEY_FN) = $typefrom(KeyFn.returns) ; diff --git a/lib/std/sort/insertionsort.c3 b/lib/std/sort/insertionsort.c3 index 4be2893af..5ddd20b34 100644 --- a/lib/std/sort/insertionsort.c3 +++ b/lib/std/sort/insertionsort.c3 @@ -25,7 +25,7 @@ fn void isort(Type list, usz low, usz high, CmpFn comp, Context context) { var $has_cmp = @is_valid_macro_slot(comp); var $has_context = @is_valid_macro_slot(context); - var $cmp_by_value = $has_cmp &&& @assignable_to(list[0], CmpFn.paramsof[0].type); + var $cmp_by_value = $has_cmp &&& $defined($typefrom(CmpFn.paramsof[0].type) p = list[0]); var $has_get_ref = $defined(&list[0]); for (usz i = low; i < high; ++i) { diff --git a/lib/std/sort/quicksort.c3 b/lib/std/sort/quicksort.c3 index c1d73b1fc..36070a7fd 100644 --- a/lib/std/sort/quicksort.c3 +++ b/lib/std/sort/quicksort.c3 @@ -115,7 +115,7 @@ macro @partition(Type list, isz l, isz h, CmpFn cmp, Context context) { var $has_cmp = @is_valid_macro_slot(cmp); var $has_context = @is_valid_macro_slot(context); - var $cmp_by_value = $has_cmp &&& @assignable_to(list[0], CmpFn.paramsof[0].type); + var $cmp_by_value = $has_cmp &&& $defined($typefrom(CmpFn.paramsof[0].type) v = list[0]); ElementType pivot = list[l]; while (l < h) diff --git a/lib/std/sort/sorted.c3 b/lib/std/sort/sorted.c3 index bf4fea6f0..ff45f1f81 100644 --- a/lib/std/sort/sorted.c3 +++ b/lib/std/sort/sorted.c3 @@ -39,7 +39,7 @@ macro int @sort_cmp(list, pos, cmp, ctx) @local { var $has_cmp = @is_valid_macro_slot(cmp); var $has_context = @is_valid_macro_slot(ctx); - var $cmp_by_value = $has_cmp &&& @assignable_to(list[0], $typeof(cmp).paramsof[0].type); + var $cmp_by_value = $has_cmp &&& $defined($typefrom($typeof(cmp).paramsof[0].type) v = list[0]); var a = list[pos]; var b = list[pos+1]; diff --git a/releasenotes.md b/releasenotes.md index fefb509dc..15b081738 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -23,6 +23,7 @@ - Allow `$defined` take declarations: `$defined(int x = y)` - Struct and typedef subtypes inherit dynamic functions. - Improved directory creation error messages in project and library creation commands. +- `@assignable_to` is deprecated in favour of `$define` ### Fixes - List.remove_at would incorrectly trigger ASAN. diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index c159924a1..55bfeb6ea 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -10618,7 +10618,7 @@ static inline bool sema_expr_analyse_iota_decl(SemaContext *context, Expr *expr) static inline bool sema_expr_analyse_assignable(SemaContext *context, Expr *expr) { - SEMA_DEPRECATED(expr, "$assignable is deprecated, use the '@assignable_to' macro instead."); + SEMA_DEPRECATED(expr, "$assignable is deprecated, use '$defined(Type t = expr)' instead."); ASSERT_SPAN(expr, expr->resolve_status == RESOLVE_RUNNING); Expr *type_expr = exprptr(expr->assignable_expr.type); bool in_no_eval = context->call_env.in_no_eval; diff --git a/test/unit/regression/castable_assignable.c3 b/test/unit/regression/castable_assignable.c3 index a88e7dfea..274515bed 100644 --- a/test/unit/regression/castable_assignable.c3 +++ b/test/unit/regression/castable_assignable.c3 @@ -2,14 +2,14 @@ module castable @test; fn void assignable() { - assert(@assignable_to(12.0, int) == false); - assert(@assignable_to(12, int)); - assert(!@assignable_to("12", int)); - assert(@assignable_to("12", String)); - assert(@assignable_to("12", char*)); - //assert($assignable("12", char[*])); - assert(@assignable_to("12", char[2])); - assert(@assignable_to("12", char[3])); + assert(!$defined(int x = 12.0)); + assert($defined(int x = 12)); + assert(!$defined(int x = "12")); + assert($defined(String x = "12")); + assert($defined(char* x = "12")); + assert($defined(char[*] x = "12")); + assert($defined(char[2] x = "12")); + assert($defined(char[3] x = "12")); } fn void castable() diff --git a/test/unit/stdlib/math/math.c3 b/test/unit/stdlib/math/math.c3 index 35eec9b1d..b9a2ac86e 100644 --- a/test/unit/stdlib/math/math.c3 +++ b/test/unit/stdlib/math/math.c3 @@ -669,6 +669,14 @@ fn void test_muldiv() assert(k.muldiv(20,-10) == (ichar[<4>]){-40,-60,-80,-100}); } +fn void test_clamp() +{ + test::eq(math::clamp(10, 2, 8), 8); + test::eq(math::clamp(1, 2, 8), 2); + test::eq(math::clamp(10.2, 2.3, 8.7), 8.7); + test::eq(math::clamp(1.0, 2.3, 8.7), 2.3); +} + fn void test_gcd() @test { assert(math::gcd(20,15) == 5);