From 9af37fe4273d390e21db2487eb7818b3426ba37d Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Mon, 23 Oct 2023 00:21:11 +0200 Subject: [PATCH] $and, $or, $is_const, $assignable, .is_eq, .is_ordered, $defined($vatype(2)) works looking if we can create a type, $defined(foo[0]) $defined(foo()). Remove $checks and @checked. Improvide casting checks to always work without destructive changes. --- lib/std/atomic.c3 | 2 +- lib/std/collections/map.c3 | 2 +- lib/std/collections/object.c3 | 3 +- lib/std/collections/range.c3 | 9 +- lib/std/core/allocators/mem_allocator_fn.c3 | 8 +- lib/std/core/builtin.c3 | 14 +- lib/std/core/mem.c3 | 54 +- lib/std/core/types.c3 | 45 +- lib/std/core/values.c3 | 3 +- lib/std/math/math.c3 | 17 +- lib/std/sort/binarysearch.c3 | 10 +- lib/std/sort/quicksort.c3 | 68 +- lib/std/sort/sort.c3 | 28 +- releasenotes.md | 4 + resources/examples/swap.c3 | 2 +- resources/grammar/c3.l | 3 +- resources/grammar/grammar.y | 14 +- src/compiler/compiler_internal.h | 39 +- src/compiler/copying.c | 10 +- src/compiler/diagnostics.c | 4 - src/compiler/enums.h | 27 +- src/compiler/expr.c | 30 +- src/compiler/parse_expr.c | 52 +- src/compiler/parse_global.c | 13 +- src/compiler/parse_stmt.c | 5 +- src/compiler/sema_casts.c | 367 +++++---- src/compiler/sema_decls.c | 225 +++--- src/compiler/sema_expr.c | 731 +++++++++++------- src/compiler/sema_internal.h | 13 +- src/compiler/sema_liveness.c | 7 +- src/compiler/sema_stmts.c | 35 +- src/compiler/sema_types.c | 3 +- src/compiler/symtab.c | 3 +- src/compiler/tokens.c | 10 +- src/compiler/types.c | 3 +- src/version.h | 2 +- .../cast/cast_untyped_list_error.c3 | 2 +- test/test_suite/compile_time/ct_and_or.c3 | 6 + test/test_suite/compile_time/ct_checks.c3t | 22 - .../compile_time_introspection/defined2.c3 | 15 + .../compile_time_introspection/defined_err.c3 | 2 +- .../defined_subscript.c3 | 7 + test/test_suite/enumerations/enum_add_sub.c3t | 1 - .../generic/generic_lambda_complex.c3t | 2 +- .../switch/switch_in_defer_macro.c3t | 4 +- test/unit/regression/castable_assignable.c3 | 24 + 46 files changed, 1149 insertions(+), 801 deletions(-) create mode 100644 test/test_suite/compile_time/ct_and_or.c3 delete mode 100644 test/test_suite/compile_time/ct_checks.c3t create mode 100644 test/test_suite/compile_time_introspection/defined2.c3 create mode 100644 test/test_suite/compile_time_introspection/defined_subscript.c3 create mode 100644 test/unit/regression/castable_assignable.c3 diff --git a/lib/std/atomic.c3 b/lib/std/atomic.c3 index f47d8ea4e..9f39017c2 100644 --- a/lib/std/atomic.c3 +++ b/lib/std/atomic.c3 @@ -247,7 +247,7 @@ macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT) * @return "returns the old value of ptr" * * @require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two." - * @require types::is_int($typeof(*ptr)) "Only intege pointers may be used." + * @require types::is_int($typeof(*ptr)) "Only integer pointers may be used." * @require types::is_int($typeof(y)) "The value for or must be an int" * @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." **/ diff --git a/lib/std/collections/map.c3 b/lib/std/collections/map.c3 index d6bfdd507..2eafc9b1f 100644 --- a/lib/std/collections/map.c3 +++ b/lib/std/collections/map.c3 @@ -7,7 +7,7 @@ import std::math; const uint DEFAULT_INITIAL_CAPACITY = 16; const uint MAXIMUM_CAPACITY = 1u << 31; const float DEFAULT_LOAD_FACTOR = 0.75; -const VALUE_IS_EQUATABLE = types::is_equatable(Value); +const VALUE_IS_EQUATABLE = Value.is_eq; const bool COPY_KEYS = types::implements_copy(Key); struct HashMap diff --git a/lib/std/collections/object.c3 b/lib/std/collections/object.c3 index 88fb72e05..ada017362 100644 --- a/lib/std/collections/object.c3 +++ b/lib/std/collections/object.c3 @@ -217,7 +217,7 @@ macro Object* Object.object_from_value(&self, value) @private $case $Type.typeid == void*.typeid: if (value != null) return CastResult.TYPE_MISMATCH?; return &NULL_OBJECT; - $case $checks(String s = value): + $case $assignable(value, String): return new_string(value, self.allocator); $default: $error "Unsupported object type."; @@ -307,7 +307,6 @@ fn void Object.set_object_at(&self, usz index, Object* to_set) /** * @require $Type.kindof.is_int() "Expected an integer type." - * @checked ($Type)1.0 "Expected an integer type." **/ macro get_integer_value(Object* value, $Type) { diff --git a/lib/std/collections/range.c3 b/lib/std/collections/range.c3 index 0da93df94..a1e38f4bc 100644 --- a/lib/std/collections/range.c3 +++ b/lib/std/collections/range.c3 @@ -1,6 +1,5 @@ /** - * @checked Type{} < Type{} : "The type must be comparable" - * @checked Type{} + (Type)1 : "The type must be possible to add to" + * @require Type.is_ordered : "The type must be ordered" **/ module std::collections::range(); @@ -13,7 +12,7 @@ struct Range (Printable) fn usz Range.len(&self) @operator(len) { if (self.end < self.start) return 0; - return (usz)(self.end - self.start + (Type)1); + return (usz)(self.end - self.start) + 1; } fn bool Range.contains(&self, Type value) @inline @@ -26,7 +25,7 @@ fn bool Range.contains(&self, Type value) @inline **/ fn Type Range.get(&self, usz index) @operator([]) { - return self.start + (Type)index; + return (Type)(self.start + (usz)index); } fn String Range.to_string(&self, Allocator* using = mem::heap()) @dynamic @@ -71,5 +70,5 @@ fn String ExclusiveRange.to_string(&self, Allocator* using = mem::heap()) @dynam **/ fn Type ExclusiveRange.get(&self, usz index) @operator([]) { - return self.start + (Type)index; + return (Type)(self.start + index); } diff --git a/lib/std/core/allocators/mem_allocator_fn.c3 b/lib/std/core/allocators/mem_allocator_fn.c3 index f4f239f2c..93b4b3aca 100644 --- a/lib/std/core/allocators/mem_allocator_fn.c3 +++ b/lib/std/core/allocators/mem_allocator_fn.c3 @@ -19,7 +19,7 @@ struct AlignedBlock macro void*! @aligned_alloc(#alloc_fn, usz bytes, usz alignment, usz offset) { usz header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset; - $if $checks(#alloc_fn(bytes)!): + $if @typekind(#alloc_fn(bytes)) == OPTIONAL: void* data = #alloc_fn(header + bytes)!; $else void* data = #alloc_fn(header + bytes); @@ -38,7 +38,7 @@ macro void*! @aligned_alloc(#alloc_fn, usz bytes, usz alignment, usz offset) macro void*! @aligned_calloc(#calloc_fn, usz bytes, usz alignment, usz offset) { usz header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset; - $if $checks(#calloc_fn(bytes)!): + $if @typekind(#calloc_fn(bytes)) == OPTIONAL: void* data = #calloc_fn(header + bytes)!; $else void* data = #calloc_fn(header + bytes); @@ -60,7 +60,7 @@ macro void*! @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes void* data_start = desc.start; void* new_data = @aligned_calloc(#calloc_fn, bytes, alignment, offset)!; mem::copy(new_data, old_pointer, desc.len < bytes ? desc.len : bytes, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT); - $if $checks(#free_fn(data_start)!): + $if @typekind(#free_fn(data_start)) == OPTIONAL: #free_fn(data_start)!; $else #free_fn(data_start); @@ -71,7 +71,7 @@ macro void*! @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes macro void! @aligned_free(#free_fn, void* old_pointer) { AlignedBlock* desc = (AlignedBlock*)old_pointer - 1; - $if $checks(#free_fn(desc.start)!): + $if @typekind(#free_fn(desc.start)) == OPTIONAL: #free_fn(desc.start)!; $else #free_fn(desc.start); diff --git a/lib/std/core/builtin.c3 b/lib/std/core/builtin.c3 index 7103e4f88..983ff8dee 100644 --- a/lib/std/core/builtin.c3 +++ b/lib/std/core/builtin.c3 @@ -44,7 +44,7 @@ macro void @scope(&variable; @body) @builtin /** * Swap two variables - * @checked *a = *b, *b = *a + * @require $assignable(*b, $typeof(*a)) && $assignable(*a, $typeof(*b)) **/ macro void @swap(&a, &b) @builtin { @@ -280,7 +280,7 @@ macro bool @unlikely(bool #value, $probability = 1.0) @builtin /** * @require values::@is_int(#value) || values::@is_bool(#value) - * @checked $typeof(#value) a = expected + * @require $assignable(expected, $typeof(#value)) * @require $probability >= 0 && $probability <= 1.0 **/ macro @expect(#value, expected, $probability = 1.0) @builtin @@ -331,16 +331,6 @@ macro swizzle2(v, v2, ...) @builtin return $$swizzle2(v, v2, $vasplat()); } -macro bool @castable(#expr, $To) @builtin -{ - return $checks(($To)#expr); -} - -macro bool @convertible(#expr, $To) @builtin -{ - return $checks($To x = #expr); -} - macro anyfault @catch(#expr) @builtin { if (catch f = #expr) return f; diff --git a/lib/std/core/mem.c3 b/lib/std/core/mem.c3 index 16abe054b..06ef130b6 100644 --- a/lib/std/core/mem.c3 +++ b/lib/std/core/mem.c3 @@ -13,7 +13,7 @@ const DEFAULT_MEM_ALIGNMENT = (void*.alignof) * 2; * @param ptr "The pointer address to load from." * @param mask "The mask for the load" * @param passthru "The value to use for non masked values" - * @checked $typeof(ptr) x = &&passthru : "Pointer and passthru must match" + * @require $assignable(&&passthru, $typeof(ptr)) : "Pointer and passthru must match" * @require @typekind(passthru) == VECTOR : "Expected passthru to be a vector" * @require passthru.len == mask.len : "Mask and passthru must have the same length" * @@ -32,7 +32,7 @@ macro masked_load(ptr, bool[<*>] mask, passthru) * @param passthru "The value to use for non masked values" * @param $alignment "The alignment to assume for the pointer" * - * @checked $typeof(ptr) x = &&passthru : "Pointer and passthru must match" + * @require $assignable(&&passthru, $typeof(ptr)) : "Pointer and passthru must match" * @require @typekind(passthru) == VECTOR : "Expected passthru to be a vector" * @require passthru.len == mask.len : "Mask and passthru must have the same length" * @require math::is_power_of_2($alignment) : "The alignment must be a power of two" @@ -53,7 +53,7 @@ macro @masked_load_aligned(ptr, bool[<*>] mask, passthru, usz $alignment) * * @require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector" * @require @typekind(passthru) == VECTOR : "Expected passthru to be a vector" - * @checked $typeof(ptrvec[0]) x = &&passthru[0] : "Pointer and passthru must match" + * @require $assignable(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match" * @require passthru.len == mask.len : "Mask and passthru must have the same length" * @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length" * @@ -75,7 +75,7 @@ macro gather(ptrvec, bool[<*>] mask, passthru) * * @require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector" * @require @typekind(passthru) == VECTOR : "Expected passthru to be a vector" - * @checked $typeof(ptrvec[0]) x = &&passthru[0] : "Pointer and passthru must match" + * @require $assignable(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match" * @require passthru.len == mask.len : "Mask and passthru must have the same length" * @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length" * @require math::is_power_of_2($alignment) : "The alignment must be a power of two" @@ -95,7 +95,7 @@ macro @gather_aligned(ptrvec, bool[<*>] mask, passthru, usz $alignment) * @param value "The value to store masked" * @param mask "The mask for the store" * - * @checked $typeof(ptr) x = &&value : "Pointer and value must match" + * @require $assignable(&&value, $typeof(ptr)) : "Pointer and value must match" * @require @typekind(value) == VECTOR : "Expected value to be a vector" * @require value.len == mask.len : "Mask and value must have the same length" **/ @@ -110,7 +110,7 @@ macro masked_store(ptr, value, bool[<*>] mask) * @param mask "The mask for the store" * @param $alignment "The alignment of the pointer" * - * @checked $typeof(ptr) x = &&value : "Pointer and value must match" + * @require $assignable(&&value, $typeof(ptr)) : "Pointer and value must match" * @require @typekind(value) == VECTOR : "Expected value to be a vector" * @require value.len == mask.len : "Mask and value must have the same length" * @require math::is_power_of_2($alignment) : "The alignment must be a power of two" @@ -127,7 +127,7 @@ macro @masked_store_aligned(ptr, value, bool[<*>] mask, usz $alignment) * @param mask "The mask for the store" * @require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector" * @require @typekind(value) == VECTOR : "Expected value to be a vector" - * @checked $typeof(ptrvec[0]) x = &&value[0] : "Pointer and value must match" + * @require $assignable(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match" * @require value.len == mask.len : "Mask and value must have the same length" * @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length" * @@ -145,7 +145,7 @@ macro scatter(ptrvec, value, bool[<*>] mask) * * @require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector" * @require @typekind(value) == VECTOR : "Expected value to be a vector" - * @checked $typeof(ptrvec[0]) x = &&value[0] : "Pointer and value must match" + * @require $assignable(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match" * @require value.len == mask.len : "Mask and value must have the same length" * @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length" * @require math::is_power_of_2($alignment) : "The alignment must be a power of two" @@ -161,7 +161,7 @@ macro @volatile_load(&x) @builtin } /** - * @checked *x == y : "The value doesn't match the variable" + * @require $assignable(y, $typeof(*x)) : "The value doesn't match the variable" **/ macro @volatile_store(&x, y) @builtin { @@ -321,7 +321,7 @@ macro void set(void* dst, char val, usz len, usz $dst_align = 0, bool $is_volati * @require values::@inner_kind(b) == TypeKind.SUBARRAY || values::@inner_kind(b) == TypeKind.POINTER * @require values::@inner_kind(a) != TypeKind.SUBARRAY || len == -1 * @require values::@inner_kind(a) != TypeKind.POINTER || len > -1 - * @checked (a = b), (b = a) + * @require values::@assign_to(a, b) && values::@assign_to(b, a) **/ macro bool equals(a, b, isz len = -1, usz $align = 0) { @@ -383,7 +383,7 @@ macro type_alloc_must_be_aligned($Type) } /** * @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len" - * @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'" + * @require $vacount != 2 || $defined($vatype(0)) "Expected 'malloc(Foo, 12)'" **/ macro malloc(..., Allocator* using = mem::heap(), usz end_padding = 0) @builtin { @@ -392,13 +392,13 @@ macro malloc(..., Allocator* using = mem::heap(), usz end_padding = 0) @builtin /** * @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len" - * @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'" + * @require $vacount != 2 || $defined($vatype(0)) "Expected 'malloc(Foo, 12)'" **/ macro malloc_checked(..., Allocator* using = mem::heap(), usz end_padding = 0) @builtin { - $if $checks($vatype(0).sizeof): + $if $defined($vatype(0)): var $Type = $vatype(0); - $assert !type_alloc_must_be_aligned($vatype(0)) : "Type must be allocated with malloc_aligned"; + $assert !type_alloc_must_be_aligned($Type) : "Type must be allocated with malloc_aligned"; $if $vacount == 2: usz size = $vaarg(1); return (($Type*)using.alloc_checked($Type.sizeof * size + end_padding))[:size]; @@ -413,12 +413,12 @@ macro malloc_checked(..., Allocator* using = mem::heap(), usz end_padding = 0) @ /** * @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len" - * @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'" + * @require $vacount != 2 || $defined($vatype(0)) "Expected 'malloc(Foo, 12)'" * @require alignment && math::is_power_of_2(alignment) **/ macro malloc_aligned(..., usz alignment = 0, usz end_padding = 0, Allocator* using = mem::heap()) @builtin { - $if $checks($vatype(0).sizeof): + $if $defined($vatype(0)): var $Type = $vatype(0); $if $vacount == 2: usz size = $vaarg(1); @@ -433,7 +433,7 @@ macro malloc_aligned(..., usz alignment = 0, usz end_padding = 0, Allocator* usi /** * @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len" - * @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'" + * @require $vacount != 2 || $defined($vatype(0)) "Expected 'malloc(Foo, 12)'" **/ macro calloc(..., Allocator* using = mem::heap(), usz end_padding = 0) @builtin { @@ -442,13 +442,13 @@ macro calloc(..., Allocator* using = mem::heap(), usz end_padding = 0) @builtin /** * @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len" - * @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'" + * @require $vacount != 2 || $defined($vatype(0)) "Expected 'malloc(Foo, 12)'" **/ macro calloc_checked(..., Allocator* using = mem::heap(), usz end_padding = 0) @builtin { - $if $checks($vatype(0).sizeof): + $if $defined($vatype(0)): var $Type = $vatype(0); - $assert !type_alloc_must_be_aligned($vatype(0)) : "Type must be allocated with calloc_aligned"; + $assert !type_alloc_must_be_aligned($Type) : "Type must be allocated with calloc_aligned"; $if $vacount == 2: usz size = $vaarg(1); return (($Type*)using.calloc_checked($Type.sizeof * size + end_padding))[:size]; @@ -463,12 +463,12 @@ macro calloc_checked(..., Allocator* using = mem::heap(), usz end_padding = 0) @ /** * @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len" - * @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'" + * @require $vacount != 2 || $defined($vatype(0)) "Expected 'malloc(Foo, 12)'" * @require alignment && math::is_power_of_2(alignment) **/ macro calloc_aligned(..., usz alignment = 0, Allocator* using = mem::heap(), usz end_padding = 0) @builtin { - $if $checks($vatype(0).sizeof): + $if $defined($vatype(0)): var $Type = $vatype(0); $if $vacount == 2: usz size = $vaarg(1); @@ -517,11 +517,11 @@ macro void @scoped(Allocator* using; @body()) /** * @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len" - * @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'" + * @require $vacount != 2 || $defined($vatype(0)) "Expected 'malloc(Foo, 12)'" **/ macro tmalloc(..., usz end_padding = 0, usz alignment = DEFAULT_MEM_ALIGNMENT) @builtin { - $if $checks($vatype(0).sizeof): + $if $defined($vatype(0)): var $Type = $vatype(0); $if $vacount == 2: usz size = $vaarg(1); @@ -536,11 +536,11 @@ macro tmalloc(..., usz end_padding = 0, usz alignment = DEFAULT_MEM_ALIGNMENT) @ /** * @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len" - * @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'" + * @require $vacount != 2 || $defined($vatype(0)) "Expected 'malloc(Foo, 12)'" **/ macro tcalloc(..., usz end_padding = 0, usz alignment = mem::DEFAULT_MEM_ALIGNMENT) @builtin { - $if $checks($vatype(0).sizeof): + $if $defined($vatype(0)): var $Type = $vatype(0); $if $vacount == 2: usz size = $vaarg(1); @@ -582,7 +582,7 @@ macro void @stack_pool(usz $size; @body) @builtin macro void @pool(TempAllocator* #other_temp = null; @body) @builtin { TempAllocator* current = temp(); - var $has_arg = !$checks(var $x = #other_temp); + var $has_arg = !$is_const(#other_temp); $if $has_arg: TempAllocator* original = current; if (current == (void*)#other_temp) current = temp_allocator_next(); diff --git a/lib/std/core/types.c3 b/lib/std/core/types.c3 index dedcfd4aa..9b7174f40 100644 --- a/lib/std/core/types.c3 +++ b/lib/std/core/types.c3 @@ -108,28 +108,6 @@ fn bool TypeKind.is_int(kind) @inline return kind == TypeKind.SIGNED_INT || kind == TypeKind.UNSIGNED_INT; } -macro bool is_indexable($Type) -{ - return $checks($Type t, int i, t[i]); -} - -macro bool is_comparable($Type) -{ - var $kind = $Type.kindof; - $if $kind == TypeKind.DISTINCT: - return is_comparable($typefrom($Type.inner)); - $else - return $kind == TypeKind.SIGNED_INT || $kind == TypeKind.UNSIGNED_INT || $kind == TypeKind.FLOAT - || $kind == TypeKind.VECTOR || $kind == TypeKind.BOOL || $kind == TypeKind.POINTER - || $kind == TypeKind.ENUM; - $endif -} - -macro bool is_equatable($Type) -{ - return $checks($Type a, a == a); -} - macro bool is_subarray_convertable($Type) { $switch ($Type.kindof) @@ -158,6 +136,18 @@ macro bool is_intlike($Type) $endswitch } +macro bool is_underlying_int($Type) +{ + $switch ($Type.kindof) + $case SIGNED_INT: + $case UNSIGNED_INT: + return true; + $case DISTINCT: + return is_underlying_int($typefrom($Type.inner)); + $default: + return false; + $endswitch +} macro bool is_float($Type) => $Type.kindof == TypeKind.FLOAT; @@ -187,11 +177,6 @@ macro TypeKind inner_kind($Type) $endif } -macro bool @convertable(#a, $TypeB) @builtin -{ - return $checks($TypeB x = #a); -} - macro bool is_same($TypeA, $TypeB) { return $TypeA.typeid == $TypeB.typeid; @@ -269,7 +254,7 @@ macro bool is_equatable_type($Type) $if $defined($Type.less) || $defined($Type.compare_to) || $defined($Type.equals): return true; $else - return is_equatable($Type); + return $Type.is_eq; $endif } @@ -278,7 +263,7 @@ macro bool is_equatable_type($Type) **/ macro bool implements_copy($Type) { - return $checks(Allocator *a, $Type x, $Type y = x.copy(a), $Type z = x.copy(), z.free(a), z.free()); + return $defined($Type.copy) && $defined($Type.free); } macro bool is_equatable_value(value) @@ -291,7 +276,7 @@ macro bool is_comparable_value(value) $if $defined(value.less) || $defined(value.compare_to): return true; $else - return is_comparable($typeof(value)); + return $typeof(value).is_ordered; $endif } diff --git a/lib/std/core/values.c3 b/lib/std/core/values.c3 index 26cd10968..b0b3eec55 100644 --- a/lib/std/core/values.c3 +++ b/lib/std/core/values.c3 @@ -8,13 +8,14 @@ macro bool @typeis(#value, $Type) @builtin => $typeof(#value).typeid == $Type.ty macro bool @is_same_type(#value1, #value2) => $typeof(#value1).typeid == $typeof(#value2).typeid; macro bool @is_bool(#value) => types::is_bool($typeof(#value)); macro bool @is_int(#value) => types::is_int($typeof(#value)); -macro bool @convertable_to(#a, #b) => $checks($typeof(#b) x = #a); macro bool @is_floatlike(#value) => types::is_floatlike($typeof(#value)); macro bool @is_float(#value) => types::is_float($typeof(#value)); macro bool @is_promotable_to_floatlike(#value) => types::is_promotable_to_floatlike($typeof(#value)); macro bool @is_promotable_to_float(#value) => types::is_promotable_to_float($typeof(#value)); macro bool @is_vector(#value) => types::is_vector($typeof(#value)); macro bool @is_same_vector_type(#value1, #value2) => types::is_same_vector_type($typeof(#value1), $typeof(#value2)); +macro bool @assign_to(#value1, #value2) => $assignable(#value1, $typeof(#value2)); + macro promote_int(x) { $if @is_int(x): diff --git a/lib/std/math/math.c3 b/lib/std/math/math.c3 index 752438ec3..636e53c78 100644 --- a/lib/std/math/math.c3 +++ b/lib/std/math/math.c3 @@ -138,7 +138,7 @@ macro sign(x) /** * @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value" - * @checked x + y + * @require values::@is_int(y) || values::@is_float(y) "Expected an integer or floating point value" **/ macro atan2(x, y) { @@ -151,8 +151,8 @@ macro atan2(x, y) /** * @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value" - * @checked (*y)[0] = x, y.len - * @require y.len == 2 + * @require $and(@typekind(y) == ARRAY || @typekind(y) == VECTOR, y.len == 2) + * @require $assignable(x, $typeof(y[0])) **/ macro sincos(x, y) { @@ -165,7 +165,6 @@ macro sincos(x, y) /** * @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value" - * @checked x **/ macro atan(x) { @@ -250,14 +249,14 @@ macro ceil(x) => $$ceil(x); * @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` - * @checked $typeof(x) z = lower `The lower bound must be convertable to the value type.` - * @checked $typeof(x) z = upper `The upper bound must be convertable to the value type.` + * @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.` **/ macro clamp(x, lower, upper) => $$max(($typeof(x))lower, $$min(x, ($typeof(x))upper)); /** * @require values::@is_promotable_to_floatlike(mag) `The input must be a number value or float vector` - * @require @convertable(sgn, $typeof(values::promote_int(mag))) + * @require values::@assign_to(sgn, values::promote_int(mag)) **/ macro copysign(mag, sgn) => $$copysign(values::promote_int(mag), ($typeof(values::promote_int(mag)))sgn); @@ -391,7 +390,7 @@ macro nearbyint(x) => $$nearbyint(x); /** * @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector` - * @require values::@convertable_to(exp, x) || values::@is_int(exp) `The input must be an integer, castable to the type of x` + * @require $assignable(exp, $typeof(x)) || values::@is_int(exp) `The input must be an integer, castable to the type of x` **/ macro pow(x, exp) { @@ -943,7 +942,7 @@ macro bool int128.is_even(int128 x) => is_even(x); macro bool int128.is_odd(int128 x) => is_odd(x); /** - * @checked x & 1 + * @require types::is_underlying_int($typeof(x)) : `is_power_of_2 may only be used on integer types` */ macro bool is_power_of_2(x) { diff --git a/lib/std/sort/binarysearch.c3 b/lib/std/sort/binarysearch.c3 index 72fb1cfdc..30799369b 100644 --- a/lib/std/sort/binarysearch.c3 +++ b/lib/std/sort/binarysearch.c3 @@ -3,8 +3,8 @@ module std::sort; /** * Perform a binary search over the sorted array and return the index * in [0, array.len) where x would be inserted or cmp(i) is true and cmp(j) is true for j in [i, array.len). - * @require is_searchable(list) "The list must be indexable and support .len or .len()" - * @require !cmp || is_comparer(cmp, list) "Expected a comparison function which compares values" + * @require $defined(list[0]) && $defined(list.len) "The list must be indexable" + * @require $or($typeof(cmp).typeid == void*.typeid, @is_comparer(cmp, list)) "Expected a comparison function which compares values" **/ macro usz binarysearch(list, x, cmp = null) @builtin { @@ -13,7 +13,7 @@ macro usz binarysearch(list, x, cmp = null) @builtin for (usz j = len; i < j;) { usz half = i + (j - i) / 2; - $if $checks(!cmp): + $if $typeof(cmp).typeid == void*.typeid: switch { case greater(list[half], x): j = half; @@ -22,9 +22,9 @@ macro usz binarysearch(list, x, cmp = null) @builtin } $else $switch - $case $checks(cmp(list[0], list[0])): + $case $typeof(cmp).params[0] == $typeof(list[0]).typeid: int res = cmp(list[half], x); - $case $checks(cmp(&list[0], &list[0])): + $default: int res = cmp(&list[half], &x); $endswitch switch diff --git a/lib/std/sort/quicksort.c3 b/lib/std/sort/quicksort.c3 index 36ce8e872..d01bbd257 100644 --- a/lib/std/sort/quicksort.c3 +++ b/lib/std/sort/quicksort.c3 @@ -3,8 +3,8 @@ import std::sort::qs; /** * Sort list using the quick sort algorithm. - * @require is_searchable(list) "The list must be indexable and support .len or .len()" - * @require !cmp || is_comparer(cmp, list) "Expected a comparison function which compares values" + * @require $defined(list[0]) && $defined(list.len) "The list must be indexable and support .len or .len()" + * @require $or($typeof(cmp).typeid == void*.typeid, @is_comparer(cmp, list)) "Expected a comparison function which compares values" **/ macro quicksort(list, cmp = null) @builtin { @@ -30,6 +30,8 @@ def Stack = StackElementItem[64] @private; fn void qsort(Type list, isz low, isz high, Comparer cmp) { + var $no_cmp = Comparer.typeid == void*.typeid; + var $cmp_by_value = $and(!$no_cmp, Comparer.params[0] == $typeof(list[0]).typeid); if (low >= 0 && high >= 0 && low < high) { Stack stack; @@ -45,40 +47,40 @@ fn void qsort(Type list, isz low, isz high, Comparer cmp) if (l < h) { - ElementType pivot = list[l]; - while (l < h) - { - $switch - $case $checks(cmp(list[0], list[0])): - while (cmp(list[h], pivot) >= 0 && l < h) h--; - $case $checks(cmp(&list[0], &list[0])): - while (cmp(&list[h], &pivot) >= 0 && l < h) h--; - $default: - while (greater_eq(list[h], pivot) && l < h) h--; - $endswitch - if (l < h) list[l++] = list[h]; - $switch - $case $checks(cmp(list[0], list[0])): - while (cmp(list[l], pivot) <= 0 && l < h) l++; - $case $checks(cmp(&list[0], &list[0])): - while (cmp(&list[l], &pivot) <= 0 && l < h) l++; - $default: - while (less_eq(list[l], pivot) && l < h) l++; - $endswitch - if (l < h) list[h--] = list[l]; - } - list[l] = pivot; - stack[i + 1].low = l + 1; - stack[i + 1].high = stack[i].high; - stack[i++].high = l; - if (stack[i].high - stack[i].low > stack[i - 1].high - stack[i - 1].low) - { - @swap(stack[i], stack[i - 1]); - } + ElementType pivot = list[l]; + while (l < h) + { + $switch + $case $cmp_by_value: + while (cmp(list[h], pivot) >= 0 && l < h) h--; + $case !$no_cmp: + while (cmp(&list[h], &pivot) >= 0 && l < h) h--; + $default: + while (greater_eq(list[h], pivot) && l < h) h--; + $endswitch + if (l < h) list[l++] = list[h]; + $switch + $case $cmp_by_value: + while (cmp(list[l], pivot) <= 0 && l < h) l++; + $case !$no_cmp: + while (cmp(&list[l], &pivot) <= 0 && l < h) l++; + $default: + while (less_eq(list[l], pivot) && l < h) l++; + $endswitch + if (l < h) list[h--] = list[l]; + } + list[l] = pivot; + stack[i + 1].low = l + 1; + stack[i + 1].high = stack[i].high; + stack[i++].high = l; + if (stack[i].high - stack[i].low > stack[i - 1].high - stack[i - 1].low) + { + @swap(stack[i], stack[i - 1]); + } } else { - i--; + i--; } } } diff --git a/lib/std/sort/sort.c3 b/lib/std/sort/sort.c3 index 7cb8f32fb..a75c1c4ee 100644 --- a/lib/std/sort/sort.c3 +++ b/lib/std/sort/sort.c3 @@ -1,21 +1,33 @@ module std::sort; -macro bool is_searchable(list) -{ - return $checks(list[0]) && ($checks(list.len) || $checks(list.len())); -} macro usz @len_from_list(&list) { - $if $checks(list.len()): + $if $defined(list.len()): return list.len(); $else return list.len; $endif } -macro bool is_comparer(cmp, list) +macro bool @is_comparer(#cmp, #list) { - return $checks(int i = cmp(list[0], list[0])) - || $checks(int i = cmp(&list[0], &list[0])); + var $params = $typeof(#cmp).params; + $if $params.len != 2: + return false; + $else + $if $params[0] != $params[1]: + return false; + $else + var $element = $typeof(#list[0]).typeid; + $switch + $case $element == $params[0]: + return true; + $case $and($params[0].kindof == POINTER, $params[0].inner == $element): + return true; + $default: + return false; + $endswitch + $endif + $endif } \ No newline at end of file diff --git a/releasenotes.md b/releasenotes.md index 39209db28..ff956e6de 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -3,6 +3,10 @@ ## 0.5.0 Change List ### Changes / improvements +- `$and` compile time "and" which does not check expressions after the first is an error. +- `$is_const` returns true if an expression is compile time const. +- `$assignable` returns true is an expression may be implicitly cast to a type. +- `$checks` and `@checked` removed, replaced by an improved `$defined` - Asm string blocks use AT&T syntax for better reliability. - Distinct methods changed to separate syntax. - 'exec' directive to run scripts at compile time. diff --git a/resources/examples/swap.c3 b/resources/examples/swap.c3 index 1d304fbf5..1e903df15 100644 --- a/resources/examples/swap.c3 +++ b/resources/examples/swap.c3 @@ -2,7 +2,7 @@ module test; import libc; /** - * @checked *a = *b, *b = *a + * @require values::@assign_to(*b, *a) && values::@assign_to(*a, *b) */ macro void @swap(&a, &b) { diff --git a/resources/grammar/c3.l b/resources/grammar/c3.l index 5c0c02af0..d95d7c137 100644 --- a/resources/grammar/c3.l +++ b/resources/grammar/c3.l @@ -39,9 +39,9 @@ int comment_level = 0; "$alignof" { count(); return(CT_ALIGNOF); } +"$and" { count(); return(CT_AND); } "$assert" { count(); return(CT_ASSERT); } "$case" { count(); return(CT_CASE); } -"$checks" { count(); return(CT_CHECKS); } "$default" { count(); return(CT_DEFAULT); } "$defined" { count(); return(CT_DEFINED); } "$echo" { count(); return(CT_ECHO); } @@ -58,6 +58,7 @@ int comment_level = 0; "$for" { count(); return(CT_FOR); } "$foreach" { count(); return(CT_FOREACH); } "$if" { count(); return(CT_IF); } +"$is_const" { count(); return(CT_IS_CONST); } "$include" { count(); return(CT_INCLUDE); } "$nameof" { count(); return(CT_NAMEOF); } "$offsetof" { count(); return(CT_OFFSETOF); } diff --git a/resources/grammar/grammar.y b/resources/grammar/grammar.y index d87af8566..731b93ccd 100644 --- a/resources/grammar/grammar.y +++ b/resources/grammar/grammar.y @@ -31,9 +31,9 @@ void yyerror(char *s); %token TRY CATCH SCOPE DEFER LVEC RVEC OPTELSE CT_TYPEFROM CT_TYPEOF TLOCAL %token CT_VASPLAT INLINE DISTINCT CT_VACONST CT_NAMEOF CT_VAREF CT_VACOUNT CT_VAARG %token CT_SIZEOF CT_STRINGIFY CT_QNAMEOF CT_OFFSETOF CT_VAEXPR CT_FEATURE -%token CT_EXTNAMEOF CT_EVAL CT_DEFINED CT_CHECKS CT_ALIGNOF ASSERT +%token CT_EXTNAMEOF CT_EVAL CT_DEFINED CT_ALIGNOF ASSERT %token ASM CHAR_LITERAL REAL TRUE FALSE CT_CONST_IDENT -%token LBRAPIPE RBRAPIPE HASH_CONST_IDENT +%token LBRAPIPE RBRAPIPE HASH_CONST_IDENT CT_CASTABLE CT_ASSIGNABLE CT_AND CT_IS_CONST %start translation_unit %% @@ -71,17 +71,22 @@ local_ident_expr ct_call : CT_ALIGNOF - | CT_DEFINED | CT_EXTNAMEOF | CT_NAMEOF | CT_OFFSETOF | CT_QNAMEOF ; +ct_castable + : CT_ASSIGNABLE + ; + ct_analyse : CT_EVAL + | CT_DEFINED | CT_SIZEOF | CT_STRINGIFY + | CT_IS_CONST ; ct_arg @@ -140,7 +145,8 @@ base_expr | ct_analyse '(' expr ')' | CT_VACOUNT | CT_FEATURE '(' CONST_IDENT ')' - | CT_CHECKS '(' expression_list ')' + | CT_AND '(' expression_list ')' + | ct_castable '(' expr ',' type ')' | lambda_decl compound_statement ; diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 71152b42a..b3aac5151 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -984,6 +984,12 @@ typedef struct ExprId arg; } ExprCtArg; +typedef struct +{ + bool is_and; + Expr** args; +} ExprCtAndOr; + typedef struct { CastKind kind : 8; @@ -1123,6 +1129,12 @@ typedef struct const char *swizzle; } ExprSwizzle; +typedef struct +{ + bool is_assign; + ExprId expr; + TypeInfoId type; +} ExprCastable; struct Expr_ @@ -1144,6 +1156,8 @@ struct Expr_ Expr** cond_expr; // 8 ExprConst const_expr; // 32 ExprCtArg ct_arg_expr; + ExprCtAndOr ct_and_or_expr; + ExprCastable castable_expr; ExprCtCall ct_call_expr; // 24 ExprIdentifierRaw ct_ident_expr; // 24 Decl *decl_expr; // 8 @@ -1631,9 +1645,6 @@ typedef enum { CALL_ENV_GLOBAL_INIT, CALL_ENV_FUNCTION, - CALL_ENV_INITIALIZER, - CALL_ENV_FINALIZER, - CALL_ENV_CHECKS, CALL_ENV_ATTR, } CallEnvKind; @@ -1708,7 +1719,6 @@ typedef struct unsigned errors_found; unsigned warnings_found; unsigned includes_used; - bool suppress_errors; Decl ***locals_list; HTable compiler_defines; HTable features; @@ -1882,7 +1892,6 @@ extern const char *kw_IoError; extern const char *kw_argc; extern const char *kw_argv; -extern const char *kw_at_checked; extern const char *kw_at_deprecated; extern const char *kw_at_ensure; extern const char *kw_at_param; @@ -2106,10 +2115,12 @@ AsmRegister *asm_reg_by_index(unsigned index); bool cast_implicit_silent(SemaContext *context, Expr *expr, Type *to_type); bool cast_implicit(SemaContext *context, Expr *expr, Type *to_type); +bool cast_explicit_silent(SemaContext *context, Expr *expr, Type *to_type); bool cast_explicit(SemaContext *context, Expr *expr, Type *to_type); -bool may_cast(SemaContext *cc, Expr *expr, Type *to_type, bool is_explicit); -void cast_no_check(Expr *expr, Type *to_type, bool add_optional); +bool may_cast(SemaContext *cc, Expr *expr, Type *to_type, bool is_explicit, bool is_silent); + +void cast_no_check(SemaContext *context, Expr *expr, Type *to_type, bool add_optional); bool cast_to_index(SemaContext *context, Expr *index); CastKind cast_to_bool_kind(Type *type); @@ -2286,7 +2297,9 @@ bool sema_analyse_statement(SemaContext *context, Ast *statement); bool sema_expr_analyse_assign_right_side(SemaContext *context, Expr *expr, Type *left_type, Expr *right, bool is_unwrapped_var); bool sema_expr_analyse_initializer_list(SemaContext *context, Type *to, Expr *expr); Expr **sema_expand_vasplat_exprs(SemaContext *c, Expr **exprs); -bool sema_expr_analyse_general_call(SemaContext *context, Expr *expr, Decl *decl, Expr *struct_var, bool optional); + +bool sema_expr_analyse_general_call(SemaContext *context, Expr *expr, Decl *decl, Expr *struct_var, bool optional, + bool *no_match_ref); Decl *sema_decl_stack_resolve_symbol(const char *symbol); Decl *sema_find_decl_in_modules(Module **module_list, Path *path, const char *interned_name); @@ -2672,11 +2685,6 @@ INLINE bool type_is_integer_unsigned(Type *type) INLINE bool type_info_poison(TypeInfo *type) { - if (global_context.suppress_errors) - { - type->resolve_status = RESOLVE_NOT_DONE; - return false; - } type->kind = TYPE_INFO_POISON; type->type = poisoned_type; type->resolve_status = RESOLVE_DONE; @@ -3076,11 +3084,6 @@ INLINE bool decl_ok(Decl *decl) INLINE bool decl_poison(Decl *decl) { - if (global_context.suppress_errors) - { - if (decl->resolve_status == RESOLVE_RUNNING) decl->resolve_status = RESOLVE_NOT_DONE; - return false; - } decl->decl_kind = DECL_POISONED; decl->resolve_status = RESOLVE_DONE; return false; } diff --git a/src/compiler/copying.c b/src/compiler/copying.c index fe7b8c4aa..10f858201 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -421,13 +421,20 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr) return expr; } UNREACHABLE + case EXPR_CT_CASTABLE: + MACRO_COPY_EXPRID(expr->castable_expr.expr); + MACRO_COPY_TYPEID(expr->castable_expr.type); + return expr; + case EXPR_CT_AND_OR: + MACRO_COPY_EXPR_LIST(expr->ct_and_or_expr.args); + return expr; case EXPR_FORCE_UNWRAP: case EXPR_OPTIONAL: case EXPR_GROUP: case EXPR_STRINGIFY: case EXPR_CT_EVAL: - case EXPR_CT_CHECKS: case EXPR_CT_DEFINED: + case EXPR_CT_IS_CONST: MACRO_COPY_EXPR(expr->inner_expr); return expr; case EXPR_TYPEID_INFO: @@ -531,7 +538,6 @@ void doc_ast_copy(CopyStruct *c, AstContractStmt *doc) { case CONTRACT_REQUIRE: case CONTRACT_ENSURE: - case CONTRACT_CHECKED: MACRO_COPY_EXPR(doc->contract.decl_exprs); break; case CONTRACT_OPTIONALS: diff --git a/src/compiler/diagnostics.c b/src/compiler/diagnostics.c index ca6902f48..b194e001a 100644 --- a/src/compiler/diagnostics.c +++ b/src/compiler/diagnostics.c @@ -160,14 +160,12 @@ static void vprint_error(SourceSpan location, const char *message, va_list args) void sema_verror_range(SourceSpan location, const char *message, va_list args) { - if (global_context.suppress_errors) return; vprint_error(location, message, args); global_context.errors_found++; } void sema_warning_at(SourceSpan loc, const char *message, ...) { - if (global_context.suppress_errors) return; va_list list; va_start(list, message); print_error(loc, str_vprintf(message, list), PRINT_TYPE_NOTE); @@ -195,7 +193,6 @@ void sema_error_at_after(SourceSpan loc, const char *message, ...) void sema_error_prev_at(SourceSpan loc, const char *message, ...) { - if (global_context.suppress_errors) return; va_list args; va_start(args, message); #define MAX_ERROR_LEN 4096 @@ -213,7 +210,6 @@ void sema_error_prev_at(SourceSpan loc, const char *message, ...) void sema_error(ParseContext *context, const char *message, ...) { - if (global_context.suppress_errors) return; global_context.errors_found++; File *file = context->unit->file; va_list list; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 087b8810b..533210651 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -162,7 +162,8 @@ typedef enum case DECL_POISONED #define NON_RUNTIME_EXPR EXPR_DESIGNATOR: case EXPR_POISONED: \ - case EXPR_CT_CHECKS: case EXPR_CT_DEFINED: \ + case EXPR_CT_DEFINED: case EXPR_CT_AND_OR:\ + case EXPR_CT_CASTABLE: case EXPR_CT_IS_CONST: \ case EXPR_CT_ARG: case EXPR_TYPEINFO: case EXPR_CT_IDENT: case EXPR_HASH_IDENT: \ case EXPR_COMPILER_CONST: case EXPR_CT_CALL: \ case EXPR_ANYSWITCH: case EXPR_STRINGIFY: case EXPR_CT_EVAL @@ -172,7 +173,6 @@ typedef enum CONTRACT_UNKNOWN, CONTRACT_PURE, CONTRACT_REQUIRE, - CONTRACT_CHECKED, CONTRACT_PARAM, CONTRACT_OPTIONALS, CONTRACT_ENSURE, @@ -220,9 +220,11 @@ typedef enum EXPR_COMPOUND_LITERAL, EXPR_COND, EXPR_CONST, + EXPR_CT_AND_OR, EXPR_CT_ARG, EXPR_CT_CALL, - EXPR_CT_CHECKS, + EXPR_CT_CASTABLE, + EXPR_CT_IS_CONST, EXPR_CT_DEFINED, EXPR_CT_EVAL, EXPR_CT_IDENT, @@ -357,11 +359,10 @@ typedef enum typedef enum { SCOPE_NONE = 0, - SCOPE_CHECKS = 1 << 0, - SCOPE_ENSURE = 1 << 2, - SCOPE_ENSURE_MACRO = 1 << 3, - SCOPE_EXPR_BLOCK = 1 << 5, - SCOPE_MACRO = 1 << 6, + SCOPE_ENSURE = 1 << 1, + SCOPE_ENSURE_MACRO = 1 << 2, + SCOPE_EXPR_BLOCK = 1 << 3, + SCOPE_MACRO = 1 << 4, } ScopeFlags; typedef enum @@ -560,9 +561,10 @@ typedef enum TOKEN_LAST_NON_CT_KEYWORD = TOKEN_WHILE, TOKEN_CT_ALIGNOF, // $alignof + TOKEN_CT_AND, // $and TOKEN_CT_ASSERT, // $assert + TOKEN_CT_ASSIGNABLE, // $assignable TOKEN_CT_CASE, // $case - TOKEN_CT_CHECKS, // $checks TOKEN_CT_DEFAULT, // $default TOKEN_CT_DEFINED, // $defined TOKEN_CT_ECHO, // $echo @@ -582,8 +584,10 @@ typedef enum TOKEN_CT_FOREACH, // $foreach TOKEN_CT_IF, // $if TOKEN_CT_INCLUDE, // $include + TOKEN_CT_IS_CONST, // $is_const TOKEN_CT_NAMEOF, // $nameof TOKEN_CT_OFFSETOF, // $offsetof + TOKEN_CT_OR, // $or TOKEN_CT_QNAMEOF, // $qnameof TOKEN_CT_SIZEOF, // $sizeof TOKEN_CT_STRINGIFY, // $stringify @@ -980,6 +984,8 @@ typedef enum TYPE_PROPERTY_ELEMENTS, TYPE_PROPERTY_EXTNAMEOF, TYPE_PROPERTY_INF, + TYPE_PROPERTY_IS_EQ, + TYPE_PROPERTY_IS_ORDERED, TYPE_PROPERTY_LEN, TYPE_PROPERTY_MAX, TYPE_PROPERTY_MEMBERSOF, @@ -1074,5 +1080,6 @@ typedef enum CONV_VOIDPTR, CONV_VAPTR, CONV_INFERRED, - CONV_LAST = CONV_INFERRED + CONV_UNTYPED_LIST, + CONV_LAST = CONV_UNTYPED_LIST } ConvGroup; diff --git a/src/compiler/expr.c b/src/compiler/expr.c index a241befa1..343f3d38c 100644 --- a/src/compiler/expr.c +++ b/src/compiler/expr.c @@ -191,8 +191,10 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) return true; case EXPR_OPERATOR_CHARS: case EXPR_STRINGIFY: - case EXPR_CT_CHECKS: + case EXPR_CT_AND_OR: + case EXPR_CT_CASTABLE: case EXPR_CT_DEFINED: + case EXPR_CT_IS_CONST: case EXPR_LAMBDA: case EXPR_EMBED: return true; @@ -662,22 +664,24 @@ bool expr_is_pure(Expr *expr) return exprid_is_pure(expr->pointer_offset_expr.ptr) && exprid_is_pure(expr->pointer_offset_expr.offset); case EXPR_COMPILER_CONST: case EXPR_CONST: - case EXPR_IDENTIFIER: - case EXPR_NOP: - case EXPR_STRINGIFY: - case EXPR_RETVAL: - case EXPR_TYPEINFO: + case EXPR_CT_AND_OR: + case EXPR_CT_ARG: + case EXPR_CT_CALL: + case EXPR_CT_CASTABLE: + case EXPR_CT_DEFINED: + case EXPR_CT_IS_CONST: case EXPR_CT_EVAL: case EXPR_CT_IDENT: - case EXPR_CT_CALL: - case EXPR_TYPEID: - case EXPR_CT_ARG: - case EXPR_OPERATOR_CHARS: - case EXPR_CT_CHECKS: - case EXPR_CT_DEFINED: - case EXPR_LAMBDA: case EXPR_EMBED: + case EXPR_IDENTIFIER: + case EXPR_LAMBDA: case EXPR_MACRO_BODY: + case EXPR_NOP: + case EXPR_OPERATOR_CHARS: + case EXPR_RETVAL: + case EXPR_STRINGIFY: + case EXPR_TYPEID: + case EXPR_TYPEINFO: return true; case EXPR_VASPLAT: return true; diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 955eec159..d44b13fed 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -1076,16 +1076,17 @@ static Expr *parse_ct_sizeof(ParseContext *c, Expr *left) return access; } + /** - * ct_checks ::= CT_CHECKS '(' expression_list ')' + * ct_is_const ::= CT_IS_CONST '(' expr ')' */ -static Expr *parse_ct_checks(ParseContext *c, Expr *left) +static Expr *parse_ct_is_const(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); - Expr *checks = expr_new(EXPR_CT_CHECKS, c->span); - advance_and_verify(c, TOKEN_CT_CHECKS); + Expr *checks = expr_new(EXPR_CT_IS_CONST, c->span); + advance_and_verify(c, TOKEN_CT_IS_CONST); CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); - ASSIGN_EXPR_OR_RET(checks->inner_expr, parse_expression_list(c, true), poisoned_expr); + ASSIGN_EXPR_OR_RET(checks->inner_expr, parse_expr(c), poisoned_expr); CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); RANGE_EXTEND_PREV(checks); return checks; @@ -1131,6 +1132,42 @@ static Expr *parse_ct_call(ParseContext *c, Expr *left) return expr; } +static Expr *parse_ct_and_or(ParseContext *c, Expr *left) +{ + assert(!left && "Unexpected left hand side"); + Expr *expr = EXPR_NEW_TOKEN(EXPR_CT_AND_OR); + expr->ct_and_or_expr.is_and = tok_is(c, TOKEN_CT_AND); + advance(c); + CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); + Expr **exprs = NULL; + while (true) + { + ASSIGN_EXPR_OR_RET(Expr* internal, parse_expr(c), poisoned_expr); + vec_add(exprs, internal); + if (try_consume(c, TOKEN_COMMA)) continue; + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); + break; + } + expr->ct_and_or_expr.args = exprs; + RANGE_EXTEND_PREV(expr); + return expr; +} + +static Expr *parse_ct_castable(ParseContext *c, Expr *left) +{ + assert(!left && "Unexpected left hand side"); + Expr *expr = EXPR_NEW_TOKEN(EXPR_CT_CASTABLE); + expr->castable_expr.is_assign = c->tok == TOKEN_CT_ASSIGNABLE; + advance(c); + CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); + ASSIGN_EXPRID_OR_RET(expr->castable_expr.expr, parse_expr(c), poisoned_expr); + CONSUME_OR_RET(TOKEN_COMMA, poisoned_expr); + ASSIGN_TYPEID_OR_RET(expr->castable_expr.type, parse_type(c), poisoned_expr); + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); + RANGE_EXTEND_PREV(expr); + return expr; +} + /** * ct_arg ::= VACOUNT | (VAARG | VAREF | VAEXPR | VACONST) '(' expr ')' */ @@ -1913,13 +1950,16 @@ ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_FN] = { parse_lambda, NULL, PREC_NONE }, [TOKEN_CT_SIZEOF] = { parse_ct_sizeof, NULL, PREC_NONE }, [TOKEN_CT_ALIGNOF] = { parse_ct_call, NULL, PREC_NONE }, + [TOKEN_CT_AND] = {parse_ct_and_or, NULL, PREC_NONE }, + [TOKEN_CT_ASSIGNABLE] = { parse_ct_castable, NULL, PREC_NONE }, [TOKEN_CT_DEFINED] = { parse_ct_defined, NULL, PREC_NONE }, - [TOKEN_CT_CHECKS] = { parse_ct_checks, NULL, PREC_NONE }, + [TOKEN_CT_IS_CONST] = {parse_ct_is_const, NULL, PREC_NONE }, [TOKEN_CT_EMBED] = { parse_ct_embed, NULL, PREC_NONE }, [TOKEN_CT_EVAL] = { parse_ct_eval, NULL, PREC_NONE }, [TOKEN_CT_FEATURE] = { parse_ct_call, NULL, PREC_NONE }, [TOKEN_CT_EXTNAMEOF] = { parse_ct_call, NULL, PREC_NONE }, [TOKEN_CT_OFFSETOF] = { parse_ct_call, NULL, PREC_NONE }, + [TOKEN_CT_OR] = {parse_ct_and_or, NULL, PREC_NONE }, [TOKEN_CT_NAMEOF] = { parse_ct_call, NULL, PREC_NONE }, [TOKEN_CT_QNAMEOF] = { parse_ct_call, NULL, PREC_NONE }, [TOKEN_CT_TYPEFROM] = { parse_type_expr, NULL, PREC_NONE }, diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index c901fea92..4679f833c 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -245,10 +245,9 @@ bool parse_module(ParseContext *c, AstId contracts) case CONTRACT_ENSURE: break; case CONTRACT_REQUIRE: - case CONTRACT_CHECKED: continue; } - RETURN_SEMA_ERROR(current, "Invalid constraint - only '@require' and '@checked' are valid for modules."); + RETURN_SEMA_ERROR(current, "Invalid constraint - only '@require' is valid for modules."); } } Visibility visibility = VISIBLE_PUBLIC; @@ -2414,7 +2413,7 @@ static inline bool parse_doc_contract(ParseContext *c, AstId *docs, AstId **docs ast->contract_stmt.kind = kind; const char *start = c->lexer.data.lex_start; advance(c); - ASSIGN_EXPR_OR_RET(ast->contract_stmt.contract.decl_exprs, parse_expression_list(c, kind == CONTRACT_CHECKED), false); + ASSIGN_EXPR_OR_RET(ast->contract_stmt.contract.decl_exprs, parse_expression_list(c, false), false); const char *end = start + 1; while (end[0] != '\n' && end[0] != '\0') end++; if (end > c->data.lex_start) end = c->data.lex_start; @@ -2422,9 +2421,6 @@ static inline bool parse_doc_contract(ParseContext *c, AstId *docs, AstId **docs scratch_buffer_clear(); switch (kind) { - case CONTRACT_CHECKED: - scratch_buffer_append("@checked \""); - break; case CONTRACT_ENSURE: scratch_buffer_append("@ensure \""); break; @@ -2607,11 +2603,6 @@ static bool parse_contracts(ParseContext *c, AstId *contracts_ref) if (!parse_doc_contract(c, contracts_ref, next, CONTRACT_REQUIRE)) return false; break; } - else if (name == kw_at_checked) - { - if (!parse_doc_contract(c, contracts_ref, next, CONTRACT_CHECKED)) return false; - break; - } else if (name == kw_at_ensure) { if (!parse_doc_contract(c, contracts_ref, next, CONTRACT_ENSURE)) return false; diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index 9cff262f0..b4b5430b1 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -1256,8 +1256,10 @@ Ast *parse_stmt(ParseContext *c) case TOKEN_BYTES: case TOKEN_CHAR_LITERAL: case TOKEN_CT_ALIGNOF: - case TOKEN_CT_CHECKS: + case TOKEN_CT_AND: + case TOKEN_CT_ASSIGNABLE: case TOKEN_CT_CONST_IDENT: + case TOKEN_CT_IS_CONST: case TOKEN_CT_DEFINED: case TOKEN_CT_EMBED: case TOKEN_CT_EVAL: @@ -1266,6 +1268,7 @@ Ast *parse_stmt(ParseContext *c) case TOKEN_CT_IDENT: case TOKEN_CT_NAMEOF: case TOKEN_CT_OFFSETOF: + case TOKEN_CT_OR: case TOKEN_CT_QNAMEOF: case TOKEN_CT_SIZEOF: case TOKEN_CT_STRINGIFY: diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index abedbf3a5..38342f2c6 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -23,7 +23,7 @@ static void sema_error_const_int_out_of_range(Expr *expr, Expr *problem, Type *t static Expr *recursive_may_narrow(Expr *expr, Type *type); static void expr_recursively_rewrite_untyped_list(Expr *expr, Expr **list); static inline bool insert_runtime_cast(Expr *expr, CastKind kind, Type *type); -static void vector_const_initializer_convert_to_type(ConstInitializer *initializer, Type *to_type); +static void vector_const_initializer_convert_to_type(SemaContext *context, ConstInitializer *initializer, Type *to_type); static bool cast_is_allowed(CastContext *cc, bool is_explicit, bool is_silent); INLINE bool insert_runtime_cast_unless_const(Expr *expr, CastKind kind, Type *type); @@ -31,10 +31,9 @@ static bool cast_if_valid(SemaContext *context, Expr *expr, Type *to_type, bool INLINE ConvGroup type_to_group(Type *type); INLINE void cast_context_set_from(CastContext *cc, Type *new_from); INLINE void cast_context_set_to(CastContext *cc, Type *new_to); -static bool cast_untyped_to_type(SemaContext *context, Expr *expr, Type *to_type); typedef bool(*CastRule)(CastContext *cc, bool is_explicit, bool is_silent); -typedef void(*CastFunction)(Expr *expr, Type *to_type); +typedef void(*CastFunction)(SemaContext *context, Expr *expr, Type *to_type); extern CastFunction cast_function[CONV_LAST + 1][CONV_LAST + 1]; extern CastRule cast_rules[CONV_LAST + 1][CONV_LAST + 1]; @@ -55,6 +54,13 @@ bool cast_explicit(SemaContext *context, Expr *expr, Type *to_type) return cast_if_valid(context, expr, to_type, true, false); } +/** + * Try to make an explicit cast, Optional types are allowed. + */ +bool cast_explicit_silent(SemaContext *context, Expr *expr, Type *to_type) +{ + return cast_if_valid(context, expr, to_type, true, true); +} /** * Silent implicit casting will attempt a cast, but will silently back out if it fails. */ @@ -64,7 +70,7 @@ bool cast_implicit_silent(SemaContext *context, Expr *expr, Type *to_type) } -bool may_cast(SemaContext *context, Expr *expr, Type *to_type, bool is_explicit) +bool may_cast(SemaContext *context, Expr *expr, Type *to_type, bool is_explicit, bool is_silent) { Type *from_type = expr->type->canonical; Type *to = to_type->canonical; @@ -77,7 +83,7 @@ bool may_cast(SemaContext *context, Expr *expr, Type *to_type, bool is_explicit) .expr = expr, .context = context }; - return cast_is_allowed(&cc, is_explicit, true); + return cast_is_allowed(&cc, is_explicit, is_silent); } static bool cast_is_allowed(CastContext *cc, bool is_explicit, bool is_silent) @@ -109,7 +115,7 @@ static bool cast_is_allowed(CastContext *cc, bool is_explicit, bool is_silent) /** * Perform the cast with no additional checks. Casting from untyped not allowed. */ -void cast_no_check(Expr *expr, Type *to_type, bool add_optional) +void cast_no_check(SemaContext *context, Expr *expr, Type *to_type, bool add_optional) { Type *to = type_flatten(to_type); Type *from = type_flatten(expr->type); @@ -123,7 +129,7 @@ void cast_no_check(Expr *expr, Type *to_type, bool add_optional) CastFunction func = cast_function[from_group][to_group]; if (func) { - func(expr, to_type); + func(context, expr, to_type); expr->type = type_add_optional(expr->type, add_optional); return; } @@ -134,7 +140,7 @@ void cast_no_check(Expr *expr, Type *to_type, bool add_optional) * Given lhs and rhs, promote to the maximum bit size, this will retain * signed/unsigned type of each side. */ -void cast_to_int_to_max_bit_size(Expr *lhs, Expr *rhs, Type *left_type, Type *right_type) +void cast_to_int_to_max_bit_size(SemaContext *context, Expr *lhs, Expr *rhs, Type *left_type, Type *right_type) { unsigned bit_size_left = left_type->builtin.bitsize; unsigned bit_size_right = right_type->builtin.bitsize; @@ -150,7 +156,7 @@ void cast_to_int_to_max_bit_size(Expr *lhs, Expr *rhs, Type *left_type, Type *ri Type *to = lhs->type->type_kind < TYPE_U8 ? type_int_signed_by_bitsize(bit_size_right) : type_int_unsigned_by_bitsize(bit_size_right); - cast_no_check(lhs, to, IS_OPTIONAL(lhs)); + cast_no_check(context, lhs, to, IS_OPTIONAL(lhs)); return; } @@ -158,7 +164,7 @@ void cast_to_int_to_max_bit_size(Expr *lhs, Expr *rhs, Type *left_type, Type *ri Type *to = rhs->type->type_kind < TYPE_U8 ? type_int_signed_by_bitsize(bit_size_left) : type_int_unsigned_by_bitsize(bit_size_left); - cast_no_check(rhs, to, IS_OPTIONAL(rhs)); + cast_no_check(context, rhs, to, IS_OPTIONAL(rhs)); } /** @@ -167,7 +173,7 @@ void cast_to_int_to_max_bit_size(Expr *lhs, Expr *rhs, Type *left_type, Type *ri * 2. Widen float and smaller to double * 3. Turn subarrays into pointers */ -void cast_promote_vararg(Expr *arg) +void cast_promote_vararg(SemaContext *context, Expr *arg) { // Remove things like distinct, optional, enum etc. Type *arg_type = type_flatten(arg->type); @@ -175,21 +181,21 @@ void cast_promote_vararg(Expr *arg) // 1. Promote any integer or bool to at least CInt if (type_is_promotable_int_bool(arg_type)) { - cast_no_check(arg, type_cint, IS_OPTIONAL(arg)); + cast_no_check(context, arg, type_cint, IS_OPTIONAL(arg)); return; } // 2. Promote any float to at least double if (type_is_promotable_float(arg_type)) { - cast_no_check(arg, type_double, IS_OPTIONAL(arg)); + cast_no_check(context, arg, type_double, IS_OPTIONAL(arg)); return; } // 3. Turn subarrays into pointers if (arg_type->type_kind == TYPE_SUBARRAY) { - cast_no_check(arg, type_get_ptr(arg_type->array.base), IS_OPTIONAL(arg)); + cast_no_check(context, arg, type_get_ptr(arg_type->array.base), IS_OPTIONAL(arg)); return; } @@ -273,6 +279,7 @@ static bool cast_if_valid(SemaContext *context, Expr *expr, Type *to_type, bool && to_type->canonical->pointer == from_type->canonical && expr->expr_kind == EXPR_IDENTIFIER && expr->identifier_expr.was_ref) { + if (is_silent) return false; RETURN_SEMA_ERROR(expr, "A macro ref parameter is a dereferenced pointer ('*&foo'). You can prefix it" " with '&' to pass it as a pointer."); } @@ -284,12 +291,7 @@ static bool cast_if_valid(SemaContext *context, Expr *expr, Type *to_type, bool from_type = type_no_optional(from_type); to_type = type_no_optional(to_type); - if (from_type == type_untypedlist) - { - return cast_untyped_to_type(context, expr, to_type); - } - - if (is_void_silence) + if (is_void_silence && from_type != type_untypedlist) { insert_runtime_cast(expr, CAST_VOID, type_void); return true; @@ -307,12 +309,12 @@ static bool cast_if_valid(SemaContext *context, Expr *expr, Type *to_type, bool .context = context }; if (!sema_resolve_type_decl(context, to)) return false; - if (!cast_is_allowed(&cc, is_explicit, false)) + if (!cast_is_allowed(&cc, is_explicit, is_silent)) { return false; } - cast_no_check(expr, to_type, add_optional); + cast_no_check(context, expr, to_type, add_optional); return true; } @@ -567,19 +569,7 @@ static void sema_error_const_int_out_of_range(Expr *expr, Expr *problem, Type *t type_quoted_error_string(to_type)); } -/** - * Cast an untyped list to a particular type. - */ -static bool cast_untyped_to_type(SemaContext *context, Expr *expr, Type *to_type) -{ - // Recursively set the type of all ConstInitializer inside. - expr_recursively_rewrite_untyped_list(expr, expr->const_expr.untyped_list); - // We can now analyse the list (this is where the actual check happens) - if (!sema_expr_analyse_initializer_list(context, type_flatten(to_type), expr)) return false; - // And set the type. - expr->type = type_infer_len_from_actual_type(to_type, expr->type); - return true; -} + /** * Recursively change a const list to an initializer list. @@ -680,14 +670,6 @@ INLINE bool sema_cast_error(CastContext *cc, bool may_cast_explicit, bool is_sil static TypeCmpResult match_pointers(CastContext *cc, Type *to_ptr, Type *from_ptr, bool flatten, bool is_silent) { - if (is_silent) - { - bool old_suppress_err = global_context.suppress_errors; - global_context.suppress_errors = true; - TypeCmpResult res = type_is_pointer_equivalent(cc->context, to_ptr, from_ptr, flatten); - global_context.suppress_errors = old_suppress_err; - return res; - } return type_is_pointer_equivalent(cc->context, to_ptr, from_ptr, flatten); } @@ -741,6 +723,7 @@ static bool rule_int_to_ptr(CastContext *cc, bool is_explicit, bool is_silent) return true; } + static bool rule_ptr_to_int(CastContext *cc, bool is_explicit, bool is_silent) { bool too_small = type_size(cc->to) < type_size(type_uptr); @@ -793,6 +776,71 @@ static bool rule_arrptr_to_sa(CastContext *cc, bool is_explicit, bool is_silent) return sema_cast_error(cc, may_explicit, is_silent); } +static bool rule_ulist_to_struct(CastContext *cc, bool is_explicit, bool is_silent) +{ + Expr **expressions = cc->expr->const_expr.untyped_list; + unsigned size = vec_size(expressions); + if (!size) return true; + Decl *strukt = cc->to->decl; + Decl **members = strukt->strukt.members; + if (size != vec_size(members)) + { + if (is_silent) return false; + RETURN_SEMA_ERROR(cc->expr, "%s may only be initialized with 0 elements or %d, not %d.", + type_quoted_error_string(cc->to_type), + vec_size(members), size); + } + if (!sema_analyse_decl(cc->context, strukt)) return false; + FOREACH_BEGIN_IDX(i, Expr *expr, expressions) + if (!may_cast(cc->context, expr, members[i]->type, false, is_silent)) return false; + FOREACH_END(); + return true; +} + +static bool rule_ulist_to_vecarr(CastContext *cc, bool is_explicit, bool is_silent) +{ + Expr **expressions = cc->expr->const_expr.untyped_list; + unsigned size = vec_size(expressions); + if (!size) return true; + if (size != cc->to->array.len) + { + if (is_silent) return false; + RETURN_SEMA_ERROR(cc->expr, "%s may only be initialized with 0 elements or %d, not %d.", + type_quoted_error_string(cc->to_type), + cc->to->array.len, size); + } + Type *base = cc->to->array.base; + FOREACH_BEGIN(Expr *expr, expressions) + if (!may_cast(cc->context, expr, base, false, is_silent)) return false; + FOREACH_END(); + return true; +} + +static bool rule_ulist_to_subarray(CastContext *cc, bool is_explicit, bool is_silent) +{ + Type *base = cc->to->array.base; + FOREACH_BEGIN(Expr *expr, cc->expr->const_expr.untyped_list) + if (!may_cast(cc->context, expr, base, false, is_silent)) return false; + FOREACH_END(); + return true; +} + +static bool rule_ulist_to_inferred(CastContext *cc, bool is_explicit, bool is_silent) +{ + Expr **expressions = cc->expr->const_expr.untyped_list; + unsigned size = vec_size(expressions); + if (!size) + { + if (is_silent) return false; + RETURN_SEMA_ERROR(cc->expr, "This untyped list would infer to a zero elements, which is not allowed."); + } + Type *base = cc->to->array.base; + FOREACH_BEGIN(Expr *expr, expressions) + if (!may_cast(cc->context, expr, base, false, is_silent)) return false; + FOREACH_END(); + return true; +} + static bool rule_sa_to_ptr(CastContext *cc, bool is_explicit, bool is_silent) { Type *subarray_base = cc->from_type->array.base->canonical; @@ -812,6 +860,11 @@ static bool rule_sa_to_ptr(CastContext *cc, bool is_explicit, bool is_silent) return sema_cast_error(cc, may_explicit, is_silent); } +static bool rule_untyped_to_struct(CastContext *cc, bool is_explicit, bool is_silent) +{ + TODO +} + static bool rule_sa_to_sa(CastContext *cc, bool is_explicit, bool is_silent) { Type *from_type = cc->from_type; @@ -1248,18 +1301,18 @@ static inline bool insert_runtime_cast(Expr *expr, CastKind kind, Type *type) return true; } -static void cast_vaptr_to_sa(Expr *expr, Type *type) { insert_runtime_cast(expr, CAST_APTSA, type); } -static void cast_ptr_to_any(Expr *expr, Type *type) { insert_runtime_cast(expr, CAST_PTRANY, type); } -static void cast_struct_to_inline(Expr *expr, Type *type) { insert_runtime_cast(expr, CAST_STINLINE, type); } -static void cast_fault_to_anyfault(Expr *expr, Type *type) { expr->type = type; }; -static void cast_fault_to_int(Expr *expr, Type *type) { insert_runtime_cast(expr, CAST_ERINT, type); } -static void cast_fault_to_ptr(Expr *expr, Type *type) { insert_runtime_cast(expr, CAST_ERPTR, type); } -static void cast_typeid_to_int(Expr *expr, Type *type) { insert_runtime_cast(expr, CAST_IDINT, type); } -static void cast_typeid_to_ptr(Expr *expr, Type *type) { insert_runtime_cast(expr, CAST_IDPTR, type); } -static void cast_any_to_bool(Expr *expr, Type *type) { insert_runtime_cast(expr, CAST_ANYBOOL, type); } -static void cast_any_to_ptr(Expr *expr, Type *type) { insert_runtime_cast(expr, CAST_ANYPTR, type); } -static void cast_all_to_void(Expr *expr, Type *to_type) { insert_runtime_cast(expr, CAST_VOID, type_void); } -static void cast_retype(Expr *expr, Type *to_type) { expr->type = to_type; } +static void cast_vaptr_to_sa(SemaContext *context, Expr *expr, Type *type) { insert_runtime_cast(expr, CAST_APTSA, type); } +static void cast_ptr_to_any(SemaContext *context, Expr *expr, Type *type) { insert_runtime_cast(expr, CAST_PTRANY, type); } +static void cast_struct_to_inline(SemaContext *context, Expr *expr, Type *type) { insert_runtime_cast(expr, CAST_STINLINE, type); } +static void cast_fault_to_anyfault(SemaContext *context, Expr *expr, Type *type) { expr->type = type; }; +static void cast_fault_to_int(SemaContext *context, Expr *expr, Type *type) { insert_runtime_cast(expr, CAST_ERINT, type); } +static void cast_fault_to_ptr(SemaContext *context, Expr *expr, Type *type) { insert_runtime_cast(expr, CAST_ERPTR, type); } +static void cast_typeid_to_int(SemaContext *context, Expr *expr, Type *type) { insert_runtime_cast(expr, CAST_IDINT, type); } +static void cast_typeid_to_ptr(SemaContext *context, Expr *expr, Type *type) { insert_runtime_cast(expr, CAST_IDPTR, type); } +static void cast_any_to_bool(SemaContext *context, Expr *expr, Type *type) { insert_runtime_cast(expr, CAST_ANYBOOL, type); } +static void cast_any_to_ptr(SemaContext *context, Expr *expr, Type *type) { insert_runtime_cast(expr, CAST_ANYPTR, type); } +static void cast_all_to_void(SemaContext *context, Expr *expr, Type *to_type) { insert_runtime_cast(expr, CAST_VOID, type_void); } +static void cast_retype(SemaContext *context, Expr *expr, Type *to_type) { expr->type = to_type; } /** * Insert a cast on non-const only @@ -1270,7 +1323,7 @@ INLINE bool insert_runtime_cast_unless_const(Expr *expr, CastKind kind, Type *ty return insert_runtime_cast(expr, kind, type); } -static void vector_const_initializer_convert_to_type(ConstInitializer *initializer, Type *to_type) +static void vector_const_initializer_convert_to_type(SemaContext *context, ConstInitializer *initializer, Type *to_type) { switch (initializer->kind) { @@ -1278,7 +1331,7 @@ static void vector_const_initializer_convert_to_type(ConstInitializer *initializ { Type *element_type = type_flatten(to_type)->array.base; FOREACH_BEGIN(ConstInitializer *element, initializer->init_array.elements) - vector_const_initializer_convert_to_type(element, element_type); + vector_const_initializer_convert_to_type(context, element, element_type); FOREACH_END(); break; } @@ -1286,7 +1339,7 @@ static void vector_const_initializer_convert_to_type(ConstInitializer *initializ { Type *element_type = type_flatten(to_type)->array.base; FOREACH_BEGIN(ConstInitializer *element, initializer->init_array_full) - vector_const_initializer_convert_to_type(element, element_type); + vector_const_initializer_convert_to_type(context, element, element_type); FOREACH_END(); break; } @@ -1306,7 +1359,7 @@ static void vector_const_initializer_convert_to_type(ConstInitializer *initializ } else { - cast_no_check(initializer->init_value, to_type, IS_OPTIONAL(initializer->init_value)); + cast_no_check(context, initializer->init_value, to_type, IS_OPTIONAL(initializer->init_value)); } break; } @@ -1316,7 +1369,7 @@ static void vector_const_initializer_convert_to_type(ConstInitializer *initializ case CONST_INIT_STRUCT: UNREACHABLE case CONST_INIT_ARRAY_VALUE: - vector_const_initializer_convert_to_type(initializer->init_array_value.element, to_type); + vector_const_initializer_convert_to_type(context, initializer->init_array_value.element, to_type); break; } initializer->type = to_type; @@ -1325,7 +1378,7 @@ static void vector_const_initializer_convert_to_type(ConstInitializer *initializ /** * Insert a PTRPTR cast or update the pointer type */ -static void cast_ptr_to_ptr(Expr *expr, Type *type) +static void cast_ptr_to_ptr(SemaContext *context, Expr *expr, Type *type) { if (insert_runtime_cast_unless_const(expr, CAST_PTRPTR, type)) return; @@ -1345,7 +1398,7 @@ static void cast_ptr_to_ptr(Expr *expr, Type *type) /** * Convert any fp to another fp type using CAST_FPFP */ -static void cast_float_to_float(Expr *expr, Type *type) +static void cast_float_to_float(SemaContext *context, Expr *expr, Type *type) { // Change to same type should never enter here. assert(type_flatten(type) != type_flatten(expr->type)); @@ -1361,7 +1414,7 @@ static void cast_float_to_float(Expr *expr, Type *type) * Convert from any floating point to int using CAST_FPINT * Const conversion will disable narrowable and hex. */ -static void cast_float_to_int(Expr *expr, Type *type) +static void cast_float_to_int(SemaContext *context, Expr *expr, Type *type) { if (insert_runtime_cast_unless_const(expr, CAST_FPINT, type)) return; @@ -1379,7 +1432,7 @@ static void cast_float_to_int(Expr *expr, Type *type) * Convert from integer to enum using CAST_INTENUM / or do a const conversion. * This will ensure that the conversion is valid (i.e. in the range 0 .. enumcount - 1) */ -static void cast_int_to_enum(Expr *expr, Type *type) +static void cast_int_to_enum(SemaContext *context, Expr *expr, Type *type) { Type *canonical = type_flatten(type); assert(canonical->type_kind == TYPE_ENUM); @@ -1428,7 +1481,7 @@ static inline Type *type_flatten_to_int(Type *type) /** * Convert between integers: CAST_INTINT */ -static void cast_int_to_int(Expr *expr, Type *type) +static void cast_int_to_int(SemaContext *context, Expr *expr, Type *type) { // Fold pointer casts if narrowing // So (int)(uptr)&x => (int)&x in the backend. @@ -1453,15 +1506,15 @@ static void cast_int_to_int(Expr *expr, Type *type) /** * Convert 1 => { 1, 1, 1, 1 } using CAST_EXPVEC */ -static void cast_expand_to_vec(Expr *expr, Type *type) +static void cast_expand_to_vec(SemaContext *context, Expr *expr, Type *type) { // Fold pointer casts if narrowing Type *base = type_get_indexed_type(type); - cast_no_check(expr, base, IS_OPTIONAL(expr)); + cast_no_check(context, expr, base, IS_OPTIONAL(expr)); insert_runtime_cast(expr, CAST_EXPVEC, type); } -static void cast_bitstruct_to_int_arr(Expr *expr, Type *type) +static void cast_bitstruct_to_int_arr(SemaContext *context, Expr *expr, Type *type) { if (expr->expr_kind == EXPR_CAST && expr->cast_expr.kind == CAST_INTARRBS) { @@ -1471,7 +1524,7 @@ static void cast_bitstruct_to_int_arr(Expr *expr, Type *type) insert_runtime_cast(expr, CAST_BSINTARR, type); } -static void cast_int_arr_to_bitstruct(Expr *expr, Type *type) +static void cast_int_arr_to_bitstruct(SemaContext *context, Expr *expr, Type *type) { if (expr->expr_kind == EXPR_CAST && expr->cast_expr.kind == CAST_BSINTARR) { @@ -1485,7 +1538,7 @@ static void cast_int_arr_to_bitstruct(Expr *expr, Type *type) * Cast a signed or unsigned integer -> floating point, using CAST_INTFP * for runtime, otherwise do const transformation. */ -static void cast_int_to_float(Expr *expr, Type *type) +static void cast_int_to_float(SemaContext *context, Expr *expr, Type *type) { if (insert_runtime_cast_unless_const(expr, CAST_INTFP, type)) return; @@ -1493,7 +1546,7 @@ static void cast_int_to_float(Expr *expr, Type *type) expr_rewrite_const_float(expr, type, f); } -static void cast_enum_to_int(Expr* expr, Type *to_type) +static void cast_enum_to_int(SemaContext *context, Expr* expr, Type *to_type) { assert(type_flatten(expr->type)->type_kind == TYPE_ENUM); Type *underlying_type = type_base(expr->type); @@ -1507,14 +1560,14 @@ static void cast_enum_to_int(Expr* expr, Type *to_type) *expr = *exprptr(expr->cast_expr.expr); } expr->type = type_add_optional(underlying_type, IS_OPTIONAL(expr)); - cast_int_to_int(expr, to_type); + cast_int_to_int(context, expr, to_type); } /** * Cast using CAST_VECARR, casting an array to a vector. For the constant, this * is a simple type change, see array_to_vec. */ -static void cast_vec_to_arr(Expr *expr, Type *to_type) +static void cast_vec_to_arr(SemaContext *context, Expr *expr, Type *to_type) { if (insert_runtime_cast_unless_const(expr, CAST_VECARR, to_type)) return; @@ -1528,7 +1581,7 @@ static void cast_vec_to_arr(Expr *expr, Type *to_type) * Convert vector -> vector. This is somewhat complex as there are various functions * we need to invoke depending on the underlying type. */ -static void cast_vec_to_vec(Expr *expr, Type *to_type) +static void cast_vec_to_vec(SemaContext *context, Expr *expr, Type *to_type) { if (!expr_is_const(expr)) { @@ -1623,12 +1676,23 @@ static void cast_vec_to_vec(Expr *expr, Type *to_type) // For the const initializer we need to change the internal type ConstInitializer *list = expr->const_expr.initializer; - vector_const_initializer_convert_to_type(list, to_type); + vector_const_initializer_convert_to_type(context, list, to_type); expr->type = to_type; } -static void cast_anyfault_to_fault(Expr *expr, Type *type) +static void cast_untyped_list_to_other(SemaContext *context, Expr *expr, Type *to_type) +{ + // Recursively set the type of all ConstInitializer inside. + expr_recursively_rewrite_untyped_list(expr, expr->const_expr.untyped_list); + // We can now analyse the list (this is where the actual check happens) + bool success = sema_expr_analyse_initializer_list(context, type_flatten(to_type), expr); + assert(success); + // And set the type. + expr->type = type_infer_len_from_actual_type(to_type, expr->type); +} + +static void cast_anyfault_to_fault(SemaContext *context, Expr *expr, Type *type) { if (insert_runtime_cast_unless_const(expr, CAST_EUER, type) && expr->const_expr.const_kind == CONST_ERR) return; Decl *value = expr->const_expr.enum_err_val; @@ -1641,7 +1705,7 @@ static void cast_anyfault_to_fault(Expr *expr, Type *type) expr->type = type; } -static void cast_sa_to_ptr(Expr *expr, Type *type) +static void cast_sa_to_ptr(SemaContext *context, Expr *expr, Type *type) { if (expr_is_const_string(expr) || expr_is_const_bytes(expr)) { @@ -1655,7 +1719,7 @@ static void cast_sa_to_ptr(Expr *expr, Type *type) * Cast any int to a pointer, will use CAST_INTPTR after a conversion to uptr for runtime. * Compile time it will check that the value fits the pointer size. */ -static void cast_int_to_ptr(Expr *expr, Type *type) +static void cast_int_to_ptr(SemaContext *context, Expr *expr, Type *type) { assert(type_bit_size(type_uptr) <= 64 && "For > 64 bit pointers, this code needs updating."); @@ -1668,7 +1732,7 @@ static void cast_int_to_ptr(Expr *expr, Type *type) return; } // This may be a narrowing - cast_no_check(expr, type_uptr, IS_OPTIONAL(expr)); + cast_no_check(context, expr, type_uptr, IS_OPTIONAL(expr)); insert_runtime_cast(expr, CAST_INTPTR, type); } @@ -1676,7 +1740,7 @@ static void cast_int_to_ptr(Expr *expr, Type *type) * Bool into a signed or unsigned int using CAST_BOOLINT * or rewrite to 0 / 1 for false / true. */ -static void cast_bool_to_int(Expr *expr, Type *type) +static void cast_bool_to_int(SemaContext *context, Expr *expr, Type *type) { if (insert_runtime_cast_unless_const(expr, CAST_BOOLINT, type)) return; @@ -1688,7 +1752,7 @@ static void cast_bool_to_int(Expr *expr, Type *type) * Cast bool to float using CAST_BOOLFP * or rewrite to 0.0 / 1.0 for false / true */ -static void cast_bool_to_float(Expr *expr, Type *type) +static void cast_bool_to_float(SemaContext *context, Expr *expr, Type *type) { if (insert_runtime_cast_unless_const(expr, CAST_BOOLFP, type)) return; @@ -1700,7 +1764,7 @@ static void cast_bool_to_float(Expr *expr, Type *type) * Cast int to bool using CAST_INTBOOL * or rewrite 0 => false, any other value => true */ -static void cast_int_to_bool(Expr *expr, Type *type) +static void cast_int_to_bool(SemaContext *context, Expr *expr, Type *type) { if (insert_runtime_cast_unless_const(expr, CAST_INTBOOL, type)) return; @@ -1711,7 +1775,7 @@ static void cast_int_to_bool(Expr *expr, Type *type) * Cast any float to bool using CAST_FPBOOL * or rewrite 0.0 => false, any other value => true */ -static void cast_float_to_bool(Expr *expr, Type *type) +static void cast_float_to_bool(SemaContext *context, Expr *expr, Type *type) { if (insert_runtime_cast_unless_const(expr, CAST_FPBOOL, type)) return; @@ -1721,7 +1785,7 @@ static void cast_float_to_bool(Expr *expr, Type *type) /** * Insert the PTRXI cast, or on const do a rewrite. */ -static void cast_ptr_to_int(Expr *expr, Type *type) +static void cast_ptr_to_int(SemaContext *context, Expr *expr, Type *type) { if (insert_runtime_cast_unless_const(expr, CAST_PTRINT, type)) return; @@ -1732,7 +1796,7 @@ static void cast_ptr_to_int(Expr *expr, Type *type) /** * Insert the PTRBOOL cast or on const do a rewrite. */ -static void cast_ptr_to_bool(Expr *expr, Type *type) +static void cast_ptr_to_bool(SemaContext *context, Expr *expr, Type *type) { if (insert_runtime_cast_unless_const(expr, CAST_PTRBOOL, type)) return; @@ -1748,7 +1812,7 @@ static void cast_ptr_to_bool(Expr *expr, Type *type) expr_rewrite_const_bool(expr, type, true); } -static void cast_sa_to_bool(Expr *expr, Type *type) +static void cast_sa_to_bool(SemaContext *context, Expr *expr, Type *type) { if (expr_is_const_initializer(expr)) { @@ -1779,7 +1843,7 @@ static void cast_sa_to_bool(Expr *expr, Type *type) * 1. int[] -> Foo[] where Foo is a distinct or typedef or pointer. Then we can just redefine * 2. The second case is something like int[] -> float[] for this case we need to make a bitcast using CAST_SASA. */ -static void cast_sa_to_sa(Expr *expr, Type *to_type) +static void cast_sa_to_sa(SemaContext *context, Expr *expr, Type *to_type) { Type *to_type_base = type_flatten(type_flatten(to_type)->array.base); Type *from_type_base = type_flatten(type_flatten(expr->type)->array.base); @@ -1791,14 +1855,14 @@ static void cast_sa_to_sa(Expr *expr, Type *to_type) insert_runtime_cast(expr, CAST_SASA, to_type); } -static void cast_sa_to_vecarr(Expr *expr, Type *to_type) +static void cast_sa_to_vecarr(SemaContext *context, Expr *expr, Type *to_type) { if (!expr_is_const(expr)) { assert(expr->expr_kind == EXPR_CAST); Expr *inner = exprptr(expr->cast_expr.expr)->unary_expr.expr; expr_replace(expr, inner); - cast_no_check(expr, to_type, false); + cast_no_check(context, expr, to_type, false); return; } assert(expr_is_const(expr)); @@ -1806,25 +1870,25 @@ static void cast_sa_to_vecarr(Expr *expr, Type *to_type) return; } -static void cast_sa_to_infer(Expr *expr, Type *to_type) +static void cast_sa_to_infer(SemaContext *context, Expr *expr, Type *to_type) { ArraySize len = sema_len_from_const(expr); assert(len > 0); Type *indexed = type_get_indexed_type(expr->type); to_type = type_infer_len_from_actual_type(to_type, type_get_array(indexed, len)); - cast_no_check(expr, to_type, false); + cast_no_check(context, expr, to_type, false); } -static void cast_vecarr_to_infer(Expr *expr, Type *to_type) +static void cast_vecarr_to_infer(SemaContext *context, Expr *expr, Type *to_type) { to_type = type_infer_len_from_actual_type(to_type, type_flatten(expr->type)); - cast_no_check(expr, to_type, false); + cast_no_check(context, expr, to_type, false); } -static void cast_ptr_to_infer(Expr *expr, Type *to_type) +static void cast_ptr_to_infer(SemaContext *context, Expr *expr, Type *to_type) { to_type = type_infer_len_from_actual_type(to_type, type_flatten(expr->type)); - cast_no_check(expr, to_type, false); + cast_no_check(context, expr, to_type, false); } @@ -1832,7 +1896,7 @@ static void cast_ptr_to_infer(Expr *expr, Type *to_type) * Cast using CAST_ARRVEC, casting an array to a vector. For the constant, this * is a simple type change. */ -static void cast_arr_to_vec(Expr *expr, Type *to_type) +static void cast_arr_to_vec(SemaContext *context, Expr *expr, Type *to_type) { Type *index_vec = type_flatten(type_get_indexed_type(to_type)); Type *index_arr = type_flatten(type_get_indexed_type(expr->type)); @@ -1851,18 +1915,17 @@ static void cast_arr_to_vec(Expr *expr, Type *to_type) } if (to_temp != to_type) { - cast_vec_to_vec(expr, to_type); + cast_vec_to_vec(context, expr, to_type); } } -static void cast_arr_to_arr(Expr *expr, Type *to_type) +static void cast_arr_to_arr(SemaContext *context, Expr *expr, Type *to_type) { assert(type_size(to_type) == type_size(expr->type)); expr->type = to_type; } - -static void cast_anyfault_to_bool(Expr *expr, Type *to_type) +static void cast_anyfault_to_bool(SemaContext *context, Expr *expr, Type *to_type) { if (insert_runtime_cast_unless_const(expr, CAST_EUBOOL, to_type)) return; @@ -1870,7 +1933,7 @@ static void cast_anyfault_to_bool(Expr *expr, Type *to_type) expr_rewrite_const_bool(expr, type_bool, expr->const_expr.enum_err_val != NULL); } -static void cast_typeid_to_bool(Expr *expr, Type *to_type) +static void cast_typeid_to_bool(SemaContext *context, Expr *expr, Type *to_type) { if (insert_runtime_cast_unless_const(expr, CAST_IDBOOL, to_type)) return; @@ -1921,6 +1984,7 @@ static void cast_typeid_to_bool(Expr *expr, Type *to_type) #define SA2FE &cast_sa_to_infer #define VA2FE &cast_vecarr_to_infer #define PT2FE &cast_ptr_to_infer +#define UL2XX &cast_untyped_list_to_other #define _NO__ NULL /* No */ #define RXXDI &rule_to_distinct /* Type -> distinct (match + is explicit) */ @@ -1953,56 +2017,63 @@ static void cast_typeid_to_bool(Expr *expr, Type *to_type) #define RPTFE &rule_ptr_to_infer /* Ptr -> infer (if pointee may infer) */ #define RPTIF &rule_ptr_to_interface /* Ptr -> Interface if the pointee implements it */ #define RIFIF &rule_interface_to_interface/* Interface -> Interface if the latter implements all of the former */ +#define RULST &rule_ulist_to_struct /* Untyped list -> bitstruct or union */ +#define RULAR &rule_ulist_to_vecarr /* Untyped list -> vector or array */ +#define RULFE &rule_ulist_to_inferred /* Untyped list -> inferred vector or array */ +#define RULSA &rule_ulist_to_subarray /* Untyped list -> subarray */ + CastRule cast_rules[CONV_LAST + 1][CONV_LAST + 1] = { -// void, wildc, bool, int, float, ptr, sarr, vec, bitst, distc, array, strct, union, any, infc, fault, enum, typid, afaul, voidp, arrpt, infer (to) - {_NA__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__}, // VOID (from) - {ROKOK, _NA__, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, _NO__}, // WILDCARD - {REXPL, _NO__, _NA__, REXPL, REXPL, _NO__, _NO__, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__}, // BOOL - {REXPL, _NO__, REXPL, RWIDE, RINFL, RINPT, _NO__, ROKOK, RINBS, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RINEN, _NO__, _NO__, RINPT, RINPT, _NO__}, // INT - {REXPL, _NO__, REXPL, REXPL, RWIDE, _NO__, _NO__, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__}, // FLOAT - {REXPL, _NO__, REXPL, RPTIN, _NO__, RPTPT, _NO__, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, ROKOK, RPTIF, _NO__, _NO__, _NO__, _NO__, ROKOK, RPTPT, RPTFE}, // PTR - {REXPL, _NO__, REXPL, _NO__, _NO__, RSAPT, RSASA, RSAVA, _NO__, RXXDI, RSAVA, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, ROKOK, RSAPT, RSAFE}, // SARRAY - {REXPL, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RVCVC, _NO__, RXXDI, RVCAR, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RVAFE}, // VECTOR - {REXPL, _NO__, _NO__, RBSIN, _NO__, _NO__, _NO__, _NO__, _NO__, RXXDI, RBSAR, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__}, // BITSTRUCT - {REXPL, _NO__, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX}, // DISTINCT - {REXPL, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RARVC, RARBS, RXXDI, RARAR, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RVAFE}, // ARRAY - {REXPL, _NO__, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTDI, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, _NO__}, // STRUCT - {REXPL, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__}, // UNION - {REXPL, _NO__, REXPL, _NO__, _NO__, REXPL, _NO__, _NO__, _NO__, RXXDI, _NO__, _NO__, _NO__, _NA__, REXPL, _NO__, _NO__, _NO__, _NO__, ROKOK, REXPL, _NO__}, // ANY - {REXPL, _NO__, REXPL, _NO__, _NO__, REXPL, _NO__, _NO__, _NO__, RXXDI, _NO__, _NO__, _NO__, ROKOK, RIFIF, _NO__, _NO__, _NO__, _NO__, ROKOK, REXPL, _NO__}, // INTERFACE - {REXPL, _NO__, REXPL, RPTIN, _NO__, REXPL, _NO__, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, ROKOK, REXPL, REXPL, _NO__}, // FAULT - {REXPL, _NO__, _NO__, REXPL, _NO__, _NO__, _NO__, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__}, // ENUM - {REXPL, _NO__, REXPL, RPTIN, _NO__, REXPL, _NO__, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NA__, _NO__, REXPL, REXPL, _NO__}, // TYPEID - {REXPL, _NO__, REXPL, RPTIN, _NO__, REXPL, _NO__, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, REXPL, _NO__, _NO__, _NA__, REXPL, REXPL, _NO__}, // ANYFAULT - {REXPL, _NO__, REXPL, RPTIN, _NO__, ROKOK, _NO__, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, ROKOK, ROKOK, _NO__, _NO__, _NO__, _NO__, _NA__, ROKOK, _NO__}, // VOIDPTR - {REXPL, _NO__, REXPL, RPTIN, _NO__, RPTPT, RAPSA, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, ROKOK, ROKOK, _NO__, _NO__, _NO__, _NO__, ROKOK, RPTPT, RPTFE}, // ARRPTR - {_NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__}, // INFERRED +// void, wildc, bool, int, float, ptr, sarr, vec, bitst, distc, array, strct, union, any, infc, fault, enum, typid, afaul, voidp, arrpt, infer, ulist (to) + {_NA__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__}, // VOID (from) + {ROKOK, _NA__, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, ROKOK, _NO__, _NO__}, // WILDCARD + {REXPL, _NO__, _NA__, REXPL, REXPL, _NO__, _NO__, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__}, // BOOL + {REXPL, _NO__, REXPL, RWIDE, RINFL, RINPT, _NO__, ROKOK, RINBS, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RINEN, _NO__, _NO__, RINPT, RINPT, _NO__, _NO__}, // INT + {REXPL, _NO__, REXPL, REXPL, RWIDE, _NO__, _NO__, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__}, // FLOAT + {REXPL, _NO__, REXPL, RPTIN, _NO__, RPTPT, _NO__, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, ROKOK, RPTIF, _NO__, _NO__, _NO__, _NO__, ROKOK, RPTPT, RPTFE, _NO__}, // PTR + {REXPL, _NO__, REXPL, _NO__, _NO__, RSAPT, RSASA, RSAVA, _NO__, RXXDI, RSAVA, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, ROKOK, RSAPT, RSAFE, _NO__}, // SARRAY + {REXPL, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RVCVC, _NO__, RXXDI, RVCAR, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RVAFE, _NO__}, // VECTOR + {REXPL, _NO__, _NO__, RBSIN, _NO__, _NO__, _NO__, _NO__, _NO__, RXXDI, RBSAR, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__}, // BITSTRUCT + {REXPL, _NO__, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, _NO__}, // DISTINCT + {REXPL, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RARVC, RARBS, RXXDI, RARAR, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RVAFE, _NO__}, // ARRAY + {REXPL, _NO__, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTDI, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, _NO__, _NO__}, // STRUCT + {REXPL, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__}, // UNION + {REXPL, _NO__, REXPL, _NO__, _NO__, REXPL, _NO__, _NO__, _NO__, RXXDI, _NO__, _NO__, _NO__, _NA__, REXPL, _NO__, _NO__, _NO__, _NO__, ROKOK, REXPL, _NO__, _NO__}, // ANY + {REXPL, _NO__, REXPL, _NO__, _NO__, REXPL, _NO__, _NO__, _NO__, RXXDI, _NO__, _NO__, _NO__, ROKOK, RIFIF, _NO__, _NO__, _NO__, _NO__, ROKOK, REXPL, _NO__, _NO__}, // INTERFACE + {REXPL, _NO__, REXPL, RPTIN, _NO__, REXPL, _NO__, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, ROKOK, REXPL, REXPL, _NO__, _NO__}, // FAULT + {REXPL, _NO__, _NO__, REXPL, _NO__, _NO__, _NO__, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__}, // ENUM + {REXPL, _NO__, REXPL, RPTIN, _NO__, REXPL, _NO__, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NA__, _NO__, REXPL, REXPL, _NO__, _NO__}, // TYPEID + {REXPL, _NO__, REXPL, RPTIN, _NO__, REXPL, _NO__, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, REXPL, _NO__, _NO__, _NA__, REXPL, REXPL, _NO__, _NO__}, // ANYFAULT + {REXPL, _NO__, REXPL, RPTIN, _NO__, ROKOK, _NO__, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, ROKOK, ROKOK, _NO__, _NO__, _NO__, _NO__, _NA__, ROKOK, _NO__, _NO__}, // VOIDPTR + {REXPL, _NO__, REXPL, RPTIN, _NO__, RPTPT, RAPSA, ROKOK, _NO__, RXXDI, _NO__, _NO__, _NO__, ROKOK, ROKOK, _NO__, _NO__, _NO__, _NO__, ROKOK, RPTPT, RPTFE, _NO__}, // ARRPTR + {_NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__}, // INFERRED + {_NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RULSA, RULAR, RULST, RXXDI, RULAR, RULST, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RULFE, _NO__}, // UNTYPED_LIST }; CastFunction cast_function[CONV_LAST + 1][CONV_LAST + 1] = { -//void, wildcd, bool, int, float, ptr, sarr, vec, bitst, dist, array, struct,union, any, infc, fault, enum, typeid,anyfa, vptr, aptr, ulist, infer(to) - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // VOID (from) - {XX2XX, 0, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, 0, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, 0 }, // WILDCARD - {XX2VO, 0, 0, BO2IN, BO2FP, 0, 0, EX2VC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // BOOL - {XX2VO, 0, IN2BO, IN2IN, IN2FP, IN2PT, 0, EX2VC, IA2BS, 0, 0, 0, 0, 0, 0, 0, IN2EN, 0, 0, IN2PT, IN2PT, 0 }, // INT - {XX2VO, 0, FP2BO, FP2IN, FP2FP, 0, 0, EX2VC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // FLOAT - {XX2VO, 0, PT2BO, PT2IN, 0, PT2PT, 0, EX2VC, 0, 0, 0, 0, 0, PT2AY, PT2AY, 0, 0, 0, 0, PT2PT, PT2PT, PT2FE }, // PTR - {XX2VO, 0, SA2BO, 0, 0, SA2PT, SA2SA, SA2VA, 0, 0, SA2VA, 0, 0, 0, 0, 0, 0, 0, 0, SA2PT, SA2PT, SA2FE }, // SARRAY - {XX2VO, 0, 0, 0, 0, 0, 0, VC2VC, 0, 0, VC2AR, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, VA2FE }, // VECTOR - {XX2VO, 0, 0, BS2IA, 0, 0, 0, 0, 0, 0, BS2IA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // BITSTRUCT - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // DISTINCT - {XX2VO, 0, 0, 0, 0, 0, 0, AR2VC, IA2BS, 0, AR2AR, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, VA2FE }, // ARRAY - {XX2VO, 0, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, 0, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, 0 }, // STRUCT - {XX2VO, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // UNION - {XX2VO, 0, AY2BO, 0, 0, AY2PT, 0, 0, 0, 0, 0, 0, 0, PT2PT, PT2PT, 0, 0, 0, 0, AY2PT, AY2PT, 0 }, // ANY - {XX2VO, 0, AY2BO, 0, 0, AY2PT, 0, 0, 0, 0, 0, 0, 0, PT2PT, PT2PT, 0, 0, 0, 0, AY2PT, AY2PT, 0 }, // INTERFACE - {XX2VO, 0, AF2BO, FA2IN, 0, FA2PT, 0, EX2VC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, FA2AF, FA2PT, FA2PT, 0 }, // FAULT - {XX2VO, 0, 0, EN2IN, 0, 0, 0, EX2VC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // ENUM - {XX2VO, 0, TI2BO, TI2IN, 0, TI2PT, 0, EX2VC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, TI2PT, TI2PT, 0 }, // TYPEID - {XX2VO, 0, AF2BO, FA2IN, 0, FA2IN, 0, EX2VC, 0, 0, 0, 0, 0, 0, 0, AF2FA, 0, 0, 0, FA2IN, FA2IN, 0 }, // ANYFAULT - {XX2VO, 0, PT2BO, PT2IN, 0, PT2PT, 0, EX2VC, 0, 0, 0, 0, 0, PT2AY, PT2AY, 0, 0, 0, 0, 0, PT2PT, 0 }, // VOIDPTR - {XX2VO, 0, PT2BO, PT2IN, 0, PT2PT, AP2SA, EX2VC, 0, 0, 0, 0, 0, PT2AY, PT2AY, 0, 0, 0, 0, PT2PT, PT2PT, PT2FE }, // ARRAYPTR - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // INFERRED +//void, wildcd, bool, int, float, ptr, sarr, vec, bitst, dist, array, struct,union, any, infc, fault, enum, typeid,anyfa, vptr, aptr, infer, ulist (to) + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // VOID (from) + {XX2XX, 0, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, 0, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, XX2XX, 0, 0 }, // WILDCARD + {XX2VO, 0, 0, BO2IN, BO2FP, 0, 0, EX2VC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // BOOL + {XX2VO, 0, IN2BO, IN2IN, IN2FP, IN2PT, 0, EX2VC, IA2BS, 0, 0, 0, 0, 0, 0, 0, IN2EN, 0, 0, IN2PT, IN2PT, 0, 0 }, // INT + {XX2VO, 0, FP2BO, FP2IN, FP2FP, 0, 0, EX2VC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // FLOAT + {XX2VO, 0, PT2BO, PT2IN, 0, PT2PT, 0, EX2VC, 0, 0, 0, 0, 0, PT2AY, PT2AY, 0, 0, 0, 0, PT2PT, PT2PT, PT2FE, 0 }, // PTR + {XX2VO, 0, SA2BO, 0, 0, SA2PT, SA2SA, SA2VA, 0, 0, SA2VA, 0, 0, 0, 0, 0, 0, 0, 0, SA2PT, SA2PT, SA2FE, 0 }, // SARRAY + {XX2VO, 0, 0, 0, 0, 0, 0, VC2VC, 0, 0, VC2AR, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, VA2FE, 0 }, // VECTOR + {XX2VO, 0, 0, BS2IA, 0, 0, 0, 0, 0, 0, BS2IA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // BITSTRUCT + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // DISTINCT + {XX2VO, 0, 0, 0, 0, 0, 0, AR2VC, IA2BS, 0, AR2AR, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, VA2FE, 0 }, // ARRAY + {XX2VO, 0, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, 0, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, 0, 0 }, // STRUCT + {XX2VO, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // UNION + {XX2VO, 0, AY2BO, 0, 0, AY2PT, 0, 0, 0, 0, 0, 0, 0, PT2PT, PT2PT, 0, 0, 0, 0, AY2PT, AY2PT, 0, 0 }, // ANY + {XX2VO, 0, AY2BO, 0, 0, AY2PT, 0, 0, 0, 0, 0, 0, 0, PT2PT, PT2PT, 0, 0, 0, 0, AY2PT, AY2PT, 0, 0 }, // INTERFACE + {XX2VO, 0, AF2BO, FA2IN, 0, FA2PT, 0, EX2VC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, FA2AF, FA2PT, FA2PT, 0, 0 }, // FAULT + {XX2VO, 0, 0, EN2IN, 0, 0, 0, EX2VC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // ENUM + {XX2VO, 0, TI2BO, TI2IN, 0, TI2PT, 0, EX2VC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, TI2PT, TI2PT, 0, 0 }, // TYPEID + {XX2VO, 0, AF2BO, FA2IN, 0, FA2IN, 0, EX2VC, 0, 0, 0, 0, 0, 0, 0, AF2FA, 0, 0, 0, FA2IN, FA2IN, 0, 0 }, // ANYFAULT + {XX2VO, 0, PT2BO, PT2IN, 0, PT2PT, 0, EX2VC, 0, 0, 0, 0, 0, PT2AY, PT2AY, 0, 0, 0, 0, 0, PT2PT, 0, 0 }, // VOIDPTR + {XX2VO, 0, PT2BO, PT2IN, 0, PT2PT, AP2SA, EX2VC, 0, 0, 0, 0, 0, PT2AY, PT2AY, 0, 0, 0, 0, PT2PT, PT2PT, PT2FE, 0 }, // ARRAYPTR + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // INFERRED + { 0, 0, 0, 0, 0, 0, UL2XX, UL2XX, UL2XX, 0, UL2XX, UL2XX, 0, 0, 0, 0, 0, 0, 0, 0, 0, UL2XX, 0 }, // UNTYPED }; static ConvGroup group_from_type[TYPE_LAST + 1] = { @@ -2045,7 +2116,7 @@ static ConvGroup group_from_type[TYPE_LAST + 1] = { [TYPE_INFERRED_ARRAY] = CONV_NO, [TYPE_VECTOR] = CONV_VECTOR, [TYPE_INFERRED_VECTOR] = CONV_NO, - [TYPE_UNTYPED_LIST] = CONV_NO, + [TYPE_UNTYPED_LIST] = CONV_UNTYPED_LIST, [TYPE_OPTIONAL] = CONV_NO, [TYPE_WILDCARD] = CONV_WILDCARD, [TYPE_TYPEINFO] = CONV_NO, diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index ff7cb18a3..f2b6f0cc7 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -119,7 +119,6 @@ static inline bool sema_check_param_uniqueness_and_type(Decl **decls, Decl *curr { SEMA_ERROR(current, "Duplicate parameter name '%s'.", name); SEMA_NOTE(decls[i], "Previous use of the name was here."); - decl_poison(decls[i]); decl_poison(current); return false; } @@ -264,14 +263,14 @@ static bool sema_analyse_union_members(SemaContext *context, Decl *decl) Decl *member = members[i]; // The member may already have been resolved if it is poisoned, then exit. - if (!decl_ok(member)) return decl_poison(decl); + if (!decl_ok(member)) return false; bool erase_decl = false; // Check the member if (!sema_analyse_struct_member(context, decl, member, &erase_decl)) { - // Error, so declare both as poisoned. - return decl_poison(member) || decl_poison(decl); + // Failed + return decl_poison(member); } // If we need to erase it then do so. @@ -286,7 +285,6 @@ static bool sema_analyse_union_members(SemaContext *context, Decl *decl) if (member->type->type_kind == TYPE_INFERRED_ARRAY) { decl_poison(member); - decl_poison(decl); RETURN_SEMA_ERROR(member, "Flexible array members not allowed in unions."); } AlignSize member_alignment; @@ -315,7 +313,7 @@ static bool sema_analyse_union_members(SemaContext *context, Decl *decl) member->offset = 0; } - if (!decl_ok(decl)) return false; + assert(decl_ok(decl)); // 1. If packed, then the alignment is zero, unless previously given if (decl->is_packed && !decl->alignment) decl->alignment = 1; @@ -366,18 +364,21 @@ static bool sema_analyse_struct_members(SemaContext *context, Decl *decl) bool is_packed = decl->is_packed; Decl **struct_members = decl->strukt.members; unsigned member_count = vec_size(struct_members); - assert(member_count > 0); + assert(member_count > 0 && "This analysis should only be called on member_count > 0"); for (unsigned i = 0; i < member_count; i++) { AGAIN:; Decl *member = struct_members[i]; + // We might have already analysed and poisoned this decl, if so, exit. if (!decl_ok(member)) return decl_poison(decl); bool erase_decl = false; + // Check the member if (!sema_analyse_struct_member(context, decl, member, &erase_decl)) { - return decl_poison(member) || decl_poison(decl); + return decl_poison(decl); } + // If we should erase it, do so. if (erase_decl) { vec_erase_ptr_at(struct_members, i); @@ -385,38 +386,53 @@ static bool sema_analyse_struct_members(SemaContext *context, Decl *decl) if (i < member_count) goto AGAIN; break; } + Type *member_type = type_flatten(member->type); + // If this is a struct and it has a variable array ending, then it must also be the last struct. + // So this is ok: + // struct Foo { int x; struct { int x; int[*] y; } } + // But not this: + // struct Bar { struct { int x; int[*] y; } int x; } if (member_type->type_kind == TYPE_STRUCT && member_type->decl->has_variable_array) { if (i != member_count - 1) { - SEMA_ERROR(member, "A struct member with a flexible array must be the last element."); - return decl_poison(member) || decl_poison(decl); + decl_poison(member); + RETURN_SEMA_ERROR(member, "A struct member with a flexible array must be the last element."); } + // Mark it as a variable array. decl->has_variable_array = true; } - if (member_type->type_kind == TYPE_INFERRED_ARRAY) + else if (member_type->type_kind == TYPE_INFERRED_ARRAY) { + // Check chat it is the last element. if (i != member_count - 1) { - SEMA_ERROR(member, "The flexible array member must be the last element."); - return decl_poison(member) || decl_poison(decl); + decl_poison(member); + RETURN_SEMA_ERROR(member, "The flexible array member must be the last element."); } + // And that it isn't the only element. if (i == 0) { - SEMA_ERROR(member, "The flexible array member cannot be the only element."); - return decl_poison(member) || decl_poison(decl); + decl_poison(member); + RETURN_SEMA_ERROR(member, "The flexible array member cannot be the only element."); } + // Now replace the type, because we want a TYPE_FLEXIBLE_ARRAY rather than the assumed TYPE_INFERRED_ARRAY member->type = type_get_flexible_array(member->type->array.base); + // And mark as variable array decl->has_variable_array = true; } - if (!decl_ok(decl)) return false; + assert(decl_ok(decl) && "The declaration should be fine at this point."); + // Grab the ABI alignment as its natural alignment AlignSize member_natural_alignment; if (!sema_set_abi_alignment(context, member->type, &member_natural_alignment)) return decl_poison(decl); + + // If packed, then the alignment is 1 AlignSize member_alignment = is_packed ? 1 : member_natural_alignment; + // If a member has an assigned alignment, then we use that one, even if it is packed. if (member->alignment) { member_alignment = member->alignment; @@ -498,62 +514,44 @@ static bool sema_analyse_struct_members(SemaContext *context, Decl *decl) static bool sema_analyse_struct_union(SemaContext *context, Decl *decl, bool *erase_decl) { - AttributeDomain domain; - switch (decl->decl_kind) - { - case DECL_STRUCT: - domain = ATTR_STRUCT; - break; - case DECL_UNION: - domain = ATTR_UNION; - break; - case DECL_FAULT: - domain = ATTR_FAULT; - break; - default: - UNREACHABLE - } - + // Begin by analysing attributes + bool is_union = decl->decl_kind == DECL_UNION; + AttributeDomain domain = is_union ? ATTR_UNION : ATTR_STRUCT; if (!sema_analyse_attributes(context, decl, decl->attributes, domain, erase_decl)) return decl_poison(decl); + + // If an @if attribute erases it, end here if (*erase_decl) return true; + + // Resolve any implemented interfaces shallowly (i.e. functions are not resolved) if (!sema_resolve_implemented_interfaces(context, decl, false)) return decl_poison(decl); DEBUG_LOG("Beginning analysis of %s.", decl->name ? decl->name : ".anon"); - bool success; Decl **members = decl->strukt.members; + + // We require at least one member in C3. This simplifies semantics in corner cases. if (!vec_size(members)) { - SEMA_ERROR(decl, decl->decl_kind == DECL_UNION ? "Zero sized unions are not permitted." : "Zero sized structs are not permitted."); - return false; - } - if (decl->name) - { - Decl** state = sema_decl_stack_store(); - if (decl->decl_kind == DECL_UNION) - { - success = sema_analyse_union_members(context, decl); - } - else - { - success = sema_analyse_struct_members(context, decl); - } - sema_decl_stack_restore(state); - } - else - { - if (decl->decl_kind == DECL_UNION) - { - success = sema_analyse_union_members(context, decl); - } - else - { - success = sema_analyse_struct_members(context, decl); - } + RETURN_SEMA_ERROR(decl, "Zero sized %s are not permitted.", is_union ? "unions" : "structs"); } + + // If we have a name, we need to create a new decl stack + Decl** state = decl->name ? sema_decl_stack_store() : NULL; + + bool success = is_union + ? sema_analyse_union_members(context, decl) + : sema_analyse_struct_members(context, decl); + + // Restore if needed. + if (decl->name) sema_decl_stack_restore(state); + DEBUG_LOG("Struct/union size %d, alignment %d.", (int)decl->strukt.size, (int)decl->alignment); DEBUG_LOG("Analysis complete."); + + // Failed, so exit. if (!success) return decl_poison(decl); - return decl_ok(decl); + + assert(decl_ok(decl)); + return true; } static inline bool sema_analyse_bitstruct_member(SemaContext *context, Decl *parent, Decl *member, unsigned index, bool allow_overlap, bool *erase_decl) @@ -738,29 +736,49 @@ AFTER_BIT_CHECK: member->resolve_status = RESOLVE_DONE; return true; } + +/* + * Analyse an interface declaration, because it is only called from analyse_decl, it is safe + * to just return false (and not poison explicitly), if it should be called from elsewhere, + * then this needs to be handled. + */ static bool sema_analyse_interface(SemaContext *context, Decl *decl, bool *erase_decl) { - if (!sema_analyse_attributes(context, decl, decl->attributes, ATTR_INTERFACE, erase_decl)) return decl_poison(decl); + // Begin with analysing attributes. + if (!sema_analyse_attributes(context, decl, decl->attributes, ATTR_INTERFACE, erase_decl)) return false; + + // If erased using @if, we exit. if (*erase_decl) return true; + + // Resolve the inherited interfaces deeply if (!sema_resolve_implemented_interfaces(context, decl, true)) return false; + + // Walk through the methods. Decl **functions = decl->interface_methods; unsigned count = vec_size(functions); + + // Note that zero functions are allowed, this is useful for creating combinations of interfaces. for (unsigned i = 0; i < count; i++) { RETRY:; Decl *method = functions[i]; - if (method->decl_kind != DECL_FUNC) + // The method might have been resolved earlier, if so we either exit or go to the next. + // This might happen for example if it was resolved using $checks + if (method->resolve_status == RESOLVE_DONE) { - SEMA_ERROR(method, "Only functions are allowed here."); - return decl_poison(decl); + if (!decl_ok(method)) return false; + continue; } + + if (method->decl_kind != DECL_FUNC) RETURN_SEMA_ERROR(method, "Only functions are allowed here."); if (method->func_decl.type_parent) { - SEMA_ERROR(type_infoptr(method->func_decl.type_parent), "Interfaces should not be declared as methods."); - return decl_poison(decl); + RETURN_SEMA_ERROR(type_infoptr(method->func_decl.type_parent), "Interfaces should not be declared as methods."); } method->func_decl.attr_interface_method = true; bool erase = false; + + // Insert the first parameter, which is the implicit `void*` Decl *first = decl_new_var(kw_self, decl->span, NULL, VARDECL_PARAM); first->type = type_voidptr; first->var.kind = VARDECL_PARAM; @@ -769,7 +787,17 @@ static bool sema_analyse_interface(SemaContext *context, Decl *decl, bool *erase first->alignment = type_abi_alignment(type_voidptr); vec_insert_first(method->func_decl.signature.params, first); method->unit = context->unit; - if (!sema_analyse_func(context, method, &erase)) return decl_poison(decl); + + // Now we analyse the function as a regular function. + if (!sema_analyse_func(context, method, &erase)) + { + // This is necessary in order to allow this check to run again. + decl_poison(method); + vec_erase_ptr_at(method->func_decl.signature.params, 0); + return false; + } + + // We might need to erase the function. if (erase) { vec_erase_ptr_at(functions, i); @@ -777,14 +805,17 @@ static bool sema_analyse_interface(SemaContext *context, Decl *decl, bool *erase if (i >= count) break; goto RETRY; } + const char *name = method->name; + // Do a simple check to ensure the same function isn't defined twice. + // note that this doesn't check if it's overlapping an inherited method, this is deliberate. for (unsigned j = 0; j < i; j++) { if (functions[j]->name == name) { SEMA_ERROR(method, "Duplicate definition of method '%s'.", name); SEMA_NOTE(functions[j], "The previous definition was here."); - return decl_poison(decl); + decl_poison(method); return false; } } @@ -1493,11 +1524,9 @@ static inline Decl *operator_in_module(SemaContext *c, Module *module, OperatorO return NULL; } -Decl *sema_find_operator(SemaContext *context, Expr *expr, OperatorOverload operator_overload) +Decl *sema_find_operator(SemaContext *context, Type *type, OperatorOverload operator_overload) { - Decl *ambiguous = NULL; - Decl *private = NULL; - Type *type = expr->type->canonical; + type = type->canonical; if (!type_may_have_sub_elements(type)) return NULL; Decl *def = type->decl; Decl **funcs = def->methods; @@ -1963,7 +1992,7 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, [ATTRIBUTE_BUILTIN] = ATTR_MACRO | ATTR_FUNC | ATTR_GLOBAL | ATTR_CONST, [ATTRIBUTE_CALLCONV] = ATTR_FUNC | ATTR_INTERFACE_METHOD, [ATTRIBUTE_DEFAULT] = ATTR_FUNC | ATTR_MACRO, - [ATTRIBUTE_DEPRECATED] = USER_DEFINED_TYPES | CALLABLE_TYPE | ATTR_CONST | ATTR_GLOBAL | ATTR_MEMBER | ATTR_BITSTRUCT_MEMBER, + [ATTRIBUTE_DEPRECATED] = USER_DEFINED_TYPES | CALLABLE_TYPE | ATTR_CONST | ATTR_GLOBAL | ATTR_MEMBER | ATTR_BITSTRUCT_MEMBER | ATTR_INTERFACE, [ATTRIBUTE_DYNAMIC] = ATTR_FUNC, [ATTRIBUTE_EXPORT] = ATTR_FUNC | ATTR_GLOBAL | ATTR_CONST | EXPORTED_USER_DEFINED_TYPES, [ATTRIBUTE_EXTERN] = ATTR_FUNC | ATTR_GLOBAL | ATTR_CONST | USER_DEFINED_TYPES, @@ -1972,7 +2001,7 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, [ATTRIBUTE_INIT] = ATTR_FUNC, [ATTRIBUTE_INLINE] = ATTR_FUNC | ATTR_CALL, [ATTRIBUTE_LITTLEENDIAN] = ATTR_BITSTRUCT, - [ATTRIBUTE_LOCAL] = ATTR_FUNC | ATTR_MACRO | ATTR_GLOBAL | ATTR_CONST | USER_DEFINED_TYPES | ATTR_DEF | ATTR_DISTINCT, + [ATTRIBUTE_LOCAL] = ATTR_FUNC | ATTR_MACRO | ATTR_GLOBAL | ATTR_CONST | USER_DEFINED_TYPES | ATTR_DEF | ATTR_INTERFACE, [ATTRIBUTE_MAYDISCARD] = CALLABLE_TYPE, [ATTRIBUTE_NAKED] = ATTR_FUNC, [ATTRIBUTE_NODISCARD] = CALLABLE_TYPE, @@ -1985,8 +2014,8 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, [ATTRIBUTE_OPTIONAL] = ATTR_INTERFACE_METHOD, [ATTRIBUTE_OVERLAP] = ATTR_BITSTRUCT, [ATTRIBUTE_PACKED] = ATTR_STRUCT | ATTR_UNION, - [ATTRIBUTE_PRIVATE] = ATTR_FUNC | ATTR_MACRO | ATTR_GLOBAL | ATTR_CONST | USER_DEFINED_TYPES | ATTR_DEF, - [ATTRIBUTE_PUBLIC] = ATTR_FUNC | ATTR_MACRO | ATTR_GLOBAL | ATTR_CONST | USER_DEFINED_TYPES | ATTR_DEF, + [ATTRIBUTE_PRIVATE] = ATTR_FUNC | ATTR_MACRO | ATTR_GLOBAL | ATTR_CONST | USER_DEFINED_TYPES | ATTR_DEF | ATTR_INTERFACE, + [ATTRIBUTE_PUBLIC] = ATTR_FUNC | ATTR_MACRO | ATTR_GLOBAL | ATTR_CONST | USER_DEFINED_TYPES | ATTR_DEF | ATTR_INTERFACE, [ATTRIBUTE_PURE] = ATTR_CALL, [ATTRIBUTE_REFLECT] = ATTR_FUNC | ATTR_GLOBAL | ATTR_CONST | USER_DEFINED_TYPES, [ATTRIBUTE_SECTION] = ATTR_FUNC | ATTR_CONST | ATTR_GLOBAL, @@ -3511,32 +3540,26 @@ static bool sema_analyse_generic_module_contracts(SemaContext *c, Module *module contract = ast->next; assert(ast->ast_kind == AST_CONTRACT); SemaContext temp_context; - - assert(ast->contract_stmt.kind == CONTRACT_CHECKED || ast->contract_stmt.kind == CONTRACT_REQUIRE); + assert(ast->contract_stmt.kind == CONTRACT_REQUIRE); SemaContext *new_context = context_transform_for_eval(c, &temp_context, module->units[0]); - if (ast->contract_stmt.kind == CONTRACT_CHECKED) - { - if (!sema_analyse_checked(new_context, ast, error_span)) return false; - } - else - { - FOREACH_BEGIN(Expr *expr, ast->contract_stmt.contract.decl_exprs->expression_list) - int res = sema_check_comp_time_bool(new_context, expr); - if (res == -1) return false; - if (res) continue; - if (ast->contract_stmt.contract.comment) - { - sema_error_at(error_span, - "Parameter(s) would violate constraint: %s.", - ast->contract_stmt.contract.comment); - } - else - { - sema_error_at(error_span, "Parameter(s) failed validation: %s", ast->contract_stmt.contract.expr_string); - } - return false; - FOREACH_END(); - } + FOREACH_BEGIN(Expr *expr, ast->contract_stmt.contract.decl_exprs->expression_list) + int res = sema_check_comp_time_bool(new_context, expr); + if (res == -1) goto FAIL; + if (res) continue; + if (ast->contract_stmt.contract.comment) + { + sema_error_at(error_span, + "Parameter(s) would violate constraint: %s.", + ast->contract_stmt.contract.comment); + } else + { + sema_error_at(error_span, "Parameter(s) failed validation: %s", + ast->contract_stmt.contract.expr_string); + } + FAIL: + sema_context_destroy(&temp_context); + return false; + FOREACH_END(); sema_context_destroy(&temp_context); } return true; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index f985213a6..6f5c16330 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -11,7 +11,8 @@ typedef enum { SUBSCRIPT_EVAL_VALUE, SUBSCRIPT_EVAL_REF, - SUBSCRIPT_EVAL_ASSIGN + SUBSCRIPT_EVAL_ASSIGN, + SUBSCRIPT_EVAL_VALID, } SubscriptEval; typedef struct @@ -32,7 +33,7 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, static inline bool sema_expr_analyse_pointer_offset(SemaContext *context, Expr *expr); static inline bool sema_expr_analyse_slice(SemaContext *context, Expr *expr); static inline bool sema_expr_analyse_group(SemaContext *context, Expr *expr); -static inline bool sema_expr_analyse_access(SemaContext *context, Expr *expr); +static inline bool sema_expr_analyse_access(SemaContext *context, Expr *expr, bool *missing_ref); static inline bool sema_expr_analyse_compound_literal(SemaContext *context, Expr *expr); static inline bool sema_expr_analyse_builtin(SemaContext *context, Expr *expr, bool throw_error); static inline bool sema_expr_analyse_binary(SemaContext *context, Expr *expr); @@ -41,17 +42,17 @@ static inline bool sema_expr_analyse_identifier(SemaContext *context, Type *to, static inline bool sema_expr_analyse_ct_identifier(SemaContext *context, Expr *expr); static inline bool sema_expr_analyse_hash_identifier(SemaContext *context, Expr *expr); static inline bool sema_expr_analyse_ternary(SemaContext *context, Expr *expr); -static inline bool sema_expr_analyse_cast(SemaContext *context, Expr *expr); +static inline bool sema_expr_analyse_cast(SemaContext *context, Expr *expr, bool *invalid_cast_ref); static inline bool sema_expr_analyse_or_error(SemaContext *context, Expr *expr); -static inline bool sema_expr_analyse_unary(SemaContext *context, Expr *expr); +static inline bool sema_expr_analyse_unary(SemaContext *context, Expr *expr, bool *failed_ref); static inline bool sema_expr_analyse_embed(SemaContext *context, Expr *expr, bool allow_fail); static inline bool sema_expr_analyse_rethrow(SemaContext *context, Expr *expr); static inline bool sema_expr_analyse_force_unwrap(SemaContext *context, Expr *expr); static inline bool sema_expr_analyse_typeid(SemaContext *context, Expr *expr); -static inline bool sema_expr_analyse_call(SemaContext *context, Expr *expr); +static inline bool sema_expr_analyse_call(SemaContext *context, Expr *expr, bool *no_match_ref); static inline bool sema_expr_analyse_expr_block(SemaContext *context, Type *infer_type, Expr *expr); -static inline bool sema_expr_analyse_optional(SemaContext *context, Expr *expr); +static inline bool sema_expr_analyse_optional(SemaContext *context, Expr *expr, bool *failed_ref); static inline bool sema_expr_analyse_compiler_const(SemaContext *context, Expr *expr, bool report_missing); static inline bool sema_expr_analyse_ct_arg(SemaContext *context, Expr *expr); static inline bool sema_expr_analyse_ct_stringify(SemaContext *context, Expr *expr); @@ -60,7 +61,6 @@ static inline bool sema_expr_analyse_ct_call(SemaContext *context, Expr *expr); static inline bool sema_expr_analyse_retval(SemaContext *c, Expr *expr); static inline bool sema_expr_analyse_expr_list(SemaContext *context, Expr *expr); -static inline bool sema_expr_analyse_ct_checks(SemaContext *context, Expr *expr); // -- binary static bool sema_expr_analyse_sub(SemaContext *context, Expr *expr, Expr *left, Expr *right); @@ -82,14 +82,14 @@ static bool sema_expr_analyse_comp(SemaContext *context, Expr *expr, Expr *left, static bool sema_expr_analyse_op_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right, bool int_only, bool allow_bitstruct); // -- unary -static inline bool sema_expr_analyse_addr(SemaContext *context, Expr *expr); +static inline bool sema_expr_analyse_addr(SemaContext *context, Expr *expr, bool *failed_ref); static inline bool sema_expr_analyse_neg_plus(SemaContext *context, Expr *expr); static inline bool sema_expr_analyse_bit_not(SemaContext *context, Expr *expr); static inline bool sema_expr_analyse_not(SemaContext *context, Expr *expr); -static inline bool sema_expr_analyse_deref(SemaContext *context, Expr *expr); +static inline bool sema_expr_analyse_deref(SemaContext *context, Expr *expr, bool *failed_ref); static inline bool sema_expr_analyse_ct_incdec(SemaContext *context, Expr *expr, Expr *inner); static inline bool sema_expr_analyse_incdec(SemaContext *context, Expr *expr); -static inline bool sema_expr_analyse_taddr(SemaContext *context, Expr *expr); +static inline bool sema_expr_analyse_taddr(SemaContext *context, Expr *expr, bool *failed_ref); // -- ct_call static inline bool sema_expr_analyse_ct_alignof(SemaContext *context, Expr *expr); @@ -122,20 +122,24 @@ static bool sema_binary_analyse_ct_common_assign(SemaContext *context, Expr *exp static bool sema_binary_arithmetic_promotion(SemaContext *context, Expr *left, Expr *right, Type *left_type, Type *right_type, Expr *parent, const char *error_message); static bool sema_binary_is_unsigned_always_false_comparison(SemaContext *context, Expr *expr, Expr *left, Expr *right); static bool sema_binary_is_expr_lvalue(Expr *top_expr, Expr *expr); -static void sema_binary_unify_voidptr(Expr *left, Expr *right, Type **left_type_ref, Type **right_type_ref); +static void sema_binary_unify_voidptr(SemaContext *context, Expr *left, Expr *right, Type **left_type_ref, Type **right_type_ref); // -- function helper functions -static inline bool sema_expr_analyse_var_call(SemaContext *context, Expr *expr, Type *func_ptr_type, bool optional); -static inline bool sema_expr_analyse_func_call(SemaContext *context, Expr *expr, Decl *decl, Expr *struct_var, bool optional); -static inline bool sema_call_analyse_invocation(SemaContext *context, Expr *call, CalledDecl callee, bool *optional); -static inline bool sema_call_analyse_func_invocation(SemaContext *context, Type *type, Expr *expr, Expr *struct_var, - bool optional, const char *name); +static inline bool +sema_expr_analyse_var_call(SemaContext *context, Expr *expr, Type *func_ptr_type, bool optional, bool *no_match_ref); +static inline bool +sema_expr_analyse_func_call(SemaContext *context, Expr *expr, Decl *decl, Expr *struct_var, bool optional, + bool *no_match_ref); +static inline bool +sema_call_analyse_invocation(SemaContext *context, Expr *call, CalledDecl callee, bool *optional, bool *no_match_ref); +static inline bool +sema_call_analyse_func_invocation(SemaContext *context, Type *type, Expr *expr, 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); -INLINE bool sema_call_expand_arguments(SemaContext *context, CalledDecl *callee, Expr *call, Expr **args, - unsigned func_param_count, Variadic variadic, unsigned vararg_index, - bool *optional, - Expr ***varargs_ref, - Expr **vararg_splat_ref); +INLINE bool +sema_call_expand_arguments(SemaContext *context, CalledDecl *callee, Expr *call, Expr **args, unsigned func_param_count, + Variadic variadic, unsigned vararg_index, bool *optional, Expr ***varargs_ref, + Expr **vararg_splat_ref, bool *no_match_ref); static inline int sema_call_find_index_of_named_parameter(SemaContext *context, Decl **func_params, Expr *expr); 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); @@ -185,8 +189,12 @@ static inline bool sema_expr_analyse_enum_constant(SemaContext *context, Expr *e static inline bool sema_cast_ident_rvalue(SemaContext *context, Expr *expr); static inline bool sema_cast_rvalue(SemaContext *context, Expr *expr); -static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *expr, Type *parent_type, bool was_group, Expr *identifier); -static inline bool sema_expr_analyse_member_access(SemaContext *context, Expr *expr, Expr *parent, bool was_group, Expr *identifier); +static inline bool +sema_expr_analyse_type_access(SemaContext *context, Expr *expr, Type *parent_type, bool was_group, Expr *identifier, + bool *missing_ref); +static inline bool +sema_expr_analyse_member_access(SemaContext *context, Expr *expr, Expr *parent, bool was_group, Expr *identifier, + bool *missing_ref); static inline bool sema_expr_fold_to_member(Expr *expr, Expr *parent, Decl *member); static inline void sema_expr_flatten_const(SemaContext *context, Expr *expr); @@ -215,7 +223,7 @@ static inline bool sema_constant_fold_ops(Expr *expr) UNREACHABLE } -Expr *sema_expr_analyse_ct_arg_index(SemaContext *context, Expr *index_expr, unsigned *index_ref) +Expr *sema_expr_analyse_ct_arg_index(SemaContext *context, Expr *index_expr, unsigned *index_ref, bool report_error) { unsigned args = vec_size(context->macro_varargs); uint64_t index; @@ -223,24 +231,24 @@ Expr *sema_expr_analyse_ct_arg_index(SemaContext *context, Expr *index_expr, uns if (!sema_analyse_expr(context, index_expr)) return poisoned_expr; if (!type_is_integer(index_expr->type)) { - SEMA_ERROR(index_expr, "Expected the argument index here, but found a value of type %s.", type_quoted_error_string(index_expr->type)); + if (report_error) SEMA_ERROR(index_expr, "Expected the argument index here, but found a value of type %s.", type_quoted_error_string(index_expr->type)); return poisoned_expr; } if (!expr_is_const(index_expr)) { - SEMA_ERROR(index_expr, "Vararg functions need a constant argument, but this is a runtime value."); + if (report_error) SEMA_ERROR(index_expr, "Vararg functions need a constant argument, but this is a runtime value."); return poisoned_expr; } Int index_val = index_expr->const_expr.ixx; if (int_is_neg(index_val)) { - SEMA_ERROR(index_expr, "The index cannot be negative."); + if (report_error) SEMA_ERROR(index_expr, "The index cannot be negative."); return poisoned_expr; } Int int_max = { .i = { .low = args }, .type = TYPE_U32 }; if (int_comp(index_val, int_max, BINARYOP_GE)) { - SEMA_ERROR(index_expr, "Only %u vararg%s exist.", args, args == 1 ? "" : "s"); + if (report_error) SEMA_ERROR(index_expr, "Only %u vararg%s exist.", args, args == 1 ? "" : "s"); return poisoned_expr; } if (index_ref) *index_ref = (unsigned)index_val.i.low; @@ -395,9 +403,10 @@ static bool sema_binary_is_expr_lvalue(Expr *top_expr, Expr *expr) switch (expr->expr_kind) { case EXPR_SWIZZLE: + RETURN_SEMA_ERROR(expr, "You cannot use swizzling to assign to multiple elements, use element-wise assign instead."); case EXPR_LAMBDA: case EXPR_EMBED: - return false; + RETURN_SEMA_ERROR(expr, "This expression is a value and cannot be assigned to."); case EXPR_SUBSCRIPT_ASSIGN: case EXPR_CT_IDENT: return true; @@ -484,9 +493,11 @@ static bool sema_binary_is_expr_lvalue(Expr *top_expr, Expr *expr) case EXPR_CONST: case EXPR_CT_ARG: case EXPR_CT_CALL: - case EXPR_CT_CHECKS: + case EXPR_CT_AND_OR: case EXPR_CT_DEFINED: + case EXPR_CT_IS_CONST: case EXPR_CT_EVAL: + case EXPR_CT_CASTABLE: case EXPR_DECL: case EXPR_DESIGNATED_INITIALIZER_LIST: case EXPR_DESIGNATOR: @@ -596,9 +607,11 @@ static bool expr_may_ref(Expr *expr) case EXPR_COND: case EXPR_CONST: case EXPR_CT_ARG: + case EXPR_CT_CASTABLE: + case EXPR_CT_AND_OR: case EXPR_CT_CALL: - case EXPR_CT_CHECKS: case EXPR_CT_DEFINED: + case EXPR_CT_IS_CONST: case EXPR_CT_EVAL: case EXPR_DECL: case EXPR_DESIGNATED_INITIALIZER_LIST: @@ -793,7 +806,7 @@ static inline bool sema_expr_analyse_ternary(SemaContext *context, Expr *expr) if (expr_is_constant_eval(cond, true)) { Expr *copy = copy_expr_single(cond); - cast_no_check(copy, type_bool, false); + cast_no_check(context, copy, type_bool, false); assert(expr_is_const(cond)); path = cond->const_expr.b ? 1 : 0; } @@ -1015,7 +1028,7 @@ static inline bool sema_expr_analyse_hash_identifier(SemaContext *context, Expr if (!sema_analyse_expr_lvalue_fold_const(decl->var.hash_var.context, expr)) { // Poison the decl so we don't evaluate twice. - if (!global_context.suppress_errors) decl_poison(decl); + decl_poison(decl); return false; } return true; @@ -1175,9 +1188,8 @@ static inline bool sema_call_check_invalid_body_arguments(SemaContext *context, INLINE bool sema_call_expand_arguments(SemaContext *context, CalledDecl *callee, Expr *call, Expr **args, unsigned func_param_count, Variadic variadic, unsigned vararg_index, - bool *optional, - Expr ***varargs_ref, - Expr **vararg_splat_ref) + bool *optional, Expr ***varargs_ref, Expr **vararg_splat_ref, + bool *no_match_ref) { unsigned num_args = vec_size(args); Decl **params = callee->params; @@ -1197,7 +1209,7 @@ INLINE bool sema_call_expand_arguments(SemaContext *context, CalledDecl *callee, // Find the location of the parameter. int index = sema_call_find_index_of_named_parameter(context, params, arg); - // If it's not found then this is an error. + // If it's not found then this is an error. Let's not invoke nomatch if (index < 0) return false; // We have named parameters, that will add some restrictions. @@ -1207,15 +1219,14 @@ INLINE bool sema_call_expand_arguments(SemaContext *context, CalledDecl *callee, // this is an error. if (params[index]->var.vararg) { - SEMA_ERROR(arg, "Vararg parameters may not be named parameters, use normal parameters instead.", params[index]->name); - return false; + RETURN_SEMA_ERROR(arg, "Vararg parameters may not be named parameters, " + "use normal parameters instead.", params[index]->name); } // 8e. We might have already set this parameter, that is not allowed. if (actual_args[index]) { - SEMA_ERROR(arg, "The parameter '%s' was already set.", params[index]->name); - return false; + RETURN_SEMA_ERROR(arg, "The parameter '%s' was already set.", params[index]->name); } // 8g. Set the parameter @@ -1225,16 +1236,16 @@ INLINE bool sema_call_expand_arguments(SemaContext *context, CalledDecl *callee, if (has_named) { - SEMA_ERROR(args[i - 1], "Named arguments must be placed after positional arguments."); - return false; + RETURN_SEMA_ERROR(args[i - 1], "Named arguments must be placed after positional arguments."); } // 11. We might have a typed variadic argument. if (variadic == VARIADIC_NONE && i >= func_param_count) { // 15. We have too many parameters... - SEMA_ERROR(arg, "This argument would exceed the number of parameters, did you add too many arguments?"); - return false; + if (no_match_ref) goto NO_MATCH_REF; + RETURN_SEMA_ERROR(arg, "This argument would exceed the number of parameters, " + "did you add too many arguments?"); } // 10. If we exceed the function parameter count (remember we reduced this by one @@ -1248,9 +1259,9 @@ INLINE bool sema_call_expand_arguments(SemaContext *context, CalledDecl *callee, // 11b. Is this the last argument, or did we get some before the splat? if (i < num_args - 1) { - SEMA_ERROR(arg, - "This looks like a variable argument before an splatted variable which isn't allowed. Did you add too many arguments?"); - return false; + if (no_match_ref) goto NO_MATCH_REF; + RETURN_SEMA_ERROR(arg, "This looks like a variable argument before an splatted variable which " + "isn't allowed. Did you add too many arguments?"); } *vararg_splat_ref = arg; continue; @@ -1261,9 +1272,8 @@ INLINE bool sema_call_expand_arguments(SemaContext *context, CalledDecl *callee, Type *type = arg->type; if (type_is_invalid_storage_type(type) || type == type_void) { - SEMA_ERROR(arg, "A value of type %s cannot be passed as a variadic argument.", - type_quoted_error_string(type)); - return false; + RETURN_SEMA_ERROR(arg, "A value of type %s cannot be passed as a variadic argument.", + type_quoted_error_string(type)); } expr_insert_addr(arg); } @@ -1309,17 +1319,15 @@ INLINE bool sema_call_expand_arguments(SemaContext *context, CalledDecl *callee, // 17d. Argument missing, that's bad. if (!has_named || !param->name) { + if (no_match_ref) goto NO_MATCH_REF; if (func_param_count == 1) { if (param->type) { - SEMA_ERROR(call, "This call expected a parameter of type %s, did you forget it?", type_quoted_error_string(param->type)); + RETURN_SEMA_ERROR(call, "This call expected a parameter of type %s, did you forget it?", + type_quoted_error_string(param->type)); } - else - { - SEMA_ERROR(call, "This call expected a parameter, did you forget it?"); - } - return false; + RETURN_SEMA_ERROR(call, "This call expected a parameter, did you forget it?"); } if (variadic != VARIADIC_NONE && i > vararg_index) { @@ -1345,6 +1353,9 @@ INLINE bool sema_call_expand_arguments(SemaContext *context, CalledDecl *callee, } call->call_expr.arguments = actual_args; return true; +NO_MATCH_REF: + *no_match_ref = true; + return false; } static inline bool sema_call_check_contract_param_match(SemaContext *context, Decl *param, Expr *expr) @@ -1385,7 +1396,8 @@ INLINE bool sema_arg_is_pass_through_ref(Expr *expr) return decl->var.kind == VARDECL_PARAM_REF; } -static inline bool sema_call_analyse_invocation(SemaContext *context, Expr *call, CalledDecl callee, bool *optional) +static inline bool +sema_call_analyse_invocation(SemaContext *context, Expr *call, CalledDecl callee, bool *optional, bool *no_match_ref) { // 1. Check body arguments (for macro calls, or possibly broken ) if (!sema_call_check_invalid_body_arguments(context, call, &callee)) return false; @@ -1427,9 +1439,8 @@ static inline bool sema_call_analyse_invocation(SemaContext *context, Expr *call if (variadic == VARIADIC_NONE) { assert(call->call_expr.arguments); - SEMA_ERROR(call->call_expr.arguments[num_args - 1], - "Using the splat operator is only allowed on vararg parameters."); - return false; + RETURN_SEMA_ERROR(call->call_expr.arguments[num_args - 1], + "Using the splat operator is only allowed on vararg parameters."); } } @@ -1449,14 +1460,8 @@ static inline bool sema_call_analyse_invocation(SemaContext *context, Expr *call Expr **varargs = NULL; Expr *vararg_splat = NULL; - if (!sema_call_expand_arguments(context, - &callee, - call, - args, - param_count, - variadic, - vararg_index, - optional, &varargs, &vararg_splat)) return false; + if (!sema_call_expand_arguments(context, &callee, call, args, param_count, variadic, vararg_index, + optional, &varargs, &vararg_splat, no_match_ref)) return false; args = call->call_expr.arguments; num_args = vec_size(args); @@ -1483,7 +1488,7 @@ static inline bool sema_call_analyse_invocation(SemaContext *context, Expr *call type_quoted_error_string(val->type)); return false; } - cast_promote_vararg(val); + cast_promote_vararg(context, val); } // Set the argument at the location. @@ -1632,8 +1637,9 @@ static inline bool sema_call_analyse_invocation(SemaContext *context, Expr *call return true; } -static inline bool sema_call_analyse_func_invocation(SemaContext *context, Type *type, Expr *expr, Expr *struct_var, - bool optional, const char *name) +static inline bool sema_call_analyse_func_invocation(SemaContext *context, Type *type, Expr *expr, + Expr *struct_var, bool optional, const char *name, + bool *no_match_ref) { Signature *sig = type->function.signature; CalledDecl callee = { @@ -1652,7 +1658,7 @@ static inline bool sema_call_analyse_func_invocation(SemaContext *context, Type if (sig->attrs.noreturn) expr->call_expr.no_return = true; - if (!sema_call_analyse_invocation(context, expr, callee, &optional)) return false; + if (!sema_call_analyse_invocation(context, expr, callee, &optional, no_match_ref)) return false; Type *rtype = type->function.prototype->rtype; @@ -1666,17 +1672,23 @@ static inline bool sema_call_analyse_func_invocation(SemaContext *context, Type return true; } -static inline bool sema_expr_analyse_var_call(SemaContext *context, Expr *expr, Type *func_ptr_type, bool optional) +static inline bool sema_expr_analyse_var_call(SemaContext *context, Expr *expr, Type *func_ptr_type, bool optional, bool *no_match_ref) { Decl *decl = NULL; if (func_ptr_type->type_kind != TYPE_POINTER || func_ptr_type->pointer->type_kind != TYPE_FUNC) { - SEMA_ERROR(expr, "Only macros, functions and function pointers maybe invoked, this is of type '%s'.", type_to_error_string(func_ptr_type)); - return false; + if (no_match_ref) + { + *no_match_ref = true; + return false; + } + RETURN_SEMA_ERROR(expr, "Only macros, functions and function pointers maybe invoked, this is of type '%s'.", + type_to_error_string(func_ptr_type)); } Type *pointee = func_ptr_type->pointer; expr->call_expr.is_pointer_call = true; - return sema_call_analyse_func_invocation(context, pointee, expr, NULL, optional, func_ptr_type->pointer->name); + return sema_call_analyse_func_invocation(context, pointee, expr, NULL, optional, func_ptr_type->pointer->name, + no_match_ref); } // Unify returns in a macro or expression block. @@ -1765,7 +1777,8 @@ static inline Type *context_unify_returns(SemaContext *context) return type_add_optional(common_type, optional); } -static inline bool sema_expr_analyse_func_call(SemaContext *context, Expr *expr, Decl *decl, Expr *struct_var, bool optional) +static inline bool sema_expr_analyse_func_call(SemaContext *context, Expr *expr, Decl *decl, Expr *struct_var, + bool optional, bool *no_match_ref) { expr->call_expr.is_pointer_call = false; if (decl->func_decl.attr_test) @@ -1786,25 +1799,24 @@ static inline bool sema_expr_analyse_func_call(SemaContext *context, Expr *expr, if (struct_var && decl->func_decl.attr_interface_method) expr->call_expr.is_dynamic_dispatch = true; return sema_call_analyse_func_invocation(context, - decl->type, - expr, - struct_var, - optional, - decl->name); + decl->type, + expr, + struct_var, + optional, + decl->name, no_match_ref); } - - -bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *struct_var, Decl *decl, bool call_var_optional) +bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *struct_var, Decl *decl, + bool call_var_optional, bool *no_match_ref) { assert(decl->decl_kind == DECL_MACRO); if (context->macro_call_depth > 256) { - SEMA_ERROR(call_expr, "Failure evaluating macro, max call depth reached, possibly due non-terminating macro recursion."); decl->decl_kind = DECL_POISONED; - return false; + RETURN_SEMA_ERROR(call_expr, "Failure evaluating macro, max call depth reached, " + "possibly due non-terminating macro recursion."); } sema_display_deprecated_warning_on_use(context, decl, call_expr->span); @@ -1826,7 +1838,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s }; bool has_optional_arg = call_var_optional; - if (!sema_call_analyse_invocation(context, call_expr, callee, &has_optional_arg)) return false; + if (!sema_call_analyse_invocation(context, call_expr, callee, &has_optional_arg, no_match_ref)) return false; unsigned vararg_index = sig->vararg_index; Expr **args = call_expr->call_expr.arguments; @@ -1863,13 +1875,13 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s unsigned expected_body_params = vec_size(macro_body_params); if (expected_body_params > body_params_count) { - SEMA_ERROR(call_expr, "Not enough parameters for the macro body, expected %d.", expected_body_params); - return false; + if (no_match_ref) goto NO_MATCH_REF; + RETURN_SEMA_ERROR(call_expr, "Not enough parameters for the macro body, expected %d.", expected_body_params); } if (expected_body_params < body_params_count) { - SEMA_ERROR(call_expr, "Too many parameters for the macro body, expected %d.", expected_body_params); - return false; + if (no_match_ref) goto NO_MATCH_REF; + RETURN_SEMA_ERROR(call_expr, "Too many parameters for the macro body, expected %d.", expected_body_params); } for (unsigned i = 0; i < expected_body_params; i++) { @@ -1878,8 +1890,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s Decl *body_arg = body_params[i]; if (!body_arg->var.type_info) { - SEMA_ERROR(body_arg, "Expected a type parameter before this variable name."); - return false; + RETURN_SEMA_ERROR(body_arg, "Expected a type parameter before this variable name."); } TypeInfo *type_info = vartype(body_arg); if (!sema_resolve_type_info(context, type_info, RESOLVE_TYPE_DEFAULT)) return false; @@ -1889,6 +1900,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s Type *declare_type = type_info->type->canonical; if (declare_type != body_arg->type->canonical) { + if (no_match_ref) goto NO_MATCH_REF; RETURN_SEMA_ERROR(type_info, "This parameter should be %s but was %s", type_quoted_error_string(declare_type), type_quoted_error_string(body_arg->type)); @@ -2099,6 +2111,9 @@ EXIT: EXIT_FAIL: sema_context_destroy(¯o_context); return SCOPE_POP_ERROR(); +NO_MATCH_REF: + *no_match_ref = true; + return false; } static bool sema_call_analyse_body_expansion(SemaContext *macro_context, Expr *call) @@ -2185,32 +2200,39 @@ static bool sema_call_analyse_body_expansion(SemaContext *macro_context, Expr *c return true; } -bool sema_expr_analyse_general_call(SemaContext *context, Expr *expr, Decl *decl, Expr *struct_var, bool optional) +bool sema_expr_analyse_general_call(SemaContext *context, Expr *expr, Decl *decl, Expr *struct_var, bool optional, + bool *no_match_ref) { expr->call_expr.is_type_method = struct_var != NULL; if (decl == NULL) { return sema_expr_analyse_var_call(context, expr, - type_flatten(exprptr(expr->call_expr.function)->type), optional); + type_flatten(exprptr(expr->call_expr.function)->type), optional, + no_match_ref); } switch (decl->decl_kind) { case DECL_MACRO: expr->call_expr.func_ref = declid(decl); expr->call_expr.is_func_ref = true; - return sema_expr_analyse_macro_call(context, expr, struct_var, decl, optional); + return sema_expr_analyse_macro_call(context, expr, struct_var, decl, optional, no_match_ref); case DECL_VAR: assert(struct_var == NULL); - return sema_expr_analyse_var_call(context, expr, decl->type->canonical, optional || IS_OPTIONAL(decl)); + return sema_expr_analyse_var_call(context, expr, decl->type->canonical, optional || IS_OPTIONAL(decl), + no_match_ref); case DECL_FUNC: expr->call_expr.func_ref = declid(decl); expr->call_expr.is_func_ref = true; - return sema_expr_analyse_func_call(context, expr, decl, struct_var, optional); + return sema_expr_analyse_func_call(context, expr, decl, struct_var, optional, no_match_ref); case DECL_POISONED: return false; default: - SEMA_ERROR(expr, "This expression cannot be called."); - return false; + if (no_match_ref) + { + *no_match_ref = true; + return false; + } + RETURN_SEMA_ERROR(expr, "This expression cannot be called."); } } @@ -2218,8 +2240,9 @@ bool sema_expr_analyse_general_call(SemaContext *context, Expr *expr, Decl *decl -static inline bool sema_expr_analyse_call(SemaContext *context, Expr *expr) +static inline bool sema_expr_analyse_call(SemaContext *context, Expr *expr, bool *no_match_ref) { + if (no_match_ref) *no_match_ref = true; Expr *func_expr = exprptr(expr->call_expr.function); if (!sema_analyse_expr_lvalue_fold_const(context, func_expr)) return false; @@ -2278,13 +2301,16 @@ static inline bool sema_expr_analyse_call(SemaContext *context, Expr *expr) decl = NULL; break; } - SEMA_ERROR(expr, "This value cannot be invoked, did you accidentally add ()?"); - return false; - + if (no_match_ref) + { + *no_match_ref = true; + return false; + } + RETURN_SEMA_ERROR(expr, "This value cannot be invoked, did you accidentally add ()?"); } } decl = decl ? decl_flatten(decl) : NULL; - return sema_expr_analyse_general_call(context, expr, decl, struct_var, optional); + return sema_expr_analyse_general_call(context, expr, decl, struct_var, optional, no_match_ref); } static bool sema_slice_len_is_in_range(SemaContext *context, Type *type, Expr *len_expr, bool from_end, bool *remove_from_end) @@ -2452,14 +2478,16 @@ static Expr *sema_expr_find_index_type_or_overload_for_subscript(SemaContext *co Decl *overload = NULL; switch (eval_type) { + case SUBSCRIPT_EVAL_VALID: + UNREACHABLE case SUBSCRIPT_EVAL_REF: - overload = sema_find_operator(context, current_expr, OVERLOAD_ELEMENT_REF); + overload = sema_find_operator(context, current_expr->type, OVERLOAD_ELEMENT_REF); break; case SUBSCRIPT_EVAL_VALUE: - overload = sema_find_operator(context, current_expr, OVERLOAD_ELEMENT_AT); + overload = sema_find_operator(context, current_expr->type, OVERLOAD_ELEMENT_AT); break; case SUBSCRIPT_EVAL_ASSIGN: - overload = sema_find_operator(context, current_expr, OVERLOAD_ELEMENT_SET); + overload = sema_find_operator(context, current_expr->type, OVERLOAD_ELEMENT_SET); if (overload) { *overload_ptr = overload; @@ -2497,6 +2525,9 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, { assert(expr->expr_kind == EXPR_SUBSCRIPT || expr->expr_kind == EXPR_SUBSCRIPT_ADDR); + bool is_checking = eval_type == SUBSCRIPT_EVAL_VALID; + if (is_checking) eval_type = SUBSCRIPT_EVAL_VALUE; + // 1. Evaluate the expression to index. Expr *subscripted = exprptr(expr->subscript_expr.expr); if (!sema_analyse_expr_lvalue_fold_const(context, subscripted)) return false; @@ -2516,8 +2547,9 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, bool start_from_end = expr->subscript_expr.range.start_from_end; if (start_from_end && (underlying_type->type_kind == TYPE_POINTER || underlying_type->type_kind == TYPE_FLEXIBLE_ARRAY)) { - SEMA_ERROR(index, "Indexing from the end is not allowed for pointers and flexible array members."); - return false; + if (is_checking) goto VALID_FAIL_POISON; + RETURN_SEMA_ERROR(index, "Indexing from the end is not allowed for pointers " + "and flexible array members."); } int64_t size; if (expr_is_const_int(index) && (size = expr_get_index_max(subscripted)) >= 0) @@ -2525,13 +2557,13 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, // 4c. And that it's in range. if (int_is_neg(index->const_expr.ixx)) { - SEMA_ERROR(index, "The index may not be negative."); - return false; + if (is_checking) goto VALID_FAIL_POISON; + RETURN_SEMA_ERROR(index, "The index may not be negative."); } if (!int_fits(index->const_expr.ixx, TYPE_I64) || size == 0) { - SEMA_ERROR(index, "The index is out of range.", size); - return false; + if (is_checking) goto VALID_FAIL_POISON; + RETURN_SEMA_ERROR(index, "The index is out of range.", size); } index_value = int_to_i64(index->const_expr.ixx); if (start_from_end) @@ -2540,6 +2572,7 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, } if (index_value < 0 || index_value >= size) { + if (is_checking) goto VALID_FAIL_POISON; if (start_from_end) { SEMA_ERROR(index, @@ -2566,8 +2599,7 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, { if (eval_type == SUBSCRIPT_EVAL_REF) { - SEMA_ERROR(subscripted, "You need to use && to take the address of a temporary."); - return false; + RETURN_SEMA_ERROR(subscripted, "You need to use && to take the address of a temporary."); } // 4a. This may either be an initializer list or a CT value Expr *current_expr = subscripted; @@ -2576,8 +2608,7 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, // 4b. Now we need to check that we actually have a valid type. if (index_value < 0) { - SEMA_ERROR(index, "To subscript an untyped list a compile time integer index is needed."); - return false; + RETURN_SEMA_ERROR(index, "To subscript an untyped list a compile time integer index is needed."); } if (eval_type == SUBSCRIPT_EVAL_ASSIGN) TODO; expr_replace(expr, current_expr->const_expr.untyped_list[index_value]); @@ -2605,16 +2636,17 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, return false; } } - SEMA_ERROR(subscripted, "Cannot index '%s'.", type_to_error_string(subscripted->type)); - return false; + if (is_checking) goto VALID_FAIL_POISON; + RETURN_SEMA_ERROR(subscripted, "Cannot index '%s'.", type_to_error_string(subscripted->type)); } if (overload) { if (start_from_end) { - Decl *len = sema_find_operator(context, current_expr, OVERLOAD_LEN); + Decl *len = sema_find_operator(context, current_expr->type, OVERLOAD_LEN); if (!len) { + if (is_checking) goto VALID_FAIL_POISON; SEMA_ERROR(subscripted, "Cannot index '%s' from the end, since there is no 'len' overload.", type_to_error_string(subscripted->type)); return false; } @@ -2676,6 +2708,7 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, { if (!int_fits(index->const_expr.ixx, TYPE_U32)) { + if (is_checking) goto VALID_FAIL_POISON; RETURN_SEMA_ERROR(index, "Index is out of range."); } ArraySize idx = index->const_expr.ixx.i.low; @@ -2685,6 +2718,7 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, ArraySize len = current_expr->const_expr.bytes.len; if (idx > len || (idx == len && !start_from_end) || (idx == 0 && start_from_end)) { + if (is_checking) goto VALID_FAIL_POISON; RETURN_SEMA_ERROR(index, "The index (%s%llu) is out of range, the length is just %llu.", start_from_end ? "^" : "", (unsigned long long)idx, @@ -2703,6 +2737,9 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, expr->subscript_expr.expr = exprid(current_expr); expr->type = type_add_optional(index_type, optional); return true; +VALID_FAIL_POISON: + expr_poison(expr); + return true; } static inline bool sema_expr_analyse_pointer_offset(SemaContext *context, Expr *expr) @@ -2977,7 +3014,8 @@ static inline bool sema_expr_replace_with_enum_name_array(SemaContext *context, return sema_analyse_expr(context, enum_array_expr); } -static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *expr, Type *parent_type, bool was_group, Expr *identifier) +static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *expr, Type *parent_type, bool was_group, + Expr *identifier, bool *missing_ref) { assert(identifier->expr_kind == EXPR_IDENTIFIER); Type *canonical = parent_type->canonical; @@ -2994,6 +3032,7 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp } if (!type_may_have_sub_elements(canonical)) { + if (missing_ref) goto MISSING_REF; SEMA_ERROR(expr, "'%s' does not have a property '%s'.", type_to_error_string(parent_type), name); return false; } @@ -3008,6 +3047,7 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp { if (!sema_expr_analyse_enum_constant(context, expr, name, decl)) { + if (missing_ref) goto MISSING_REF; SEMA_ERROR(expr, "'%s' has no enumeration value '%s'.", decl->name, name); return false; } @@ -3020,6 +3060,7 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp { if (!sema_expr_analyse_enum_constant(context, expr, name, decl)) { + if (missing_ref) goto MISSING_REF; SEMA_ERROR(expr, "'%s' has no error value '%s'.", decl->name, name); return false; } @@ -3043,20 +3084,21 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp member = sema_resolve_type_method(context->unit, decl->type, name, &ambiguous, &private); if (private) { + if (missing_ref) goto MISSING_REF; SEMA_ERROR(expr, "The method '%s' has private visibility.", name); return false; } if (ambiguous) { - SEMA_ERROR(expr, "'%s' is an ambiguous name and so cannot be resolved, it may refer to method defined in '%s' or one in '%s'", + RETURN_SEMA_ERROR(expr, "'%s' is an ambiguous name and so cannot be resolved, " + "it may refer to method defined in '%s' or one in '%s'", name, member->unit->module->name->module, ambiguous->unit->module->name->module); - return false; } } if (!member) { - SEMA_ERROR(expr, "No method or inner struct/union '%s.%s' found.", type_to_error_string(decl->type), name); - return false; + if (missing_ref) goto MISSING_REF; + RETURN_SEMA_ERROR(expr, "No method or inner struct/union '%s.%s' found.", type_to_error_string(decl->type), name); } if (member->decl_kind == DECL_VAR || member->decl_kind == DECL_UNION || member->decl_kind == DECL_STRUCT || member->decl_kind == DECL_BITSTRUCT) @@ -3077,9 +3119,14 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp expr->expr_kind = EXPR_IDENTIFIER; expr_resolve_ident(expr, member); return true; +MISSING_REF: + *missing_ref = true; + return false; } -static inline bool sema_expr_analyse_member_access(SemaContext *context, Expr *expr, Expr *parent, bool was_group, Expr *identifier) +static inline bool sema_expr_analyse_member_access(SemaContext *context, Expr *expr, + Expr *parent, bool was_group, Expr *identifier, + bool *missing_ref) { assert(identifier->expr_kind == EXPR_IDENTIFIER); @@ -3089,8 +3136,8 @@ static inline bool sema_expr_analyse_member_access(SemaContext *context, Expr *e if (is_const) { - SEMA_ERROR(expr, "There is no member '%s' for %s.", name, type_to_error_string(decl->type)); - return false; + if (missing_ref) goto MISSING_REF; + RETURN_SEMA_ERROR(expr, "There is no member '%s' for %s.", name, type_to_error_string(decl->type)); } if (name == kw_offsetof) @@ -3135,6 +3182,8 @@ static inline bool sema_expr_analyse_member_access(SemaContext *context, Expr *e case TYPE_PROPERTY_VALUES: case TYPE_PROPERTY_ASSOCIATED: case TYPE_PROPERTY_PARENTOF: + case TYPE_PROPERTY_IS_EQ: + case TYPE_PROPERTY_IS_ORDERED: break; } @@ -3142,16 +3191,16 @@ static inline bool sema_expr_analyse_member_access(SemaContext *context, Expr *e if (!type_is_union_or_strukt(underlying_type) && underlying_type->type_kind != TYPE_BITSTRUCT) { - SEMA_ERROR(parent, "No member or property '%s' was found.", name); - return false; + if (missing_ref) goto MISSING_REF; + RETURN_SEMA_ERROR(parent, "No member or property '%s' was found.", name); } Decl *underlying_type_decl = underlying_type->decl; Decl *member = sema_decl_stack_find_decl_member(underlying_type_decl, name); if (!member || !(decl_is_struct_type(member) || member->decl_kind == DECL_VAR || member->decl_kind == DECL_BITSTRUCT)) { - SEMA_ERROR(expr, "No member '%s' found.", name); - return false; + if (missing_ref) goto MISSING_REF; + RETURN_SEMA_ERROR(expr, "No member '%s' found.", name); } expr->expr_kind = EXPR_CONST; @@ -3163,6 +3212,9 @@ static inline bool sema_expr_analyse_member_access(SemaContext *context, Expr *e }; expr->type = type_member; return true; +MISSING_REF: + *missing_ref = true; + return false; } static inline void sema_expr_rewrite_typeid_kind(Expr *expr, Expr *parent) @@ -3528,6 +3580,8 @@ static bool sema_expr_rewrite_to_typeid_property(SemaContext *context, Expr *exp case TYPE_PROPERTY_NAMEOF: case TYPE_PROPERTY_QNAMEOF: case TYPE_PROPERTY_ASSOCIATED: + case TYPE_PROPERTY_IS_EQ: + case TYPE_PROPERTY_IS_ORDERED: // Not supported by dynamic typeid case TYPE_PROPERTY_NONE: return false; @@ -3592,6 +3646,7 @@ EVAL: return true; } + static bool sema_type_property_is_valid_for_type(Type *original_type, TypeProperty property) { Type *type = type_flatten(original_type); @@ -3626,6 +3681,8 @@ static bool sema_type_property_is_valid_for_type(Type *original_type, TypeProper case TYPE_PROPERTY_NAMEOF: case TYPE_PROPERTY_QNAMEOF: case TYPE_PROPERTY_PARENTOF: + case TYPE_PROPERTY_IS_ORDERED: + case TYPE_PROPERTY_IS_EQ: return true; case TYPE_PROPERTY_LEN: switch (type->type_kind) @@ -3682,6 +3739,12 @@ static bool sema_expr_rewrite_to_type_property(SemaContext *context, Expr *expr, expr->type = type; expr->resolve_status = RESOLVE_DONE; return true; + case TYPE_PROPERTY_IS_ORDERED: + expr_rewrite_const_bool(expr, type_bool, type_is_ordered(flat)); + return true; + case TYPE_PROPERTY_IS_EQ: + expr_rewrite_const_bool(expr, type_bool, type_is_comparable(flat)); + return true; case TYPE_PROPERTY_INNER: return sema_create_const_inner(context, expr, type); case TYPE_PROPERTY_PARENTOF: @@ -3843,10 +3906,11 @@ static bool sema_flattened_expr_is_const_initializer(SemaContext *context, Expr /** * Analyse "x.y" */ -static inline bool sema_expr_analyse_access(SemaContext *context, Expr *expr) +static inline bool sema_expr_analyse_access(SemaContext *context, Expr *expr, bool *missing_ref) { Expr *parent = expr->access_expr.parent; bool was_group = parent->expr_kind == EXPR_GROUP; + if (missing_ref) *missing_ref = false; // 1. Resolve the left hand if (!sema_analyse_expr_lvalue_fold_const(context, parent)) return false; @@ -3895,21 +3959,21 @@ static inline bool sema_expr_analyse_access(SemaContext *context, Expr *expr) // 3. Find the actual token. SourceSpan span; - Expr *identifier = sema_expr_resolve_access_child(context, child, NULL); + Expr *identifier = sema_expr_resolve_access_child(context, child, missing_ref); if (!identifier) return false; // 2. If our left-hand side is a type, e.g. MyInt.abc, handle this here. if (parent->expr_kind == EXPR_TYPEINFO) { - return sema_expr_analyse_type_access(context, expr, parent->type_expr->type, was_group, identifier); + return sema_expr_analyse_type_access(context, expr, parent->type_expr->type, was_group, identifier, missing_ref); } if (parent->expr_kind == EXPR_IDENTIFIER && parent->type->type_kind == TYPE_FUNC) { - return sema_expr_analyse_type_access(context, expr, parent->type, was_group, identifier); + return sema_expr_analyse_type_access(context, expr, parent->type, was_group, identifier, missing_ref); } if (expr_is_const_member(parent)) { - return sema_expr_analyse_member_access(context, expr, parent, was_group, identifier); + return sema_expr_analyse_member_access(context, expr, parent, was_group, identifier, missing_ref); } // 6. Copy failability @@ -3991,6 +4055,7 @@ CHECK_DEEPER: char c = kw[i]; if (c < 'w' || c > 'z') goto NOT_SWIZZLE; } + // TODO should we do a missing for this as well? return sema_expr_analyse_swizzle(context, expr, parent, flat_type, kw, len); NOT_SWIZZLE:; } @@ -4073,6 +4138,7 @@ CHECK_DEEPER: Decl *method = sema_resolve_type_method(context->unit, actual, kw, &ambiguous, &private); if (private) { + if (missing_ref) goto MISSING_REF; RETURN_SEMA_ERROR(expr, "The method '%s' has private visibility.", kw); } if (ambiguous) @@ -4083,6 +4149,7 @@ CHECK_DEEPER: } if (!method) { + if (missing_ref) goto MISSING_REF; RETURN_SEMA_ERROR(expr, "There is no member or method '%s' on '%s'", kw, type_to_error_string(actual)); } expr->access_expr.parent = current_parent; @@ -4155,15 +4222,16 @@ CHECK_DEEPER: // 11b. Otherwise we give up. if (private) { - SEMA_ERROR(expr, "The method '%s' has private visibility.", kw); - return false; + if (missing_ref) goto MISSING_REF; + RETURN_SEMA_ERROR(expr, "The method '%s' has private visibility.", kw); } if (parent->type->canonical->type_kind == TYPE_INFPTR) { + if (missing_ref) goto MISSING_REF; RETURN_SEMA_ERROR(expr, "The '%s' interface has no method '%s', did you spell it correctly?", parent->type->canonical->pointer->canonical->name, kw); } + if (missing_ref) goto MISSING_REF; RETURN_SEMA_ERROR(expr, "There is no field or method '%s.%s'.", type_to_error_string(parent->type), kw); - return false; } assert(member->type); @@ -4185,6 +4253,9 @@ CHECK_DEEPER: expr->type = type_add_optional(member->type, optional); expr->access_expr.ref = member; return true; +MISSING_REF: + *missing_ref = true; + return false; } static Expr **sema_vasplat_append(SemaContext *c, Expr **init_expressions, Expr *expr) @@ -4333,7 +4404,7 @@ INLINE Expr **sema_expand_vasplat(SemaContext *c, Expr **list, unsigned index) Expr **sema_expand_vasplat_exprs(SemaContext *c, Expr **exprs) { - if (!c->current_macro) return exprs; + if (!c || !c->current_macro) return exprs; unsigned count = vec_size(exprs); bool expand; @@ -4375,8 +4446,9 @@ static inline bool sema_expr_analyse_expr_list(SemaContext *context, Expr *expr) } -static inline bool sema_expr_analyse_cast(SemaContext *context, Expr *expr) +static inline bool sema_expr_analyse_cast(SemaContext *context, Expr *expr, bool *invalid_cast_ref) { + if (invalid_cast_ref) *invalid_cast_ref = false; Expr *inner = exprptr(expr->cast_expr.expr); TypeInfo *type_info = type_infoptr(expr->cast_expr.type_info); bool success = sema_resolve_type_info(context, type_info, RESOLVE_TYPE_DEFAULT); @@ -4385,11 +4457,21 @@ static inline bool sema_expr_analyse_cast(SemaContext *context, Expr *expr) Type *target_type = type_info->type; if (type_is_optional(target_type)) { - SEMA_ERROR(type_info, "Casting to an optional type is not allowed."); - return false; + RETURN_SEMA_ERROR(type_info, "Casting to an optional type is not allowed."); } - if (!cast_explicit(context, inner, target_type)) return expr_poison(expr); + if (invalid_cast_ref) + { + if (!cast_explicit_silent(context, inner, target_type)) + { + *invalid_cast_ref = true; + return false; + } + } + else + { + if (!cast_explicit(context, inner, target_type)) return expr_poison(expr); + } expr_replace(expr, inner); return true; } @@ -4436,16 +4518,11 @@ static bool sema_expr_analyse_slice_assign(SemaContext *context, Expr *expr, Typ } // By default we need to make a silent attempt. - bool suppress = global_context.suppress_errors; - global_context.suppress_errors = true; - Expr *right_copy = expr_copy(right); - bool could_cast = cast_implicit(context, right_copy, base); - global_context.suppress_errors = suppress; + bool could_cast = cast_implicit_silent(context, right, base); // Failed, so let's go back to the original if (!could_cast) goto SLICE_COPY; // Use the copy - right = right_copy; SLICE_ASSIGN: expr->expr_kind = EXPR_SLICE_ASSIGN; @@ -4906,17 +4983,17 @@ static bool sema_binary_arithmetic_promotion(SemaContext *context, Expr *left, E cast_implicit(context, right, max); } -static void sema_binary_unify_voidptr(Expr *left, Expr *right, Type **left_type_ref, Type **right_type_ref) +static void sema_binary_unify_voidptr(SemaContext *context, Expr *left, Expr *right, Type **left_type_ref, Type **right_type_ref) { if (*left_type_ref == *right_type_ref) return; if (*left_type_ref == type_voidptr) { - cast_no_check(left, *right_type_ref, IS_OPTIONAL(left)); + cast_no_check(context, left, *right_type_ref, IS_OPTIONAL(left)); *left_type_ref = *right_type_ref; } if (*right_type_ref == type_voidptr) { - cast_no_check(right, *left_type_ref, IS_OPTIONAL(right)); + cast_no_check(context, right, *left_type_ref, IS_OPTIONAL(right)); *right_type_ref = *left_type_ref; } } @@ -5043,7 +5120,7 @@ static bool sema_expr_analyse_sub(SemaContext *context, Expr *expr, Expr *left, right_type = type_no_optional(right->type)->canonical; // 3a. Require that both types are the same. - sema_binary_unify_voidptr(left, right, &left_type, &right_type); + sema_binary_unify_voidptr(context, left, right, &left_type, &right_type); if (left_type != right_type) { SEMA_ERROR(expr, "'%s' - '%s' is not allowed. Subtracting pointers of different types is not allowed.", type_to_error_string(left_type), type_to_error_string(right_type)); @@ -5740,7 +5817,7 @@ static bool sema_expr_analyse_comp(SemaContext *context, Expr *expr, Expr *left, || (type_is_signed(left_type) && type_is_unsigned(right_type))) { // 2a. Resize so that both sides have the same bit width. This will always work. - cast_to_int_to_max_bit_size(left, right, left_type, right_type); + cast_to_int_to_max_bit_size(context, left, right, left_type, right_type); goto DONE; } @@ -5841,7 +5918,7 @@ DONE: * Analyse *a * @return true if analysis succeeds. */ -static inline bool sema_expr_analyse_deref(SemaContext *context, Expr *expr) +static inline bool sema_expr_analyse_deref(SemaContext *context, Expr *expr, bool *failed_ref) { // 1. Check the inner expression Expr *inner = expr->unary_expr.expr; @@ -5853,20 +5930,21 @@ static inline bool sema_expr_analyse_deref(SemaContext *context, Expr *expr) // 2. Check that we have a pointer, or dereference is not allowed. if (canonical->type_kind != TYPE_POINTER) { - SEMA_ERROR(inner, "Cannot dereference a value of type %s, it must be a pointer.", type_quoted_error_string(inner_type_nofail)); - return false; + if (failed_ref) goto ON_FAILED; + RETURN_SEMA_ERROR(inner, "Cannot dereference a value of type %s, it must be a pointer.", + type_quoted_error_string(inner_type_nofail)); } if (canonical->pointer == type_void) { - SEMA_ERROR(inner, "A 'void*' cannot be dereferenced, you need to first cast it to a concrete type."); - return false; + if (failed_ref) goto ON_FAILED; + RETURN_SEMA_ERROR(inner, "A 'void*' cannot be dereferenced, you need to first cast it to a concrete type."); } // 3. This could be a constant, in which case it is a null which is an error. if (expr_is_const(inner)) { - SEMA_ERROR(inner, "Dereferencing null is not allowed, did you do it by mistake?"); - return false; + if (failed_ref) goto ON_FAILED; + RETURN_SEMA_ERROR(inner, "Dereferencing null is not allowed, did you do it by mistake?"); } // 4. Now the type might not be a pointer because of a typedef, @@ -5877,6 +5955,9 @@ static inline bool sema_expr_analyse_deref(SemaContext *context, Expr *expr) expr->type = type_add_optional(deref_type->pointer, IS_OPTIONAL(inner)); return true; +ON_FAILED: + *failed_ref = true; + return false; } static inline const char *sema_addr_may_take_of_var(Expr *expr, Decl *decl) @@ -5987,7 +6068,7 @@ static const char *sema_addr_check_may_take(Expr *inner) * Analyse &a * @return true if analysis succeeds. */ -static inline bool sema_expr_analyse_addr(SemaContext *context, Expr *expr) +static inline bool sema_expr_analyse_addr(SemaContext *context, Expr *expr, bool *failed_ref) { // 1. Evaluate the expression Expr *inner = expr->unary_expr.expr; @@ -6021,6 +6102,11 @@ static inline bool sema_expr_analyse_addr(SemaContext *context, Expr *expr) const char *error = sema_addr_check_may_take(inner); if (error) { + if (failed_ref) + { + *failed_ref = true; + return false; + } SEMA_ERROR(inner, error); return expr_poison(expr); } @@ -6263,7 +6349,7 @@ static inline bool sema_expr_analyse_incdec(SemaContext *context, Expr *expr) /** * Take an address of a temporary &&x. */ -static inline bool sema_expr_analyse_taddr(SemaContext *context, Expr *expr) +static inline bool sema_expr_analyse_taddr(SemaContext *context, Expr *expr, bool *failed_ref) { Expr *inner = expr->unary_expr.expr; if (!sema_analyse_expr(context, inner)) return false; @@ -6271,9 +6357,13 @@ static inline bool sema_expr_analyse_taddr(SemaContext *context, Expr *expr) Type *type = inner->type; if (type_is_invalid_storage_type(type) || type == type_void) { - SEMA_ERROR(expr, "It is not possible to take the address from a value of type %s.", - type_quoted_error_string(type)); - return false; + if (failed_ref) + { + *failed_ref = true; + return false; + } + RETURN_SEMA_ERROR(expr, "It is not possible to take the address from a value of type %s.", + type_quoted_error_string(type)); } // 2. The type is the resulting type of the expression. expr->type = type_get_ptr_recurse(inner->type); @@ -6463,15 +6553,16 @@ static inline bool sema_expr_analyse_binary(SemaContext *context, Expr *expr) * &&x * -x */ -static inline bool sema_expr_analyse_unary(SemaContext *context, Expr *expr) +static inline bool sema_expr_analyse_unary(SemaContext *context, Expr *expr, bool *failed_ref) { + if (failed_ref) *failed_ref = false; assert(expr->resolve_status == RESOLVE_RUNNING); switch (expr->unary_expr.operator) { case UNARYOP_DEREF: - return sema_expr_analyse_deref(context, expr); + return sema_expr_analyse_deref(context, expr, failed_ref); case UNARYOP_ADDR: - return sema_expr_analyse_addr(context, expr); + return sema_expr_analyse_addr(context, expr, failed_ref); case UNARYOP_NEG: case UNARYOP_PLUS: return sema_expr_analyse_neg_plus(context, expr); @@ -6483,7 +6574,7 @@ static inline bool sema_expr_analyse_unary(SemaContext *context, Expr *expr) case UNARYOP_INC: return sema_expr_analyse_incdec(context, expr); case UNARYOP_TADDR: - return sema_expr_analyse_taddr(context, expr); + return sema_expr_analyse_taddr(context, expr, failed_ref); case UNARYOP_ERROR: return false; } @@ -6493,7 +6584,7 @@ static inline bool sema_expr_analyse_unary(SemaContext *context, Expr *expr) static inline bool sema_expr_analyse_rethrow(SemaContext *context, Expr *expr) { - if (context->call_env.kind != CALL_ENV_FUNCTION && context->call_env.kind != CALL_ENV_CHECKS) + if (context->call_env.kind != CALL_ENV_FUNCTION) { RETURN_SEMA_ERROR(expr, "Rethrow cannot be used outside of a function."); } @@ -6629,33 +6720,38 @@ static inline bool sema_expr_analyse_expr_block(SemaContext *context, Type *infe -static inline bool sema_expr_analyse_optional(SemaContext *context, Expr *expr) +static inline bool sema_expr_analyse_optional(SemaContext *context, Expr *expr, bool *failed_ref) { Expr *inner = expr->inner_expr; + if (failed_ref) *failed_ref = false; if (!sema_analyse_expr(context, inner)) return false; if (IS_OPTIONAL(inner)) { - SEMA_ERROR(inner, "The inner expression is already an optional."); - return false; + if (failed_ref) goto ON_FAILED; + RETURN_SEMA_ERROR(inner, "The inner expression is already an optional."); } if (inner->expr_kind == EXPR_OPTIONAL) { - SEMA_ERROR(inner, "It looks like you added one too many '!' after the error."); - return false; + if (failed_ref) goto ON_FAILED; + RETURN_SEMA_ERROR(inner, "It looks like you added one too many '?' after the error."); } Type *type = inner->type->canonical; if (type->type_kind != TYPE_FAULTTYPE && type->type_kind != TYPE_ANYFAULT) { - SEMA_ERROR(inner, "You cannot use the '!' operator on expressions of type %s", type_quoted_error_string(type)); - return false; + if (failed_ref) goto ON_FAILED; + RETURN_SEMA_ERROR(inner, "You cannot use the '?' operator on expressions of type %s", + type_quoted_error_string(type)); } assert(type->type_kind == TYPE_ANYFAULT || type->decl->resolve_status == RESOLVE_DONE); expr->type = type_wildcard_optional; return true; +ON_FAILED: + *failed_ref = true; + return false; } static inline bool sema_expr_analyse_compiler_const(SemaContext *context, Expr *expr, bool report_missing) @@ -6704,9 +6800,6 @@ static inline bool sema_expr_analyse_compiler_const(SemaContext *context, Expr * switch (context->call_env.kind) { case CALL_ENV_GLOBAL_INIT: - case CALL_ENV_CHECKS: - case CALL_ENV_INITIALIZER: - case CALL_ENV_FINALIZER: case CALL_ENV_ATTR: if (report_missing) { @@ -6725,9 +6818,6 @@ static inline bool sema_expr_analyse_compiler_const(SemaContext *context, Expr * case CALL_ENV_GLOBAL_INIT: expr_rewrite_to_string(expr, ""); return true; - case CALL_ENV_CHECKS: - expr_rewrite_to_string(expr, ""); - return true; case CALL_ENV_FUNCTION: { Decl *current_func = context->call_env.current_function; @@ -6744,12 +6834,6 @@ static inline bool sema_expr_analyse_compiler_const(SemaContext *context, Expr * expr_rewrite_to_string(expr, current_func->name); return true; } - case CALL_ENV_INITIALIZER: - expr_rewrite_to_string(expr, ""); - return true; - case CALL_ENV_FINALIZER: - expr_rewrite_to_string(expr, ""); - return true; case CALL_ENV_ATTR: expr_rewrite_to_string(expr, ""); return true; @@ -7164,8 +7248,14 @@ RETRY: return decl->type->canonical; } case TYPE_INFO_VATYPE: + { + if (!context->current_macro) return NULL; + Expr *arg_expr = sema_expr_analyse_ct_arg_index(context, type_info->unresolved_type_expr, NULL, false); + if (!expr_ok(arg_expr)) return NULL; + if (arg_expr->expr_kind != EXPR_TYPEINFO) return NULL; if (!sema_resolve_type_info(context, type_info, RESOLVE_TYPE_DEFAULT)) return poisoned_type; return type_info->type->canonical; + } case TYPE_INFO_TYPEFROM: case TYPE_INFO_TYPEOF: if (!sema_resolve_type_info(context, type_info, RESOLVE_TYPE_DEFAULT)) return poisoned_type; @@ -7219,35 +7309,6 @@ RETRY: UNREACHABLE } -static inline Expr *sema_ct_checks_exprlist_compiles(SemaContext *context, Expr *exprlist) -{ - assert(exprlist->expr_kind == EXPR_EXPRESSION_LIST); - Expr *failed = NULL; - bool suppress_error = global_context.suppress_errors; - global_context.suppress_errors = true; - CallEnvKind eval_kind = context->call_env.kind; - context->call_env.kind = CALL_ENV_CHECKS; - SCOPE_START_WITH_FLAGS(SCOPE_CHECKS); - FOREACH_BEGIN(Expr *expr, exprlist->expression_list) - if (!sema_analyse_expr(context, expr)) - { - failed = expr; - break; - } - FOREACH_END(); - SCOPE_END; - context->call_env.kind = eval_kind; - global_context.suppress_errors = suppress_error; - return failed; -} - -static inline bool sema_expr_analyse_ct_checks(SemaContext *context, Expr *expr) -{ - Expr *err = sema_ct_checks_exprlist_compiles(context, expr->inner_expr); - expr_rewrite_const_bool(expr, type_bool, err == NULL); - return true; -} - static inline bool sema_may_reuse_lambda(SemaContext *context, Decl *lambda, Type **types) { @@ -7484,10 +7545,6 @@ static inline bool sema_expr_analyse_lambda(SemaContext *context, Type *target_t scratch_buffer_clear(); switch (context->call_env.kind) { - case CALL_ENV_CHECKS: - scratch_buffer_append(unit->module->name->module); - scratch_buffer_append(".$checks"); - break; case CALL_ENV_GLOBAL_INIT: scratch_buffer_append(unit->module->name->module); scratch_buffer_append(".$global"); @@ -7504,14 +7561,6 @@ static inline bool sema_expr_analyse_lambda(SemaContext *context, Type *target_t scratch_buffer_append(decl_get_extname(context->call_env.current_function)); } break; - case CALL_ENV_INITIALIZER: - scratch_buffer_append(unit->module->name->module); - scratch_buffer_append(".$initializer"); - break; - case CALL_ENV_FINALIZER: - scratch_buffer_append(unit->module->name->module); - scratch_buffer_append(".$finalizer"); - break; case CALL_ENV_ATTR: scratch_buffer_append(unit->module->name->module); scratch_buffer_append(".$attr"); @@ -7579,6 +7628,15 @@ ERROR: RETURN_SEMA_ERROR(inner, "Expected a feature name here, e.g. $feature(MY_FEATURE)."); } +static inline bool sema_expr_analyse_ct_is_const(SemaContext *context, Expr *expr) +{ + assert(expr->resolve_status == RESOLVE_RUNNING); + Expr *inner = expr->inner_expr; + if (!sema_analyse_expr(context, inner)) return false; + expr_rewrite_const_bool(expr, type_bool, expr_is_constant_eval(inner, CONSTANT_EVAL_CONSTANT_VALUE)); + return true; +} + static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr) { if (expr->resolve_status == RESOLVE_DONE) return expr_ok(expr); @@ -7586,27 +7644,18 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr Expr *inner = expr->inner_expr; bool success = true; + bool failed = false; Expr *main = inner; RETRY:; - switch (inner->expr_kind) + switch (main->expr_kind) { case EXPR_ACCESS: - { - // Resolve the parent normally. - Expr *parent = main->access_expr.parent; - if (!sema_analyse_expr_lvalue_fold_const(context, parent)) return false; - bool suppress_error = global_context.suppress_errors; - global_context.suppress_errors = true; - CallEnvKind eval_kind = context->call_env.kind; - context->call_env.kind = CALL_ENV_CHECKS; - // Resolve the access using "checks" scope. - SCOPE_START_WITH_FLAGS(SCOPE_CHECKS); - success = sema_analyse_expr_lvalue(context, main); - SCOPE_END; - context->call_env.kind = eval_kind; - global_context.suppress_errors = suppress_error; + if (!sema_expr_analyse_access(context, main, &failed)) + { + if (!failed) return false; + success = false; + } break; - } case EXPR_IDENTIFIER: { Decl *decl = sema_find_path_symbol(context, main->identifier_expr.ident, main->identifier_expr.path); @@ -7620,6 +7669,14 @@ RETRY:; case EXPR_BUILTIN: success = sema_expr_analyse_builtin(context, main, false); break; + case EXPR_UNARY: + main->resolve_status = RESOLVE_RUNNING; + if (!sema_expr_analyse_unary(context, main, &failed)) + { + if (!failed) return false; + success = false; + } + break; case EXPR_TYPEINFO: { Type *type = sema_expr_check_type_exists(context, main->type_expr); @@ -7638,6 +7695,25 @@ RETRY:; main = copy_expr_single(decl->var.init_expr); goto RETRY; } + case EXPR_SUBSCRIPT: + { + if (!sema_expr_analyse_subscript(context, main, SUBSCRIPT_EVAL_VALID)) + { + return false; + } + if (!expr_ok(main)) + { + success = false; + } + break; + } + case EXPR_CAST: + if (!sema_expr_analyse_cast(context, main, &failed)) + { + if (!failed) return false; + success = false; + } + break; case EXPR_CT_IDENT: { Decl *decl = sema_resolve_symbol(context, main->ct_ident_expr.identifier, NULL, main->span); @@ -7645,8 +7721,87 @@ RETRY:; success = decl != NULL; break; } - default: - RETURN_SEMA_ERROR(inner, "Expected an identifier or a field name."); + case EXPR_CALL: + { + bool no_match; + if (!sema_expr_analyse_call(context, main, &no_match)) + { + if (!no_match) return false; + success = false; + } + break; + } + case EXPR_FORCE_UNWRAP: + if (!sema_analyse_expr(context, main->inner_expr)) return false; + success = IS_OPTIONAL(main->inner_expr); + break; + case EXPR_RETHROW: + if (!sema_analyse_expr(context, main->rethrow_expr.inner)) return false; + success = IS_OPTIONAL(main->rethrow_expr.inner); + break; + case EXPR_OPTIONAL: + if (!sema_expr_analyse_optional(context, main, &failed)) + { + if (!failed) return false; + success = false; + } + break; + case EXPR_POISONED: + success = false; + break; + case EXPR_COND: + case EXPR_TEST_HOOK: + case EXPR_CATCH_UNWRAP: + case EXPR_DESIGNATOR: + case EXPR_BENCHMARK_HOOK: + case EXPR_TRY_UNWRAP: + case EXPR_TRY_UNWRAP_CHAIN: + case EXPR_ANYSWITCH: + case EXPR_OPERATOR_CHARS: + case EXPR_MACRO_BODY_EXPANSION: + case EXPR_BUILTIN_ACCESS: + UNREACHABLE + case EXPR_BINARY: + case EXPR_BITACCESS: + case EXPR_BITASSIGN: + case EXPR_COMPOUND_LITERAL: + case EXPR_DECL: + case EXPR_EMBED: + case EXPR_GENERIC_IDENT: + case EXPR_POINTER_OFFSET: + case EXPR_RETVAL: + case EXPR_SLICE: + case EXPR_SLICE_ASSIGN: + case EXPR_SLICE_COPY: + case EXPR_SWIZZLE: + case EXPR_SUBSCRIPT_ADDR: + case EXPR_SUBSCRIPT_ASSIGN: + case EXPR_VASPLAT: + case EXPR_MACRO_BODY: + // Above needs to be analysed + case EXPR_GROUP: + case EXPR_INITIALIZER_LIST: + case EXPR_DESIGNATED_INITIALIZER_LIST: + case EXPR_ASM: + case EXPR_CONST: + case EXPR_NOP: + case EXPR_MACRO_BLOCK: + case EXPR_LAMBDA: + case EXPR_EXPR_BLOCK: + case EXPR_CT_IS_CONST: + case EXPR_CT_DEFINED: + case EXPR_CT_AND_OR: + case EXPR_STRINGIFY: + case EXPR_TERNARY: + case EXPR_CT_CASTABLE: + case EXPR_CT_ARG: + case EXPR_CT_CALL: + case EXPR_EXPRESSION_LIST: + case EXPR_POST_UNARY: + case EXPR_TYPEID: + case EXPR_TYPEID_INFO: + if (!sema_analyse_expr(context, main)) return false; + break; } expr_rewrite_const_bool(expr, type_bool, success); return true; @@ -7670,7 +7825,8 @@ static inline bool sema_expr_analyse_ct_arg(SemaContext *context, Expr *expr) { unsigned index; // A normal argument, this means we only evaluate it once. - ASSIGN_EXPR_OR_RET(Expr *arg_expr, sema_expr_analyse_ct_arg_index(context, exprptr(expr->ct_arg_expr.arg), &index), false); + ASSIGN_EXPR_OR_RET(Expr *arg_expr, sema_expr_analyse_ct_arg_index(context, exprptr(expr->ct_arg_expr.arg), + &index, true), false); index++; assert(index < 0x10000); @@ -7700,14 +7856,16 @@ static inline bool sema_expr_analyse_ct_arg(SemaContext *context, Expr *expr) case TOKEN_CT_VAEXPR: { // An expr argument, this means we copy and evaluate. - ASSIGN_EXPR_OR_RET(Expr *arg_expr, sema_expr_analyse_ct_arg_index(context, exprptr(expr->ct_arg_expr.arg), NULL), false); + ASSIGN_EXPR_OR_RET(Expr *arg_expr, sema_expr_analyse_ct_arg_index(context, exprptr(expr->ct_arg_expr.arg), + NULL, true), false); expr_replace(expr, copy_expr_single(arg_expr)); return true; } case TOKEN_CT_VACONST: { // An expr argument, this means we copy and evaluate. - ASSIGN_EXPR_OR_RET(Expr *arg_expr, sema_expr_analyse_ct_arg_index(context, exprptr(expr->ct_arg_expr.arg), NULL), false); + ASSIGN_EXPR_OR_RET(Expr *arg_expr, sema_expr_analyse_ct_arg_index(context, exprptr(expr->ct_arg_expr.arg), + NULL, true), false); arg_expr = copy_expr_single(arg_expr); if (!expr_is_constant_eval(arg_expr, CONSTANT_EVAL_CONSTANT_VALUE)) { @@ -7721,7 +7879,8 @@ static inline bool sema_expr_analyse_ct_arg(SemaContext *context, Expr *expr) { // A normal argument, this means we only evaluate it once. unsigned index; - ASSIGN_EXPR_OR_RET(Expr *arg_expr, sema_expr_analyse_ct_arg_index(context, exprptr(expr->ct_arg_expr.arg), &index), false); + ASSIGN_EXPR_OR_RET(Expr *arg_expr, sema_expr_analyse_ct_arg_index(context, exprptr(expr->ct_arg_expr.arg), + &index, true), false); if (!sema_binary_is_expr_lvalue(arg_expr, arg_expr)) return false; index++; @@ -7758,6 +7917,37 @@ static inline bool sema_expr_analyse_ct_arg(SemaContext *context, Expr *expr) } } +static inline bool sema_expr_analyse_castable(SemaContext *context, Expr *expr) +{ + assert(expr->resolve_status == RESOLVE_RUNNING); + TypeInfo *type_info = type_infoptr(expr->castable_expr.type); + if (!sema_resolve_type_info(context, type_info, RESOLVE_TYPE_ALLOW_INFER)) return false; + Type *type = type_info->type; + Expr *inner = exprptr(expr->castable_expr.expr); + if (!sema_analyse_inferred_expr(context, type, inner)) return false; + bool ok = may_cast(context, inner, type, !expr->castable_expr.is_assign, true); + expr_rewrite_const_bool(expr, type_bool, ok); + return true; +} + +static inline bool sema_expr_analyse_ct_and_or(SemaContext *context, Expr *expr) +{ + assert(expr->resolve_status == RESOLVE_RUNNING); + bool is_and = expr->ct_and_or_expr.is_and; + Expr **exprs = expr->ct_and_or_expr.args; + FOREACH_BEGIN(Expr *single_expr, exprs) + if (!sema_analyse_expr(context, single_expr)) return false; + if (!expr_is_const_bool(single_expr)) RETURN_SEMA_ERROR(single_expr, "Expected this to evaluate to a constant boolean."); + if (single_expr->const_expr.b != is_and) + { + expr_rewrite_const_bool(expr, type_bool, !is_and); + return true; + } + FOREACH_END(); + expr_rewrite_const_bool(expr, type_bool, is_and); + return true; +} + static inline bool sema_expr_analyse_ct_stringify(SemaContext *context, Expr *expr) { Expr *inner = expr->inner_expr; @@ -7954,6 +8144,8 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr) case EXPR_SWIZZLE: case EXPR_MACRO_BODY: UNREACHABLE + case EXPR_CT_CASTABLE: + return sema_expr_analyse_castable(context, expr); case EXPR_EMBED: return sema_expr_analyse_embed(context, expr, false); case EXPR_VASPLAT: @@ -7963,10 +8155,12 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr) return sema_expr_analyse_generic_ident(context, expr); case EXPR_LAMBDA: return sema_expr_analyse_lambda(context, NULL, expr); + case EXPR_CT_IS_CONST: + return sema_expr_analyse_ct_is_const(context, expr); case EXPR_CT_DEFINED: return sema_expr_analyse_ct_defined(context, expr); - case EXPR_CT_CHECKS: - return sema_expr_analyse_ct_checks(context, expr); + case EXPR_CT_AND_OR: + return sema_expr_analyse_ct_and_or(context, expr); case EXPR_CT_ARG: return sema_expr_analyse_ct_arg(context, expr); case EXPR_STRINGIFY: @@ -7987,7 +8181,7 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr) case EXPR_CT_IDENT: return sema_expr_analyse_ct_identifier(context, expr); case EXPR_OPTIONAL: - return sema_expr_analyse_optional(context, expr); + return sema_expr_analyse_optional(context, expr, NULL); case EXPR_COMPILER_CONST: return sema_expr_analyse_compiler_const(context, expr, true); case EXPR_POINTER_OFFSET: @@ -8025,13 +8219,13 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr) return sema_expr_analyse_ternary(context, expr); case EXPR_UNARY: case EXPR_POST_UNARY: - return sema_expr_analyse_unary(context, expr); + return sema_expr_analyse_unary(context, expr, NULL); case EXPR_TYPEID: return sema_expr_analyse_typeid(context, expr); case EXPR_IDENTIFIER: return sema_expr_analyse_identifier(context, NULL, expr); case EXPR_CALL: - return sema_expr_analyse_call(context, expr); + return sema_expr_analyse_call(context, expr, NULL); case EXPR_SUBSCRIPT: return sema_expr_analyse_subscript(context, expr, SUBSCRIPT_EVAL_VALUE); case EXPR_SUBSCRIPT_ADDR: @@ -8042,12 +8236,12 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr) case EXPR_SUBSCRIPT_ASSIGN: UNREACHABLE case EXPR_ACCESS: - return sema_expr_analyse_access(context, expr); + return sema_expr_analyse_access(context, expr, NULL); case EXPR_INITIALIZER_LIST: case EXPR_DESIGNATED_INITIALIZER_LIST: return sema_expr_analyse_initializer_list(context, type_untypedlist, expr); case EXPR_CAST: - return sema_expr_analyse_cast(context, expr); + return sema_expr_analyse_cast(context, expr, NULL); case EXPR_EXPRESSION_LIST: return sema_expr_analyse_expr_list(context, expr); } @@ -8114,10 +8308,10 @@ bool sema_analyse_expr_rhs(SemaContext *context, Type *to, Expr *expr, bool allo RETURN_SEMA_ERROR(expr, "Slice length mismatch, expected %u but got %u.", to_canonical->array.len, len); } // Given x[3..7] -> (int[5]*)x[3..7] - cast_no_check(expr, type_get_ptr(type_get_array(element, len)), IS_OPTIONAL(expr)); + cast_no_check(context, expr, type_get_ptr(type_get_array(element, len)), IS_OPTIONAL(expr)); // Deref expr_rewrite_insert_deref(expr); - cast_no_check(expr, to, IS_OPTIONAL(expr)); + cast_no_check(context, expr, to, IS_OPTIONAL(expr)); return true; } NO_SLICE:; @@ -8516,7 +8710,8 @@ bool sema_insert_method_call(SemaContext *context, Expr *method_call, Decl *meth expr_rewrite_insert_deref(parent); } assert(parent && parent->type && first == parent->type->canonical); - if (!sema_expr_analyse_general_call(context, method_call, method_decl, parent, false)) return expr_poison(method_call); + if (!sema_expr_analyse_general_call(context, method_call, method_decl, parent, false, + NULL)) return expr_poison(method_call); method_call->resolve_status = RESOLVE_DONE; return true; } diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index e65af1c72..8f86f97a4 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -70,11 +70,13 @@ bool sema_analyse_expr_lvalue(SemaContext *context, Expr *expr); bool sema_analyse_expr_lvalue_fold_const(SemaContext *context, Expr *expr); Expr *expr_access_inline_member(Expr *parent, Decl *parent_decl); bool sema_analyse_ct_expr(SemaContext *context, Expr *expr); -Decl *sema_find_operator(SemaContext *context, Expr *expr, OperatorOverload operator_overload); +Decl *sema_find_operator(SemaContext *context, Type *type, OperatorOverload operator_overload); bool sema_insert_method_call(SemaContext *context, Expr *method_call, Decl *method_decl, Expr *parent, Expr **arguments); bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr); -bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *struct_var, Decl *decl, bool optional); -Expr *sema_expr_analyse_ct_arg_index(SemaContext *context, Expr *index_expr, unsigned *index_ref); + +bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *struct_var, Decl *decl, bool optional, + bool *no_match_ref); +Expr *sema_expr_analyse_ct_arg_index(SemaContext *context, Expr *index_expr, unsigned *index_ref, bool report_error); Expr *sema_ct_eval_expr(SemaContext *c, bool is_type, Expr *inner, bool report_missing); bool sema_analyse_asm(SemaContext *context, AsmInlineBlock *block, Ast *asm_stmt); bool sema_bit_assignment_check(Expr *right, Decl *member); @@ -84,13 +86,12 @@ bool sema_analyse_function_signature(SemaContext *context, Decl *func_decl, Call MemberIndex sema_len_from_const(Expr *expr); -void cast_promote_vararg(Expr *arg); +void cast_promote_vararg(SemaContext *context, Expr *arg); Type *cast_numeric_arithmetic_promotion(Type *type); -void cast_to_int_to_max_bit_size(Expr *lhs, Expr *rhs, Type *left_type, Type *right_type); +void cast_to_int_to_max_bit_size(SemaContext *context, Expr *lhs, Expr *rhs, Type *left_type, Type *right_type); bool sema_decl_if_cond(SemaContext *context, Decl *decl); bool sema_flattened_expr_is_const(SemaContext *context, Expr *expr); Decl *sema_analyse_parameterized_identifier(SemaContext *c, Path *decl_path, const char *name, SourceSpan span, Expr **params); -bool sema_analyse_checked(SemaContext *context, Ast *directive, SourceSpan span); INLINE bool sema_set_abi_alignment(SemaContext *context, Type *type, AlignSize *result); INLINE bool sema_set_alloca_alignment(SemaContext *context, Type *type, AlignSize *result); diff --git a/src/compiler/sema_liveness.c b/src/compiler/sema_liveness.c index 6a73fdd3e..a13719bfa 100644 --- a/src/compiler/sema_liveness.c +++ b/src/compiler/sema_liveness.c @@ -270,23 +270,24 @@ RETRY: case EXPR_COMPILER_CONST: case EXPR_CT_ARG: case EXPR_CT_CALL: - case EXPR_CT_CHECKS: case EXPR_CT_DEFINED: + case EXPR_CT_IS_CONST: case EXPR_CT_EVAL: case EXPR_CT_IDENT: case EXPR_ANYSWITCH: case EXPR_GENERIC_IDENT: case EXPR_EMBED: + case EXPR_CT_CASTABLE: + case EXPR_CT_AND_OR: case EXPR_MACRO_BODY: UNREACHABLE - case EXPR_BUILTIN: - TODO case EXPR_DESIGNATOR: sema_trace_expr_liveness(expr->designator_expr.value); return; case EXPR_HASH_IDENT: case EXPR_STRINGIFY: case EXPR_TYPEINFO: + case EXPR_BUILTIN: return; case EXPR_ACCESS: case EXPR_BITACCESS: diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index f92079206..dd527db0e 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -1413,9 +1413,9 @@ static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statemen if (!value_type) { - len = sema_find_operator(context, enumerator, OVERLOAD_LEN); - Decl *by_val = sema_find_operator(context, enumerator, OVERLOAD_ELEMENT_AT); - Decl *by_ref = sema_find_operator(context, enumerator, OVERLOAD_ELEMENT_REF); + len = sema_find_operator(context, enumerator->type, OVERLOAD_LEN); + Decl *by_val = sema_find_operator(context, enumerator->type, OVERLOAD_ELEMENT_AT); + Decl *by_ref = sema_find_operator(context, enumerator->type, OVERLOAD_ELEMENT_REF); if (!len || (!by_val && !by_ref)) { SEMA_ERROR(enumerator, "It's not possible to enumerate an expression of type %s.", type_quoted_error_string(enumerator->type)); @@ -2983,32 +2983,6 @@ NEXT:; return true; } -bool sema_analyse_checked(SemaContext *context, Ast *directive, SourceSpan span) -{ - Expr *declexpr = directive->contract_stmt.contract.decl_exprs; - bool success = true; - bool suppress_error = global_context.suppress_errors; - global_context.suppress_errors = true; - CallEnvKind eval_kind = context->call_env.kind; - context->call_env.kind = CALL_ENV_CHECKS; - SCOPE_START_WITH_FLAGS(SCOPE_CHECKS) - VECEACH(declexpr->cond_expr, j) - { - Expr *expr = declexpr->cond_expr[j]; - if (sema_analyse_expr(context, expr)) continue; - const char *comment = directive->contract_stmt.contract.comment; - global_context.suppress_errors = suppress_error; - sema_error_at(span.row == 0 ? expr->span : span, "Contraint failed: %s", - comment ? comment : directive->contract_stmt.contract.expr_string); - success = false; - goto END; - } -END: - context->call_env.kind = eval_kind; - SCOPE_END; - global_context.suppress_errors = suppress_error; - return success; -} void sema_append_contract_asserts(AstId assert_first, Ast* compound_stmt) { @@ -3032,9 +3006,6 @@ bool sema_analyse_contracts(SemaContext *context, AstId doc, AstId **asserts, So case CONTRACT_REQUIRE: if (!sema_analyse_require(context, directive, asserts, call_span)) return false; break; - case CONTRACT_CHECKED: - if (!sema_analyse_checked(context, directive, call_span)) return false; - break; case CONTRACT_PARAM: break; case CONTRACT_OPTIONALS: diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index 80036db68..c89dd1acf 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -348,7 +348,8 @@ INLINE bool sema_resolve_vatype(SemaContext *context, TypeInfo *type_info) { RETURN_SEMA_ERROR(type_info, "'%s' can only be used inside of a macro.", token_type_to_string(TOKEN_CT_VATYPE)); } - ASSIGN_EXPR_OR_RET(Expr *arg_expr, sema_expr_analyse_ct_arg_index(context, type_info->unresolved_type_expr, NULL), + ASSIGN_EXPR_OR_RET(Expr *arg_expr, sema_expr_analyse_ct_arg_index(context, type_info->unresolved_type_expr, NULL, + true), false); if (arg_expr->expr_kind != EXPR_TYPEINFO) RETURN_SEMA_ERROR(arg_expr, "The argument was not a type."); diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index 5d086520d..691c1bb85 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -182,6 +182,8 @@ void symtab_init(uint32_t capacity) type_property_list[TYPE_PROPERTY_EXTNAMEOF] = KW_DEF("extnameof"); type_property_list[TYPE_PROPERTY_INF] = KW_DEF("inf"); type_property_list[TYPE_PROPERTY_INNER] = KW_DEF("inner"); + type_property_list[TYPE_PROPERTY_IS_EQ] = KW_DEF("is_eq"); + type_property_list[TYPE_PROPERTY_IS_ORDERED] = KW_DEF("is_ordered"); type_property_list[TYPE_PROPERTY_KINDOF] = KW_DEF("kindof"); type_property_list[TYPE_PROPERTY_MEMBERSOF] = KW_DEF("membersof"); type_property_list[TYPE_PROPERTY_NAMEOF] = KW_DEF("nameof"); @@ -310,7 +312,6 @@ void symtab_init(uint32_t capacity) type = TOKEN_AT_IDENT; - kw_at_checked = KW_DEF("@checked"); kw_at_ensure = KW_DEF("@ensure"); kw_at_deprecated = KW_DEF("@deprecated"); kw_at_param = KW_DEF("@param"); diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index e7a9b26ae..550f15e95 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -324,12 +324,14 @@ const char *token_type_to_string(TokenType type) case TOKEN_CT_ALIGNOF: return "$alignof"; + case TOKEN_CT_AND: + return "$and"; case TOKEN_CT_ASSERT: return "$assert"; + case TOKEN_CT_ASSIGNABLE: + return "$assignable"; case TOKEN_CT_CASE: return "$case"; - case TOKEN_CT_CHECKS: - return "$checks"; case TOKEN_CT_DEFAULT: return "$default"; case TOKEN_CT_DEFINED: @@ -364,6 +366,8 @@ const char *token_type_to_string(TokenType type) return "$foreach"; case TOKEN_CT_IF: return "$if"; + case TOKEN_CT_IS_CONST: + return "$is_const"; case TOKEN_CT_INCLUDE: return "$include"; case TOKEN_CT_VACOUNT: @@ -384,6 +388,8 @@ const char *token_type_to_string(TokenType type) return "$nameof"; case TOKEN_CT_OFFSETOF: return "$offsetof"; + case TOKEN_CT_OR: + return "$or"; case TOKEN_CT_QNAMEOF: return "$qnameof"; case TOKEN_CT_SIZEOF: diff --git a/src/compiler/types.c b/src/compiler/types.c index 57b72e01b..4302b7eda 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -2280,9 +2280,10 @@ unsigned type_get_introspection_kind(TypeKind kind) case TYPE_VECTOR: case TYPE_INFERRED_VECTOR: return INTROSPECT_TYPE_VECTOR; + case TYPE_OPTIONAL: + return INTROSPECT_TYPE_OPTIONAL; case TYPE_UNTYPED_LIST: case TYPE_TYPEINFO: - case TYPE_OPTIONAL: case TYPE_MEMBER: case TYPE_WILDCARD: UNREACHABLE diff --git a/src/version.h b/src/version.h index 16aef3d22..262ca2839 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.686" +#define COMPILER_VERSION "0.4.687" diff --git a/test/test_suite/cast/cast_untyped_list_error.c3 b/test/test_suite/cast/cast_untyped_list_error.c3 index 2bad2c257..22c70ec6f 100644 --- a/test/test_suite/cast/cast_untyped_list_error.c3 +++ b/test/test_suite/cast/cast_untyped_list_error.c3 @@ -2,5 +2,5 @@ module testing; fn void main() { - (void){}; // #error: cannot use compound literal initialization + (void){}; // #error: You cannot cast 'untyped_list' to 'void' } \ No newline at end of file diff --git a/test/test_suite/compile_time/ct_and_or.c3 b/test/test_suite/compile_time/ct_and_or.c3 new file mode 100644 index 000000000..3edbb498f --- /dev/null +++ b/test/test_suite/compile_time/ct_and_or.c3 @@ -0,0 +1,6 @@ +fn void test() +{ + $assert $or(false, false, true, hello("")); + $assert !$and(false, hello("")); + $assert !$and(true, true, false, hello("")); +} \ No newline at end of file diff --git a/test/test_suite/compile_time/ct_checks.c3t b/test/test_suite/compile_time/ct_checks.c3t deleted file mode 100644 index 214382b48..000000000 --- a/test/test_suite/compile_time/ct_checks.c3t +++ /dev/null @@ -1,22 +0,0 @@ -// #target: macos-x64 -module test; -import std::io; - -fn void main() -{ - int a; - bool b = $checks(a = 12.0); - var $y = 23; - bool c = $checks(int z = 23, $y += 23, &c); - bool d = $checks(&c, $y, int yy = 23); - int z = $y; - io::printfn("%s %s %s", b, $y, c); -} - -/* #expect: test.ll - - store i32 0 - store i8 0 - store i8 1 - store i8 1 - store i32 46 diff --git a/test/test_suite/compile_time_introspection/defined2.c3 b/test/test_suite/compile_time_introspection/defined2.c3 new file mode 100644 index 000000000..36ac00559 --- /dev/null +++ b/test/test_suite/compile_time_introspection/defined2.c3 @@ -0,0 +1,15 @@ +fn void main() +{ + int[<2>] ab = { 11, 22 }; + $assert !$defined(&1); + int! a; + $assert $defined(a!!); + $assert !$defined(ab!!); + $assert $defined(a!); + $assert !$defined(ab!); + $assert !$defined(*ab); + int* z = &&1; + $assert $defined(*z); + void* g; + $assert !$defined(*g); +} \ No newline at end of file diff --git a/test/test_suite/compile_time_introspection/defined_err.c3 b/test/test_suite/compile_time_introspection/defined_err.c3 index 3270134ed..a3d8e1c46 100644 --- a/test/test_suite/compile_time_introspection/defined_err.c3 +++ b/test/test_suite/compile_time_introspection/defined_err.c3 @@ -1,6 +1,6 @@ fn void test1() { - bool x = $defined(1); // #error: Expected an identifier + bool x = $defined(1); } struct Foo diff --git a/test/test_suite/compile_time_introspection/defined_subscript.c3 b/test/test_suite/compile_time_introspection/defined_subscript.c3 new file mode 100644 index 000000000..a1ccec390 --- /dev/null +++ b/test/test_suite/compile_time_introspection/defined_subscript.c3 @@ -0,0 +1,7 @@ +fn void main() +{ + int[<2>] ab = { 11, 22 }; + $assert !$defined(ab[3]); + $assert $defined(ab[1]); + $assert !$defined(ab[0][0]); +} \ No newline at end of file diff --git a/test/test_suite/enumerations/enum_add_sub.c3t b/test/test_suite/enumerations/enum_add_sub.c3t index 0d0b5eec1..ed5c71c45 100644 --- a/test/test_suite/enumerations/enum_add_sub.c3t +++ b/test/test_suite/enumerations/enum_add_sub.c3t @@ -20,7 +20,6 @@ fn int main() b += 1; $assert $typeof(b++).typeid == Abc.typeid; $assert $typeof(b += 1).typeid == Abc.typeid; - $assert !$checks(a += Foo.ABC); Bar c; c += 1; $assert $typeof(c++).typeid == Bar.typeid; diff --git a/test/test_suite/generic/generic_lambda_complex.c3t b/test/test_suite/generic/generic_lambda_complex.c3t index 1eea75ea0..abab5ca2e 100644 --- a/test/test_suite/generic/generic_lambda_complex.c3t +++ b/test/test_suite/generic/generic_lambda_complex.c3t @@ -74,7 +74,7 @@ fn void! TextTemplate.init(&self, String template, String tag_start = "{{", Stri .data = (String*)(data + $m.offsetof), }; $default: - $if $checks(self.data.$eval($m.nameof).as_stream()): + $if $defined(self.data.$eval($m.nameof).as_stream): return TextTag{ .kind = TEMPLATE, .template = self.data.$eval($m.nameof).as_stream(), diff --git a/test/test_suite/switch/switch_in_defer_macro.c3t b/test/test_suite/switch/switch_in_defer_macro.c3t index 545471eaf..017bfd844 100644 --- a/test/test_suite/switch/switch_in_defer_macro.c3t +++ b/test/test_suite/switch/switch_in_defer_macro.c3t @@ -2,8 +2,8 @@ /** * @require Token.kindof == ENUM && $typefrom(Token.inner).kindof == UNSIGNED_INT - * @require $checks(((Token)0).token) - * @require $checks(((Comment)0).start) && $checks(((Comment)0).end) + * @require $defined(((Token)0).token) + * @require $defined(((Comment)0).start) && $defined(((Comment)0).end) **/ module lexer(); import std::io; diff --git a/test/unit/regression/castable_assignable.c3 b/test/unit/regression/castable_assignable.c3 new file mode 100644 index 000000000..73224337f --- /dev/null +++ b/test/unit/regression/castable_assignable.c3 @@ -0,0 +1,24 @@ +module castable @test; + +fn void assignable() +{ + assert($assignable(12.0, int) == false); + assert($assignable(12, int)); + assert(!$assignable("12", int)); + assert($assignable("12", String)); + assert($assignable("12", char*)); + assert($assignable("12", char[*])); + assert($assignable("12", char[2])); + assert($assignable("12", char[3])); +} + +fn void castable() +{ + assert($defined((int)12.0)); + assert($defined((int)12)); + assert(!$defined((int)"12")); + assert($defined((String)"12")); + assert($defined((char*)"12")); + assert($defined((char[2])"12")); + assert($defined((char[3])"12")); +} \ No newline at end of file