Improved errors on optional return. Fixes to @nodiscard erroring. Addresses #1062

This commit is contained in:
Christoffer Lerno
2023-11-03 23:50:15 +01:00
parent 5dedaa8e67
commit 69470b8738
6 changed files with 50 additions and 9 deletions

View File

@@ -784,6 +784,7 @@ typedef struct
bool is_dynamic_dispatch : 1;
bool has_optional_arg : 1;
bool must_use : 1;
bool is_optional_return : 1;
Expr **arguments;
union
{
@@ -1025,6 +1026,7 @@ typedef struct
AstId first_stmt;
bool is_noreturn : 1;
bool is_must_use : 1;
bool is_optional_return : 1;
bool had_optional_arg : 1;
Decl **params;
Decl *macro;

View File

@@ -1664,9 +1664,12 @@ static inline bool sema_call_analyse_func_invocation(SemaContext *context, Type
Type *rtype = type->function.prototype->rtype;
expr->call_expr.has_optional_arg = optional;
if (rtype != type_void)
{
expr->call_expr.must_use = sig->attrs.nodiscard || (type_is_optional(rtype) && !sig->attrs.maydiscard);
bool is_optional_return = type_is_optional(rtype);
expr->call_expr.is_optional_return = is_optional_return;
expr->call_expr.must_use = sig->attrs.nodiscard || (is_optional_return && !sig->attrs.maydiscard);
}
expr->type = type_add_optional(rtype, optional);
@@ -2098,6 +2101,7 @@ NOT_CT:
call_expr->expr_kind = EXPR_MACRO_BLOCK;
call_expr->macro_block.had_optional_arg = has_optional_arg;
call_expr->macro_block.is_must_use = must_use;
call_expr->macro_block.is_optional_return = optional_return;
call_expr->macro_block.first_stmt = body->compound_stmt.first_stmt;
call_expr->macro_block.macro = decl;
call_expr->macro_block.params = params;
@@ -8530,21 +8534,41 @@ bool sema_analyse_expr_lvalue(SemaContext *context, Expr *expr)
bool sema_expr_check_discard(Expr *expr)
{
if (!IS_OPTIONAL(expr)) return true;
if (expr->expr_kind == EXPR_SUBSCRIPT_ASSIGN || expr->expr_kind == EXPR_SLICE_ASSIGN) return true;
if (expr->expr_kind == EXPR_BINARY && expr->binary_expr.operator >= BINARYOP_ASSIGN) return true;
if (expr->expr_kind == EXPR_MACRO_BLOCK)
{
if (expr->macro_block.is_must_use) RETURN_SEMA_ERROR(expr, "The result of the macro must be used.");
if (expr->macro_block.is_must_use)
{
if (expr->macro_block.is_optional_return)
{
RETURN_SEMA_ERROR(expr, "The macro returns %s, which is an optional and must be handled. "
"You can either assign it to a variable, rethrow it using '!', "
"panic with '!!', use if-catch etc. You can also silence the error using a void cast (e.g. '(void)the_call()') to ignore the error.",
type_quoted_error_string(expr->type));
}
RETURN_SEMA_ERROR(expr, "The called macro is marked `@nodiscard` meaning the result should be kept. You can still discard it using a void cast (e.g. '(void)the_call()') if you want.");
}
if (expr->macro_block.had_optional_arg) goto ERROR_ARGS;
return true;
}
if (expr->expr_kind == EXPR_CALL)
{
if (expr->call_expr.must_use) RETURN_SEMA_ERROR(expr, "The result of the function must be used.");
if (expr->call_expr.must_use)
{
if (expr->call_expr.is_optional_return)
{
RETURN_SEMA_ERROR(expr, "The function returns %s, which is an optional and must be handled. "
"You can either assign it to a variable, rethrow it using '!', "
"panic with '!!', use if-catch etc. You can also silence the error using a void cast (e.g. '(void)the_call()') to ignore the error.",
type_quoted_error_string(expr->type));
}
RETURN_SEMA_ERROR(expr, "The called function is marked `@nodiscard` meaning the result should be kept. You can still discard it using a void cast (e.g. '(void)the_call()') if you want.");
}
if (expr->call_expr.has_optional_arg) goto ERROR_ARGS;
return true;
}
if (!IS_OPTIONAL(expr)) return true;
RETURN_SEMA_ERROR(expr, "An optional value may not be discarded, you can ignore it with a void cast '(void)', rethrow on optional with '!' or panic '!!' to avoid this error.");
ERROR_ARGS:
RETURN_SEMA_ERROR(expr, "The result of this call is optional due to its argument(s). The optional result may not be implicitly discarded. Consider using '(void)', '!' or '!!' to handle this.");

View File

@@ -1 +1 @@
#define COMPILER_VERSION "0.4.694"
#define COMPILER_VERSION "0.4.696"

View File

@@ -0,0 +1,15 @@
module abc;
macro int mtest1(int a) @nodiscard { return 0; }
macro int! mtest2(int a) { return 0; }
fn int ftest1(int a) @nodiscard { return 0; }
fn int! ftest2(int a) { return 0; }
fn void main()
{
mtest1(3); // #error: The called macro is marked `@nodiscard`
mtest2(3); // #error: The macro returns 'int!', which is an optional
ftest1(3); // #error: The called function is marked `@nodiscard`
ftest2(3); // #error: The function returns 'int!'
}

View File

@@ -37,14 +37,14 @@ fn void test5()
{
int! x;
int y;
def3(y); // #error: The result of the function must be used
def3(y); // #error: The function returns 'int!'
}
fn void test6()
{
int! x;
int y;
def3(x); // #error: The result of the function must be used
def3(x); // #error: The function returns 'int!'
}
fn void test7()

View File

@@ -37,14 +37,14 @@ fn void test5()
{
int! x;
int y;
def3(y); // #error: The result of the macro must
def3(y); // #error: The macro returns 'int!'
}
fn void test6()
{
int! x;
int y;
def3(x); // #error: The result of the macro must
def3(x); // #error: The macro returns 'int!'
}
fn void test7()