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;
|
module std::io;
|
||||||
import std::mem;
|
import std::mem;
|
||||||
import libc;
|
import libc;
|
||||||
|
import std::env;
|
||||||
|
|
||||||
|
|
||||||
struct File
|
struct File
|
||||||
{
|
{
|
||||||
void *file;
|
CFile file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
extern fn int _puts(char* message) @extname("puts");
|
extern fn int _puts(char* message) @extname("puts");
|
||||||
extern fn int printf(char* message, ...);
|
extern fn int printf(char* message, ...);
|
||||||
extern fn int _putchar(char c) @extname("putchar");
|
extern fn int _putchar(char c) @extname("putchar");
|
||||||
|
|
||||||
extern File *__stdinp;
|
|
||||||
|
|
||||||
fn int putchar(char c) @inline
|
fn int putchar(char c) @inline
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import std::os::macos;
|
|||||||
import std::os::windows;
|
import std::os::windows;
|
||||||
// stdlib
|
// stdlib
|
||||||
|
|
||||||
|
|
||||||
// Constants need to be per os/arch
|
// Constants need to be per os/arch
|
||||||
const int EXIT_FAILURE = 1;
|
const int EXIT_FAILURE = 1;
|
||||||
const int EXIT_SUCCESS = 0;
|
const int EXIT_SUCCESS = 0;
|
||||||
@@ -166,11 +167,11 @@ enum Errno : ErrnoType
|
|||||||
fn Errno errno()
|
fn Errno errno()
|
||||||
{
|
{
|
||||||
$if (env::OS_TYPE == OsType.WIN32):
|
$if (env::OS_TYPE == OsType.WIN32):
|
||||||
return windows::errno();
|
return (Errno)windows::errno();
|
||||||
$elif (env::OS_TYPE == OsType.MACOSX):
|
$elif (env::OS_TYPE == OsType.MACOSX):
|
||||||
return macos::errno();
|
return (Errno)macos::errno();
|
||||||
$elif (env::OS_TYPE == OsType.LINUX):
|
$elif (env::OS_TYPE == OsType.LINUX):
|
||||||
return linux::errno();
|
return (Errno)linux::errno();
|
||||||
$else:
|
$else:
|
||||||
return Errno.ENOTRECOVERABLE;
|
return Errno.ENOTRECOVERABLE;
|
||||||
$endif;
|
$endif;
|
||||||
@@ -181,10 +182,10 @@ define TerminateFunction = fn void();
|
|||||||
define CompareFunction = fn int(void*, void*);
|
define CompareFunction = fn int(void*, void*);
|
||||||
extern fn double atof(char* str);
|
extern fn double atof(char* str);
|
||||||
extern fn int atoi(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 double strtod(char* str, char** endptr);
|
||||||
extern fn long strtol(char* str, char** endptr, int base);
|
extern fn CLong strtol(char* str, char** endptr, int base);
|
||||||
extern fn ulong stroul(char* str, char** endptr, int base);
|
extern fn CULong stroul(char* str, char** endptr, int base);
|
||||||
extern fn void abort();
|
extern fn void abort();
|
||||||
extern fn void atexit(TerminateFunction f);
|
extern fn void atexit(TerminateFunction f);
|
||||||
extern fn void exit(int status);
|
extern fn void exit(int status);
|
||||||
@@ -234,6 +235,28 @@ extern fn void* realloc(void* ptr, usize size);
|
|||||||
|
|
||||||
define Fpos = long;
|
define Fpos = long;
|
||||||
define CFile = void*;
|
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
|
// The following needs to be set per arch+os
|
||||||
// For now I have simply pulled the defaults from MacOS
|
// For now I have simply pulled the defaults from MacOS
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ fn int errno() @inline
|
|||||||
return *__errno_location();
|
return *__errno_location();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void errno_set(int errno)
|
fn void errno_set(int err)
|
||||||
{
|
{
|
||||||
*(__errno_location()) = errno;
|
*(__errno_location()) = err;
|
||||||
}
|
}
|
||||||
|
|
||||||
$endif;
|
$endif;
|
||||||
|
|||||||
@@ -2,14 +2,15 @@ module std::os::macos;
|
|||||||
import std::env;
|
import std::env;
|
||||||
|
|
||||||
$if (env::OS_TYPE == OsType.MACOSX):
|
$if (env::OS_TYPE == OsType.MACOSX):
|
||||||
|
|
||||||
extern fn int* __error();
|
extern fn int* __error();
|
||||||
|
|
||||||
fn int errno() @inline
|
fn int errno() @inline
|
||||||
{
|
{
|
||||||
return *__error();
|
return *__error();
|
||||||
}
|
}
|
||||||
fn void errno_set(int errno)
|
fn void errno_set(int err)
|
||||||
{
|
{
|
||||||
*(__error()) = errno;
|
*(__error()) = err;
|
||||||
}
|
}
|
||||||
$endif;
|
$endif;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ module std::os::windows;
|
|||||||
import std::env;
|
import std::env;
|
||||||
|
|
||||||
$if (env::OS_TYPE == OsType.WIN32):
|
$if (env::OS_TYPE == OsType.WIN32):
|
||||||
|
|
||||||
extern fn int getLastError() @stdcall @extname("GetLastError");
|
extern fn int getLastError() @stdcall @extname("GetLastError");
|
||||||
fn int errno() @inline
|
fn int errno() @inline
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -721,6 +721,23 @@ bool int_is_zero(Int op)
|
|||||||
return !op.i.high && !op.i.low;
|
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)
|
Int int_add(Int op1, Int op2)
|
||||||
{
|
{
|
||||||
assert(op1.type == op2.type);
|
assert(op1.type == op2.type);
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ typedef struct TypeInfo_ TypeInfo;
|
|||||||
typedef struct Expr_ Expr;
|
typedef struct Expr_ Expr;
|
||||||
typedef struct Module_ Module;
|
typedef struct Module_ Module;
|
||||||
typedef struct Type_ Type;
|
typedef struct Type_ Type;
|
||||||
|
typedef Type CanonicalType;
|
||||||
|
|
||||||
typedef unsigned AstId;
|
typedef unsigned AstId;
|
||||||
|
|
||||||
@@ -259,7 +260,7 @@ typedef struct
|
|||||||
struct Type_
|
struct Type_
|
||||||
{
|
{
|
||||||
TypeKind type_kind;
|
TypeKind type_kind;
|
||||||
Type *canonical;
|
CanonicalType *canonical;
|
||||||
const char *name;
|
const char *name;
|
||||||
Type **type_cache;
|
Type **type_cache;
|
||||||
void *backend_type;
|
void *backend_type;
|
||||||
@@ -1428,7 +1429,9 @@ typedef struct
|
|||||||
|
|
||||||
typedef struct SemaContext_
|
typedef struct SemaContext_
|
||||||
{
|
{
|
||||||
|
// Evaluated in this.
|
||||||
CompilationUnit *unit;
|
CompilationUnit *unit;
|
||||||
|
// Compiled in this unit.
|
||||||
CompilationUnit *compilation_unit;
|
CompilationUnit *compilation_unit;
|
||||||
Decl *current_function;
|
Decl *current_function;
|
||||||
Decl *current_macro;
|
Decl *current_macro;
|
||||||
@@ -1709,6 +1712,7 @@ bool int_comp(Int op1, Int op2, BinaryOp op);
|
|||||||
uint64_t int_to_u64(Int op);
|
uint64_t int_to_u64(Int op);
|
||||||
int64_t int_to_i64(Int op);
|
int64_t int_to_i64(Int op);
|
||||||
bool int_is_zero(Int op);
|
bool int_is_zero(Int op);
|
||||||
|
unsigned int_bits_needed(Int op);
|
||||||
bool int_fits(Int op1, TypeKind kind);
|
bool int_fits(Int op1, TypeKind kind);
|
||||||
Int int_rightmost_bits(Int op, unsigned to_bits, TypeKind result_type);
|
Int int_rightmost_bits(Int op, unsigned to_bits, TypeKind result_type);
|
||||||
Int int_conv(Int op, TypeKind to_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 Decl *decl_raw(Decl *decl);
|
||||||
static inline bool decl_ok(Decl *decl) { return !decl || decl->decl_kind != DECL_POISONED; }
|
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_struct_type(Decl *decl);
|
||||||
static inline bool decl_is_callable_type(Decl *decl);
|
static inline bool decl_is_callable_type(Decl *decl);
|
||||||
static inline bool decl_is_user_defined_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_is_numeric(Type *type);
|
||||||
static inline bool type_underlying_is_numeric(Type *type);
|
static inline bool type_underlying_is_numeric(Type *type);
|
||||||
static inline bool type_is_pointer(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_float(Type *type);
|
||||||
static inline bool type_is_promotable_integer(Type *type);
|
static inline bool type_is_promotable_integer(Type *type);
|
||||||
static inline bool type_is_signed(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);
|
bool type_may_have_sub_elements(Type *type);
|
||||||
static inline bool type_ok(Type *type);
|
static inline bool type_ok(Type *type);
|
||||||
TypeSize type_size(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_to_error_string(Type *type);
|
||||||
const char *type_quoted_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;
|
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)
|
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)
|
void unit_register_external_symbol(CompilationUnit *unit, Decl *decl)
|
||||||
{
|
{
|
||||||
if (decl->decl_kind == DECL_MACRO) return;
|
if (!decl->module || decl->module == unit->module || !decl->external_name) return;
|
||||||
assert(decl->external_name && "Missing external name");
|
|
||||||
Decl *prev = stable_get(&unit->external_symbols, decl->external_name);
|
Decl *prev = stable_get(&unit->external_symbols, decl->external_name);
|
||||||
if (prev) return;
|
if (prev) return;
|
||||||
if (decl != stable_set(&unit->external_symbols, decl->external_name, decl))
|
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 expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp op)
|
||||||
{
|
{
|
||||||
bool is_eq;
|
bool is_eq;
|
||||||
|
|
||||||
switch (left->const_kind)
|
switch (left->const_kind)
|
||||||
{
|
{
|
||||||
case CONST_BOOL:
|
case CONST_BOOL:
|
||||||
return compare_bool(left->b, right->b, op);
|
return compare_bool(left->b, right->b, op);
|
||||||
case CONST_INTEGER:
|
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);
|
return int_comp(left->ixx, right->ixx, op);
|
||||||
case CONST_FLOAT:
|
case CONST_FLOAT:
|
||||||
return compare_fps(left->fxx.f, right->fxx.f, op);
|
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);
|
const char *name_str = TOKSTR(name);
|
||||||
Decl *symbol = module_find_symbol(instantiated_module, name_str);
|
Decl *symbol = module_find_symbol(instantiated_module, name_str);
|
||||||
assert(symbol);
|
assert(symbol);
|
||||||
unit_register_external_symbol(c->unit, symbol);
|
unit_register_external_symbol(c->compilation_unit, symbol);
|
||||||
switch (decl->define_decl.define_kind)
|
switch (decl->define_decl.define_kind)
|
||||||
{
|
{
|
||||||
case DEFINE_IDENT_GENERIC:
|
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 inline bool sema_cast_rvalue(SemaContext *context, Expr *expr);
|
||||||
static Expr *expr_access_inline_member(Expr *parent, Decl *parent_decl);
|
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 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 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 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 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)
|
static inline void expr_set_as_const_list(Expr *expr, ConstInitializer *list)
|
||||||
{
|
{
|
||||||
expr->expr_kind = EXPR_CONST;
|
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)
|
static bool sema_decay_array_pointers(Expr *expr)
|
||||||
{
|
{
|
||||||
Type *expr_type = expr->type->canonical;
|
|
||||||
if (expr_type->type_kind != TYPE_POINTER) return true;
|
CanonicalType *pointer_type = type_pointer_type(type_no_fail(expr->type));
|
||||||
switch (expr_type->pointer->type_kind)
|
|
||||||
{
|
if (!pointer_type || !type_is_arraylike(pointer_type)) return true;
|
||||||
case TYPE_ARRAY:
|
|
||||||
case TYPE_VECTOR:
|
return cast_implicit(expr, type_get_opt_fail(type_get_ptr(pointer_type->array.base), IS_FAILABLE(expr)));
|
||||||
return cast_implicit(expr, type_get_ptr(expr_type->pointer->array.base));
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int BINOP_PREC_REQ[BINARYOP_LAST + 1] =
|
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;
|
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)
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the assignment fits
|
||||||
static bool sema_bit_assignment_check(Expr *right, Decl *member)
|
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;
|
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.
|
if (int_bits_needed(right->const_expr.ixx) > bits)
|
||||||
|
|
||||||
TypeKind kind = right->const_expr.ixx.type;
|
|
||||||
Int128 i = right->const_expr.ixx.i;
|
|
||||||
int bits_used;
|
|
||||||
if (type_kind_is_signed(kind))
|
|
||||||
{
|
{
|
||||||
if (i128_is_neg(i))
|
SEMA_ERROR(right,
|
||||||
{
|
"This constant would be truncated if stored in the bitstruct, do you need a wider bit range?");
|
||||||
i = i128_neg(i);
|
return false;
|
||||||
i = i128_sub64(i, 1);
|
|
||||||
}
|
|
||||||
bits_used = (int) (1 + 128 - i128_clz(&i));
|
|
||||||
}
|
}
|
||||||
else
|
return true;
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind)
|
bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind)
|
||||||
{
|
{
|
||||||
RETRY:
|
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));
|
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_replace(expr, path ? left : right);
|
||||||
}
|
}
|
||||||
|
|
||||||
expr_unify_binary(expr, left, right);
|
expr_unify_binary_failability(expr, left, right);
|
||||||
return true;
|
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)
|
static inline bool sema_expr_analyse_identifier(SemaContext *context, Type *to, Expr *expr)
|
||||||
{
|
{
|
||||||
Decl *ambiguous_decl = NULL;
|
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.identifier,
|
||||||
expr->identifier_expr.path,
|
expr->identifier_expr.path,
|
||||||
true);
|
true);
|
||||||
|
(void)decl;
|
||||||
assert(!decl_ok(decl));
|
assert(!decl_ok(decl));
|
||||||
return false;
|
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);
|
if (!sema_analyse_decl(context, decl)) return decl_poison(decl);
|
||||||
}
|
}
|
||||||
|
unit_register_external_symbol(context->compilation_unit, decl);
|
||||||
if (decl->decl_kind == DECL_VAR)
|
if (decl->decl_kind == DECL_VAR)
|
||||||
{
|
{
|
||||||
decl->var.is_read = true;
|
decl->var.is_read = true;
|
||||||
@@ -2022,7 +2000,6 @@ static bool sema_analyse_body_expansion(SemaContext *macro_context, Expr *call)
|
|||||||
assert(macro);
|
assert(macro);
|
||||||
assert(macro->macro_decl.block_parameter.index);
|
assert(macro->macro_decl.block_parameter.index);
|
||||||
|
|
||||||
// TODO handle named arguments
|
|
||||||
ExprCall *call_expr = &call->call_expr;
|
ExprCall *call_expr = &call->call_expr;
|
||||||
if (vec_size(call_expr->body_arguments))
|
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.");
|
SEMA_ERROR(call, "Expanding parameters is not allowed for macro invocations.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// Theoretically we could support named arguments, but that's unnecessary.
|
||||||
unsigned expressions = vec_size(call_expr->arguments);
|
unsigned expressions = vec_size(call_expr->arguments);
|
||||||
if (expressions != vec_size(macro->macro_decl.body_parameters))
|
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;
|
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;
|
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);
|
AttributeType attribute = sema_analyse_attribute(context, attr, ATTR_CALL);
|
||||||
if (attribute == ATTRIBUTE_NONE) return expr_poison(expr);
|
if (attribute == ATTRIBUTE_NONE) return false;
|
||||||
|
|
||||||
bool had = false;
|
|
||||||
switch (attribute)
|
switch (attribute)
|
||||||
{
|
{
|
||||||
case ATTRIBUTE_INLINE:
|
case ATTRIBUTE_INLINE:
|
||||||
case ATTRIBUTE_NOINLINE:
|
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");
|
SEMA_TOKID_ERROR(attr->name,
|
||||||
return expr_poison(expr);
|
"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;
|
force_inline = attribute == ATTRIBUTE_INLINE ? 1 : 0;
|
||||||
break;
|
break;
|
||||||
@@ -2095,7 +2078,17 @@ bool sema_expr_analyse_general_call(SemaContext *context, Expr *expr, Decl *decl
|
|||||||
UNREACHABLE;
|
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;
|
expr->call_expr.is_type_method = struct_var != NULL;
|
||||||
|
if (!sema_analyse_call_attributes(context, decl, expr)) return expr_poison(expr);
|
||||||
if (decl == NULL)
|
if (decl == NULL)
|
||||||
{
|
{
|
||||||
return sema_expr_analyse_var_call(context, expr, type_flatten_distinct_failable(expr->call_expr.function->type), failable);
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
expr->call_expr.func_ref = decl;
|
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);
|
return sema_expr_analyse_func_call(context, expr, decl, struct_var, failable);
|
||||||
case DECL_GENERIC:
|
case DECL_GENERIC:
|
||||||
if (is_macro)
|
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.
|
// 6. Set the type & other properties.
|
||||||
expr_unify_binary(expr, left, right);
|
expr_unify_binary_failability(expr, left, right);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@@ -4953,7 +4944,7 @@ static bool sema_expr_analyse_bit(SemaContext *context, Expr *expr, Expr *left,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 5. Assign the type
|
// 5. Assign the type
|
||||||
expr_unify_binary(expr, left, right);
|
expr_unify_binary_failability(expr, left, right);
|
||||||
return true;
|
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)
|
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 (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 (!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))
|
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;
|
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))
|
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.");
|
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;
|
bool success = true;
|
||||||
expr->type = type_void;
|
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;
|
Type *prev_expected_block_type = context->expected_block_type;
|
||||||
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 = NULL;
|
context->expected_block_type = infer_type;
|
||||||
SCOPE_START_WITH_FLAGS(SCOPE_EXPR_BLOCK)
|
SCOPE_START_WITH_FLAGS(SCOPE_EXPR_BLOCK)
|
||||||
|
|
||||||
PUSH_CONTINUE(NULL);
|
PUSH_CONTINUE(NULL);
|
||||||
@@ -6934,7 +6925,7 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr)
|
|||||||
case EXPR_COMPOUND_LITERAL:
|
case EXPR_COMPOUND_LITERAL:
|
||||||
return sema_expr_analyse_compound_literal(context, expr);
|
return sema_expr_analyse_compound_literal(context, expr);
|
||||||
case EXPR_EXPR_BLOCK:
|
case EXPR_EXPR_BLOCK:
|
||||||
return sema_expr_analyse_expr_block(context, expr);
|
return sema_expr_analyse_expr_block(context, NULL, expr);
|
||||||
case EXPR_RETHROW:
|
case EXPR_RETHROW:
|
||||||
return sema_expr_analyse_rethrow(context, expr);
|
return sema_expr_analyse_rethrow(context, expr);
|
||||||
case EXPR_CONST:
|
case EXPR_CONST:
|
||||||
@@ -7284,6 +7275,9 @@ bool sema_analyse_inferred_expr(SemaContext *context, Type *infer_type, Expr *ex
|
|||||||
case EXPR_DESIGNATED_INITIALIZER_LIST:
|
case EXPR_DESIGNATED_INITIALIZER_LIST:
|
||||||
if (!sema_expr_analyse_initializer_list(context, infer_type, expr)) return expr_poison(expr);
|
if (!sema_expr_analyse_initializer_list(context, infer_type, expr)) return expr_poison(expr);
|
||||||
break;
|
break;
|
||||||
|
case EXPR_EXPR_BLOCK:
|
||||||
|
if (!sema_expr_analyse_expr_block(context, infer_type, expr)) return expr_poison(expr);
|
||||||
|
break;
|
||||||
case EXPR_CONST_IDENTIFIER:
|
case EXPR_CONST_IDENTIFIER:
|
||||||
if (!sema_expr_analyse_identifier(context, infer_type, expr)) return expr_poison(expr);
|
if (!sema_expr_analyse_identifier(context, infer_type, expr)) return expr_poison(expr);
|
||||||
break;
|
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 PUSH_BREAKCONT(ast) PUSH_CONTINUE(ast); PUSH_BREAK(ast)
|
||||||
#define POP_BREAKCONT() POP_CONTINUE(); POP_BREAK()
|
#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);
|
AttributeType sema_analyse_attribute(SemaContext *context, Attr *attr, AttributeDomain domain);
|
||||||
bool expr_is_ltype(Expr *expr);
|
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_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);
|
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);
|
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);
|
sema_report_error_on_decl(symbol_str, span, decl, ambiguous_other_decl, private_decl);
|
||||||
return poisoned_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;
|
return decl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,9 +61,14 @@ static inline bool sema_analyse_block_return_stmt(SemaContext *context, Ast *sta
|
|||||||
context->active_scope.jump_end = true;
|
context->active_scope.jump_end = true;
|
||||||
if (statement->return_stmt.expr)
|
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);
|
vec_add(context->returns, statement);
|
||||||
|
|||||||
@@ -1552,9 +1552,10 @@ Type *type_find_max_type(Type *type, Type *other)
|
|||||||
case TYPE_ERRTYPE:
|
case TYPE_ERRTYPE:
|
||||||
if (other->type_kind == TYPE_ERRTYPE) return type_anyerr;
|
if (other->type_kind == TYPE_ERRTYPE) return type_anyerr;
|
||||||
return NULL;
|
return NULL;
|
||||||
|
case TYPE_ANYERR:
|
||||||
|
return type_anyerr;
|
||||||
case TYPE_FUNC:
|
case TYPE_FUNC:
|
||||||
case TYPE_UNION:
|
case TYPE_UNION:
|
||||||
case TYPE_ANYERR:
|
|
||||||
case TYPE_TYPEID:
|
case TYPE_TYPEID:
|
||||||
case TYPE_STRUCT:
|
case TYPE_STRUCT:
|
||||||
case TYPE_UNTYPED_LIST:
|
case TYPE_UNTYPED_LIST:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
macro int frob()
|
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()
|
fn void test1()
|
||||||
|
|||||||
Reference in New Issue
Block a user