mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Added "guess a number". Fix CT enum / int comparison. Fix some array pointer decay scenarios. Infer type of expression blocks. Correctly merge anyerr.
This commit is contained in:
committed by
Christoffer Lerno
parent
ba66aaaf12
commit
06917f2e65
95
resources/examples/guess_number.c3
Normal file
95
resources/examples/guess_number.c3
Normal file
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user