mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Adding "require" precondition. Corrects inferred arrays and fixes so that it uses [*] everywhere. Distict type will now allow methods to be added to it. Added $alignof and $sizeof.
This commit is contained in:
committed by
Christoffer Lerno
parent
12ffeeaad7
commit
a5ce7c47ba
@@ -581,6 +581,12 @@ void fprint_expr_recursive(Context *context, FILE *file, Expr *expr, int indent)
|
||||
if (!expr) return;
|
||||
switch (expr->expr_kind)
|
||||
{
|
||||
case EXPR_FLATPATH:
|
||||
DUMP("(idents)");
|
||||
return;
|
||||
case EXPR_CT_CALL:
|
||||
DUMP("(ct-call)");
|
||||
return;
|
||||
case EXPR_MACRO_BODY_EXPANSION:
|
||||
DUMP("(macro-body-expansion)");
|
||||
return;
|
||||
|
||||
@@ -516,6 +516,7 @@ typedef struct Decl_
|
||||
// Unions, Errtype and Struct use strukt
|
||||
StructDecl strukt;
|
||||
EnumDecl enums;
|
||||
DistinctDecl distinct_decl;
|
||||
};
|
||||
};
|
||||
ImportDecl import;
|
||||
@@ -526,7 +527,6 @@ typedef struct Decl_
|
||||
AttrDecl attr;
|
||||
TypedefDecl typedef_decl;
|
||||
InterfaceDecl interface_decl;
|
||||
DistinctDecl distinct_decl;
|
||||
MacroDecl macro_decl;
|
||||
GenericDecl generic_decl;
|
||||
DefineDecl define_decl;
|
||||
@@ -720,6 +720,30 @@ typedef struct
|
||||
Decl *decl;
|
||||
} ExprIdentifierRaw;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool array : 1;
|
||||
union
|
||||
{
|
||||
ArrayIndex index;
|
||||
const char *ident;
|
||||
};
|
||||
} ExprFlatElement;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
TokenType token_type;
|
||||
union
|
||||
{
|
||||
Expr **arguments;
|
||||
struct {
|
||||
Decl *decl;
|
||||
Type *type;
|
||||
ExprFlatElement *flatpath;
|
||||
};
|
||||
};
|
||||
} ExprCtCall;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Expr *inner;
|
||||
@@ -805,6 +829,7 @@ typedef struct
|
||||
} ExprLen;
|
||||
|
||||
|
||||
|
||||
struct Expr_
|
||||
{
|
||||
ExprKind expr_kind : 8;
|
||||
@@ -826,6 +851,7 @@ struct Expr_
|
||||
ExprGuard guard_expr;
|
||||
Expr *trycatch_expr;
|
||||
ExprElse else_expr;
|
||||
ExprFlatElement *flatpath_expr;
|
||||
ExprSliceAssign slice_assign_expr;
|
||||
ExprBinary binary_expr;
|
||||
ExprTernary ternary_expr;
|
||||
@@ -840,6 +866,7 @@ struct Expr_
|
||||
ExprPlaceholder placeholder_expr;
|
||||
ExprIdentifier macro_identifier_expr;
|
||||
ExprIdentifierRaw ct_ident_expr;
|
||||
ExprCtCall ct_call_expr;
|
||||
ExprIdentifierRaw ct_macro_ident_expr;
|
||||
ExprMacroExpansion macro_expansion_expr;
|
||||
ExprIdentifierRaw hash_ident_expr;
|
||||
@@ -1628,6 +1655,7 @@ 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_is_struct_type(Decl *decl);
|
||||
static inline bool decl_is_user_defined_type(Decl *decl);
|
||||
static inline DeclKind decl_from_token(TokenType type);
|
||||
static inline Decl *decl_flatten(Decl *decl)
|
||||
{
|
||||
@@ -1744,6 +1772,8 @@ bool sema_expr_analyse_assign_right_side(Context *context, Expr *expr, Type *lef
|
||||
Decl *sema_resolve_symbol_in_current_dynamic_scope(Context *context, const char *symbol);
|
||||
Decl *sema_resolve_parameterized_symbol(Context *context, TokenId symbol, Path *path);
|
||||
Decl *sema_resolve_normal_symbol(Context *context, TokenId symbol, Path *path, bool handle_error);
|
||||
Decl *sema_resolve_string_symbol(Context *context, const char *symbol, SourceSpan span, Path *path);
|
||||
|
||||
bool sema_resolve_type_info(Context *context, TypeInfo *type_info);
|
||||
bool sema_resolve_type_info_maybe_inferred(Context *context, TypeInfo *type_info, bool allow_inferred_type);
|
||||
bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info, bool allow_inferred_type, bool in_shallow);
|
||||
@@ -1784,7 +1814,7 @@ char *scratch_buffer_to_string(void);
|
||||
const char *scratch_buffer_interned(void);
|
||||
|
||||
const char *symtab_add(const char *symbol, uint32_t len, uint32_t fnv1hash, TokenType *type);
|
||||
|
||||
const char *symtab_find(const char *symbol, uint32_t len, uint32_t fnv1hash, TokenType *type);
|
||||
void *llvm_target_machine_create(void);
|
||||
void target_setup(BuildTarget *build_target);
|
||||
int target_alloca_addr_space();
|
||||
@@ -1848,6 +1878,7 @@ static inline bool type_is_signed(Type *type);
|
||||
static inline bool type_is_structlike(Type *type);
|
||||
static inline size_t type_min_alignment(size_t a, size_t b);
|
||||
bool type_is_subtype(Type *type, Type *possible_subtype);
|
||||
Type *type_from_token(TokenType type);
|
||||
bool type_is_union_struct(Type *type);
|
||||
bool type_is_user_defined(Type *type);
|
||||
bool type_is_structurally_equivalent(Type *type1, Type *type);
|
||||
@@ -2121,6 +2152,13 @@ static inline bool decl_is_struct_type(Decl *decl)
|
||||
return (kind == DECL_UNION) | (kind == DECL_STRUCT) | (kind == DECL_ERR);
|
||||
}
|
||||
|
||||
static inline bool decl_is_user_defined_type(Decl *decl)
|
||||
{
|
||||
DeclKind kind = decl->decl_kind;
|
||||
return (kind == DECL_UNION) | (kind == DECL_STRUCT) | (kind == DECL_ERR)
|
||||
| (kind == DECL_ENUM) | (kind == DECL_DISTINCT);
|
||||
}
|
||||
|
||||
static inline DeclKind decl_from_token(TokenType type)
|
||||
{
|
||||
if (type == TOKEN_STRUCT) return DECL_STRUCT;
|
||||
|
||||
@@ -144,10 +144,10 @@ void context_register_global_decl(Context *context, Decl *decl)
|
||||
vec_add(context->vars, decl);
|
||||
decl_set_external_name(decl);
|
||||
break;
|
||||
case DECL_DISTINCT:
|
||||
case DECL_STRUCT:
|
||||
case DECL_UNION:
|
||||
case DECL_TYPEDEF:
|
||||
case DECL_DISTINCT:
|
||||
case DECL_ERR:
|
||||
vec_add(context->types, decl);
|
||||
decl_set_external_name(decl);
|
||||
@@ -179,7 +179,7 @@ void context_register_global_decl(Context *context, Decl *decl)
|
||||
vec_add(context->ct_asserts, decl);
|
||||
return;
|
||||
}
|
||||
DEBUG_LOG("Registering symbol '%s'.", decl->name);
|
||||
DEBUG_LOG("Registering symbol '%s' in %s.", decl->name, context->module->name->module);
|
||||
|
||||
Decl *old = stable_set(&context->local_symbols, decl->name, decl);
|
||||
if (!old)
|
||||
|
||||
@@ -73,8 +73,12 @@ Expr *copy_expr(Expr *source_expr)
|
||||
case EXPR_ENUM_CONSTANT:
|
||||
case EXPR_MEMBER_ACCESS:
|
||||
UNREACHABLE
|
||||
case EXPR_FLATPATH:
|
||||
case EXPR_UNDEF:
|
||||
return expr;
|
||||
case EXPR_CT_CALL:
|
||||
MACRO_COPY_EXPR_LIST(expr->ct_call_expr.arguments);
|
||||
return expr;
|
||||
case EXPR_PLACEHOLDER:
|
||||
case EXPR_CONST_IDENTIFIER:
|
||||
case EXPR_CT_IDENT:
|
||||
|
||||
@@ -197,6 +197,7 @@ typedef enum
|
||||
EXPR_MACRO_EXPANSION,
|
||||
EXPR_MEMBER_ACCESS,
|
||||
EXPR_IDENTIFIER,
|
||||
EXPR_FLATPATH,
|
||||
EXPR_INITIALIZER_LIST,
|
||||
EXPR_LEN,
|
||||
EXPR_PLACEHOLDER,
|
||||
@@ -212,6 +213,7 @@ typedef enum
|
||||
EXPR_TYPEOF,
|
||||
EXPR_UNARY,
|
||||
EXPR_UNDEF,
|
||||
EXPR_CT_CALL,
|
||||
} ExprKind;
|
||||
|
||||
typedef enum
|
||||
@@ -440,6 +442,7 @@ typedef enum
|
||||
TOKEN_VOLATILE,
|
||||
TOKEN_WHILE,
|
||||
|
||||
TOKEN_CT_ALIGNOF, // $alignof
|
||||
TOKEN_CT_ASSERT, // $assert
|
||||
TOKEN_CT_CASE, // $case
|
||||
TOKEN_CT_DEFAULT, // $default
|
||||
@@ -449,6 +452,8 @@ typedef enum
|
||||
TOKEN_CT_ENDIF, // $endif
|
||||
TOKEN_CT_ENDSWITCH, // $endswitch
|
||||
TOKEN_CT_IF, // $if
|
||||
TOKEN_CT_OFFSETOF, // $offsetof
|
||||
TOKEN_CT_SIZEOF, // $sizeof
|
||||
TOKEN_CT_SWITCH, // $switch
|
||||
TOKEN_CT_UNREACHABLE, // $unreachable
|
||||
|
||||
|
||||
@@ -677,6 +677,14 @@ static void skip_doc_stars(Lexer *lexer)
|
||||
while (peek(lexer) == '*' && peek_next(lexer) != '/') next(lexer);
|
||||
}
|
||||
|
||||
static bool end_of_docs_found(Lexer *lexer)
|
||||
{
|
||||
int lookahead = 0;
|
||||
// while we see '*' walk forward.
|
||||
while (lexer->current[lookahead] == '*') lookahead++;
|
||||
// And if it doesn't have a '/' at the last position it isn't either.
|
||||
return lexer->current[lookahead] == '/';
|
||||
}
|
||||
/**
|
||||
* OPTIONALLY adds * / token. This allows any number of '*' to preceed it.
|
||||
* @param lexer
|
||||
@@ -732,6 +740,8 @@ static DocEnd parse_doc_remainder(Lexer *lexer)
|
||||
case '*':
|
||||
// Did we find the end of the directives?
|
||||
// If so return control.
|
||||
if (!end_of_docs_found(lexer)) break;
|
||||
|
||||
if (characters_read > 0)
|
||||
{
|
||||
add_token(lexer, TOKEN_DOCS_LINE, 0);
|
||||
|
||||
@@ -3134,6 +3134,8 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr)
|
||||
case EXPR_CT_IDENT:
|
||||
case EXPR_HASH_IDENT:
|
||||
case EXPR_PLACEHOLDER:
|
||||
case EXPR_CT_CALL:
|
||||
case EXPR_FLATPATH:
|
||||
UNREACHABLE
|
||||
case EXPR_UNDEF:
|
||||
// Should never reach this.
|
||||
|
||||
@@ -248,6 +248,7 @@ void llvm_emit_stmt(GenContext *c, Ast *ast);
|
||||
static inline LLVMValueRef llvm_emit_store(GenContext *context, Decl *decl, LLVMValueRef value);
|
||||
void llvm_emit_panic_on_true(GenContext *c, LLVMValueRef value, const char *panic_name);
|
||||
void llvm_emit_ptr_from_array(GenContext *c, BEValue *value);
|
||||
void llvm_emit_puts_output(GenContext *c, const char *message);
|
||||
void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failable);
|
||||
void llvm_emit_return_implicit(GenContext *c);
|
||||
LLVMValueRef llvm_emit_struct_gep_raw(GenContext *context, LLVMValueRef ptr, LLVMTypeRef struct_type, unsigned index, unsigned struct_alignment, unsigned offset, unsigned *alignment);
|
||||
|
||||
@@ -1059,10 +1059,22 @@ static inline void gencontext_emit_assert_stmt(GenContext *c, Ast *ast)
|
||||
LLVMBasicBlockRef on_fail = llvm_basic_block_new(c, "assert_fail");
|
||||
LLVMBasicBlockRef on_ok = llvm_basic_block_new(c, "assert_ok");
|
||||
assert(value.kind == BE_BOOLEAN);
|
||||
llvm_emit_cond_br(c, &value, on_fail, on_ok);
|
||||
llvm_emit_cond_br(c, &value, on_ok, on_fail);
|
||||
llvm_emit_block(c, on_fail);
|
||||
// TODO emit message
|
||||
SourceLocation *loc = TOKLOC(ast->assert_stmt.expr->span.loc);
|
||||
const char *error;
|
||||
if (ast->assert_stmt.message)
|
||||
{
|
||||
error = ast->assert_stmt.message->const_expr.string.chars;
|
||||
error = strformat("Assert violation '%s' on line %d, in file '%s'.", error, loc->line, loc->file->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
error = strformat("Assert violation on line %d, in file '%s'.", loc->line, loc->file->name);
|
||||
}
|
||||
llvm_emit_puts_output(c, error);
|
||||
llvm_emit_call_intrinsic(c, intrinsic_id_trap, NULL, 0, NULL, 0);
|
||||
llvm_emit_br(c, on_ok);
|
||||
llvm_emit_block(c, on_ok);
|
||||
return;
|
||||
}
|
||||
@@ -1071,7 +1083,7 @@ static inline void gencontext_emit_assert_stmt(GenContext *c, Ast *ast)
|
||||
|
||||
static inline void gencontext_emit_unreachable_stmt(GenContext *context, Ast *ast)
|
||||
{
|
||||
// TODO emit message
|
||||
llvm_emit_puts_output(context, "Unreachable statement reached.");
|
||||
llvm_emit_call_intrinsic(context, intrinsic_id_trap, NULL, 0, NULL, 0);
|
||||
LLVMBuildUnreachable(context->builder);
|
||||
LLVMBasicBlockRef block = llvm_basic_block_new(context, "unreachable_block");
|
||||
|
||||
@@ -577,6 +577,78 @@ static Expr *parse_hash_ident(Context *context, Expr *left)
|
||||
return expr;
|
||||
}
|
||||
|
||||
static Expr *parse_ct_call(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Unexpected left hand side");
|
||||
Expr *expr = EXPR_NEW_TOKEN(EXPR_CT_CALL, context->tok);
|
||||
expr->ct_call_expr.token_type = context->tok.type;
|
||||
advance(context);
|
||||
CONSUME_OR(TOKEN_LPAREN, poisoned_expr);
|
||||
Expr *element = TRY_EXPR_OR(parse_expr(context), poisoned_expr);
|
||||
vec_add(expr->ct_call_expr.arguments, element);
|
||||
if (try_consume(context, TOKEN_COMMA))
|
||||
{
|
||||
if (context->tok.type == TOKEN_IDENT || context->tok.type == TOKEN_LBRACKET)
|
||||
{
|
||||
Expr *idents = EXPR_NEW_TOKEN(EXPR_FLATPATH, context->tok);
|
||||
bool first = true;
|
||||
while (1)
|
||||
{
|
||||
ExprFlatElement flat_element;
|
||||
if (try_consume(context, TOKEN_LBRACKET))
|
||||
{
|
||||
Expr *int_expr = TRY_EXPR_OR(parse_expr(context), poisoned_expr);
|
||||
if (int_expr->expr_kind != EXPR_CONST || !type_kind_is_any_integer(int_expr->const_expr.kind))
|
||||
{
|
||||
SEMA_TOKEN_ERROR(context->tok, "Expected an integer index.");
|
||||
return poisoned_expr;
|
||||
}
|
||||
BigInt *value = &int_expr->const_expr.i;
|
||||
BigInt limit;
|
||||
bigint_init_unsigned(&limit, MAX_ARRAYINDEX);
|
||||
if (bigint_cmp(value, &limit) == CMP_GT)
|
||||
{
|
||||
SEMA_ERROR(int_expr, "Array index out of range.");
|
||||
return poisoned_expr;
|
||||
}
|
||||
if (bigint_cmp_zero(value) == CMP_LT)
|
||||
{
|
||||
SEMA_ERROR(int_expr, "Array index must be zero or greater.");
|
||||
return poisoned_expr;
|
||||
}
|
||||
TRY_CONSUME_OR(TOKEN_RBRACKET, "Expected a ']' after the number.", poisoned_expr);
|
||||
flat_element.array = true;
|
||||
flat_element.index = (ArrayIndex)bigint_as_unsigned(value);
|
||||
}
|
||||
else if (try_consume(context, TOKEN_DOT) || first)
|
||||
{
|
||||
TRY_CONSUME_OR(TOKEN_IDENT, "Expected an identifier here.", poisoned_expr);
|
||||
flat_element.array = false;
|
||||
flat_element.ident = TOKSTR(context->prev_tok);
|
||||
}
|
||||
else
|
||||
{
|
||||
SEMA_TOKEN_ERROR(context->tok, "Expected '.' or '[' here.");
|
||||
return poisoned_expr;
|
||||
}
|
||||
first = false;
|
||||
vec_add(idents->flatpath_expr, flat_element);
|
||||
if (TOKEN_IS(TOKEN_RPAREN)) break;
|
||||
}
|
||||
vec_add(expr->ct_call_expr.arguments, idents);
|
||||
RANGE_EXTEND_PREV(expr);
|
||||
}
|
||||
else
|
||||
{
|
||||
Expr *idents = TRY_EXPR_OR(parse_expr(context), poisoned_expr);
|
||||
vec_add(expr->ct_call_expr.arguments, idents);
|
||||
}
|
||||
}
|
||||
CONSUME_OR(TOKEN_RPAREN, poisoned_expr);
|
||||
RANGE_EXTEND_PREV(expr);
|
||||
return expr;
|
||||
}
|
||||
|
||||
static Expr *parse_identifier(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Unexpected left hand side");
|
||||
@@ -1112,4 +1184,7 @@ ParseRule rules[TOKEN_EOF + 1] = {
|
||||
[TOKEN_HASH_IDENT] = { parse_hash_ident, NULL, PREC_NONE },
|
||||
//[TOKEN_HASH_TYPE_IDENT] = { parse_type_identifier(, NULL, PREC_NONE }
|
||||
|
||||
[TOKEN_CT_SIZEOF] = { parse_ct_call, NULL, PREC_NONE },
|
||||
[TOKEN_CT_ALIGNOF] = { parse_ct_call, NULL, PREC_NONE },
|
||||
[TOKEN_CT_OFFSETOF] = { parse_ct_call, NULL, PREC_NONE }
|
||||
};
|
||||
|
||||
@@ -531,71 +531,9 @@ static inline TypeInfo *parse_base_type(Context *context)
|
||||
type_info = type_info_new(TYPE_INFO_IDENTIFIER, source_span_from_token_id(context->tok.id));
|
||||
type_info->unresolved.name_loc = context->tok.id;
|
||||
break;
|
||||
case TYPE_TOKENS:
|
||||
case TOKEN_ERR:
|
||||
type_found = type_error;
|
||||
break;
|
||||
case TOKEN_VOID:
|
||||
type_found = type_void;
|
||||
break;
|
||||
case TOKEN_BOOL:
|
||||
type_found = type_bool;
|
||||
break;
|
||||
case TOKEN_CHAR:
|
||||
type_found = type_char;
|
||||
break;
|
||||
case TOKEN_DOUBLE:
|
||||
type_found = type_double;
|
||||
break;
|
||||
case TOKEN_FLOAT:
|
||||
type_found = type_float;
|
||||
break;
|
||||
case TOKEN_I128:
|
||||
type_found = type_i128;
|
||||
break;
|
||||
case TOKEN_ICHAR:
|
||||
type_found = type_ichar;
|
||||
break;
|
||||
case TOKEN_INT:
|
||||
type_found = type_int;
|
||||
break;
|
||||
case TOKEN_IPTR:
|
||||
type_found = type_iptr;
|
||||
break;
|
||||
case TOKEN_IPTRDIFF:
|
||||
type_found = type_iptrdiff;
|
||||
break;
|
||||
case TOKEN_ISIZE:
|
||||
type_found = type_isize;
|
||||
break;
|
||||
case TOKEN_LONG:
|
||||
type_found = type_long;
|
||||
break;
|
||||
case TOKEN_SHORT:
|
||||
type_found = type_short;
|
||||
break;
|
||||
case TOKEN_U128:
|
||||
type_found = type_u128;
|
||||
break;
|
||||
case TOKEN_UINT:
|
||||
type_found = type_uint;
|
||||
break;
|
||||
case TOKEN_ULONG:
|
||||
type_found = type_ulong;
|
||||
break;
|
||||
case TOKEN_UPTR:
|
||||
type_found = type_uptr;
|
||||
break;
|
||||
case TOKEN_UPTRDIFF:
|
||||
type_found = type_uptrdiff;
|
||||
break;
|
||||
case TOKEN_USHORT:
|
||||
type_found = type_ushort;
|
||||
break;
|
||||
case TOKEN_USIZE:
|
||||
type_found = type_usize;
|
||||
break;
|
||||
case TOKEN_TYPEID:
|
||||
type_found = type_typeid;
|
||||
type_found = type_from_token(context->tok.type);
|
||||
break;
|
||||
default:
|
||||
// Special case: "virtual *"
|
||||
@@ -2128,18 +2066,18 @@ static bool parse_docs(Context *context, Ast **docs)
|
||||
if (directive == kw_ensure)
|
||||
{
|
||||
doc_ast->doc_directive.kind = DOC_DIRECTIVE_ENSURE;
|
||||
if (!parse_doc_contract(context, ast)) return false;
|
||||
if (!parse_doc_contract(context, doc_ast)) return false;
|
||||
goto LINE_END;
|
||||
}
|
||||
if (directive == kw_require)
|
||||
{
|
||||
doc_ast->doc_directive.kind = DOC_DIRECTIVE_REQUIRE;
|
||||
if (!parse_doc_contract(context, ast)) return false;
|
||||
if (!parse_doc_contract(context, doc_ast)) return false;
|
||||
goto LINE_END;
|
||||
}
|
||||
if (directive == kw_errors)
|
||||
{
|
||||
if (!parse_doc_errors(context, ast)) return false;
|
||||
if (!parse_doc_errors(context, doc_ast)) return false;
|
||||
goto LINE_END;
|
||||
}
|
||||
doc_ast->doc_directive.kind = DOC_DIRECTIVE_UNKNOWN;
|
||||
@@ -2147,9 +2085,11 @@ static bool parse_docs(Context *context, Ast **docs)
|
||||
doc_ast->doc_directive.generic.rest_of_line = parse_doc_opt_rest_of_line(context);
|
||||
|
||||
LINE_END:
|
||||
vec_add(ast->directives, doc_ast);
|
||||
if (try_consume(context, TOKEN_DOCS_EOL)) continue;
|
||||
EXPECT_OR(TOKEN_DOCS_END, false);
|
||||
}
|
||||
*docs = ast;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1072,6 +1072,9 @@ Ast *parse_stmt(Context *context)
|
||||
case TOKEN_TRUE:
|
||||
case TOKEN_LBRAPIPE:
|
||||
case TOKEN_TYPEOF:
|
||||
case TOKEN_CT_OFFSETOF:
|
||||
case TOKEN_CT_ALIGNOF:
|
||||
case TOKEN_CT_SIZEOF:
|
||||
return parse_expr_stmt(context);
|
||||
case TOKEN_ASSERT:
|
||||
return parse_assert_stmt(context);
|
||||
|
||||
@@ -553,8 +553,6 @@ bool cast_may_implicit(Type *from_type, Type *to_type)
|
||||
{
|
||||
return from->pointer->type_kind == TYPE_ARRAY && from->pointer->array.base == to->array.base;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -123,6 +123,12 @@ static bool sema_analyse_union_members(Context *context, Decl *decl, Decl **memb
|
||||
|
||||
decl->strukt.union_rep = max_alignment_element;
|
||||
|
||||
// All members share the same alignment
|
||||
VECEACH(members, i)
|
||||
{
|
||||
members[i]->alignment = decl->alignment;
|
||||
}
|
||||
|
||||
if (!max_size)
|
||||
{
|
||||
decl->strukt.size = 0;
|
||||
@@ -222,6 +228,8 @@ static bool sema_analyse_struct_members(Context *context, Decl *decl, Decl **mem
|
||||
}
|
||||
}
|
||||
|
||||
member->alignment = member_alignment;
|
||||
|
||||
offset = align_offset;
|
||||
member->offset = offset;
|
||||
offset += type_size(member->type);
|
||||
@@ -404,7 +412,7 @@ static inline Type *sema_analyse_function_signature(Context *context, FunctionSi
|
||||
if (vec_size(signature->params) > MAX_PARAMS)
|
||||
{
|
||||
SEMA_ERROR(signature->params[MAX_PARAMS], "Number of params exceeds %d which is unsupported.", MAX_PARAMS);
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
STable *names = &global_context.scratch_table;
|
||||
stable_clear(names);
|
||||
@@ -770,6 +778,32 @@ static AttributeType sema_analyse_attribute(Context *context, Attr *attr, Attrib
|
||||
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_doc_header(Ast *docs, Decl **params, Decl **extra_params)
|
||||
{
|
||||
if (!docs) return true;
|
||||
assert(docs->ast_kind == AST_DOCS);
|
||||
Ast **doc_directives = docs->directives;
|
||||
VECEACH(doc_directives, i)
|
||||
{
|
||||
Ast *directive = doc_directives[i];
|
||||
if (directive->doc_directive.kind != DOC_DIRECTIVE_PARAM) continue;
|
||||
TokenId param = directive->doc_directive.param.param;
|
||||
const char *param_name = TOKSTR(param);
|
||||
VECEACH(params, j)
|
||||
{
|
||||
if (params[j]->name == param_name) goto NEXT;
|
||||
}
|
||||
VECEACH(extra_params, j)
|
||||
{
|
||||
if (extra_params[j]->name == param_name) goto NEXT;
|
||||
}
|
||||
SEMA_TOKID_ERROR(param, "There is no parameter '%s', did you misspell it?", param_name);
|
||||
return false;
|
||||
NEXT:
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static inline bool sema_analyse_func(Context *context, Decl *decl)
|
||||
{
|
||||
DEBUG_LOG("----Analysing function %s", decl->name);
|
||||
@@ -793,6 +827,7 @@ static inline bool sema_analyse_func(Context *context, Decl *decl)
|
||||
}
|
||||
decl_set_external_name(decl);
|
||||
}
|
||||
if (!sema_analyse_doc_header(decl->docs, decl->func_decl.function_signature.params, NULL)) return decl_poison(decl);
|
||||
VECEACH(decl->attributes, i)
|
||||
{
|
||||
Attr *attr = decl->attributes[i];
|
||||
@@ -958,6 +993,7 @@ static inline bool sema_analyse_macro(Context *context, Decl *decl)
|
||||
}
|
||||
if (!sema_check_unique_parameters(decl->macro_decl.parameters)) return decl_poison(decl);
|
||||
if (!sema_check_unique_parameters(decl->macro_decl.body_parameters)) return decl_poison(decl);
|
||||
if (!sema_analyse_doc_header(decl->docs, decl->macro_decl.parameters, decl->macro_decl.body_parameters)) return decl_poison(decl);
|
||||
if (decl->macro_decl.type_parent)
|
||||
{
|
||||
if (!sema_analyse_macro_method(context, decl)) return decl_poison(decl);
|
||||
@@ -1349,6 +1385,7 @@ bool sema_analyse_decl(Context *context, Decl *decl)
|
||||
break;
|
||||
case DECL_DISTINCT:
|
||||
if (!sema_analyse_distinct(context, decl)) return decl_poison(decl);
|
||||
decl_set_external_name(decl);
|
||||
break;
|
||||
case DECL_TYPEDEF:
|
||||
if (!sema_analyse_typedef(context, decl)) return decl_poison(decl);
|
||||
|
||||
@@ -1762,6 +1762,8 @@ static inline void expr_rewrite_to_int_const(Expr *expr_to_rewrite, Type *type,
|
||||
expr_to_rewrite->expr_kind = EXPR_CONST;
|
||||
expr_const_set_int(&expr_to_rewrite->const_expr, value, type->canonical->type_kind);
|
||||
expr_set_type(expr_to_rewrite, type);
|
||||
expr_to_rewrite->constant = true;
|
||||
expr_to_rewrite->pure = true;
|
||||
expr_to_rewrite->resolve_status = RESOLVE_DONE;
|
||||
}
|
||||
|
||||
@@ -1871,6 +1873,17 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr)
|
||||
|
||||
static void add_members_to_context(Context *context, Decl *decl)
|
||||
{
|
||||
VECEACH(decl->methods, i)
|
||||
{
|
||||
Decl *func = decl->methods[i];
|
||||
sema_add_member(context, func);
|
||||
}
|
||||
while (decl->decl_kind == DECL_DISTINCT)
|
||||
{
|
||||
Type *type = decl->distinct_decl.base_type->canonical;
|
||||
if (!type_is_user_defined(type)) break;
|
||||
decl = type->decl;
|
||||
}
|
||||
if (decl_is_struct_type(decl))
|
||||
{
|
||||
Decl **members = decl->strukt.members;
|
||||
@@ -1885,11 +1898,6 @@ static void add_members_to_context(Context *context, Decl *decl)
|
||||
sema_add_member(context, member);
|
||||
}
|
||||
}
|
||||
VECEACH(decl->methods, i)
|
||||
{
|
||||
Decl *func = decl->methods[i];
|
||||
sema_add_member(context, func);
|
||||
}
|
||||
}
|
||||
|
||||
static Expr *enum_minmax_value(Decl *decl, BinaryOp comparison)
|
||||
@@ -1952,8 +1960,9 @@ static TokenId sema_expr_resolve_access_child(Expr *child)
|
||||
return INVALID_TOKEN_ID;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_type_access(Expr *expr, TypeInfo *parent, bool was_group)
|
||||
static inline bool sema_expr_analyse_type_access(Expr *expr, TypeInfo *parent, bool was_group, bool is_macro, TokenId identifier_token)
|
||||
{
|
||||
// 1. Foo*.sizeof is not allowed, it must be (Foo*).sizeof
|
||||
if (!was_group && type_kind_is_derived(parent->type->type_kind))
|
||||
{
|
||||
SEMA_ERROR(expr->access_expr.parent, "Array and pointer types must be enclosed in (), did you forget it?");
|
||||
@@ -1962,15 +1971,9 @@ static inline bool sema_expr_analyse_type_access(Expr *expr, TypeInfo *parent, b
|
||||
|
||||
expr->constant = true;
|
||||
expr->pure = true;
|
||||
|
||||
Type *canonical = parent->type->canonical;
|
||||
Expr *child = expr->access_expr.child;
|
||||
|
||||
TokenId identifier_token = sema_expr_resolve_access_child(child);
|
||||
if (TOKEN_IS_INVALID(identifier_token)) return false;
|
||||
|
||||
TokenType type = TOKTYPE(identifier_token);
|
||||
const char *name = TOKSTR(identifier_token);
|
||||
|
||||
// 2. Handle Foo.typeid => return a typeid expression.
|
||||
if (type == TOKEN_TYPEID)
|
||||
{
|
||||
expr_set_type(expr, type_typeid);
|
||||
@@ -1979,26 +1982,36 @@ static inline bool sema_expr_analyse_type_access(Expr *expr, TypeInfo *parent, b
|
||||
expr->resolve_status = RESOLVE_DONE;
|
||||
return true;
|
||||
}
|
||||
|
||||
Type *canonical = parent->type->canonical;
|
||||
const char *name = TOKSTR(identifier_token);
|
||||
|
||||
// Foo.sizeof => return the size in bytes, e.g. 11
|
||||
if (name == kw_sizeof)
|
||||
{
|
||||
expr_rewrite_to_int_const(expr, type_compint, type_size(canonical));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Foo.alignof => return the alignment, e.g. 4
|
||||
if (name == kw_alignof)
|
||||
{
|
||||
expr_rewrite_to_int_const(expr, type_compint, type_abi_alignment(canonical));
|
||||
return true;
|
||||
}
|
||||
// Foo.nameof => return the name, e.g. "Foo"
|
||||
if (name == kw_nameof)
|
||||
{
|
||||
expr_rewrite_to_string(expr, canonical->name);
|
||||
return true;
|
||||
}
|
||||
// Foo.qnameof => return the qualified name, e.g. "mylib::Foo"
|
||||
if (name == kw_qnameof)
|
||||
{
|
||||
expr_rewrite_to_string(expr, type_generate_qname(canonical));
|
||||
return true;
|
||||
}
|
||||
//
|
||||
if (!type_may_have_sub_elements(canonical))
|
||||
{
|
||||
SEMA_ERROR(expr, "'%s' does not have a property '%s'.", type_to_error_string(parent->type), name);
|
||||
@@ -2081,7 +2094,7 @@ static inline bool sema_expr_analyse_type_access(Expr *expr, TypeInfo *parent, b
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr)
|
||||
static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr, bool is_macro, TokenId identifier_token)
|
||||
{
|
||||
Expr *parent = expr->access_expr.parent;
|
||||
|
||||
@@ -2090,20 +2103,17 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr)
|
||||
|
||||
Expr *child = expr->access_expr.child;
|
||||
|
||||
TokenId identifier_token = sema_expr_resolve_access_child(child);
|
||||
if (TOKEN_IS_INVALID(identifier_token)) return false;
|
||||
TokenType type = TOKTYPE(identifier_token);
|
||||
const char *name = TOKSTR(identifier_token);
|
||||
|
||||
Decl *ref = parent->access_expr.ref;
|
||||
bool is_macro = child->expr_kind == EXPR_MACRO_EXPANSION;
|
||||
|
||||
bool is_plain_member = ref->decl_kind == DECL_VAR;
|
||||
bool is_leaf_member_ref = ref->decl_kind == DECL_VAR;
|
||||
if (type == TOKEN_TYPEID && !is_macro)
|
||||
{
|
||||
expr_set_type(expr, type_typeid);
|
||||
expr->expr_kind = EXPR_TYPEID;
|
||||
if (is_plain_member)
|
||||
if (is_leaf_member_ref)
|
||||
{
|
||||
expr->typeid_expr = ref->var.type_info;
|
||||
}
|
||||
@@ -2116,22 +2126,23 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr)
|
||||
}
|
||||
if (name == kw_sizeof && !is_macro)
|
||||
{
|
||||
expr_rewrite_to_int_const(expr, type_usize, type_size(ref->type));
|
||||
expr_rewrite_to_int_const(expr, type_compint, type_size(ref->type));
|
||||
return true;
|
||||
}
|
||||
if (name == kw_alignof && !is_macro)
|
||||
{
|
||||
expr_rewrite_to_int_const(expr, type_usize, type_abi_alignment(ref->type));
|
||||
expr_rewrite_to_int_const(expr, type_compint, type_abi_alignment(ref->type));
|
||||
return true;
|
||||
}
|
||||
if (name == kw_ordinal && !is_macro)
|
||||
{
|
||||
if (ref->decl_kind == DECL_ENUM_CONSTANT)
|
||||
{
|
||||
expr_rewrite_to_int_const(expr, type_usize, ref->enum_constant.ordinal);
|
||||
expr_rewrite_to_int_const(expr, type_compint, ref->enum_constant.ordinal);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (name == kw_nameof && !is_macro)
|
||||
{
|
||||
expr_rewrite_to_string(expr, ref->name);
|
||||
@@ -2139,7 +2150,8 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr)
|
||||
}
|
||||
if (name == kw_qnameof && !is_macro)
|
||||
{
|
||||
TODO
|
||||
expr_rewrite_to_string(expr, ref->external_name ?: ref->name);
|
||||
return true;
|
||||
}
|
||||
if (name == kw_kindof && !is_macro)
|
||||
{
|
||||
@@ -2148,7 +2160,7 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr)
|
||||
// If we have something like struct Foo { Bar b; int c; struct d { int e; } }
|
||||
// If we are inspecting Foo.c we're done here. Otherwise handle struct d / Bar b case
|
||||
// The same way.
|
||||
if (is_plain_member)
|
||||
if (is_leaf_member_ref)
|
||||
{
|
||||
if (!type_is_structlike(ref->type))
|
||||
{
|
||||
@@ -2163,6 +2175,7 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr)
|
||||
// Pretend to be an inline struct.
|
||||
ref = ref->type->decl;
|
||||
}
|
||||
|
||||
VECEACH(ref->methods, i)
|
||||
{
|
||||
Decl *decl = ref->methods[i];
|
||||
@@ -2186,125 +2199,129 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
VECEACH(ref->strukt.members, i)
|
||||
// We want a distinct to be able to access members.
|
||||
Decl *flat_ref = ref;
|
||||
while (flat_ref->decl_kind == DECL_DISTINCT)
|
||||
{
|
||||
Decl *member = ref->strukt.members[i];
|
||||
if (name == member->name)
|
||||
{
|
||||
if (is_macro)
|
||||
Type *flat_type = flat_ref->distinct_decl.base_type->canonical;
|
||||
if (!type_is_user_defined(flat_type)) break;
|
||||
flat_ref = flat_type->decl;
|
||||
}
|
||||
|
||||
switch (flat_ref->decl_kind)
|
||||
{
|
||||
case DECL_STRUCT:
|
||||
case DECL_UNION:
|
||||
case DECL_ERR:
|
||||
VECEACH(flat_ref->strukt.members, i)
|
||||
{
|
||||
SEMA_ERROR(child, "member '%s' should not be prefixed with '@'.", name);
|
||||
return false;
|
||||
Decl *member = ref->strukt.members[i];
|
||||
if (name == member->name)
|
||||
{
|
||||
if (is_macro)
|
||||
{
|
||||
SEMA_ERROR(child, "member '%s' should not be prefixed with '@'.", name);
|
||||
return false;
|
||||
}
|
||||
expr->expr_kind = EXPR_MEMBER_ACCESS;
|
||||
expr->access_expr.ref = member;
|
||||
expr_set_type(expr, member->type);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
expr->expr_kind = EXPR_MEMBER_ACCESS;
|
||||
expr->access_expr.ref = member;
|
||||
expr_set_type(expr, member->type);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
SEMA_ERROR(expr, "No function or member '%s.%s' found.", "todo", name);
|
||||
TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Analyse "x.y"
|
||||
*/
|
||||
static inline bool sema_expr_analyse_access(Context *context, Expr *expr)
|
||||
{
|
||||
Expr *parent = expr->access_expr.parent;
|
||||
bool was_group = parent->expr_kind == EXPR_GROUP;
|
||||
|
||||
// 1. Resolve the left hand
|
||||
if (!sema_analyse_expr_value(context, NULL, parent)) return false;
|
||||
|
||||
if (parent->type == type_typeinfo)
|
||||
{
|
||||
return sema_expr_analyse_type_access(expr, parent->type_expr, was_group);
|
||||
}
|
||||
if (parent->expr_kind == EXPR_MEMBER_ACCESS)
|
||||
{
|
||||
return sema_expr_analyse_member_access(context, expr);
|
||||
}
|
||||
bool is_function_or_macro = false;
|
||||
if (parent->expr_kind == EXPR_ACCESS)
|
||||
{
|
||||
is_function_or_macro = parent->access_expr.ref->decl_kind == DECL_FUNC || parent->access_expr.ref->decl_kind == DECL_MACRO;
|
||||
}
|
||||
if (parent->expr_kind == EXPR_IDENTIFIER)
|
||||
{
|
||||
is_function_or_macro = parent->identifier_expr.decl->decl_kind == DECL_FUNC || parent->identifier_expr.decl->decl_kind == DECL_MACRO;
|
||||
}
|
||||
|
||||
if (is_function_or_macro)
|
||||
{
|
||||
SEMA_ERROR(expr->access_expr.child, "No such member or function could be found.");
|
||||
return false;
|
||||
}
|
||||
// 2. The right hand side may be a @ident or ident
|
||||
Expr *child = expr->access_expr.child;
|
||||
bool is_macro = child->expr_kind == EXPR_MACRO_EXPANSION;
|
||||
|
||||
// 3. Find the actual token.
|
||||
TokenId identifier_token = sema_expr_resolve_access_child(child);
|
||||
if (TOKEN_IS_INVALID(identifier_token)) return false;
|
||||
|
||||
// 2. If our left hand side is a type, e.g. MyInt.abc, handle this here.
|
||||
Type *parent_type = parent->type;
|
||||
if (parent->expr_kind == EXPR_TYPEINFO)
|
||||
{
|
||||
return sema_expr_analyse_type_access(expr, parent->type_expr, was_group, is_macro, identifier_token);
|
||||
}
|
||||
|
||||
// 3. The left hand side may already be indexing into a type:
|
||||
// x.y.z. In this case "x.y" is the parent.
|
||||
if (parent->expr_kind == EXPR_MEMBER_ACCESS)
|
||||
{
|
||||
return sema_expr_analyse_member_access(context, expr, is_macro, identifier_token);
|
||||
}
|
||||
|
||||
|
||||
// 6. Copy failability
|
||||
expr->failable = parent->failable;
|
||||
|
||||
assert(expr->expr_kind == EXPR_ACCESS);
|
||||
assert(parent->resolve_status == RESOLVE_DONE);
|
||||
|
||||
Type *parent_type = parent->type;
|
||||
Type *type = type_flatten(parent_type);
|
||||
|
||||
// 7. Is this a pointer? If so we insert a deref.
|
||||
Type *type = parent_type->canonical;
|
||||
bool is_pointer = type->type_kind == TYPE_POINTER;
|
||||
if (is_pointer)
|
||||
{
|
||||
type = type_flatten(type->pointer);
|
||||
type = type->pointer->canonical;
|
||||
parent_type = type;
|
||||
if (!sema_cast_rvalue(context, NULL, parent)) return false;
|
||||
insert_access_deref(expr);
|
||||
parent = expr->access_expr.parent;
|
||||
}
|
||||
|
||||
// 8. Depending on parent type, we have some hard coded types
|
||||
const char *kw = TOKSTR(identifier_token);
|
||||
Expr *current_parent = parent;
|
||||
|
||||
Type *flat_type = type_flatten(type);
|
||||
CHECK_DEEPER:
|
||||
|
||||
switch (type->type_kind)
|
||||
// 9. Fix hard coded function `len` on subarrays and arrays
|
||||
if (!is_macro && kw == kw_len)
|
||||
{
|
||||
case TYPE_SUBARRAY:
|
||||
if (is_macro) goto NO_MATCH;
|
||||
if (kw == kw_sizeof)
|
||||
{
|
||||
expr_rewrite_to_int_const(expr, type_usize, type_size(type));
|
||||
return true;
|
||||
}
|
||||
if (kw == kw_len)
|
||||
{
|
||||
expr->expr_kind = EXPR_LEN;
|
||||
expr->len_expr.inner = parent;
|
||||
expr_set_type(expr, type_void);
|
||||
expr->resolve_status = RESOLVE_DONE;
|
||||
return true;
|
||||
}
|
||||
goto NO_MATCH;
|
||||
case TYPE_ARRAY:
|
||||
if (is_macro) goto NO_MATCH;
|
||||
if (kw == kw_sizeof)
|
||||
{
|
||||
expr_rewrite_to_int_const(expr, type_usize, type_size(type));
|
||||
return true;
|
||||
}
|
||||
if (kw == kw_len)
|
||||
{
|
||||
expr_rewrite_to_int_const(expr, type_usize, type->array.len);
|
||||
return true;
|
||||
}
|
||||
goto NO_MATCH;
|
||||
case TYPE_ENUM:
|
||||
case TYPE_ERRTYPE:
|
||||
case TYPE_STRUCT:
|
||||
case TYPE_UNION:
|
||||
break;
|
||||
default:
|
||||
NO_MATCH:
|
||||
SEMA_ERROR(expr, "There is no member or method '%s' on '%s'", TOKSTR(identifier_token), type_to_error_string(parent_type));
|
||||
return false;
|
||||
if (flat_type->type_kind == TYPE_SUBARRAY)
|
||||
{
|
||||
expr->expr_kind = EXPR_LEN;
|
||||
expr->len_expr.inner = parent;
|
||||
expr_set_type(expr, type_void);
|
||||
expr->resolve_status = RESOLVE_DONE;
|
||||
return true;
|
||||
}
|
||||
if (flat_type->type_kind == TYPE_ARRAY)
|
||||
{
|
||||
expr_rewrite_to_int_const(expr, type_usize, type->array.len);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 9. At this point we may only have distinct, struct, union, error, enum
|
||||
if (!type_may_have_sub_elements(type))
|
||||
{
|
||||
SEMA_ERROR(expr, "There is no member or method '%s' on '%s'", kw, type_to_error_string(parent_type));
|
||||
return false;
|
||||
}
|
||||
|
||||
// 10. Dump all members and methods into the scope.
|
||||
Decl *decl = type->decl;
|
||||
Decl *member;
|
||||
SCOPE_START
|
||||
@@ -2312,10 +2329,10 @@ CHECK_DEEPER:
|
||||
member = sema_resolve_symbol_in_current_dynamic_scope(context, kw);
|
||||
SCOPE_END;
|
||||
|
||||
|
||||
// 11. If we didn't find a match...
|
||||
if (!member)
|
||||
{
|
||||
// We have a potential embedded struct check:
|
||||
// 11a. We have a potential embedded struct check:
|
||||
if (type_is_substruct(type))
|
||||
{
|
||||
Expr *embedded_struct = expr_access_inline_member(parent, type->decl);
|
||||
@@ -2323,23 +2340,29 @@ CHECK_DEEPER:
|
||||
type = embedded_struct->type->canonical;
|
||||
goto CHECK_DEEPER;
|
||||
}
|
||||
|
||||
// 11b. Otherwise we give up.
|
||||
SEMA_ERROR(expr, "There is no field or method '%s.%s'.", decl->name, kw);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 12a. If the member was a macro and it isn't prefixed with `@` then that's an error.
|
||||
if (member->decl_kind == DECL_MACRO && !is_macro)
|
||||
{
|
||||
SEMA_ERROR(expr, "Expected '@' before the macro name.");
|
||||
return false;
|
||||
}
|
||||
// 12b. If the member was *not* a macro but was prefixed with `@` then that's an error.
|
||||
if (member->decl_kind != DECL_MACRO && is_macro)
|
||||
{
|
||||
SEMA_ERROR(expr, "'@' should only be placed in front of macro names.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 13. Copy properties.
|
||||
expr->access_expr.parent = current_parent;
|
||||
expr->constant = expr->access_expr.parent->constant;
|
||||
expr->pure = expr->access_expr.parent->pure;
|
||||
|
||||
expr_set_type(expr, member->type);
|
||||
expr->access_expr.ref = member;
|
||||
return true;
|
||||
@@ -3197,10 +3220,29 @@ bool sema_expr_analyse_assign_right_side(Context *context, Expr *expr, Type *lef
|
||||
{
|
||||
return sema_expr_analyse_slice_assign(context, expr, left_type, right, lhs_is_failable);
|
||||
}
|
||||
// 1. Evaluate right side to required type.
|
||||
if (!sema_analyse_expr_of_required_type(context, left_type, right, lhs_is_failable != FAILABLE_NO)) return false;
|
||||
|
||||
// 2. Set the result to the type on the right side.
|
||||
// 1. Evaluate right side to required type.
|
||||
if (!sema_analyse_expr(context, left_type, right)) return false;
|
||||
if (right->failable && lhs_is_failable == FAILABLE_NO)
|
||||
{
|
||||
if (!left_type) left_type = right->type;
|
||||
SEMA_ERROR(right, "'%s!' cannot be converted into '%s'.", type_to_error_string(right->type), type_to_error_string(left_type));
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. Evaluate right hand side, making special concession for inferred arrays.
|
||||
do
|
||||
{
|
||||
if (!left_type) break;
|
||||
if (left_type->canonical->type_kind == TYPE_INFERRED_ARRAY && right->type->canonical->type_kind == TYPE_ARRAY)
|
||||
{
|
||||
if (left_type->canonical->array.base == right->type->canonical->array.base) break;
|
||||
}
|
||||
assert(right->type->canonical->type_kind != TYPE_INFERRED_ARRAY);
|
||||
if (!cast_implicit(right, left_type)) return false;
|
||||
} while (0);
|
||||
|
||||
// 3. Set the result to the type on the right side.
|
||||
if (expr) expr_copy_types(expr, right);
|
||||
|
||||
return true;
|
||||
@@ -5010,6 +5052,492 @@ static inline bool sema_expr_analyse_placeholder(Context *context, Type *to, Exp
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef struct MiniLexer_
|
||||
{
|
||||
const char *chars;
|
||||
uint32_t index;
|
||||
} MiniLexer;
|
||||
|
||||
static inline char minilex_next(MiniLexer *lexer)
|
||||
{
|
||||
return lexer->chars[lexer->index++];
|
||||
}
|
||||
|
||||
static inline char minilex_peek(MiniLexer *lexer, int32_t offset)
|
||||
{
|
||||
return lexer->chars[lexer->index + offset];
|
||||
}
|
||||
|
||||
static inline bool minilex_match(MiniLexer *lexer, char c)
|
||||
{
|
||||
if (lexer->chars[lexer->index] != c) return false;
|
||||
lexer->index++;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void minilex_skip_whitespace(MiniLexer *lexer)
|
||||
{
|
||||
char c;
|
||||
while (1)
|
||||
{
|
||||
c = lexer->chars[lexer->index];
|
||||
if ((c != ' ') && (c != '\t')) break;
|
||||
lexer->index++;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t minilex_parse_number(MiniLexer *lexer, uint64_t max)
|
||||
{
|
||||
assert(max < UINT64_MAX);
|
||||
ByteSize value = 0;
|
||||
while (is_number(minilex_peek(lexer, 0)))
|
||||
{
|
||||
ByteSize old_value = value;
|
||||
value = value * 10 + minilex_next(lexer) - '0';
|
||||
if (old_value > value || value > max)
|
||||
{
|
||||
return UINT64_MAX;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_idents_string(Context *context, MiniLexer *lexer, ExprFlatElement **elements_ref)
|
||||
{
|
||||
ExprFlatElement *elements = NULL;
|
||||
|
||||
char c;
|
||||
ExprFlatElement element;
|
||||
while (1)
|
||||
{
|
||||
minilex_skip_whitespace(lexer);
|
||||
if (minilex_match(lexer, '['))
|
||||
{
|
||||
minilex_skip_whitespace(lexer);
|
||||
if (!is_number(minilex_peek(lexer, 0))) return false;
|
||||
uint64_t value = minilex_parse_number(lexer, MAX_ARRAYINDEX);
|
||||
if (value == UINT64_MAX) return false;
|
||||
if (!minilex_match(lexer, ']')) return false;
|
||||
element.array = true;
|
||||
element.index = (ArrayIndex)value;
|
||||
}
|
||||
else
|
||||
{
|
||||
scratch_buffer_clear();
|
||||
while (is_alphanum_(minilex_peek(lexer, 0)))
|
||||
{
|
||||
scratch_buffer_append_char(minilex_next(lexer));
|
||||
}
|
||||
if (!global_context.scratch_buffer_len) return false;
|
||||
TokenType token_type;
|
||||
const char *ident = symtab_find(global_context.scratch_buffer,
|
||||
global_context.scratch_buffer_len,
|
||||
fnv1a(global_context.scratch_buffer, global_context.scratch_buffer_len),
|
||||
&token_type);
|
||||
if (!ident || token_type != TOKEN_IDENT) return false;
|
||||
element.array = false;
|
||||
element.ident = ident;
|
||||
}
|
||||
vec_add(elements, element);
|
||||
minilex_skip_whitespace(lexer);
|
||||
if (minilex_match(lexer, '\0')) break;
|
||||
if (!minilex_match(lexer, '.') && minilex_peek(lexer, 0) != '[') return false;
|
||||
}
|
||||
*elements_ref = elements;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_identifier_path_string(Context *context, Expr *expr, Decl **decl_ref, Type **type_ref, ExprFlatElement **idents_ref)
|
||||
{
|
||||
char *chars = expr->const_expr.string.chars;
|
||||
uint32_t len = expr->const_expr.string.len;
|
||||
if (!len)
|
||||
{
|
||||
SEMA_ERROR(expr, "Expected a name here.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO HANDLE VIRTUAL!
|
||||
|
||||
MiniLexer lexer = { .chars = chars };
|
||||
|
||||
// Do we parse a path?
|
||||
Path *path = NULL;
|
||||
|
||||
// Skip past the start.
|
||||
while (is_lower_(minilex_peek(&lexer, 0))) minilex_next(&lexer);
|
||||
|
||||
minilex_skip_whitespace(&lexer);
|
||||
|
||||
// Yes, parse a path.
|
||||
if (minilex_match(&lexer, ':'))
|
||||
{
|
||||
if (!minilex_match(&lexer, ':')) goto FAIL_PARSE;
|
||||
lexer.index = 0;
|
||||
NEXT_PART:
|
||||
scratch_buffer_clear();
|
||||
minilex_skip_whitespace(&lexer);
|
||||
while (is_lower(minilex_peek(&lexer, 0)))
|
||||
{
|
||||
scratch_buffer_append_char(minilex_next(&lexer));
|
||||
}
|
||||
minilex_skip_whitespace(&lexer);
|
||||
|
||||
if (!(minilex_match(&lexer, ':') && minilex_match(&lexer, ':')))
|
||||
{
|
||||
UNREACHABLE;
|
||||
}
|
||||
uint32_t start_index = lexer.index;
|
||||
|
||||
minilex_skip_whitespace(&lexer);
|
||||
|
||||
while (is_lower_(minilex_peek(&lexer, 0))) minilex_next(&lexer);
|
||||
|
||||
minilex_skip_whitespace(&lexer);
|
||||
if (minilex_match(&lexer, ':'))
|
||||
{
|
||||
if (!minilex_match(&lexer, ':')) goto FAIL_PARSE;
|
||||
scratch_buffer_append_len("::", 2);
|
||||
lexer.index = start_index;
|
||||
goto NEXT_PART;
|
||||
}
|
||||
lexer.index = start_index;
|
||||
path = path_create_from_string(scratch_buffer_to_string(), global_context.scratch_buffer_len, expr->span);
|
||||
}
|
||||
else
|
||||
{
|
||||
lexer.index = 0;
|
||||
}
|
||||
|
||||
scratch_buffer_clear();
|
||||
minilex_skip_whitespace(&lexer);
|
||||
while (is_alphanum_(minilex_peek(&lexer, 0)))
|
||||
{
|
||||
scratch_buffer_append_char(minilex_next(&lexer));
|
||||
}
|
||||
if (!global_context.scratch_buffer_len) goto FAIL;
|
||||
TokenType token_type;
|
||||
const char *symbol = symtab_find(global_context.scratch_buffer,
|
||||
global_context.scratch_buffer_len,
|
||||
fnv1a(global_context.scratch_buffer, global_context.scratch_buffer_len),
|
||||
&token_type);
|
||||
if (!symbol)
|
||||
{
|
||||
SEMA_ERROR(expr, "'%s' could not be found, did you misspell it?", chars);
|
||||
return false;
|
||||
}
|
||||
Type *type = NULL;
|
||||
Decl *decl = NULL;
|
||||
if (token_is_type(token_type))
|
||||
{
|
||||
type = type_from_token(token_type);
|
||||
}
|
||||
else
|
||||
{
|
||||
decl = TRY_DECL_OR(sema_resolve_string_symbol(context, symbol, expr->span, path), false);
|
||||
if (decl->decl_kind == DECL_TYPEDEF)
|
||||
{
|
||||
type = decl->typedef_decl.type_info->type;
|
||||
decl = NULL;
|
||||
}
|
||||
}
|
||||
if (type || decl_is_user_defined_type(decl))
|
||||
{
|
||||
if (decl)
|
||||
{
|
||||
type = decl->type;
|
||||
decl = NULL;
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
minilex_skip_whitespace(&lexer);
|
||||
if (minilex_match(&lexer, '*'))
|
||||
{
|
||||
type = type_get_ptr(type);
|
||||
continue;
|
||||
}
|
||||
if (!minilex_match(&lexer, '[')) break;
|
||||
minilex_skip_whitespace(&lexer);
|
||||
if (minilex_match(&lexer, ']'))
|
||||
{
|
||||
type = type_get_subarray(type);
|
||||
continue;
|
||||
}
|
||||
ByteSize array_size = 0;
|
||||
while (is_number(minilex_peek(&lexer, 0)))
|
||||
{
|
||||
ByteSize old_array_size = array_size;
|
||||
array_size = array_size * 10 + minilex_next(&lexer) - '0';
|
||||
if (old_array_size > array_size || array_size > MAX_ARRAYINDEX)
|
||||
{
|
||||
SEMA_ERROR(expr, "Array index out of bounds.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
minilex_skip_whitespace(&lexer);
|
||||
if (!minilex_match(&lexer, ']')) goto FAIL_PARSE;
|
||||
type = type_get_array(type, array_size);
|
||||
}
|
||||
}
|
||||
|
||||
minilex_skip_whitespace(&lexer);
|
||||
if (minilex_match(&lexer, '\0')) goto EXIT;
|
||||
|
||||
|
||||
minilex_skip_whitespace(&lexer);
|
||||
if (!minilex_match(&lexer, '.'))
|
||||
{
|
||||
SEMA_ERROR(expr, "A '.' was expected after '%s'.", symbol);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!sema_analyse_idents_string(context, &lexer, idents_ref))
|
||||
{
|
||||
SEMA_ERROR(expr, "The path to an existing member was expected after '%s', did you make a mistake?", symbol);
|
||||
return false;
|
||||
}
|
||||
|
||||
EXIT:
|
||||
*decl_ref = decl;
|
||||
*type_ref = type;
|
||||
return true;
|
||||
|
||||
FAIL:
|
||||
SEMA_ERROR(expr, "'%s' is not a known identifier, did you misspell it?", chars);
|
||||
return false;
|
||||
|
||||
FAIL_PARSE:
|
||||
SEMA_ERROR(expr, "'%s' could not be parsed as an identifier, did you make a mistake?", chars);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static inline bool sema_analyse_ct_call_parameters(Context *context, Expr *expr)
|
||||
{
|
||||
if (expr->resolve_status == RESOLVE_DONE) return true;
|
||||
|
||||
Expr **elements = expr->ct_call_expr.arguments;
|
||||
unsigned count = vec_size(elements);
|
||||
if (count > 2)
|
||||
{
|
||||
SEMA_ERROR(elements[2], "The function can only take one or two arguments.");
|
||||
return expr_poison(expr);
|
||||
}
|
||||
Expr *first_expr = elements[0];
|
||||
if (!sema_analyse_expr_value(context, NULL, first_expr)) return false;
|
||||
ExprFlatElement *flatpath = NULL;
|
||||
Expr *second_expr = NULL;
|
||||
if (count == 2)
|
||||
{
|
||||
second_expr = elements[1];
|
||||
if (second_expr->expr_kind != EXPR_FLATPATH)
|
||||
{
|
||||
if (!sema_analyse_expr_value(context, NULL, second_expr)) return false;
|
||||
if (second_expr->expr_kind != EXPR_CONST || second_expr->const_expr.kind != TYPE_STRLIT)
|
||||
{
|
||||
SEMA_ERROR(second_expr, "Expected a string here.");
|
||||
return false;
|
||||
}
|
||||
MiniLexer lexer = { .chars = second_expr->const_expr.string.chars };
|
||||
if (!sema_analyse_idents_string(context, &lexer, &flatpath))
|
||||
{
|
||||
SEMA_ERROR(expr, "The path to an existing member was expected, did you make a mistake?");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
flatpath = second_expr->flatpath_expr;
|
||||
}
|
||||
}
|
||||
Decl *decl = NULL;
|
||||
Type *type = NULL;
|
||||
if (first_expr->expr_kind == EXPR_CONST && first_expr->const_expr.kind == TYPE_STRLIT)
|
||||
{
|
||||
ExprFlatElement *path_elements = NULL;
|
||||
if (!sema_analyse_identifier_path_string(context, first_expr, &decl, &type, &path_elements)) return false;
|
||||
if (path_elements)
|
||||
{
|
||||
if (flatpath)
|
||||
{
|
||||
SEMA_ERROR(elements[1], "You cannot combine a string with a member path with a separate path here.");
|
||||
return false;
|
||||
}
|
||||
flatpath = path_elements;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (first_expr->expr_kind)
|
||||
{
|
||||
case EXPR_IDENTIFIER:
|
||||
decl = first_expr->identifier_expr.decl;
|
||||
break;
|
||||
case EXPR_TYPEINFO:
|
||||
type = first_expr->type_expr->type;
|
||||
break;
|
||||
case EXPR_TYPEOF:
|
||||
type = first_expr->typeof_expr->type;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!type && !decl)
|
||||
{
|
||||
SEMA_ERROR(first_expr, "A type or declaration was expected.");
|
||||
return false;
|
||||
}
|
||||
if (!decl && type_is_user_defined(type)) decl = type->decl;
|
||||
if (decl)
|
||||
{
|
||||
if (!sema_analyse_decl(context, decl)) return false;
|
||||
if (decl)
|
||||
{
|
||||
switch (decl->decl_kind)
|
||||
{
|
||||
case DECL_DISTINCT:
|
||||
case DECL_ENUM:
|
||||
case DECL_ERR:
|
||||
case DECL_FUNC:
|
||||
case DECL_STRUCT:
|
||||
case DECL_TYPEDEF:
|
||||
case DECL_UNION:
|
||||
break;
|
||||
case DECL_VAR:
|
||||
if (decl->type) break;
|
||||
FALLTHROUGH;
|
||||
default:
|
||||
SEMA_ERROR(first_expr, "A type or typed declaration was expected.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
type = decl->type;
|
||||
}
|
||||
expr->ct_call_expr.flatpath = flatpath;
|
||||
expr->ct_call_expr.decl = decl;
|
||||
expr->ct_call_expr.type = type;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_ct_sizeof(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
Expr *first = expr->ct_call_expr.arguments[0];
|
||||
if (!sema_analyse_ct_call_parameters(context, expr)) return false;
|
||||
Type *type = expr->ct_call_expr.type->canonical;
|
||||
ExprFlatElement *elements = expr->ct_call_expr.flatpath;
|
||||
VECEACH(elements, i)
|
||||
{
|
||||
ExprFlatElement element = elements[i];
|
||||
type = type_flatten_distinct(type);
|
||||
if (element.array)
|
||||
{
|
||||
if (type->type_kind != TYPE_ARRAY)
|
||||
{
|
||||
SEMA_ERROR(first, "%s is not a fixed size array.", type_quoted_error_string(expr->ct_call_expr.type));
|
||||
return false;
|
||||
}
|
||||
type = type->array.base;
|
||||
continue;
|
||||
}
|
||||
if (!type_is_structlike(type))
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
SEMA_ERROR(first, "%s has no members.", type_quoted_error_string(expr->ct_call_expr.type));
|
||||
}
|
||||
else
|
||||
{
|
||||
SEMA_ERROR(first, "There is no such member in %s.", type_quoted_error_string(expr->ct_call_expr.type));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
Decl *decl = type->decl;
|
||||
SCOPE_START
|
||||
add_members_to_context(context, type->decl);
|
||||
decl = sema_resolve_symbol_in_current_dynamic_scope(context, element.ident);
|
||||
SCOPE_END;
|
||||
if (!decl)
|
||||
{
|
||||
SEMA_ERROR(first, "There is no such member in %s.", type_quoted_error_string(expr->ct_call_expr.type));
|
||||
return false;
|
||||
}
|
||||
type = decl->type;
|
||||
}
|
||||
expr_rewrite_to_int_const(expr, type_compint, type_size(type));
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_ct_alignof(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
Expr *first = expr->ct_call_expr.arguments[0];
|
||||
if (!sema_analyse_ct_call_parameters(context, expr)) return false;
|
||||
Type *type = expr->ct_call_expr.type->canonical;
|
||||
ExprFlatElement *elements = expr->ct_call_expr.flatpath;
|
||||
AlignSize align = expr->ct_call_expr.decl ? expr->ct_call_expr.decl->alignment : type_abi_alignment(type);
|
||||
VECEACH(elements, i)
|
||||
{
|
||||
ExprFlatElement element = elements[i];
|
||||
type = type_flatten_distinct(type);
|
||||
if (element.array)
|
||||
{
|
||||
if (type->type_kind != TYPE_ARRAY)
|
||||
{
|
||||
SEMA_ERROR(first, "%s is not a fixed size array.", type_quoted_error_string(expr->ct_call_expr.type));
|
||||
return false;
|
||||
}
|
||||
type = type->array.base;
|
||||
ByteSize size = type_size(type);
|
||||
align = (AlignSize)type_min_alignment(size * element.index, align);
|
||||
continue;
|
||||
}
|
||||
if (!type_is_structlike(type))
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
SEMA_ERROR(first, "%s has no members.", type_quoted_error_string(expr->ct_call_expr.type));
|
||||
}
|
||||
else
|
||||
{
|
||||
SEMA_ERROR(first, "There is no such member in %s.", type_quoted_error_string(expr->ct_call_expr.type));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
Decl *decl = type->decl;
|
||||
SCOPE_START
|
||||
add_members_to_context(context, type->decl);
|
||||
decl = sema_resolve_symbol_in_current_dynamic_scope(context, element.ident);
|
||||
SCOPE_END;
|
||||
if (!decl)
|
||||
{
|
||||
SEMA_ERROR(first, "There is no such member in %s.", type_quoted_error_string(expr->ct_call_expr.type));
|
||||
return false;
|
||||
}
|
||||
type = decl->type;
|
||||
align = type_min_alignment(decl->offset, align);
|
||||
}
|
||||
|
||||
expr_rewrite_to_int_const(expr, type_compint, align);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_ct_call(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
switch (expr->ct_call_expr.token_type)
|
||||
{
|
||||
case TOKEN_CT_SIZEOF:
|
||||
return sema_expr_analyse_ct_sizeof(context, to, expr);
|
||||
case TOKEN_CT_ALIGNOF:
|
||||
return sema_expr_analyse_ct_alignof(context, to, expr);
|
||||
case TOKEN_CT_OFFSETOF:
|
||||
TODO
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
@@ -5021,7 +5549,10 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr *
|
||||
case EXPR_MEMBER_ACCESS:
|
||||
case EXPR_DESIGNATOR:
|
||||
case EXPR_MACRO_BODY_EXPANSION:
|
||||
case EXPR_FLATPATH:
|
||||
UNREACHABLE
|
||||
case EXPR_CT_CALL:
|
||||
return sema_expr_analyse_ct_call(context, to, expr);
|
||||
case EXPR_HASH_IDENT:
|
||||
return sema_expr_analyse_hash_identifier(context, to, expr);
|
||||
case EXPR_CT_IDENT:
|
||||
|
||||
@@ -170,20 +170,20 @@ static Decl *sema_resolve_no_path_symbol(Context *context, const char *symbol,
|
||||
}
|
||||
|
||||
|
||||
static void sema_report_error_on_decl(const char *symbol_str, TokenId symbol, Decl *found, Decl *ambiguous_decl,
|
||||
static void sema_report_error_on_decl(const char *symbol_str, SourceSpan span, Decl *found, Decl *ambiguous_decl,
|
||||
Decl *private_decl)
|
||||
{
|
||||
if (!found && private_decl)
|
||||
{
|
||||
SEMA_TOKID_ERROR(symbol, "The %s '%s' is not visible from this module.", decl_to_name(private_decl), symbol_str);
|
||||
sema_error_range(span, "The %s '%s' is not visible from this module.", decl_to_name(private_decl), symbol_str);
|
||||
return;
|
||||
}
|
||||
if (ambiguous_decl)
|
||||
{
|
||||
assert(found);
|
||||
const char *symbol_type = decl_to_name(found);
|
||||
SEMA_TOKID_ERROR(symbol,
|
||||
"The %s '%s' is defined in both '%s' and '%s', please use either %s::%s or %s::%s to resolve the ambiguity.",
|
||||
sema_error_range(span,
|
||||
"The %s '%s' is defined in both '%s' and '%s', please use either %s::%s or %s::%s to resolve the ambiguity.",
|
||||
symbol_type,
|
||||
symbol_str,
|
||||
found->module->name->module,
|
||||
@@ -195,27 +195,15 @@ static void sema_report_error_on_decl(const char *symbol_str, TokenId symbol, De
|
||||
return;
|
||||
}
|
||||
assert(!found);
|
||||
switch (TOKTYPE(symbol))
|
||||
{
|
||||
case TOKEN_TYPE_IDENT:
|
||||
SEMA_TOKID_ERROR(symbol, "The type '%s' could not be found, did you spell it right?", symbol_str);
|
||||
break;
|
||||
case TOKEN_CONST_IDENT:
|
||||
SEMA_TOKID_ERROR(symbol, "The constant '%s' could not be found, did you spell it right?", symbol_str);
|
||||
break;
|
||||
default:
|
||||
SEMA_TOKID_ERROR(symbol, "The identifier '%s' could not be found, did you spell it right?", symbol_str);
|
||||
break;
|
||||
}
|
||||
sema_error_range(span, "'%s' could not be found, did you spell it right?", symbol_str);
|
||||
}
|
||||
|
||||
static Decl *sema_resolve_symbol(Context *context, TokenId symbol, Path *path, bool report_error)
|
||||
static Decl *sema_resolve_symbol(Context *context, const char *symbol_str, SourceSpan span, Path *path, bool report_error)
|
||||
{
|
||||
Decl *ambiguous_other_decl = NULL;
|
||||
Decl *private_decl = NULL;
|
||||
bool path_found = false;
|
||||
Decl *decl;
|
||||
const char *symbol_str = TOKSTR(symbol);
|
||||
if (path)
|
||||
{
|
||||
decl = sema_resolve_path_symbol(context, symbol_str, path, &ambiguous_other_decl, &private_decl, &path_found);
|
||||
@@ -233,7 +221,7 @@ static Decl *sema_resolve_symbol(Context *context, TokenId symbol, Path *path, b
|
||||
if (!decl || ambiguous_other_decl)
|
||||
{
|
||||
if (!report_error) return NULL;
|
||||
sema_report_error_on_decl(symbol_str, symbol, decl, ambiguous_other_decl, private_decl);
|
||||
sema_report_error_on_decl(symbol_str, span, decl, ambiguous_other_decl, private_decl);
|
||||
return poisoned_decl;
|
||||
}
|
||||
if (decl->module && decl->module != context->module)
|
||||
@@ -293,7 +281,7 @@ Decl *sema_resolve_parameterized_symbol(Context *context, TokenId symbol, Path *
|
||||
// 14. Error report
|
||||
if (!decl || ambiguous_other_decl)
|
||||
{
|
||||
sema_report_error_on_decl(symbol_str, symbol, decl, ambiguous_other_decl, private_decl);
|
||||
sema_report_error_on_decl(symbol_str, source_span_from_token_id(symbol), decl, ambiguous_other_decl, private_decl);
|
||||
return poisoned_decl;
|
||||
}
|
||||
return decl;
|
||||
@@ -335,7 +323,7 @@ Decl *sema_resolve_parameterized_symbol(Context *context, TokenId symbol, Path *
|
||||
// 14. Error report
|
||||
if (!decl || ambiguous_other_decl)
|
||||
{
|
||||
sema_report_error_on_decl(symbol_str, symbol, decl, ambiguous_other_decl, private_decl);
|
||||
sema_report_error_on_decl(symbol_str, source_span_from_token_id(symbol), decl, ambiguous_other_decl, private_decl);
|
||||
return poisoned_decl;
|
||||
}
|
||||
return decl;
|
||||
@@ -343,7 +331,12 @@ Decl *sema_resolve_parameterized_symbol(Context *context, TokenId symbol, Path *
|
||||
|
||||
Decl *sema_resolve_normal_symbol(Context *context, TokenId symbol, Path *path, bool handle_error)
|
||||
{
|
||||
return sema_resolve_symbol(context, symbol, path, handle_error);
|
||||
return sema_resolve_symbol(context, TOKSTR(symbol), source_span_from_token_id(symbol), path, handle_error);
|
||||
}
|
||||
|
||||
Decl *sema_resolve_string_symbol(Context *context, const char *symbol, SourceSpan span, Path *path)
|
||||
{
|
||||
return sema_resolve_symbol(context, symbol, span, path, true);
|
||||
}
|
||||
|
||||
static inline bool sema_append_local(Context *context, Decl *decl)
|
||||
|
||||
@@ -376,17 +376,17 @@ static inline bool sema_analyse_local_decl(Context *context, Decl *decl)
|
||||
|
||||
if (!sema_expr_analyse_assign_right_side(context, NULL, decl->type, init, decl->var.failable || decl->var.unwrap ? FAILABLE_YES : FAILABLE_NO)) return decl_poison(decl);
|
||||
|
||||
if (decl->type)
|
||||
{
|
||||
expr_set_type(decl->var.init_expr, decl->type);
|
||||
}
|
||||
|
||||
if (type_is_inferred)
|
||||
{
|
||||
Type *right_side_type = init->type->canonical;
|
||||
assert(right_side_type->type_kind == TYPE_ARRAY);
|
||||
decl->type = type_get_array(decl->type->array.base, right_side_type->array.len);
|
||||
}
|
||||
else if (decl->type)
|
||||
{
|
||||
expr_set_type(decl->var.init_expr, decl->type);
|
||||
}
|
||||
|
||||
|
||||
if (decl->var.unwrap && !init->failable)
|
||||
{
|
||||
@@ -1813,6 +1813,46 @@ bool sema_analyse_statement(Context *context, Ast *statement)
|
||||
}
|
||||
|
||||
|
||||
static bool sema_analyse_requires(Context *context, Ast *docs, Ast ***asserts)
|
||||
{
|
||||
if (!docs) return true;
|
||||
Ast **directives = docs->directives;
|
||||
Ast **output = NULL;
|
||||
VECEACH(directives, i)
|
||||
{
|
||||
Ast *directive = directives[i];
|
||||
if (directive->doc_directive.kind != DOC_DIRECTIVE_REQUIRE) continue;
|
||||
Expr *comment = directive->doc_directive.contract.comment;
|
||||
Expr *declexpr = directive->doc_directive.contract.decl_exprs;
|
||||
if (comment)
|
||||
{
|
||||
if (comment->expr_kind != EXPR_CONST || comment->const_expr.kind != TYPE_STRLIT)
|
||||
{
|
||||
SEMA_ERROR(comment, "Expected a string here.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
assert(declexpr->expr_kind == EXPR_DECL_LIST);
|
||||
|
||||
VECEACH(declexpr->dexpr_list_expr, j)
|
||||
{
|
||||
Ast *ast = declexpr->dexpr_list_expr[j];
|
||||
if (ast->ast_kind != AST_EXPR_STMT)
|
||||
{
|
||||
SEMA_ERROR(ast, "Only expressions are allowed.");
|
||||
return false;
|
||||
}
|
||||
Expr *expr = ast->expr_stmt;
|
||||
if (!sema_analyse_expr_of_required_type(context, type_bool, expr, false)) return false;
|
||||
Ast *assert = new_ast(AST_ASSERT_STMT, expr->span);
|
||||
assert->assert_stmt.expr = expr;
|
||||
assert->assert_stmt.message = comment;
|
||||
vec_add(output, assert);
|
||||
}
|
||||
}
|
||||
*asserts = output;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sema_analyse_function_body(Context *context, Decl *func)
|
||||
{
|
||||
@@ -1846,6 +1886,8 @@ bool sema_analyse_function_body(Context *context, Decl *func)
|
||||
{
|
||||
if (!sema_add_local(context, params[i])) return false;
|
||||
}
|
||||
Ast **asserts = NULL;
|
||||
if (!sema_analyse_requires(context, func->docs, &asserts)) return false;
|
||||
if (!sema_analyse_compound_statement_no_scope(context, func->func_decl.body)) return false;
|
||||
assert(context->active_scope.depth == 1);
|
||||
if (!context->active_scope.jump_end)
|
||||
@@ -1858,6 +1900,13 @@ bool sema_analyse_function_body(Context *context, Decl *func)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (asserts)
|
||||
{
|
||||
Ast *ast = new_ast(AST_COMPOUND_STMT, func->func_decl.body->span);
|
||||
ast->compound_stmt.stmts = asserts;
|
||||
vec_add(ast->compound_stmt.stmts, func->func_decl.body);
|
||||
func->func_decl.body = ast;
|
||||
}
|
||||
SCOPE_END;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ void symtab_init(uint32_t capacity)
|
||||
kw_param = KW_DEF("param");
|
||||
kw_pure = KW_DEF("pure");
|
||||
kw_qnameof = KW_DEF("qnameof");
|
||||
kw_require = KW_DEF("required");
|
||||
kw_require = KW_DEF("require");
|
||||
kw_sizeof = KW_DEF("sizeof");
|
||||
kw_std = KW_DEF("std");
|
||||
kw___ceil = KW_DEF("__ceil");
|
||||
@@ -180,6 +180,14 @@ const char *symtab_add(const char *symbol, uint32_t len, uint32_t fnv1hash, Toke
|
||||
return entry->value;
|
||||
}
|
||||
|
||||
const char *symtab_find(const char *symbol, uint32_t len, uint32_t fnv1hash, TokenType *type)
|
||||
{
|
||||
SymEntry *entry = entry_find(symbol, len, fnv1hash);
|
||||
if (!entry->value) return NULL;
|
||||
*type = entry->type;
|
||||
return entry->value;
|
||||
}
|
||||
|
||||
void stable_init(STable *table, uint32_t initial_size)
|
||||
{
|
||||
assert(initial_size && "Size must be larger than 0");
|
||||
|
||||
@@ -319,6 +319,8 @@ const char *token_type_to_string(TokenType type)
|
||||
case TOKEN_DOCS_LINE:
|
||||
return "DOCS_LINE";
|
||||
|
||||
case TOKEN_CT_ALIGNOF:
|
||||
return "$alignof";
|
||||
case TOKEN_CT_ASSERT:
|
||||
return "$assert";
|
||||
case TOKEN_CT_CASE:
|
||||
@@ -337,6 +339,10 @@ const char *token_type_to_string(TokenType type)
|
||||
return "$endswitch";
|
||||
case TOKEN_CT_IF:
|
||||
return "$if";
|
||||
case TOKEN_CT_OFFSETOF:
|
||||
return "$offsetof";
|
||||
case TOKEN_CT_SIZEOF:
|
||||
return "$sizeof";
|
||||
case TOKEN_CT_SWITCH:
|
||||
return "$switch";
|
||||
case TOKEN_CT_UNREACHABLE:
|
||||
|
||||
@@ -50,7 +50,7 @@ Type *type_error = &t.error;
|
||||
Type *type_varheader = &t.varheader;
|
||||
|
||||
static unsigned size_subarray;
|
||||
static unsigned alignment_subarray;
|
||||
static AlignSize alignment_subarray;
|
||||
unsigned size_error_code;
|
||||
unsigned alignment_error_code;
|
||||
|
||||
@@ -147,7 +147,7 @@ const char *type_to_error_string(Type *type)
|
||||
asprintf(&buffer, "%s[%llu]", type_to_error_string(type->array.base), (unsigned long long)type->array.len);
|
||||
return buffer;
|
||||
case TYPE_INFERRED_ARRAY:
|
||||
asprintf(&buffer, "%s[?]", type_to_error_string(type->array.base));
|
||||
asprintf(&buffer, "%s[*]", type_to_error_string(type->array.base));
|
||||
return buffer;
|
||||
case TYPE_SUBARRAY:
|
||||
asprintf(&buffer, "%s[]", type_to_error_string(type->array.base));
|
||||
@@ -753,7 +753,7 @@ static Type *type_generate_inferred_array(Type *arr_type, bool canonical)
|
||||
Type *arr = arr_type->type_cache[INFERRED_ARRAY_OFFSET];
|
||||
if (arr == NULL)
|
||||
{
|
||||
arr = type_new(TYPE_INFERRED_ARRAY, strformat("%s[_]", arr_type->name));
|
||||
arr = type_new(TYPE_INFERRED_ARRAY, strformat("%s[*]", arr_type->name));
|
||||
arr->array.base = arr_type;
|
||||
arr_type->type_cache[INFERRED_ARRAY_OFFSET] = arr;
|
||||
if (arr_type == arr_type->canonical)
|
||||
@@ -887,6 +887,7 @@ bool type_is_user_defined(Type *type)
|
||||
case TYPE_UNION:
|
||||
case TYPE_ERRTYPE:
|
||||
case TYPE_TYPEDEF:
|
||||
case TYPE_DISTINCT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -1227,12 +1228,64 @@ bool type_is_subtype(Type *type, Type *possible_subtype)
|
||||
|
||||
}
|
||||
|
||||
|
||||
Type *type_from_token(TokenType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case TOKEN_ERR:
|
||||
return type_error;
|
||||
case TOKEN_VOID:
|
||||
return type_void;
|
||||
case TOKEN_BOOL:
|
||||
return type_bool;
|
||||
case TOKEN_CHAR:
|
||||
return type_char;
|
||||
case TOKEN_DOUBLE:
|
||||
return type_double;
|
||||
case TOKEN_FLOAT:
|
||||
return type_float;
|
||||
case TOKEN_I128:
|
||||
return type_i128;
|
||||
case TOKEN_ICHAR:
|
||||
return type_ichar;
|
||||
case TOKEN_INT:
|
||||
return type_int;
|
||||
case TOKEN_IPTR:
|
||||
return type_iptr;
|
||||
case TOKEN_IPTRDIFF:
|
||||
return type_iptrdiff;
|
||||
case TOKEN_ISIZE:
|
||||
return type_isize;
|
||||
case TOKEN_LONG:
|
||||
return type_long;
|
||||
case TOKEN_SHORT:
|
||||
return type_short;
|
||||
case TOKEN_U128:
|
||||
return type_u128;
|
||||
case TOKEN_UINT:
|
||||
return type_uint;
|
||||
case TOKEN_ULONG:
|
||||
return type_ulong;
|
||||
case TOKEN_UPTR:
|
||||
return type_uptr;
|
||||
case TOKEN_UPTRDIFF:
|
||||
return type_uptrdiff;
|
||||
case TOKEN_USHORT:
|
||||
return type_ushort;
|
||||
case TOKEN_USIZE:
|
||||
return type_usize;
|
||||
case TOKEN_TYPEID:
|
||||
return type_typeid;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
bool type_may_have_sub_elements(Type *type)
|
||||
{
|
||||
// An alias is not ok.
|
||||
switch (type->type_kind)
|
||||
{
|
||||
case TYPE_DISTINCT:
|
||||
case TYPE_UNION:
|
||||
case TYPE_STRUCT:
|
||||
case TYPE_ENUM:
|
||||
|
||||
@@ -59,6 +59,11 @@ static inline bool is_lower(char c)
|
||||
return c >= 'a' && c <= 'z';
|
||||
}
|
||||
|
||||
static inline bool is_lower_(char c)
|
||||
{
|
||||
return c == '_' || (c >= 'a' && c <= 'z');
|
||||
}
|
||||
|
||||
static inline bool is_upper(char c)
|
||||
{
|
||||
return c >= 'A' && c <= 'Z';
|
||||
@@ -269,6 +274,11 @@ static inline bool is_letter(char c)
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool is_number(char c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
|
||||
#define FNV1_PRIME 0x01000193u
|
||||
#define FNV1_SEED 0x811C9DC5u
|
||||
|
||||
90
test/test_suite/compile_time_introspection/alignof.c3t
Normal file
90
test/test_suite/compile_time_introspection/alignof.c3t
Normal file
@@ -0,0 +1,90 @@
|
||||
// #target: x64_darwin
|
||||
module foo;
|
||||
|
||||
int[100] zfe;
|
||||
struct Bob
|
||||
{
|
||||
Bob[] x;
|
||||
char[100] y;
|
||||
struct w {
|
||||
int z;
|
||||
}
|
||||
}
|
||||
|
||||
union Ex
|
||||
{
|
||||
char[8] c;
|
||||
int[2] y;
|
||||
double z;
|
||||
}
|
||||
|
||||
union Br
|
||||
{
|
||||
int y;
|
||||
char x;
|
||||
}
|
||||
struct Ar
|
||||
{
|
||||
long z;
|
||||
Br[10] br;
|
||||
}
|
||||
|
||||
union Foob
|
||||
{
|
||||
long a;
|
||||
char[8] c;
|
||||
}
|
||||
Ar izzy;
|
||||
|
||||
|
||||
long x = $alignof(zfe);
|
||||
short y = $alignof("Bob.y");
|
||||
int z = $alignof("Bob.y");
|
||||
int w = $alignof(Bob, y);
|
||||
int v = $alignof(v);
|
||||
int x1 = $alignof("Ex.c");
|
||||
int x2 = $alignof("Ex", y);
|
||||
int x3 = $alignof(char[8]);
|
||||
int x4 = $alignof("Ar.br[1]");
|
||||
int x5 = $alignof("Ar", "br[1]");
|
||||
int x6 = $alignof(Ar, "br[1]");
|
||||
int x7 = $alignof("Ar", br[1]);
|
||||
int x8 = $alignof(Ar, br[2]);
|
||||
int x9 = $alignof("izzy.br[1]");
|
||||
int x10 = $alignof("izzy", "br[1]");
|
||||
int x11 = $alignof("izzy", br[1]);
|
||||
int z0 = $alignof("Foob.c");
|
||||
int z00 = $alignof("Foob.c[0]");
|
||||
int z01 = $alignof("Foob.c[1]");
|
||||
int z02 = $alignof("Foob.c[2]");
|
||||
int z03 = $alignof("Foob.c[3]");
|
||||
int z04 = $alignof("Foob.c[4]");
|
||||
int z05 = $alignof("Foob.c[5]");
|
||||
|
||||
|
||||
|
||||
// #expect: foo.ll
|
||||
|
||||
@foo.x = global i64 16, align 8
|
||||
@foo.y = global i16 8, align 2
|
||||
@foo.z = global i32 8, align 4
|
||||
@foo.w = global i32 8, align 4
|
||||
@foo.v = global i32 4, align 4
|
||||
@foo.x1 = global i32 8, align 4
|
||||
@foo.x2 = global i32 8, align 4
|
||||
@foo.x3 = global i32 1, align 4
|
||||
@foo.x4 = global i32 4, align 4
|
||||
@foo.x5 = global i32 4, align 4
|
||||
@foo.x6 = global i32 4, align 4
|
||||
@foo.x7 = global i32 4, align 4
|
||||
@foo.x8 = global i32 8, align 4
|
||||
@foo.x9 = global i32 4, align 4
|
||||
@foo.x10 = global i32 4, align 4
|
||||
@foo.x11 = global i32 4, align 4
|
||||
@foo.z0 = global i32 8, align 4
|
||||
@foo.z00 = global i32 8, align 4
|
||||
@foo.z01 = global i32 1, align 4
|
||||
@foo.z02 = global i32 2, align 4
|
||||
@foo.z03 = global i32 1, align 4
|
||||
@foo.z04 = global i32 4, align 4
|
||||
@foo.z05 = global i32 1, align 4
|
||||
66
test/test_suite/compile_time_introspection/sizeof.c3t
Normal file
66
test/test_suite/compile_time_introspection/sizeof.c3t
Normal file
@@ -0,0 +1,66 @@
|
||||
module foo;
|
||||
import bar;
|
||||
import bar::abc;
|
||||
|
||||
long x = $sizeof(Baz);
|
||||
short y = $sizeof("Baz");
|
||||
int z = $sizeof(bar::Baz);
|
||||
int w = $sizeof("bar::Baz");
|
||||
int v = $sizeof("bar::abc::Foo");
|
||||
int x1 = $sizeof(x);
|
||||
int y1 = $sizeof("y");
|
||||
int a = $sizeof("Baz.y");
|
||||
int b = $sizeof("Deep.a.b");
|
||||
int c = $sizeof("Deep.a.b.c");
|
||||
int d = $sizeof("Deep[][100]");
|
||||
int e = $sizeof("Deep[][100]**[100][]*");
|
||||
int a2 = $sizeof("Baz", "y");
|
||||
int a3 = $sizeof("Baz", y);
|
||||
int a4 = $sizeof(Baz, "y");
|
||||
int a5 = $sizeof(Baz, y);
|
||||
|
||||
module bar;
|
||||
|
||||
struct Deep
|
||||
{
|
||||
int x;
|
||||
struct a
|
||||
{
|
||||
struct b
|
||||
{
|
||||
char[5] c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Baz
|
||||
{
|
||||
int x;
|
||||
char[60] y;
|
||||
}
|
||||
|
||||
module bar::abc;
|
||||
|
||||
struct Foo
|
||||
{
|
||||
char x;
|
||||
}
|
||||
|
||||
// #expect: foo.ll
|
||||
|
||||
@foo.x = global i64 64, align 8
|
||||
@foo.y = global i16 64, align 2
|
||||
@foo.z = global i32 64, align 4
|
||||
@foo.w = global i32 64, align 4
|
||||
@foo.v = global i32 1, align 4
|
||||
@foo.x1 = global i32 8, align 4
|
||||
@foo.y1 = global i32 2, align 4
|
||||
@foo.a = global i32 60, align 4
|
||||
@foo.b = global i32 5, align 4
|
||||
@foo.c = global i32 5, align 4
|
||||
@foo.d = global i32 1600, align 4
|
||||
@foo.e = global i32 8, align 4
|
||||
@foo.a2 = global i32 60, align 4
|
||||
@foo.a3 = global i32 60, align 4
|
||||
@foo.a4 = global i32 60, align 4
|
||||
@foo.a5 = global i32 60, align 4
|
||||
84
test/test_suite/compile_time_introspection/sizeof_errors.c3
Normal file
84
test/test_suite/compile_time_introspection/sizeof_errors.c3
Normal file
@@ -0,0 +1,84 @@
|
||||
module foo;
|
||||
import bar;
|
||||
import bar::abc;
|
||||
|
||||
func void a()
|
||||
{
|
||||
int x = $sizeof(Bazy); // #error: 'Bazy' could not be found, did you spell it right
|
||||
}
|
||||
|
||||
func void b()
|
||||
{
|
||||
int x = $sizeof("Bazz"); // #error: 'Bazz' could not be found, did you misspell it
|
||||
}
|
||||
|
||||
func void c()
|
||||
{
|
||||
int x = $sizeof("Baz#"); // #error: A '.' was expected after 'Baz'
|
||||
}
|
||||
|
||||
func void c2()
|
||||
{
|
||||
int x = $sizeof("#Baz"); // #error: '#Baz' is not a known identifier, did you misspell it?
|
||||
}
|
||||
|
||||
func void d()
|
||||
{
|
||||
int x = $sizeof("Baz."); // #error: The path to an existing member was expected after 'Baz', did you make a mistake?
|
||||
}
|
||||
|
||||
func void e()
|
||||
{
|
||||
int x = $sizeof(bar::Baze); // #error: 'Baze' could not be found, did you spell it right
|
||||
}
|
||||
|
||||
func void f()
|
||||
{
|
||||
int x = $sizeof("bar::Bazy"); // #error: 'Bazy' could not be found, did you spell it right
|
||||
}
|
||||
|
||||
func void g()
|
||||
{
|
||||
int x = $sizeof("bar::"); // #error: 'bar::' is not a known identifier, did you misspell it?
|
||||
}
|
||||
|
||||
func void k()
|
||||
{
|
||||
int v = $sizeof("int.x"); // #error: 'int' has no members
|
||||
}
|
||||
|
||||
func void l()
|
||||
{
|
||||
int v = $sizeof("int[].len"); // #error: 'int[]' has no members
|
||||
}
|
||||
|
||||
func void m()
|
||||
{
|
||||
int v = $sizeof("int[4].len"); // #error: 'int[4]' has no members
|
||||
}
|
||||
|
||||
func void n()
|
||||
{
|
||||
int v = $sizeof("Baz.x1"); // #error: The path to an existing member was expected after
|
||||
}
|
||||
|
||||
func void o()
|
||||
{
|
||||
int v = $sizeof("Baz.x", x); // #error: You cannot combine a string with a member path with a separate path here
|
||||
}
|
||||
|
||||
module bar;
|
||||
|
||||
struct Baz
|
||||
{
|
||||
int x;
|
||||
char[60] y;
|
||||
}
|
||||
|
||||
module bar::abc;
|
||||
|
||||
struct Foo
|
||||
{
|
||||
char x;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ define StructArr = distinct Struct2[3];
|
||||
func void test(int x)
|
||||
{
|
||||
StructArr z = { { .x = 1 }, { .y = x }, { 1, 2 }};
|
||||
usize xr = z.sizeof;
|
||||
usize len = z.len;
|
||||
Foo zz = z[2].x;
|
||||
}
|
||||
|
||||
@@ -6,16 +6,16 @@ func void test1()
|
||||
|
||||
int b = (Number)(a);
|
||||
|
||||
int c = (Foo)(a); // #error: type 'Foo' could not be found
|
||||
int c = (Foo)(a); // #error: 'Foo' could not be found
|
||||
}
|
||||
|
||||
func void test2()
|
||||
{
|
||||
int d = (Number)(bar);; // #error: identifier 'bar' could not be found
|
||||
int d = (Number)(bar);; // #error: 'bar' could not be found
|
||||
}
|
||||
|
||||
func void test3()
|
||||
{
|
||||
int e = (Bar)( // #error: type 'Bar' could not be found
|
||||
faa); // #error: identifier 'faa' could not be found
|
||||
int e = (Bar)( // #error: 'Bar' could not be found
|
||||
faa); // #error: 'faa' could not be found
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
func void test1()
|
||||
{
|
||||
int a = (i ? 1 : 1); // #error: identifier 'i' could not be found, did you spell it right
|
||||
int a = (i ? 1 : 1); // #error: 'i' could not be found, did you spell it right
|
||||
}
|
||||
|
||||
func void test2()
|
||||
{
|
||||
int a = (1 ? i : 2); // #error: identifier 'i' could not be found, did you spell it right
|
||||
int a = (1 ? i : 2); // #error: 'i' could not be found, did you spell it right
|
||||
}
|
||||
|
||||
func void test3()
|
||||
{
|
||||
int a = (1 ? 2 : i); // #error: identifier 'i' could not be found, did you spell it right
|
||||
int a = (1 ? 2 : i); // #error: 'i' could not be found, did you spell it right
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func void test2()
|
||||
func void test3()
|
||||
{
|
||||
An2 a;
|
||||
a.@helloWorld.b; // #error: No such member or function could be found.
|
||||
a.@helloWorld.b; // #error: There is no member or method 'b' on 'void'
|
||||
}
|
||||
|
||||
func void test4()
|
||||
|
||||
@@ -19,6 +19,6 @@ func void main()
|
||||
@cofefe(x += 1);
|
||||
@cofefe(xx());
|
||||
@cofefe($x += 1); // #error: Cannot modify '$x' inside of a runtime scope.
|
||||
@cofefe(y += 1); // #error: identifier 'y' could not be found
|
||||
@cofefe(y += 1); // #error: 'y' could not be found
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ func void test2()
|
||||
|
||||
macro foo2(x)
|
||||
{
|
||||
@body(x); // #error: The identifier 'body' could not be found, did you spell it right?
|
||||
@body(x); // #error: 'body' could not be found, did you spell it right?
|
||||
}
|
||||
|
||||
func void test4()
|
||||
|
||||
@@ -43,7 +43,7 @@ func void test_scope(int i)
|
||||
int a = 0;
|
||||
break;
|
||||
case 2:
|
||||
test_scope(a + 1); // #error: identifier 'a' could not be found, did you spell it right
|
||||
test_scope(a + 1); // #error: 'a' could not be found, did you spell it right
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
enum EnumTestErrorType3 : int
|
||||
{
|
||||
A = FOO // #error: constant 'FOO' could not be found, did you spell it
|
||||
A = FOO // #error: 'FOO' could not be found, did you spell it
|
||||
}
|
||||
|
||||
func int foo()
|
||||
|
||||
@@ -23,9 +23,9 @@ entry:
|
||||
%b = alloca %UnionB, align 8
|
||||
%0 = bitcast %UnionB* %b to i32*
|
||||
%1 = call i32 @bar()
|
||||
store i32 %1, i32* %0, align 4
|
||||
store i32 %1, i32* %0, align 8
|
||||
%2 = bitcast %UnionB* %b to %b*
|
||||
%3 = bitcast %b* %2 to i8*
|
||||
call void @llvm.memset.p0i8.i64(i8* align 4 %3, i8 0, i64 4, i1 false)
|
||||
call void @llvm.memset.p0i8.i64(i8* align 8 %3, i8 0, i64 4, i1 false)
|
||||
ret void
|
||||
|
||||
|
||||
Reference in New Issue
Block a user