Fix in nested block handling. @maydiscard and @nodiscard annotations. If the common type of int[x] and int[y] is int[]

This commit is contained in:
Christoffer Lerno
2022-07-17 19:23:42 +02:00
committed by Christoffer Lerno
parent 4beb7eff8f
commit 6cf3c9f46b
21 changed files with 787 additions and 612 deletions

View File

@@ -208,14 +208,14 @@ fn void DynamicArenaAllocator.destroy(DynamicArenaAllocator* this)
while (page) while (page)
{ {
DynamicArenaPage* next_page = page.prev_arena; DynamicArenaPage* next_page = page.prev_arena;
this.backing_allocator.free(page); this.backing_allocator.free(page)!!;
page = next_page; page = next_page;
} }
page = this.unused_page; page = this.unused_page;
while (page) while (page)
{ {
DynamicArenaPage* next_page = page.prev_arena; DynamicArenaPage* next_page = page.prev_arena;
this.backing_allocator.free(page); this.backing_allocator.free(page)!!;
page = next_page; page = next_page;
} }
this.page = null; this.page = null;
@@ -297,7 +297,7 @@ private fn void*! DynamicArenaAllocator.alloc_new(DynamicArenaAllocator* this, u
DynamicArenaPage*! page = this.backing_allocator.alloc(DynamicArenaPage.sizeof); DynamicArenaPage*! page = this.backing_allocator.alloc(DynamicArenaPage.sizeof);
if (catch err = page) if (catch err = page)
{ {
this.backing_allocator.free(mem); this.backing_allocator.free(mem)?;
return err!; return err!;
} }
page.memory = mem; page.memory = mem;

View File

@@ -196,7 +196,7 @@ fn void String.destroy(String* str)
if (!*str) return; if (!*str) return;
StringData* data = str.data(); StringData* data = str.data();
if (!data) return; if (!data) return;
data.allocator.free(data); data.allocator.free(data)!!;
*str = (String)null; *str = (String)null;
} }

View File

@@ -542,7 +542,7 @@ private fn void! etoa(PrintParam* param, FloatType value)
if (minwidth) if (minwidth)
{ {
// output the exponential symbol // output the exponential symbol
param.out(param.flags.uppercase ? 'E' : 'e'); param.out(param.flags.uppercase ? 'E' : 'e')?;
// output the exponent value // output the exponent value
param.flags = { .zeropad = true, .plus = true }; param.flags = { .zeropad = true, .plus = true };
param.width = minwidth - 1; param.width = minwidth - 1;
@@ -653,7 +653,7 @@ private fn NtoaType int_from_variant(variant arg, bool *is_neg)
} }
fn usize! printf(char[] format, args...) fn usize! printf(char[] format, args...) @maydiscard
{ {
return vsnprintf(&out_putchar_fn, " ", format, args); return vsnprintf(&out_putchar_fn, " ", format, args);
} }

View File

@@ -458,6 +458,8 @@ typedef struct
bool attr_noinline : 1; bool attr_noinline : 1;
bool attr_extname : 1; bool attr_extname : 1;
bool attr_naked : 1; bool attr_naked : 1;
bool attr_nodiscard : 1;
bool attr_maydiscard: 1;
}; };
TypeInfoId type_parent; TypeInfoId type_parent;
FunctionSignature function_signature; FunctionSignature function_signature;
@@ -498,6 +500,8 @@ typedef struct
struct struct
{ {
bool attr_noreturn : 1; bool attr_noreturn : 1;
bool attr_nodiscard : 1;
bool attr_maydiscard : 1;
}; };
TypeInfoId type_parent; // May be 0 TypeInfoId type_parent; // May be 0
TypeInfoId rtype; // May be 0 TypeInfoId rtype; // May be 0
@@ -687,6 +691,7 @@ typedef struct
bool is_builtin : 1; bool is_builtin : 1;
bool is_func_ref : 1; bool is_func_ref : 1;
bool attr_pure : 1; bool attr_pure : 1;
bool result_unused : 1;
AstId body; AstId body;
union union
{ {
@@ -808,16 +813,27 @@ typedef struct
} ExprBodyExpansion; } ExprBodyExpansion;
typedef struct
{
void *block_return_exit;
void *block_failable_exit;
void *block_error_var;
void *block_return_out;
} BlockExit;
typedef struct typedef struct
{ {
AstId first_stmt; AstId first_stmt;
BlockExit **block_exit_ref;
} ExprFuncBlock; } ExprFuncBlock;
typedef struct typedef struct
{ {
AstId first_stmt; AstId first_stmt;
Expr **args; Expr **args;
Decl **params; Decl **params;
BlockExit **block_exit;
} ExprMacroBlock; } ExprMacroBlock;
@@ -982,6 +998,7 @@ typedef struct
{ {
Expr *expr; // May be NULL Expr *expr; // May be NULL
AstId cleanup; AstId cleanup;
BlockExit** block_exit_ref; // For block exits
} AstReturnStmt; } AstReturnStmt;
typedef struct typedef struct
@@ -1412,6 +1429,7 @@ typedef struct SemaContext_
uint32_t original_inline_line; uint32_t original_inline_line;
Decl **yield_params; Decl **yield_params;
Ast *yield_body; Ast *yield_body;
BlockExit** block_exit_ref;
Type *expected_block_type; Type *expected_block_type;
Ast **returns; Ast **returns;
// Reusable returns cache. // Reusable returns cache.

View File

@@ -701,6 +701,8 @@ typedef enum
ATTRIBUTE_OPERATOR, ATTRIBUTE_OPERATOR,
ATTRIBUTE_PURE, ATTRIBUTE_PURE,
ATTRIBUTE_REFLECT, ATTRIBUTE_REFLECT,
ATTRIBUTE_MAYDISCARD,
ATTRIBUTE_NODISCARD,
ATTRIBUTE_NONE, ATTRIBUTE_NONE,
NUMBER_OF_ATTRIBUTES = ATTRIBUTE_NONE, NUMBER_OF_ATTRIBUTES = ATTRIBUTE_NONE,
} AttributeType; } AttributeType;

View File

@@ -65,6 +65,20 @@ static void linker_setup_windows(const char ***args_ref, LinkerType linker_type)
{ {
if (linker_type == LINKER_CC) return; if (linker_type == LINKER_CC) return;
//add_arg("/MACHINE:X64"); //add_arg("/MACHINE:X64");
switch (active_target.debug_info)
{
case DEBUG_INFO_NOT_SET:
break;
case DEBUG_INFO_NONE:
add_arg("/DEBUG:NONE");
break;
case DEBUG_INFO_LINE_TABLES:
case DEBUG_INFO_FULL:
add_arg("/DEBUG:FULL");
break;
default:
UNREACHABLE
}
if (active_target.win.sdk) if (active_target.win.sdk)
{ {
add_arg(str_printf("/LIBPATH:%s", active_target.win.sdk)); add_arg(str_printf("/LIBPATH:%s", active_target.win.sdk));

View File

@@ -4943,7 +4943,7 @@ static inline void gencontext_emit_expression_list_expr(GenContext *context, BEV
} }
} }
static inline void llvm_emit_return_block(GenContext *context, BEValue *be_value, Type *type, AstId current) static inline void llvm_emit_return_block(GenContext *context, BEValue *be_value, Type *type, AstId current, BlockExit **block_exit)
{ {
// First case - an empty block // First case - an empty block
if (!current) if (!current)
@@ -4954,25 +4954,26 @@ static inline void llvm_emit_return_block(GenContext *context, BEValue *be_value
Type *type_lowered = type_lowering(type); Type *type_lowered = type_lowering(type);
LLVMValueRef old_ret_out = context->return_out; LLVMValueRef old_ret_out = context->return_out;
LLVMBasicBlockRef saved_block_return_exit = context->block_return_exit;
LLVMBasicBlockRef saved_block_failable_exit = context->block_failable_exit;
LLVMValueRef saved_block_error = context->block_error_var;
context->in_block++; context->in_block++;
LLVMBasicBlockRef expr_block = llvm_basic_block_new(context, "expr_block.exit");
context->block_return_exit = expr_block;
LLVMValueRef return_out = NULL;
LLVMValueRef error_out = context->error_var; LLVMValueRef error_out = context->error_var;
LLVMBasicBlockRef error_block = context->catch_block; LLVMBasicBlockRef error_block = context->catch_block;
LLVMValueRef return_out = NULL;
LLVMBasicBlockRef expr_block = llvm_basic_block_new(context, "expr_block.exit");
BlockExit exit = {
.block_return_exit = expr_block,
.block_failable_exit = error_block,
.block_error_var = error_out,
.block_return_out = NULL,
};
*block_exit= &exit;
if (type_no_fail(type_lowered) != type_void) if (type_no_fail(type_lowered) != type_void)
{ {
return_out = llvm_emit_alloca_aligned(context, type_lowered, "blockret"); exit.block_return_out = llvm_emit_alloca_aligned(context, type_lowered, "blockret");
} }
context->block_error_var = error_out;
context->block_failable_exit = error_block;
context->return_out = return_out;
context->error_var = NULL; context->error_var = NULL;
context->catch_block = NULL; context->catch_block = NULL;
@@ -5013,7 +5014,7 @@ static inline void llvm_emit_return_block(GenContext *context, BEValue *be_value
// Optimization, emit directly to value // Optimization, emit directly to value
llvm_emit_expr(context, be_value, ret_expr); llvm_emit_expr(context, be_value, ret_expr);
// And remove the alloca // And remove the alloca
LLVMInstructionEraseFromParent(context->return_out); LLVMInstructionEraseFromParent(exit.block_return_out);
goto DONE; goto DONE;
} while (0); } while (0);
@@ -5034,9 +5035,9 @@ static inline void llvm_emit_return_block(GenContext *context, BEValue *be_value
// Emit the exit block. // Emit the exit block.
llvm_emit_block(context, expr_block); llvm_emit_block(context, expr_block);
if (return_out) if (exit.block_return_out)
{ {
llvm_value_set_address_abi_aligned(be_value, return_out, type_lowered); llvm_value_set_address_abi_aligned(be_value, exit.block_return_out, type_lowered);
} }
else else
{ {
@@ -5047,16 +5048,13 @@ DONE:
context->return_out = old_ret_out; context->return_out = old_ret_out;
context->catch_block = error_block; context->catch_block = error_block;
context->error_var = error_out; context->error_var = error_out;
context->block_return_exit = saved_block_return_exit;
context->block_failable_exit = saved_block_failable_exit;
context->block_error_var = saved_block_error;
context->in_block--; context->in_block--;
} }
static inline void llvm_emit_expr_block(GenContext *context, BEValue *be_value, Expr *expr) static inline void llvm_emit_expr_block(GenContext *context, BEValue *be_value, Expr *expr)
{ {
llvm_emit_return_block(context, be_value, expr->type, expr->expr_block.first_stmt); llvm_emit_return_block(context, be_value, expr->type, expr->expr_block.first_stmt, expr->expr_block.block_exit_ref);
} }
static inline void llvm_emit_macro_block(GenContext *context, BEValue *be_value, Expr *expr) static inline void llvm_emit_macro_block(GenContext *context, BEValue *be_value, Expr *expr)
@@ -5098,7 +5096,8 @@ static inline void llvm_emit_macro_block(GenContext *context, BEValue *be_value,
llvm_emit_expr(context, &value, expr->macro_block.args[i]); llvm_emit_expr(context, &value, expr->macro_block.args[i]);
llvm_store_decl_raw(context, decl, llvm_load_value_store(context, &value)); llvm_store_decl_raw(context, decl, llvm_load_value_store(context, &value));
} }
llvm_emit_return_block(context, be_value, expr->type, expr->macro_block.first_stmt);
llvm_emit_return_block(context, be_value, expr->type, expr->macro_block.first_stmt, expr->macro_block.block_exit);
} }
LLVMValueRef llvm_emit_call_intrinsic(GenContext *context, unsigned intrinsic, LLVMTypeRef *types, unsigned type_count, LLVMValueRef llvm_emit_call_intrinsic(GenContext *context, unsigned intrinsic, LLVMTypeRef *types, unsigned type_count,

View File

@@ -437,7 +437,6 @@ void llvm_emit_function_body(GenContext *c, Decl *decl)
LLVMBasicBlockRef entry = LLVMAppendBasicBlockInContext(c->context, c->function, "entry"); LLVMBasicBlockRef entry = LLVMAppendBasicBlockInContext(c->context, c->function, "entry");
c->current_block = entry; c->current_block = entry;
c->current_block_is_target = true; c->current_block_is_target = true;
c->block_return_exit = NULL;
c->in_block = 0; c->in_block = 0;
c->builder = LLVMCreateBuilderInContext(c->context); c->builder = LLVMCreateBuilderInContext(c->context);
LLVMPositionBuilderAtEnd(c->builder, entry); LLVMPositionBuilderAtEnd(c->builder, entry);

View File

@@ -99,9 +99,6 @@ typedef struct
Module *code_module; Module *code_module;
LLVMValueRef return_out; LLVMValueRef return_out;
LLVMValueRef failable_out; LLVMValueRef failable_out;
LLVMBasicBlockRef block_return_exit;
LLVMBasicBlockRef block_failable_exit;
LLVMValueRef block_error_var;
BEValue retval; BEValue retval;
int in_block; int in_block;
bool current_block_is_target : 1; bool current_block_is_target : 1;

View File

@@ -228,8 +228,9 @@ static inline void llvm_emit_block_exit_return(GenContext *c, Ast *ast)
LLVMBasicBlockRef error_return_block = NULL; LLVMBasicBlockRef error_return_block = NULL;
LLVMValueRef error_out = NULL; LLVMValueRef error_out = NULL;
c->error_var = c->block_error_var; BlockExit *exit = *ast->return_stmt.block_exit_ref;
c->catch_block = c->block_failable_exit; c->error_var = exit->block_error_var;
c->catch_block = exit->block_failable_exit;
LLVMBasicBlockRef err_cleanup_block = NULL; LLVMBasicBlockRef err_cleanup_block = NULL;
Expr *ret_expr = ast->return_stmt.expr; Expr *ret_expr = ast->return_stmt.expr;
@@ -250,21 +251,21 @@ static inline void llvm_emit_block_exit_return(GenContext *c, Ast *ast)
POP_ERROR(); POP_ERROR();
llvm_emit_statement_chain(c, ast->return_stmt.cleanup); llvm_emit_statement_chain(c, ast->return_stmt.cleanup);
if (c->return_out && return_value.value) if (exit->block_return_out && return_value.value)
{ {
llvm_store_value_aligned(c, c->return_out, &return_value, type_alloca_alignment(return_value.type)); llvm_store_value_aligned(c, exit->block_return_out, &return_value, type_alloca_alignment(return_value.type));
} }
if (err_cleanup_block) if (err_cleanup_block)
{ {
llvm_emit_br(c, c->block_return_exit); llvm_emit_br(c, exit->block_return_exit);
llvm_emit_block(c, err_cleanup_block); llvm_emit_block(c, err_cleanup_block);
llvm_emit_statement_chain(c, ast->return_stmt.cleanup); llvm_emit_statement_chain(c, ast->return_stmt.cleanup);
llvm_emit_jmp(c, c->block_failable_exit); llvm_emit_jmp(c, exit->block_failable_exit);
} }
else else
{ {
llvm_emit_jmp(c, c->block_return_exit); llvm_emit_jmp(c, exit->block_return_exit);
} }
} }

View File

@@ -1163,6 +1163,8 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr,
ATTR_MEMBER, ATTR_MEMBER,
[ATTRIBUTE_INLINE] = ATTR_FUNC | ATTR_CALL, [ATTRIBUTE_INLINE] = ATTR_FUNC | ATTR_CALL,
[ATTRIBUTE_NOINLINE] = ATTR_FUNC | ATTR_CALL, [ATTRIBUTE_NOINLINE] = ATTR_FUNC | ATTR_CALL,
[ATTRIBUTE_NODISCARD] = ATTR_FUNC | ATTR_MACRO,
[ATTRIBUTE_MAYDISCARD] = ATTR_FUNC | ATTR_MACRO,
[ATTRIBUTE_BIGENDIAN] = ATTR_BITSTRUCT, [ATTRIBUTE_BIGENDIAN] = ATTR_BITSTRUCT,
[ATTRIBUTE_LITTLEENDIAN] = ATTR_BITSTRUCT, [ATTRIBUTE_LITTLEENDIAN] = ATTR_BITSTRUCT,
[ATTRIBUTE_USED] = (AttributeDomain)~ATTR_CALL, [ATTRIBUTE_USED] = (AttributeDomain)~ATTR_CALL,
@@ -1340,6 +1342,22 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr,
decl->func_decl.attr_noinline = true; decl->func_decl.attr_noinline = true;
decl->func_decl.attr_inline = false; decl->func_decl.attr_inline = false;
break; break;
case ATTRIBUTE_NODISCARD:
if (domain == ATTR_MACRO)
{
decl->macro_decl.attr_nodiscard = true;
break;
}
decl->func_decl.attr_nodiscard = true;
break;
case ATTRIBUTE_MAYDISCARD:
if (domain == ATTR_MACRO)
{
decl->macro_decl.attr_maydiscard = true;
break;
}
decl->func_decl.attr_maydiscard = true;
break;
case ATTRIBUTE_INLINE: case ATTRIBUTE_INLINE:
decl->func_decl.attr_inline = true; decl->func_decl.attr_inline = true;
decl->func_decl.attr_noinline = false; decl->func_decl.attr_noinline = false;
@@ -1689,9 +1707,27 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl)
if (!sema_analyse_attributes(context, decl, decl->attributes, ATTR_FUNC)) return decl_poison(decl); if (!sema_analyse_attributes(context, decl, decl->attributes, ATTR_FUNC)) return decl_poison(decl);
Type *func_type = sema_analyse_function_signature(context, decl->func_decl.function_signature.abi, &decl->func_decl.function_signature, true); Type *func_type = sema_analyse_function_signature(context, decl->func_decl.function_signature.abi, &decl->func_decl.function_signature, true);
decl->type = func_type; decl->type = func_type;
if (!func_type) return decl_poison(decl); if (!func_type) return decl_poison(decl);
TypeInfo *rtype_info = type_infoptr(decl->func_decl.function_signature.returntype);
assert(rtype_info);
Type *rtype = rtype_info->type->canonical;
if (decl->func_decl.attr_nodiscard)
{
if (rtype == type_void)
{
SEMA_ERROR(rtype_info, "@nodiscard cannot be used on functions returning 'void'.");
return decl_poison(decl);
}
}
if (decl->func_decl.attr_maydiscard)
{
if (!type_is_failable(rtype))
{
SEMA_ERROR(rtype_info, "@maydiscard can only be used on functions returning optional values.");
return decl_poison(decl);
}
}
if (decl->func_decl.type_parent) if (decl->func_decl.type_parent)
{ {
if (!sema_analyse_method(context, decl)) return decl_poison(decl); if (!sema_analyse_method(context, decl)) return decl_poison(decl);
@@ -1749,9 +1785,28 @@ static inline bool sema_analyse_macro(SemaContext *context, Decl *decl)
if (!sema_analyse_attributes(context, decl, decl->attributes, ATTR_MACRO)) return decl_poison(decl); if (!sema_analyse_attributes(context, decl, decl->attributes, ATTR_MACRO)) return decl_poison(decl);
if (decl->macro_decl.rtype)
TypeInfo *rtype = type_infoptr(decl->macro_decl.rtype); {
if (decl->macro_decl.rtype && !sema_resolve_type_info(context, rtype)) return decl_poison(decl); TypeInfo *rtype_info = type_infoptr(decl->macro_decl.rtype);
if (!sema_resolve_type_info(context, rtype_info)) return decl_poison(decl);
Type *rtype = rtype_info->type;
if (decl->macro_decl.attr_nodiscard)
{
if (rtype == type_void)
{
SEMA_ERROR(rtype_info, "@nodiscard cannot be used on macros returning 'void'.");
return decl_poison(decl);
}
}
if (decl->macro_decl.attr_maydiscard)
{
if (!type_is_failable(rtype))
{
SEMA_ERROR(rtype_info, "@maydiscard can only be used on macros returning optional values.");
return decl_poison(decl);
}
}
}
decl->macro_decl.unit = context->unit; decl->macro_decl.unit = context->unit;
Decl **parameters = decl->macro_decl.parameters; Decl **parameters = decl->macro_decl.parameters;
unsigned param_count = vec_size(parameters); unsigned param_count = vec_size(parameters);

View File

@@ -1611,10 +1611,26 @@ static inline bool sema_expr_analyse_func_invocation(SemaContext *context, Funct
SEMA_ERROR(expr, "Only '@pure' functions may be called, you can override this with an attribute."); SEMA_ERROR(expr, "Only '@pure' functions may be called, you can override this with an attribute.");
return false; return false;
} }
bool is_unused = expr->call_expr.result_unused;
if (!sema_expr_analyse_call_invocation(context, expr, callee, &failable)) return false; if (!sema_expr_analyse_call_invocation(context, expr, callee, &failable)) return false;
Type *rtype = prototype->rtype; Type *rtype = prototype->rtype;
if (is_unused && rtype != type_void && decl && decl->decl_kind == DECL_FUNC)
{
if (decl->func_decl.attr_nodiscard)
{
SEMA_ERROR(expr, "The result of the function must be used.");
return false;
}
if (type_is_failable(rtype) && !decl->func_decl.attr_maydiscard)
{
SEMA_ERROR(expr, "The optional result of the macro must be used.");
return false;
}
}
expr->type = type_get_opt_fail(rtype, failable); expr->type = type_get_opt_fail(rtype, failable);
return true; return true;
@@ -1882,6 +1898,9 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
macro_context.yield_context = context; macro_context.yield_context = context;
macro_context.original_inline_line = context->original_inline_line ? context->original_inline_line : call_expr->span.row; macro_context.original_inline_line = context->original_inline_line ? context->original_inline_line : call_expr->span.row;
BlockExit** block_exit_ref = MALLOCS(BlockExit*);
macro_context.block_exit_ref = block_exit_ref;
VECEACH(params, i) VECEACH(params, i)
{ {
Decl *param = params[i]; Decl *param = params[i];
@@ -1944,7 +1963,32 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
if (!sum_returns) return SCOPE_POP_ERROR(); if (!sum_returns) return SCOPE_POP_ERROR();
call_expr->type = type_get_opt_fail(sum_returns, failable); call_expr->type = type_get_opt_fail(sum_returns, failable);
} }
if (vec_size(macro_context.returns) == 1)
if (call_expr->call_expr.result_unused)
{
Type *type = call_expr->type;
if (type != type_void)
{
if (decl->macro_decl.attr_nodiscard)
{
SEMA_ERROR(call_expr, "The result of the macro must be used.");
return SCOPE_POP_ERROR();
}
if (type_is_failable(type) && !decl->macro_decl.attr_maydiscard)
{
SEMA_ERROR(call_expr, "The optional result of the macro must be used.");
return SCOPE_POP_ERROR();
}
}
}
unsigned returns_found = vec_size(macro_context.returns);
// We may have zero normal macro returns but the active scope still has a "jump end".
// In this case it is triggered by the @body()
if (!returns_found && macro_context.active_scope.jump_end)
{
is_no_return = true;
}
if (returns_found == 1)
{ {
Ast *ret = macro_context.returns[0]; Ast *ret = macro_context.returns[0];
Expr *result = ret ? ret->return_stmt.expr : NULL; Expr *result = ret ? ret->return_stmt.expr : NULL;
@@ -1962,6 +2006,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
call_expr->macro_block.first_stmt = body->compound_stmt.first_stmt; call_expr->macro_block.first_stmt = body->compound_stmt.first_stmt;
call_expr->macro_block.params = params; call_expr->macro_block.params = params;
call_expr->macro_block.args = args; call_expr->macro_block.args = args;
call_expr->macro_block.block_exit = block_exit_ref;
EXIT: EXIT:
assert(context->active_scope.defer_last == context->active_scope.defer_start); assert(context->active_scope.defer_last == context->active_scope.defer_start);
context->active_scope = old_scope; context->active_scope = old_scope;
@@ -2107,7 +2152,6 @@ static bool sema_analyse_body_expansion(SemaContext *macro_context, Expr *call)
SemaContext *context = macro_context->yield_context; SemaContext *context = macro_context->yield_context;
Decl **params = macro_context->yield_params; Decl **params = macro_context->yield_params;
Expr *func_expr = exprptr(call_expr->function); Expr *func_expr = exprptr(call_expr->function);
assert(func_expr->expr_kind == EXPR_MACRO_BODY_EXPANSION); assert(func_expr->expr_kind == EXPR_MACRO_BODY_EXPANSION);
expr_replace(call, func_expr); expr_replace(call, func_expr);
@@ -2115,6 +2159,7 @@ static bool sema_analyse_body_expansion(SemaContext *macro_context, Expr *call)
call->body_expansion_expr.declarations = macro_context->yield_params; call->body_expansion_expr.declarations = macro_context->yield_params;
AstId last_defer = context->active_scope.defer_last; AstId last_defer = context->active_scope.defer_last;
bool success; bool success;
bool ends_in_jump;
SCOPE_START SCOPE_START
if (macro_defer) if (macro_defer)
@@ -2133,15 +2178,20 @@ static bool sema_analyse_body_expansion(SemaContext *macro_context, Expr *call)
Decl *param = params[i]; Decl *param = params[i];
if (!sema_add_local(context, param)) return SCOPE_POP_ERROR(); if (!sema_add_local(context, param)) return SCOPE_POP_ERROR();
} }
call->body_expansion_expr.ast = ast_macro_copy(macro_context->yield_body); Ast *ast = call->body_expansion_expr.ast = ast_macro_copy(macro_context->yield_body);
success = sema_analyse_statement(context, call->body_expansion_expr.ast); success = sema_analyse_statement(context, ast);
assert(ast->ast_kind == AST_COMPOUND_STMT);
if (context->active_scope.jump_end)
{
macro_context->active_scope.jump_end = true;
}
if (first_defer) if (first_defer)
{ {
first_defer->defer_stmt.prev_defer = 0; first_defer->defer_stmt.prev_defer = 0;
context->active_scope.defer_last = last_defer; context->active_scope.defer_last = last_defer;
} }
SCOPE_END; SCOPE_END;
return success; return success;
} }
@@ -3473,6 +3523,7 @@ static inline bool sema_expr_analyse_access(SemaContext *context, Expr *expr)
if (parent->expr_kind != EXPR_TYPEINFO) if (parent->expr_kind != EXPR_TYPEINFO)
{ {
SEMA_ERROR(expr, "'typeid' can only be used with types, not values"); SEMA_ERROR(expr, "'typeid' can only be used with types, not values");
return false;
} }
expr->type = type_typeid; expr->type = type_typeid;
@@ -6439,6 +6490,10 @@ static inline bool sema_expr_analyse_expr_block(SemaContext *context, Type *infe
Ast **saved_returns = context_push_returns(context); Ast **saved_returns = context_push_returns(context);
Type *stored_block_type = context->expected_block_type; Type *stored_block_type = context->expected_block_type;
context->expected_block_type = infer_type; context->expected_block_type = infer_type;
BlockExit **ref = MALLOCS(BlockExit*);
BlockExit **stored_block_exit = context->block_exit_ref;
context->block_exit_ref = ref;
expr->expr_block.block_exit_ref = ref;
SCOPE_START_WITH_FLAGS(SCOPE_EXPR_BLOCK) SCOPE_START_WITH_FLAGS(SCOPE_EXPR_BLOCK)
context->block_return_defer = context->active_scope.defer_last; context->block_return_defer = context->active_scope.defer_last;
@@ -6487,6 +6542,7 @@ static inline bool sema_expr_analyse_expr_block(SemaContext *context, Type *infe
context_pop_defers(context, &stmt->next); context_pop_defers(context, &stmt->next);
SCOPE_END; SCOPE_END;
context->expected_block_type = stored_block_type; context->expected_block_type = stored_block_type;
context->block_exit_ref = stored_block_exit;
context_pop_returns(context, saved_returns); context_pop_returns(context, saved_returns);
return success; return success;

View File

@@ -126,6 +126,7 @@ static inline bool sema_analyse_block_exit_stmt(SemaContext *context, Ast *state
return false; return false;
} }
} }
statement->return_stmt.block_exit_ref = context->block_exit_ref;
statement->return_stmt.cleanup = context_get_defers(context, context->active_scope.defer_last, context->block_return_defer); statement->return_stmt.cleanup = context_get_defers(context, context->active_scope.defer_last, context->block_return_defer);
vec_add(context->returns, statement); vec_add(context->returns, statement);
return true; return true;
@@ -706,10 +707,28 @@ static inline bool sema_analyse_declare_stmt(SemaContext *context, Ast *statemen
return sema_analyse_var_decl(context, statement->declare_stmt, true); return sema_analyse_var_decl(context, statement->declare_stmt, true);
} }
static inline bool expr_is_assign(Expr *expr)
{
switch (expr->expr_kind)
{
case EXPR_DECL:
case EXPR_BITASSIGN:
case EXPR_SLICE_ASSIGN:
return true;
case EXPR_BINARY:
return expr->binary_expr.operator >= BINARYOP_ASSIGN;
default:
return false;
}
}
static inline bool sema_analyse_expr_stmt(SemaContext *context, Ast *statement) static inline bool sema_analyse_expr_stmt(SemaContext *context, Ast *statement)
{ {
if (!sema_analyse_expr(context, statement->expr_stmt)) return false; Expr *expr = statement->expr_stmt;
return true; if (expr->expr_kind == EXPR_CALL)
{
expr->call_expr.result_unused = true;
}
return sema_analyse_expr(context, expr);
} }
bool sema_analyse_defer_stmt_body(SemaContext *context, Ast *statement, Ast *body) bool sema_analyse_defer_stmt_body(SemaContext *context, Ast *statement, Ast *body)

View File

@@ -230,6 +230,8 @@ void symtab_init(uint32_t capacity)
attribute_list[ATTRIBUTE_PURE] = kw_at_pure; attribute_list[ATTRIBUTE_PURE] = kw_at_pure;
attribute_list[ATTRIBUTE_REFLECT] = KW_DEF("@reflect"); attribute_list[ATTRIBUTE_REFLECT] = KW_DEF("@reflect");
attribute_list[ATTRIBUTE_AUTOIMPORT] = KW_DEF("@autoimport"); attribute_list[ATTRIBUTE_AUTOIMPORT] = KW_DEF("@autoimport");
attribute_list[ATTRIBUTE_MAYDISCARD] = KW_DEF("@maydiscard");
attribute_list[ATTRIBUTE_NODISCARD] = KW_DEF("@nodiscard");
for (unsigned i = 0; i < NUMBER_OF_ATTRIBUTES; i++) for (unsigned i = 0; i < NUMBER_OF_ATTRIBUTES; i++)
{ {

View File

@@ -1501,6 +1501,14 @@ Type *type_find_max_type(Type *type, Type *other)
{ {
return other; return other;
} }
if (other->type_kind == TYPE_POINTER)
{
Type *other_pointer = other->pointer;
if (other_pointer->type_kind == TYPE_ARRAY && other_pointer->array.base->canonical == array_base)
{
return type_get_subarray(array_base);
}
}
} }
if (type->pointer->type_kind == TYPE_VECTOR) if (type->pointer->type_kind == TYPE_VECTOR)
{ {

View File

@@ -17,7 +17,7 @@ fn void Summary.print(Summary *s, CFile out)
// We don't have a native printf in C3 yet, so use libc, // We don't have a native printf in C3 yet, so use libc,
// which is not all that nice for the strings but... // which is not all that nice for the strings but...
char[] title = s.title ? *s.title : "missing"; char[] title = s.title ? *s.title : "missing";
libc::fprintf(out, "Summary({ .title = %.*s, .ok = %s})", (int)title.len, title.ptr, s.ok ? "true" : "false"); libc::fprintf(out, "Summary({ .title = %.*s, .ok = %s})", (int)title.len, title.ptr, s.ok ? (char*)"true" : (char*)"false");
} }
fn bool contains(char[] haystack, char[] needle) fn bool contains(char[] haystack, char[] needle)
@@ -144,7 +144,7 @@ fn void main()
bool! has_title = readWhetherTitleNonEmpty(url); bool! has_title = readWhetherTitleNonEmpty(url);
// This looks a bit less than elegant, but as you see it's mostly due to having to // This looks a bit less than elegant, but as you see it's mostly due to having to
// use printf here. // use printf here.
libc::printf(" Has title: %s vs %s\n", bool_to_string(has_title) ?? nameFromError(catch(has_title)), (has_title ?? false) ? "true" : "false"); libc::printf(" Has title: %s vs %s\n", bool_to_string(has_title) ?? nameFromError(catch(has_title)), (has_title ?? false) ? (char*)"true" : (char*)"false");
} }
} }
@@ -757,8 +757,9 @@ err_retblock: ; preds = %error3, %error
define i8* @test.bool_to_string(i8 zeroext %0) #0 { define i8* @test.bool_to_string(i8 zeroext %0) #0 {
entry: entry:
%1 = trunc i8 %0 to i1 %1 = trunc i8 %0 to i1
%ternary = select i1 %1, i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str.7, i32 0, i32 0), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.8, i32 0, i32 0) %ternary = select i1 %1, %"char[]" { i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str.7, i32 0, i32 0), i64 4 }, %"char[]" { i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.8, i32 0, i32 0), i64 5 }
ret i8* %ternary %2 = extractvalue %"char[]" %ternary, 0
ret i8* %2
} }
; Function Attrs: nounwind ; Function Attrs: nounwind

View File

@@ -13,13 +13,13 @@ struct Bar
int x; int x;
} }
fn void! test1() fn void! test1() @maydiscard
{ {
Bar! x = Foo.MY_VAL1!; Bar! x = Foo.MY_VAL1!;
Bar y = x?; Bar y = x?;
} }
fn void! test2() fn void! test2() @maydiscard
{ {
Bar! x = {}; Bar! x = {};
Bar y = x?; Bar y = x?;

View File

@@ -87,17 +87,17 @@ loop.exit: ; preds = %loop.body
br label %loop.body2 br label %loop.body2
loop.body2: ; preds = %loop.exit loop.body2: ; preds = %loop.exit
call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.13, i32 0, i32 0)) call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.10, i32 0, i32 0))
store i32 3, i32* %i3, align 4 store i32 3, i32* %i3, align 4
call void (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str.14, i32 0, i32 0)) call void (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str.11, i32 0, i32 0))
call void (i8*, ...) @printf(i8* getelementptr inbounds ([22 x i8], [22 x i8]* @.str.15, i32 0, i32 0)) call void (i8*, ...) @printf(i8* getelementptr inbounds ([22 x i8], [22 x i8]* @.str.12, i32 0, i32 0))
%3 = load i32, i32* %i3, align 4 %3 = load i32, i32* %i3, align 4
call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.16, i32 0, i32 0), i32 %3) call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.13, i32 0, i32 0), i32 %3)
call void (i8*, ...) @printf(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.17, i32 0, i32 0)) call void (i8*, ...) @printf(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.14, i32 0, i32 0))
ret i64 0 ret i64 0
loop.exit4: ; No predecessors! loop.exit4: ; No predecessors!
call void (i8*, ...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @.str.21, i32 0, i32 0)) call void (i8*, ...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @.str.15, i32 0, i32 0))
ret i64 0 ret i64 0
} }

File diff suppressed because it is too large Load Diff

View File

@@ -13,13 +13,13 @@ struct Bar
int x; int x;
} }
fn void! test1() fn void! test1() @maydiscard
{ {
Bar! x = Foo.MY_VAL1!; Bar! x = Foo.MY_VAL1!;
Bar y = x?; Bar y = x?;
} }
fn void! test2() fn void! test2() @maydiscard
{ {
Bar! x = {}; Bar! x = {};
Bar y = x?; Bar y = x?;

View File

@@ -87,17 +87,17 @@ loop.exit: ; preds = %loop.body
br label %loop.body2 br label %loop.body2
loop.body2: ; preds = %loop.exit loop.body2: ; preds = %loop.exit
call void (ptr, ...) @printf(ptr @.str.13) call void (ptr, ...) @printf(ptr @.str.10)
store i32 3, ptr %i3, align 4 store i32 3, ptr %i3, align 4
call void (ptr, ...) @printf(ptr @.str.14) call void (ptr, ...) @printf(ptr @.str.11)
call void (ptr, ...) @printf(ptr @.str.15) call void (ptr, ...) @printf(ptr @.str.12)
%3 = load i32, ptr %i3, align 4 %3 = load i32, ptr %i3, align 4
call void (ptr, ...) @printf(ptr @.str.16, i32 %3) call void (ptr, ...) @printf(ptr @.str.13, i32 %3)
call void (ptr, ...) @printf(ptr @.str.17) call void (ptr, ...) @printf(ptr @.str.14)
ret i64 0 ret i64 0
loop.exit4: ; No predecessors! loop.exit4: ; No predecessors!
call void (ptr, ...) @printf(ptr @.str.21) call void (ptr, ...) @printf(ptr @.str.15)
ret i64 0 ret i64 0
} }