$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.

This commit is contained in:
Christoffer Lerno
2023-10-23 00:21:11 +02:00
committed by Christoffer Lerno
parent 8a12dc5bd4
commit 9af37fe427
46 changed files with 1149 additions and 801 deletions

View File

@@ -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."
**/

View File

@@ -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

View File

@@ -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)
{

View File

@@ -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(<Type>);
@@ -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);
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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();

View File

@@ -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
}

View File

@@ -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):

View File

@@ -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)
{

View File

@@ -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

View File

@@ -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--;
}
}
}

View File

@@ -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
}

View File

@@ -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.

View File

@@ -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)
{

View File

@@ -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); }

View File

@@ -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
;

View File

@@ -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;
}

View File

@@ -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:

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 },

View File

@@ -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;

View File

@@ -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:

View File

@@ -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,

View File

@@ -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;

File diff suppressed because it is too large Load Diff

View File

@@ -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);

View File

@@ -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:

View File

@@ -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:

View File

@@ -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.");

View File

@@ -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");

View File

@@ -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:

View File

@@ -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

View File

@@ -1 +1 @@
#define COMPILER_VERSION "0.4.686"
#define COMPILER_VERSION "0.4.687"

View File

@@ -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'
}

View File

@@ -0,0 +1,6 @@
fn void test()
{
$assert $or(false, false, true, hello(""));
$assert !$and(false, hello(""));
$assert !$and(true, true, false, hello(""));
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -1,6 +1,6 @@
fn void test1()
{
bool x = $defined(1); // #error: Expected an identifier
bool x = $defined(1);
}
struct Foo

View File

@@ -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]);
}

View File

@@ -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;

View File

@@ -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(),

View File

@@ -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(<Token, Comment>);
import std::io;

View File

@@ -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"));
}