Improve error message when doing a rethrow in a function that doesn't return an optional.

Array indices are now using int64 internally.
This commit is contained in:
Christoffer Lerno
2025-07-06 20:20:42 +02:00
parent 457244e3de
commit e15fdc709f
9 changed files with 64 additions and 47 deletions

View File

@@ -10,6 +10,7 @@
- Improved error messages on missing qualifier on enum value. #2260
- Add `--echo-prefix` to edit the prefix with `$echo` statements. Supports {FILE} and {LINE}
- Catch accidental `foo == BAR;` where `foo = BAR;` was most likely intended. #2274
- Improve error message when doing a rethrow in a function that doesn't return an optional.
### Fixes
- mkdir/rmdir would not work properly with substring paths on non-windows platforms.
@@ -31,6 +32,7 @@
- Make `to_float` more tolerant to spaces.
- Fixes to thread local pointer handling.
- Fixes to JSON parsing and Object.
- Array indices are now using int64 internally.
### Stdlib changes

View File

@@ -257,7 +257,7 @@ static bool c_emit_type_decl(GenContext *c, Type *type)
if (prev) return false;
c_emit_type_decl(c, type->array.base);
int id = ++c->typename;
PRINTF("typedef struct { %s ptr[%u]; } __c3_array%d;\n", c_type_name(c, type->array.base), type->array.len, id);
PRINTF("typedef struct { %s ptr[%llu]; } __c3_array%d;\n", c_type_name(c, type->array.base), (unsigned long long)type->array.len, id);
scratch_buffer_clear();
scratch_buffer_printf(" __c3_array%d", id);
htable_set(&c->gen_decl, type, scratch_buffer_copy());

View File

@@ -16,11 +16,11 @@ typedef double Real;
typedef uint64_t ByteSize;
typedef uint32_t TypeSize;
typedef int32_t IndexDiff;
typedef int32_t ArrayIndex;
typedef int64_t ArrayIndex;
typedef uint16_t StructIndex;
typedef uint32_t AlignSize;
typedef int32_t ScopeId;
typedef uint32_t ArraySize;
typedef uint64_t ArraySize;
typedef uint64_t BitSize;
typedef uint16_t FileId;
@@ -34,7 +34,7 @@ typedef uint16_t FileId;
#define UINT12_MAX 4095
#define UINT20_MAX 1048575U
#define MAX_ARRAYINDEX INT32_MAX
#define MAX_ARRAYINDEX INT64_MAX
#define MAX_FIXUPS 0xFFFFF
#define MAX_HASH_SIZE (512 * 1024 * 1024)
#define INVALID_SPAN ((SourceSpan){ .row = 0 })
@@ -72,6 +72,15 @@ typedef uint16_t FileId;
#define TABLE_MAX_LOAD 0.5
#define OUTF(...) do { if (!compiler.build.silent) printf(__VA_ARGS__); } while(0)
#define OUTN(str__) do { if (!compiler.build.silent) puts(str__); } while(0)
#ifdef NDEBUG
#define ASSERT_SPANF(node__, check__, format__, ...) do { (void)(check__); } while(0)
#define ASSERT_SPAN(node__, check__) do { (void)(check__); } while(0)
#define ASSERT_AT(span__, check__) do { (void)(check__);} while(0)
#else
#define ASSERT_SPANF(node__, check__, format__, ...) do { if (!(check__)) { assert_print_line((node__)->span); eprintf(format__, __VA_ARGS__); ASSERT(check__); } } while(0)
#define ASSERT_SPAN(node__, check__) do { if (!(check__)) { assert_print_line((node__)->span); ASSERT(check__); } } while(0)
#define ASSERT_AT(span__, check__) do { if (!(check__)) { assert_print_line(span__); ASSERT(check__); } } while(0)
#endif
#define INVALID_PTR ((void*)(uintptr_t)0xAAAAAAAAAAAAAAAA)
@@ -704,7 +713,7 @@ typedef enum RangeType
typedef struct
{
ResolveStatus status : 3;
RangeType range_type;
RangeType range_type : 4;
bool start_from_end : 1;
bool end_from_end : 1;
bool is_len : 1;
@@ -2021,6 +2030,8 @@ INLINE bool compile_asserts(void)
return safe_mode_enabled() || compiler.build.testing;
}
void assert_print_line(SourceSpan span);
bool ast_is_not_empty(Ast *ast);
bool ast_is_compile_time(Ast *ast);
@@ -2945,6 +2956,7 @@ INLINE TypeInfoId type_info_id_new_base(Type *type, SourceSpan span)
return type_infoid(type_info_new_base(type, span));
}
INLINE Type *type_new(TypeKind kind, const char *name)
{
Type *type = CALLOCS(Type);
@@ -4236,16 +4248,12 @@ INLINE bool check_module_name(Path *path)
return true;
}
#ifdef NDEBUG
#define ASSERT_SPANF(node__, check__, format__, ...) do { (void)(check__); } while(0)
#define ASSERT_SPAN(node__, check__) do { (void)(check__); } while(0)
#define ASSERT_AT(span__, check__) do { (void)(check__);} while(0)
#else
#define ASSERT_SPANF(node__, check__, format__, ...) do { if (!(check__)) { assert_print_line((node__)->span); eprintf(format__, __VA_ARGS__); ASSERT(check__); } } while(0)
#define ASSERT_SPAN(node__, check__) do { if (!(check__)) { assert_print_line((node__)->span); ASSERT(check__); } } while(0)
#define ASSERT_AT(span__, check__) do { if (!(check__)) { assert_print_line(span__); ASSERT(check__); } } while(0)
#endif
void assert_print_line(SourceSpan span);
INLINE bool expr_is_valid_index(Expr *expr)
{
ASSERT_SPAN(expr, expr_is_const_int(expr));
return int_fits(expr->const_expr.ixx, TYPE_I64);
}
const char *default_c_compiler(void);

View File

@@ -203,7 +203,7 @@ static void header_print_type(HeaderContext *c, Type *type)
case TYPE_ARRAY:
PRINTF("struct { ");
header_print_type(c, type->array.base);
PRINTF(" arr[%d]; }", type->array.len);
PRINTF(" arr[%llu]; }", (unsigned long long)type->array.len);
return;
case TYPE_ANY:
case TYPE_INTERFACE:
@@ -227,7 +227,7 @@ static void header_print_type(HeaderContext *c, Type *type)
default:
UNREACHABLE;
}
PRINTF("%dx%d", (int)type_bit_size(type->array.base), type->array.len);
PRINTF("%dx%llu", (int)type_bit_size(type->array.base), (unsigned long long)type->array.len);
return;
}
}
@@ -345,7 +345,7 @@ static void header_gen_members(HeaderContext *c, int indent, Decl **members)
{
case TYPE_ARRAY:
header_print_type(c, type->array.base);
PRINTF(" %s[%d];\n", member->name, type->array.len);
PRINTF(" %s[%llu];\n", member->name, (unsigned long long)type->array.len);
break;
case TYPE_FLEXIBLE_ARRAY:
header_print_type(c, type->array.base);
@@ -574,7 +574,7 @@ RETRY:
header_print_type(c, flat_type);
PRINTF(" ");
header_print_type(c, type);
PRINTF(" __attribute__((vector_size(%d)));\n", (int)type_size(flat_type) * type->array.len);
PRINTF(" __attribute__((vector_size(%llu)));\n", (int)type_size(flat_type) * (unsigned long long)type->array.len);
return;
}
}

View File

@@ -491,7 +491,7 @@ const char *expr_const_to_error_string(const ExprConst *expr)
case CONST_FLOAT:
return str_printf("%g", expr->fxx.f);
case CONST_STRING:
return str_printf("\"%*.s\"", expr->bytes.len, expr->bytes.ptr);
return str_printf("\"%*.s\"", (int)expr->bytes.len, expr->bytes.ptr);
case CONST_BYTES:
return "<binary data>";
case CONST_REF:

View File

@@ -3989,8 +3989,7 @@ INLINE bool sema_expr_analyse_range_internal(SemaContext *context, Range *range,
{
SourceSpan span = start->span;
span = extend_span_with_token(span, end->span);
sema_error_at(context, span, "No common type can be found between start and end index.");
return false;
RETURN_SEMA_ERROR_AT(span, "No common type can be found between start and end index.");
}
if (!cast_implicit(context, start, common, false) || !cast_implicit(context, end, common, false)) return false;
}
@@ -4013,12 +4012,12 @@ INLINE bool sema_expr_analyse_range_internal(SemaContext *context, Range *range,
if (end && sema_cast_const(end))
{
// Only ArrayIndex sized
if (!int_fits(end->const_expr.ixx, TYPE_I64))
if (!expr_is_valid_index(end))
{
RETURN_SEMA_ERROR(end, "The index cannot be stored in a 64-signed integer, which isn't supported.");
}
int64_t end_index = int_to_i64(end->const_expr.ixx);
ArrayIndex end_index = int_to_i64(end->const_expr.ixx);
if (range->end_from_end)
{
@@ -4026,11 +4025,11 @@ INLINE bool sema_expr_analyse_range_internal(SemaContext *context, Range *range,
// Something like 1 .. ^4 with an unknown length.
if (len < 0) return true;
// Otherwise we fold the "from end"
end_index = len - end_index;
if (end_index < 0)
if (end_index > len)
{
RETURN_SEMA_ERROR(end, "An index may only be negative for pointers (it was: %lld).", end_index);
RETURN_SEMA_ERROR(end, "An index may only be negative for pointers (it was: %lld).", len - end_index);
}
end_index = len - end_index;
range->end_from_end = false;
}
if (end_index < 0 && env != RANGE_PTR)
@@ -4038,8 +4037,7 @@ INLINE bool sema_expr_analyse_range_internal(SemaContext *context, Range *range,
RETURN_SEMA_ERROR(end, "An index may only be negative for pointers (it was: %lld).", end_index);
}
// No more analysis
if (end_index > MAX_ARRAYINDEX || end_index < -MAX_ARRAYINDEX) return true;
range->const_end = (ArrayIndex)end_index;
range->const_end = end_index;
range->range_type = range->is_len ? RANGE_CONST_LEN : RANGE_CONST_END;
}
else if (!end && len > 0)
@@ -4052,24 +4050,23 @@ INLINE bool sema_expr_analyse_range_internal(SemaContext *context, Range *range,
if (sema_cast_const(start))
{
// Only ArrayIndex sized
if (!int_fits(start->const_expr.ixx, TYPE_I64))
if (!expr_is_valid_index(start))
{
RETURN_SEMA_ERROR(end, "The index cannot be stored in a 64-signed integer, which isn't supported.");
}
// Only ArrayIndex sized
int64_t start_index = int_to_i64(start->const_expr.ixx);
ArrayIndex start_index = int_to_i64(start->const_expr.ixx);
if (range->start_from_end)
{
if (start_index < 0) RETURN_SEMA_ERROR(end, "Negative numbers are not allowed when indexing from the end.");
// Something like ^1 .. 4 with an unknown length.
if (len < 0) return true;
// Otherwise we fold the "from end"
start_index = len - start_index;
if (start_index < 0)
if (len < start_index)
{
RETURN_SEMA_ERROR(start, "An index may only be negative for pointers (it was: %lld).", start_index);
RETURN_SEMA_ERROR(start, "An index may only be negative for pointers (it was: %lld).", len - start_index);
}
if (start_index > MAX_ARRAYINDEX || start_index < -MAX_ARRAYINDEX) return true;
start_index = len - start_index;
range->start_from_end = false;
}
if (start_index < 0 && env != RANGE_PTR)
@@ -4083,20 +4080,19 @@ INLINE bool sema_expr_analyse_range_internal(SemaContext *context, Range *range,
}
if (range->range_type == RANGE_CONST_END)
{
int64_t end_index = range->const_end;
ArrayIndex end_index = range->const_end;
if (end_index < start_index) RETURN_SEMA_ERROR(start, "The start index (%lld) should not be greater than the end index (%lld).",
start_index, end_index);
if (start_index > MAX_ARRAYINDEX) return true;
range->const_end = (ArrayIndex)(end_index + 1 - start_index);
range->const_end = end_index + 1 - start_index;
range->range_type = RANGE_CONST_LEN;
range->is_len = true;
}
if (range->range_type == RANGE_CONST_LEN)
{
int64_t end_index = range->const_end;
ArrayIndex end_index = range->const_end;
range->range_type = RANGE_CONST_RANGE;
range->start_index = (ArrayIndex)start_index;
range->len_index = (ArrayIndex)end_index;
range->start_index = start_index;
range->len_index = end_index;
}
}
if (len > -1)
@@ -9002,8 +8998,10 @@ static inline bool sema_expr_analyse_rethrow(SemaContext *context, Expr *expr)
expr->rethrow_expr.in_block = NULL;
if (context->rtype && context->rtype->type_kind != TYPE_OPTIONAL)
{
RETURN_SEMA_ERROR(expr, "This expression implicitly returns with an optional result, "
"but the function does not allow optional results. Did you mean to use '!!' instead?");
RETURN_SEMA_ERROR(expr, "This expression is doing a rethrow, "
"but '%s' returns %s, which isn't an optional type. Did you intend to use '!!' instead?",
context->call_env.current_function->name,
type_quoted_error_string(context->rtype));
}
}
return true;

View File

@@ -1130,13 +1130,13 @@ static Type *type_create_array(Type *element_type, ArraySize len, bool vector, b
Type *vec_arr;
if (vector)
{
vec_arr = type_new(TYPE_VECTOR, str_printf("%s[<%u>]", element_type->name, len));
vec_arr = type_new(TYPE_VECTOR, str_printf("%s[<%llu>]", element_type->name, (unsigned long long)len));
vec_arr->array.base = element_type;
vec_arr->array.len = len;
}
else
{
vec_arr = type_new(TYPE_ARRAY, str_printf("%s[%u]", element_type->name, len));
vec_arr = type_new(TYPE_ARRAY, str_printf("%s[%llu]", element_type->name, (unsigned long long)len));
vec_arr->array.base = element_type;
vec_arr->array.len = len;
}

View File

@@ -5,8 +5,8 @@ fn void test()
int i = 0;
if (i!) return; // #error: No optional to rethrow before '!' in the expression, please remove '!'
int? j = 0;
if (j!) return; // #error: This expression implicitly returns with an optional result, but the function
if ((j!)!) return; // #error: This expression implicitly returns with an optional result, but the function
if (j!) return; // #error: This expression is doing a rethrow
if ((j!)!) return; // #error: This expression is doing a rethrow
}
fn void? test2()

View File

@@ -0,0 +1,9 @@
module test;
import std;
fn int? test() => 1;
fn int main()
{
test()!; // #error: This expression is doing a rethrow
return 0;
}