diff --git a/resources/examples/guess_number.c3 b/resources/examples/guess_number.c3 new file mode 100644 index 000000000..f9abc9529 --- /dev/null +++ b/resources/examples/guess_number.c3 @@ -0,0 +1,95 @@ +module guess_number; +import std::mem; +import std::io; +import libc; + +extern fn void printf(char*, ...); +extern fn isize getline(char** linep, usize* linecapp, CFile stream); + +struct Game +{ + int answer; + bool done; + int guesses; + int high; +} + +optnum InputResult +{ + NOT_AN_INT, + FAILED_TO_READ, +} + +int err_count = 0; + +fn int! askGuess(int high) +{ + printf("Guess a number between 1 and %d: ", high); + char[] text = readLine()?; + char* end = null; + int value = (int)libc::strtol(text.ptr, &end, 10); + if (end && end[0] >= ' ') return InputResult.NOT_AN_INT!; + return value; +} + +fn char[]! readLine() +{ + char* chars = mem::talloc(1024)?; + isize loaded = getline(&chars, &&(usize)1023, @libc::stdin()); + if (loaded < 0) return InputResult.FAILED_TO_READ!; + chars[loaded] = 0; + return chars[0..(loaded - 1)]; +} + +fn int! askGuessMulti(int high) + { + while (true) + { + int! result = askGuess(high); + if (catch(result) == InputResult.NOT_AN_INT) + { + printf("I didn't understand that.\n"); + err_count++; + continue; + } + return result; + } + $unreachable; +} + +fn void! Game.play(Game *game) +{ + while (!game.done) + { + int guess = askGuessMulti(game.high)?; + game.report(guess); + game.update(guess); + } +} + +fn void Game.report(Game *game, int guess) +{ + char[] desc = {| + if (guess < game.answer) return "too low"; + if (guess > game.answer) return "too high"; + return "the answer"; + |}; + printf("%d is %.*s.\n", guess, (int)desc.len, desc.ptr); +} + +fn void Game.update(Game *game, int guess) +{ + if (guess == game.answer) game.done = true; + game.guesses++; +} + +fn void! main() +{ + libc::srand((int)libc::clock()); + int high = 100; + int answer = libc::rand() % high + 1; + Game game = { .answer = answer, .high = high }; + game.play(); + printf("Finished in %d guesses.\n", game.guesses); + printf("Total input errors: %d.\n", err_count); +} \ No newline at end of file diff --git a/resources/lib/std/io.c3 b/resources/lib/std/io.c3 index 718d878b1..8b336d277 100644 --- a/resources/lib/std/io.c3 +++ b/resources/lib/std/io.c3 @@ -4,19 +4,18 @@ module std::io; import std::mem; import libc; - - +import std::env; struct File { - void *file; + CFile file; } + extern fn int _puts(char* message) @extname("puts"); extern fn int printf(char* message, ...); extern fn int _putchar(char c) @extname("putchar"); -extern File *__stdinp; fn int putchar(char c) @inline { diff --git a/resources/lib/std/libc.c3 b/resources/lib/std/libc.c3 index 952cded6e..b29239b7d 100644 --- a/resources/lib/std/libc.c3 +++ b/resources/lib/std/libc.c3 @@ -9,6 +9,7 @@ import std::os::macos; import std::os::windows; // stdlib + // Constants need to be per os/arch const int EXIT_FAILURE = 1; const int EXIT_SUCCESS = 0; @@ -166,11 +167,11 @@ enum Errno : ErrnoType fn Errno errno() { $if (env::OS_TYPE == OsType.WIN32): - return windows::errno(); + return (Errno)windows::errno(); $elif (env::OS_TYPE == OsType.MACOSX): - return macos::errno(); + return (Errno)macos::errno(); $elif (env::OS_TYPE == OsType.LINUX): - return linux::errno(); + return (Errno)linux::errno(); $else: return Errno.ENOTRECOVERABLE; $endif; @@ -181,10 +182,10 @@ define TerminateFunction = fn void(); define CompareFunction = fn int(void*, void*); extern fn double atof(char* str); extern fn int atoi(char* str); -extern fn long atol(char* str); +extern fn CLongLong atoll(char* str); extern fn double strtod(char* str, char** endptr); -extern fn long strtol(char* str, char** endptr, int base); -extern fn ulong stroul(char* str, char** endptr, int base); +extern fn CLong strtol(char* str, char** endptr, int base); +extern fn CULong stroul(char* str, char** endptr, int base); extern fn void abort(); extern fn void atexit(TerminateFunction f); extern fn void exit(int status); @@ -234,6 +235,28 @@ extern fn void* realloc(void* ptr, usize size); define Fpos = long; define CFile = void*; +$switch (env::OS_TYPE): +$case OsType.LINUX: + extern CFile __stdin @extname("stdin"); + extern CFile __stdout @extname("stdout"); + extern CFile __stderr @extname("stderr"); + macro CFile stdin() { return __stdin; } + macro CFile stdout() { return __stdout; } + macro CFile stderr() { return __stderr; } +$case OsType.MACOSX: + extern CFile __stdinp; + extern CFile __stdoutp; + extern CFile __stderrp; + macro CFile stdin() { return __stdinp; } + macro CFile stdout() { return __stdoutp; } + macro CFile stderr() { return __stderrp; } +$case OsType.WIN32: + extern fn CFile __acrt_iob_func(CInt c); + macro CFile stdin() { return __acrt_iob_func(0); } + macro CFile stdout() { return __acrt_iob_func(1); } + macro CFile stderr() { return __acrt_iob_func(2); } +$default: +$endswitch; // The following needs to be set per arch+os // For now I have simply pulled the defaults from MacOS diff --git a/resources/lib/std/os/linux.c3 b/resources/lib/std/os/linux.c3 index 9c477ae66..1e738a329 100644 --- a/resources/lib/std/os/linux.c3 +++ b/resources/lib/std/os/linux.c3 @@ -10,9 +10,9 @@ fn int errno() @inline return *__errno_location(); } -fn void errno_set(int errno) +fn void errno_set(int err) { - *(__errno_location()) = errno; + *(__errno_location()) = err; } $endif; diff --git a/resources/lib/std/os/macos.c3 b/resources/lib/std/os/macos.c3 index bb543c069..5bea3eddd 100644 --- a/resources/lib/std/os/macos.c3 +++ b/resources/lib/std/os/macos.c3 @@ -2,14 +2,15 @@ module std::os::macos; import std::env; $if (env::OS_TYPE == OsType.MACOSX): + extern fn int* __error(); fn int errno() @inline { return *__error(); } -fn void errno_set(int errno) +fn void errno_set(int err) { - *(__error()) = errno; + *(__error()) = err; } $endif; diff --git a/resources/lib/std/os/windows.c3 b/resources/lib/std/os/windows.c3 index d7729920d..d634ed918 100644 --- a/resources/lib/std/os/windows.c3 +++ b/resources/lib/std/os/windows.c3 @@ -2,6 +2,7 @@ module std::os::windows; import std::env; $if (env::OS_TYPE == OsType.WIN32): + extern fn int getLastError() @stdcall @extname("GetLastError"); fn int errno() @inline { diff --git a/src/compiler/bigint.c b/src/compiler/bigint.c index d67078199..109d415c5 100644 --- a/src/compiler/bigint.c +++ b/src/compiler/bigint.c @@ -721,6 +721,23 @@ bool int_is_zero(Int op) return !op.i.high && !op.i.low; } +unsigned int_bits_needed(Int op) +{ + TypeKind kind = op.type; + Int128 i = op.i; + int bits_used; + if (type_kind_is_signed(kind)) + { + if (i128_is_neg(i)) + { + i = i128_neg(i); + i = i128_sub64(i, 1); + } + return (unsigned) (1 + 128 - i128_clz(&i)); + } + return (unsigned) (128 - i128_clz(&i)); +} + Int int_add(Int op1, Int op2) { assert(op1.type == op2.type); diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 0bba3dfe9..effae5064 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -56,6 +56,7 @@ typedef struct TypeInfo_ TypeInfo; typedef struct Expr_ Expr; typedef struct Module_ Module; typedef struct Type_ Type; +typedef Type CanonicalType; typedef unsigned AstId; @@ -259,7 +260,7 @@ typedef struct struct Type_ { TypeKind type_kind; - Type *canonical; + CanonicalType *canonical; const char *name; Type **type_cache; void *backend_type; @@ -1428,7 +1429,9 @@ typedef struct typedef struct SemaContext_ { + // Evaluated in this. CompilationUnit *unit; + // Compiled in this unit. CompilationUnit *compilation_unit; Decl *current_function; Decl *current_macro; @@ -1709,6 +1712,7 @@ bool int_comp(Int op1, Int op2, BinaryOp op); uint64_t int_to_u64(Int op); int64_t int_to_i64(Int op); bool int_is_zero(Int op); +unsigned int_bits_needed(Int op); bool int_fits(Int op1, TypeKind kind); Int int_rightmost_bits(Int op, unsigned to_bits, TypeKind result_type); Int int_conv(Int op, TypeKind to_type); @@ -1851,7 +1855,9 @@ const char *decl_to_name(Decl *decl); static inline Decl *decl_raw(Decl *decl); static inline bool decl_ok(Decl *decl) { return !decl || decl->decl_kind != DECL_POISONED; } -static inline bool decl_poison(Decl *decl) { decl->decl_kind = DECL_POISONED; decl->resolve_status = RESOLVE_DONE; return false; } +static inline bool decl_poison(Decl *decl) { + decl->decl_kind = DECL_POISONED; decl->resolve_status = RESOLVE_DONE; return false; +} static inline bool decl_is_struct_type(Decl *decl); static inline bool decl_is_callable_type(Decl *decl); static inline bool decl_is_user_defined_type(Decl *decl); @@ -2112,6 +2118,8 @@ static inline bool type_is_integer_or_bool_kind(Type *type); static inline bool type_is_numeric(Type *type); static inline bool type_underlying_is_numeric(Type *type); static inline bool type_is_pointer(Type *type); +static inline CanonicalType *type_pointer_type(Type *type); +static inline bool type_is_arraylike(Type *type); static inline bool type_is_promotable_float(Type *type); static inline bool type_is_promotable_integer(Type *type); static inline bool type_is_signed(Type *type); @@ -2128,6 +2136,7 @@ bool type_is_float_or_float_vector(Type *type); bool type_may_have_sub_elements(Type *type); static inline bool type_ok(Type *type); TypeSize type_size(Type *type); +#define type_bit_size(type) (type_size(type) * 8) const char *type_to_error_string(Type *type); const char *type_quoted_error_string(Type *type); @@ -2217,6 +2226,18 @@ static inline bool type_info_poison(TypeInfo *type) return false; } +static inline bool type_is_arraylike(Type *type) +{ + DECL_TYPE_KIND_REAL(kind, type); + return kind == TYPE_ARRAY || kind == TYPE_VECTOR; +} + +static inline CanonicalType *type_pointer_type(Type *type) +{ + CanonicalType *res = type->canonical; + if (res->type_kind != TYPE_POINTER) return NULL; + return res->pointer; +} static inline bool type_is_pointer(Type *type) { diff --git a/src/compiler/context.c b/src/compiler/context.c index a593ba5b3..e82b2ad7c 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -123,8 +123,7 @@ bool context_set_module(ParseContext *context, Path *path, TokenId *generic_para void unit_register_external_symbol(CompilationUnit *unit, Decl *decl) { - if (decl->decl_kind == DECL_MACRO) return; - assert(decl->external_name && "Missing external name"); + if (!decl->module || decl->module == unit->module || !decl->external_name) return; Decl *prev = stable_get(&unit->external_symbols, decl->external_name); if (prev) return; if (decl != stable_set(&unit->external_symbols, decl->external_name, decl)) diff --git a/src/compiler/number.c b/src/compiler/number.c index 94b4f4642..e77a234e3 100644 --- a/src/compiler/number.c +++ b/src/compiler/number.c @@ -76,11 +76,16 @@ static inline bool compare_fps(Real left, Real right, BinaryOp op) bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp op) { bool is_eq; + switch (left->const_kind) { case CONST_BOOL: return compare_bool(left->b, right->b, op); case CONST_INTEGER: + if (right->const_kind == CONST_ENUM) + { + return int_comp(left->ixx, right->enum_val->enum_constant.expr->const_expr.ixx, op); + } return int_comp(left->ixx, right->ixx, op); case CONST_FLOAT: return compare_fps(left->fxx.f, right->fxx.f, op); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 14499f6ea..8b4936334 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -2124,7 +2124,7 @@ static bool sema_analyse_parameterized_define(SemaContext *c, Decl *decl) const char *name_str = TOKSTR(name); Decl *symbol = module_find_symbol(instantiated_module, name_str); assert(symbol); - unit_register_external_symbol(c->unit, symbol); + unit_register_external_symbol(c->compilation_unit, symbol); switch (decl->define_decl.define_kind) { case DEFINE_IDENT_GENERIC: diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index d8e8df6fd..31bc58062 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -14,10 +14,10 @@ static inline bool sema_expr_analyse_binary(SemaContext *context, Expr *expr); static inline bool sema_cast_rvalue(SemaContext *context, Expr *expr); static Expr *expr_access_inline_member(Expr *parent, Decl *parent_decl); static inline void expr_set_as_const_list(Expr *expr, ConstInitializer *list); -static inline bool is_const(Expr *expr); static inline bool sema_expr_analyse_builtin(SemaContext *context, Expr *expr, bool throw_error); static bool sema_check_stmt_compile_time(SemaContext *context, Ast *ast); static bool binary_arithmetic_promotion(SemaContext *context, Expr *left, Expr *right, Type *left_type, Type *right_type, Expr *parent, const char *error_message); +static inline bool expr_both_const(Expr *left, Expr *right); static inline void expr_set_as_const_list(Expr *expr, ConstInitializer *list) { expr->expr_kind = EXPR_CONST; @@ -27,16 +27,12 @@ static inline void expr_set_as_const_list(Expr *expr, ConstInitializer *list) static bool sema_decay_array_pointers(Expr *expr) { - Type *expr_type = expr->type->canonical; - if (expr_type->type_kind != TYPE_POINTER) return true; - switch (expr_type->pointer->type_kind) - { - case TYPE_ARRAY: - case TYPE_VECTOR: - return cast_implicit(expr, type_get_ptr(expr_type->pointer->array.base)); - default: - return true; - } + + CanonicalType *pointer_type = type_pointer_type(type_no_fail(expr->type)); + + if (!pointer_type || !type_is_arraylike(pointer_type)) return true; + + return cast_implicit(expr, type_get_opt_fail(type_get_ptr(pointer_type->array.base), IS_FAILABLE(expr))); } int BINOP_PREC_REQ[BINARYOP_LAST + 1] = @@ -71,12 +67,6 @@ static Expr *expr_access_inline_member(Expr *parent, Decl *parent_decl) return embedded_struct; } -#define IS_CONST(_x) ((_x)->expr_kind == EXPR_CONST) - -static inline bool is_const(Expr *expr) -{ - return expr->expr_kind == EXPR_CONST; -} static inline bool expr_both_const(Expr *left, Expr *right) { @@ -271,38 +261,26 @@ static inline bool expr_list_is_constant_eval(Expr **exprs, ConstantEvalKind eva return true; } +// Check if the assignment fits static bool sema_bit_assignment_check(Expr *right, Decl *member) { - if (right->expr_kind != EXPR_CONST || !type_is_integer(right->type)) return true; + // Don't check non-consts and non integers. + if (!IS_CONST(right) || !type_is_integer(right->type)) return true; unsigned bits = member->var.end_bit - member->var.start_bit + 1; - if (bits >= type_size(right->type) * 8 || int_is_zero(right->const_expr.ixx)) return true; + // If we have enough bits to fit, then we're done. + if (bits >= type_bit_size(right->type) || int_is_zero(right->const_expr.ixx)) return true; - // Check that we're not assigning consts that will be cut. - - TypeKind kind = right->const_expr.ixx.type; - Int128 i = right->const_expr.ixx.i; - int bits_used; - if (type_kind_is_signed(kind)) + if (int_bits_needed(right->const_expr.ixx) > bits) { - if (i128_is_neg(i)) - { - i = i128_neg(i); - i = i128_sub64(i, 1); - } - bits_used = (int) (1 + 128 - i128_clz(&i)); + SEMA_ERROR(right, + "This constant would be truncated if stored in the bitstruct, do you need a wider bit range?"); + return false; } - else - { - bits_used = (int) (128 - i128_clz(&i)); - } - if (bits_used <= bits) return true; - - SEMA_ERROR(right, - "This constant would be truncated if stored in the bitstruct, do you need a wider bit range?"); - return false; + return true; } + bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) { RETRY: @@ -488,7 +466,7 @@ void expr_insert_deref(Expr *original) } -static void expr_unify_binary(Expr *expr, Expr *left, Expr *right) +static void expr_unify_binary_failability(Expr *expr, Expr *left, Expr *right) { expr->type = type_get_opt_fail(left->type, IS_FAILABLE(right)); } @@ -792,7 +770,7 @@ static inline bool sema_expr_analyse_ternary(SemaContext *context, Expr *expr) expr_replace(expr, path ? left : right); } - expr_unify_binary(expr, left, right); + expr_unify_binary_failability(expr, left, right); return true; } @@ -853,8 +831,6 @@ static inline bool find_possible_inferred_identifier(Type *to, Expr *expr) } - - static inline bool sema_expr_analyse_identifier(SemaContext *context, Type *to, Expr *expr) { Decl *ambiguous_decl = NULL; @@ -876,6 +852,7 @@ static inline bool sema_expr_analyse_identifier(SemaContext *context, Type *to, expr->identifier_expr.identifier, expr->identifier_expr.path, true); + (void)decl; assert(!decl_ok(decl)); return false; } @@ -910,6 +887,7 @@ static inline bool sema_expr_analyse_identifier(SemaContext *context, Type *to, { if (!sema_analyse_decl(context, decl)) return decl_poison(decl); } + unit_register_external_symbol(context->compilation_unit, decl); if (decl->decl_kind == DECL_VAR) { decl->var.is_read = true; @@ -2022,7 +2000,6 @@ static bool sema_analyse_body_expansion(SemaContext *macro_context, Expr *call) assert(macro); assert(macro->macro_decl.block_parameter.index); - // TODO handle named arguments ExprCall *call_expr = &call->call_expr; if (vec_size(call_expr->body_arguments)) { @@ -2034,6 +2011,7 @@ static bool sema_analyse_body_expansion(SemaContext *macro_context, Expr *call) SEMA_ERROR(call, "Expanding parameters is not allowed for macro invocations."); return false; } + // Theoretically we could support named arguments, but that's unnecessary. unsigned expressions = vec_size(call_expr->arguments); if (expressions != vec_size(macro->macro_decl.body_parameters)) { @@ -2070,24 +2048,29 @@ static bool sema_analyse_body_expansion(SemaContext *macro_context, Expr *call) return success; } -bool sema_expr_analyse_general_call(SemaContext *context, Expr *expr, Decl *decl, Expr *struct_var, bool is_macro, bool failable) +static inline bool sema_analyse_call_attributes(SemaContext *context, Decl *decl, Expr *call_expr) { + Attr **attributes = call_expr->call_expr.attributes; int force_inline = -1; - VECEACH(expr->call_expr.attributes, i) + VECEACH(attributes, i) { - Attr *attr = expr->call_expr.attributes[i]; + Attr *attr = attributes[i]; AttributeType attribute = sema_analyse_attribute(context, attr, ATTR_CALL); - if (attribute == ATTRIBUTE_NONE) return expr_poison(expr); - - bool had = false; + if (attribute == ATTRIBUTE_NONE) return false; switch (attribute) { case ATTRIBUTE_INLINE: case ATTRIBUTE_NOINLINE: - if (decl->decl_kind != DECL_FUNC) + if (decl && decl->decl_kind != DECL_FUNC) { - SEMA_TOKID_ERROR(attr->name, "Inline / noinline attribute is only allowed for direct function/method calls"); - return expr_poison(expr); + SEMA_TOKID_ERROR(attr->name, + "Inline / noinline attribute is only allowed for direct function/method calls"); + return false; + } + if (force_inline != -1) + { + SEMA_TOKID_ERROR(attr->name, "Only a single inline / noinline attribute is allowed on a call."); + return false; } force_inline = attribute == ATTRIBUTE_INLINE ? 1 : 0; break; @@ -2095,7 +2078,17 @@ bool sema_expr_analyse_general_call(SemaContext *context, Expr *expr, Decl *decl UNREACHABLE; } } + if (force_inline != -1) + { + call_expr->call_expr.force_inline = force_inline == 1; + call_expr->call_expr.force_noinline = force_inline == 0; + } + return true; +} +bool sema_expr_analyse_general_call(SemaContext *context, Expr *expr, Decl *decl, Expr *struct_var, bool is_macro, bool failable) +{ expr->call_expr.is_type_method = struct_var != NULL; + if (!sema_analyse_call_attributes(context, decl, expr)) return expr_poison(expr); if (decl == NULL) { return sema_expr_analyse_var_call(context, expr, type_flatten_distinct_failable(expr->call_expr.function->type), failable); @@ -2125,8 +2118,6 @@ bool sema_expr_analyse_general_call(SemaContext *context, Expr *expr, Decl *decl return false; } expr->call_expr.func_ref = decl; - expr->call_expr.force_inline = force_inline == 1; - expr->call_expr.force_noinline = force_inline == 0; return sema_expr_analyse_func_call(context, expr, decl, struct_var, failable); case DECL_GENERIC: if (is_macro) @@ -4770,7 +4761,7 @@ static bool sema_expr_analyse_add(SemaContext *context, Expr *expr, Expr *left, } // 6. Set the type & other properties. - expr_unify_binary(expr, left, right); + expr_unify_binary_failability(expr, left, right); return true; @@ -4953,7 +4944,7 @@ static bool sema_expr_analyse_bit(SemaContext *context, Expr *expr, Expr *left, } // 5. Assign the type - expr_unify_binary(expr, left, right); + expr_unify_binary_failability(expr, left, right); return true; } @@ -5122,9 +5113,9 @@ static void cast_to_max_bit_size(SemaContext *context, Expr *left, Expr *right, static bool sema_is_unsigned_always_false_comparison(SemaContext *context, Expr *expr, Expr *left, Expr *right) { if (context->active_scope.flags & SCOPE_MACRO) return true; - if (!is_const(left) && !is_const(right)) return true; + if (!expr_is_const(left) && !expr_is_const(right)) return true; if (!type_is_integer(left->type)) return true; - if (is_const(left) && type_is_unsigned(type_flatten_distinct(right->type))) + if (expr_is_const(left) && type_is_unsigned(type_flatten_distinct(right->type))) { if (int_is_neg(left->const_expr.ixx)) { @@ -5146,7 +5137,7 @@ static bool sema_is_unsigned_always_false_comparison(SemaContext *context, Expr return true; } } - if (!is_const(right) || !type_is_unsigned(type_flatten_distinct(left->type))) return true; + if (!expr_is_const(right) || !type_is_unsigned(type_flatten_distinct(left->type))) return true; if (int_is_neg(right->const_expr.ixx)) { SEMA_ERROR(right, "Comparing an unsigned value with a negative constant is only allowed inside of macros."); @@ -5957,7 +5948,7 @@ static inline bool sema_expr_analyse_typeid(SemaContext *context, Expr *expr) } -static inline bool sema_expr_analyse_expr_block(SemaContext *context, Expr *expr) +static inline bool sema_expr_analyse_expr_block(SemaContext *context, Type *infer_type, Expr *expr) { bool success = true; expr->type = type_void; @@ -5965,7 +5956,7 @@ static inline bool sema_expr_analyse_expr_block(SemaContext *context, Expr *expr Type *prev_expected_block_type = context->expected_block_type; Ast **saved_returns = context_push_returns(context); Type *stored_block_type = context->expected_block_type; - context->expected_block_type = NULL; + context->expected_block_type = infer_type; SCOPE_START_WITH_FLAGS(SCOPE_EXPR_BLOCK) PUSH_CONTINUE(NULL); @@ -6934,7 +6925,7 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr) case EXPR_COMPOUND_LITERAL: return sema_expr_analyse_compound_literal(context, expr); case EXPR_EXPR_BLOCK: - return sema_expr_analyse_expr_block(context, expr); + return sema_expr_analyse_expr_block(context, NULL, expr); case EXPR_RETHROW: return sema_expr_analyse_rethrow(context, expr); case EXPR_CONST: @@ -7284,6 +7275,9 @@ bool sema_analyse_inferred_expr(SemaContext *context, Type *infer_type, Expr *ex case EXPR_DESIGNATED_INITIALIZER_LIST: if (!sema_expr_analyse_initializer_list(context, infer_type, expr)) return expr_poison(expr); break; + case EXPR_EXPR_BLOCK: + if (!sema_expr_analyse_expr_block(context, infer_type, expr)) return expr_poison(expr); + break; case EXPR_CONST_IDENTIFIER: if (!sema_expr_analyse_identifier(context, infer_type, expr)) return expr_poison(expr); break; diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index fad4c15a5..60530a9b7 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -52,6 +52,8 @@ void context_change_scope_with_flags(SemaContext *context, ScopeFlags flags); #define PUSH_BREAKCONT(ast) PUSH_CONTINUE(ast); PUSH_BREAK(ast) #define POP_BREAKCONT() POP_CONTINUE(); POP_BREAK() +#define IS_CONST(_x) ((_x)->expr_kind == EXPR_CONST) + AttributeType sema_analyse_attribute(SemaContext *context, Attr *attr, AttributeDomain domain); bool expr_is_ltype(Expr *expr); @@ -71,4 +73,9 @@ bool sema_analyse_expr_lvalue(SemaContext *context, Expr *expr); bool sema_analyse_ct_expr(SemaContext *context, Expr *expr); bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *struct_var, Decl *decl, bool failable); void expr_rewrite_to_int_const(Expr *expr_to_rewrite, Type *type, uint64_t value, bool narrowable); +static inline bool expr_is_const(Expr *expr); +static inline bool expr_is_const(Expr *expr) +{ + return expr->expr_kind == EXPR_CONST; +} diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index 97de018e6..b528ce83d 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -239,10 +239,7 @@ static inline Decl *sema_resolve_symbol(SemaContext *context, const char *symbol sema_report_error_on_decl(symbol_str, span, decl, ambiguous_other_decl, private_decl); return poisoned_decl; } - if (decl->module && decl->module != context->unit->module) - { - unit_register_external_symbol(context->compilation_unit, decl); - } + unit_register_external_symbol(context->compilation_unit, decl); return decl; } diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 25eb8df73..2d32bbf02 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -61,9 +61,14 @@ static inline bool sema_analyse_block_return_stmt(SemaContext *context, Ast *sta context->active_scope.jump_end = true; if (statement->return_stmt.expr) { - if (!sema_analyse_expr(context, statement->return_stmt.expr)) + Type *block_type = context->expected_block_type; + if (block_type) { - return false; + if (!sema_analyse_expr_rhs(context, block_type, statement->return_stmt.expr, type_is_failable(block_type))) return false; + } + else + { + if (!sema_analyse_expr(context, statement->return_stmt.expr)) return false; } } vec_add(context->returns, statement); diff --git a/src/compiler/types.c b/src/compiler/types.c index a3759a4bf..589412384 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -1552,9 +1552,10 @@ Type *type_find_max_type(Type *type, Type *other) case TYPE_ERRTYPE: if (other->type_kind == TYPE_ERRTYPE) return type_anyerr; return NULL; + case TYPE_ANYERR: + return type_anyerr; case TYPE_FUNC: case TYPE_UNION: - case TYPE_ANYERR: case TYPE_TYPEID: case TYPE_STRUCT: case TYPE_UNTYPED_LIST: diff --git a/test/test_suite/macros/macro_rtype.c3 b/test/test_suite/macros/macro_rtype.c3 index bb95c1daa..e7a7aadff 100644 --- a/test/test_suite/macros/macro_rtype.c3 +++ b/test/test_suite/macros/macro_rtype.c3 @@ -1,6 +1,6 @@ macro int frob() { - return 0.0; // #error: Expected 'int', not 'double'. + return 0.0; // #error: Implicitly casting 'double' to 'int' is not permitted } fn void test1()