Fix bug preventing optionals from being used in ranges or as indices.

This commit is contained in:
Christoffer Lerno
2024-11-28 00:48:58 +01:00
parent b536a23124
commit cc9a501351
5 changed files with 133 additions and 5 deletions

View File

@@ -25,6 +25,7 @@
- Prevent methods from using names of properties or fields. #1638
- b64 / hex data strings can now be used with \` as well.
- Contracts on generic modules would evaluate too late, sometimes not catching the error until it already occurred elsewhere.
- Fix bug preventing optionals from being used in ranges or as indices.
### Stdlib changes
- Add `io::MultiReader`, `io::MultiWriter`, and `io::TeeReader` structs.

View File

@@ -681,6 +681,7 @@ typedef struct
bool end_from_end : 1;
bool is_len : 1;
bool is_range : 1;
bool is_optional : 1;
union
{
struct

View File

@@ -607,6 +607,7 @@ bool cast_to_index(SemaContext *context, Expr *index, Type *subscripted_type)
Type *type = index->type;
RETRY:
type = type_flat_distinct_inline(type);
type = type_no_optional(type);
switch (type->type_kind)
{
case TYPE_I8:

View File

@@ -3043,6 +3043,7 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr,
// Cast to an appropriate type for index.
if (!cast_to_index(context, index, subscripted->type)) return false;
optional |= IS_OPTIONAL(index);
// Check range
bool remove_from_back = false;
if (!sema_slice_index_is_in_range(context, current_type, index, false, start_from_end, &remove_from_back)) return false;
@@ -3149,7 +3150,7 @@ typedef enum RangeEnv
RANGE_FLEXIBLE,
} RangeEnv;
INLINE bool sema_expre_analyse_range_internal(SemaContext *context, Range *range, Type *indexed_type, ArrayIndex len, RangeEnv env)
INLINE bool sema_expr_analyse_range_internal(SemaContext *context, Range *range, Type *indexed_type, ArrayIndex len, RangeEnv env)
{
Expr *start = exprptr(range->start);
ASSERT0(start);
@@ -3160,9 +3161,13 @@ INLINE bool sema_expre_analyse_range_internal(SemaContext *context, Range *range
if (!cast_to_index(context, start, indexed_type)) return false;
if (end && !cast_to_index(context, end, indexed_type)) return false;
if (end && end->type != start->type)
Type *end_type = end ? type_no_optional(end->type) : NULL;
Type *start_type = type_no_optional(start->type);
if (end && IS_OPTIONAL(end)) range->is_optional = true;
if (IS_OPTIONAL(start)) range->is_optional = true;
if (end && end_type != start_type)
{
Type *common = type_find_max_type(start->type, end->type);
Type *common = type_find_max_type(start_type, end_type);
if (!common)
{
SourceSpan span = start->span;
@@ -3316,7 +3321,7 @@ static inline bool sema_expr_analyse_range(SemaContext *context, Range *range, T
return true;
case RESOLVE_NOT_DONE:
range->status = RESOLVE_RUNNING;
if (!sema_expre_analyse_range_internal(context, range, indexed_type, len, env))
if (!sema_expr_analyse_range_internal(context, range, indexed_type, len, env))
{
range->status = RESOLVE_NOT_DONE;
return false;
@@ -3421,7 +3426,7 @@ static inline bool sema_expr_analyse_slice(SemaContext *context, Expr *expr, Che
ArrayIndex length = sema_len_from_expr(subscripted);
Range *range = &expr->slice_expr.range;
if (!sema_expr_analyse_range(context, range, subscripted->type, length, env)) return false;
if (range->is_optional) optional = true;
if (check == CHECK_VALUE && sema_cast_const(subscripted) && range->range_type == RANGE_CONST_RANGE)
{
switch (subscripted->const_expr.const_kind)

View File

@@ -0,0 +1,120 @@
// #target: macos-x64
module test;
fn void test()
{
int[] a = { 1, 2, 3 };
for (int i = 0; i < 3; i++)
{
int! b = i == 0 ? SearchResult.MISSING? : i;
int[]! y = a[:b];
int[]! w = a[b..];
int! z = a[b];
}
}
/* #expect: test.ll
store %"int[]" %1, ptr %a, align 8
store i32 0, ptr %i, align 4
br label %loop.cond
loop.cond: ; preds = %after_assign15, %entry
%2 = load i32, ptr %i, align 4
%lt = icmp slt i32 %2, 3
br i1 %lt, label %loop.body, label %loop.exit
loop.body: ; preds = %loop.cond
%3 = load i32, ptr %i, align 4
%eq = icmp eq i32 %3, 0
br i1 %eq, label %cond.lhs, label %cond.rhs
cond.lhs: ; preds = %loop.body
store i64 ptrtoint (ptr @"std.core.builtin.SearchResult$MISSING" to i64), ptr %b.f, align 8
br label %after_assign
cond.rhs: ; preds = %loop.body
%4 = load i32, ptr %i, align 4
br label %cond.phi
cond.phi: ; preds = %cond.rhs
store i32 %4, ptr %b, align 4
store i64 0, ptr %b.f, align 8
br label %after_assign
after_assign: ; preds = %cond.phi, %cond.lhs
%5 = load %"int[]", ptr %a, align 8
%6 = extractvalue %"int[]" %5, 0
%optval = load i64, ptr %b.f, align 8
%not_err = icmp eq i64 %optval, 0
%7 = call i1 @llvm.expect.i1(i1 %not_err, i1 true)
br i1 %7, label %after_check, label %assign_optional
assign_optional: ; preds = %after_assign
store i64 %optval, ptr %y.f, align 8
br label %after_assign1
after_check: ; preds = %after_assign
%8 = load i32, ptr %b, align 4
%sext = sext i32 %8 to i64
%add = add i64 0, %sext
%size = sub i64 %add, 0
%9 = insertvalue %"int[]" undef, ptr %6, 0
%10 = insertvalue %"int[]" %9, i64 %size, 1
store %"int[]" %10, ptr %y, align 8
store i64 0, ptr %y.f, align 8
br label %after_assign1
after_assign1: ; preds = %after_check, %assign_optional
%11 = load %"int[]", ptr %a, align 8
%12 = extractvalue %"int[]" %11, 0
%optval2 = load i64, ptr %b.f, align 8
%not_err3 = icmp eq i64 %optval2, 0
%13 = call i1 @llvm.expect.i1(i1 %not_err3, i1 true)
br i1 %13, label %after_check5, label %assign_optional4
assign_optional4: ; preds = %after_assign1
store i64 %optval2, ptr %w.f, align 8
br label %after_assign8
after_check5: ; preds = %after_assign1
%14 = load i32, ptr %b, align 4
%sext6 = sext i32 %14 to i64
%15 = extractvalue %"int[]" %11, 1
%size7 = sub i64 %15, %sext6
%ptroffset = getelementptr inbounds [4 x i8], ptr %12, i64 %sext6
%16 = insertvalue %"int[]" undef, ptr %ptroffset, 0
%17 = insertvalue %"int[]" %16, i64 %size7, 1
store %"int[]" %17, ptr %w, align 8
store i64 0, ptr %w.f, align 8
br label %after_assign8
after_assign8: ; preds = %after_check5, %assign_optional4
%18 = load ptr, ptr %a, align 8
%optval9 = load i64, ptr %b.f, align 8
%not_err10 = icmp eq i64 %optval9, 0
%19 = call i1 @llvm.expect.i1(i1 %not_err10, i1 true)
br i1 %19, label %after_check12, label %assign_optional11
assign_optional11: ; preds = %after_assign8
store i64 %optval9, ptr %z.f, align 8
br label %after_assign15
after_check12: ; preds = %after_assign8
%20 = load i32, ptr %b, align 4
%sext13 = sext i32 %20 to i64
%ptroffset14 = getelementptr inbounds [4 x i8], ptr %18, i64 %sext13
%21 = load i32, ptr %ptroffset14, align 4
store i32 %21, ptr %z, align 4
store i64 0, ptr %z.f, align 8
br label %after_assign15
after_assign15: ; preds = %after_check12, %assign_optional11
%22 = load i32, ptr %i, align 4
%add16 = add i32 %22, 1
store i32 %add16, ptr %i, align 4
br label %loop.cond
loop.exit: ; preds = %loop.cond
ret void
}