Foreach over distinct iterable would ignore operator(len).

This commit is contained in:
Christoffer Lerno
2024-10-01 13:00:54 +02:00
parent c9b9de2838
commit 0ea423d022
3 changed files with 78 additions and 17 deletions

View File

@@ -69,6 +69,7 @@
- Fix thread tests.
- Detect recursion errors on non-recursive mutexes in safe mode.
- Foreach over distinct pointer failed to be caught as error #1506.
- Foreach over distinct iterable would ignore operator(len).
### Stdlib changes
- Additional init functions for hashmap.

View File

@@ -1463,10 +1463,6 @@ static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statemen
// Check that we can even index this expression.
Type *value_type = type_get_indexed_type(enumerator->type);
// Special case, we might have "distinct Foo = char*" in which case the
// deref didn't happen above, but it will get a value_type.
// However, that would make the code later assume it's possible
// To use foreach on it.
if (canonical->type_kind == TYPE_DISTINCT && type_flatten(canonical)->type_kind == TYPE_POINTER)
{
value_type = NULL;
@@ -1478,20 +1474,19 @@ static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statemen
Decl *index_macro = NULL;
Type *index_type = type_usz;
if (!value_type)
if (!value_type || canonical->type_kind == TYPE_DISTINCT)
{
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));
return false;
if (value_type) goto SKIP_OVERLOAD;
RETURN_SEMA_ERROR(enumerator, "It's not possible to enumerate an expression of type %s.", type_quoted_error_string(enumerator->type));
}
if (!by_ref && value_by_ref)
{
SEMA_ERROR(enumerator, "%s does not support 'foreach' by reference, but you iterate by value.", type_quoted_error_string(enumerator->type));
return false;
RETURN_SEMA_ERROR(enumerator, "%s does not support 'foreach' by reference, but you iterate by value.", type_quoted_error_string(enumerator->type));
}
if (!decl_ok(len) || !decl_ok(by_val) || !decl_ok(by_ref)) return false;
index_macro = value_by_ref ? by_ref : by_val;
@@ -1499,13 +1494,13 @@ static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statemen
index_type = index_macro->func_decl.signature.params[1]->type;
if (!type_is_integer(index_type))
{
SEMA_ERROR(enumerator, "Only integer indexed types may be used with foreach.");
return false;
RETURN_SEMA_ERROR(enumerator, "Only integer indexed types may be used with foreach.");
}
TypeInfoId rtype = index_macro->func_decl.signature.rtype;
value_type = rtype ? type_infoptr(rtype)->type : NULL;
}
SKIP_OVERLOAD:;
TypeInfo *type_info = vartype(var);
// Set up the value, assigning the type as needed.
@@ -1531,15 +1526,13 @@ static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statemen
index_var_type = idx_type_info->type;
if (type_is_optional(index_var_type))
{
SEMA_ERROR(idx_type_info, "The index may not be an optional.");
return false;
RETURN_SEMA_ERROR(idx_type_info, "The index may not be an optional.");
}
if (!type_is_integer(type_flatten(index_var_type)))
{
SEMA_ERROR(idx_type_info,
"Index must be an integer type, '%s' is not valid.",
type_to_error_string(index_var_type));
return false;
RETURN_SEMA_ERROR(idx_type_info,
"Index must be an integer type, '%s' is not valid.",
type_to_error_string(index_var_type));
}
}

View File

@@ -0,0 +1,67 @@
// #target: macos-x64
module test;
import std;
distinct TypeA = char[];
fn char TypeA.get(self, usz i) @operator([]) {
return ((char[])self)[i];
}
fn usz TypeA.len(self) @operator(len)
{
return self.len;
}
fn int main() {
TypeA x = "AAAAA";
foreach(y : x)
{
int z = y;
}
return 0;
}
/* #expect: test.ll
et i64 %2
}
define i32 @main() #0 {
entry:
%x = alloca %"char[]", align 8
%.anon = alloca i64, align 8
%.anon1 = alloca i64, align 8
%y = alloca i8, align 1
%z = alloca i32, align 4
store %"char[]" { ptr @.str, i64 5 }, ptr %x, align 8
%lo = load ptr, ptr %x, align 8
%ptradd = getelementptr inbounds i8, ptr %x, i64 8
%hi = load i64, ptr %ptradd, align 8
%0 = call i64 @test.TypeA.len(ptr %lo, i64 %hi)
store i64 %0, ptr %.anon, align 8
store i64 0, ptr %.anon1, align 8
br label %loop.cond
loop.cond: ; preds = %loop.body, %entry
%1 = load i64, ptr %.anon1, align 8
%2 = load i64, ptr %.anon, align 8
%lt = icmp ult i64 %1, %2
br i1 %lt, label %loop.body, label %loop.exit
loop.body: ; preds = %loop.cond
%lo2 = load ptr, ptr %x, align 8
%ptradd3 = getelementptr inbounds i8, ptr %x, i64 8
%hi4 = load i64, ptr %ptradd3, align 8
%3 = load i64, ptr %.anon1, align 8
%4 = call i8 @test.TypeA.get(ptr %lo2, i64 %hi4, i64 %3)
store i8 %4, ptr %y, align 1
%5 = load i8, ptr %y, align 1
%zext = zext i8 %5 to i32
store i32 %zext, ptr %z, align 4
%6 = load i64, ptr %.anon1, align 8
%addnuw = add nuw i64 %6, 1
store i64 %addnuw, ptr %.anon1, align 8
br label %loop.cond
loop.exit: ; preds = %loop.cond
ret i32 0