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:
Christoffer Lerno
2021-06-30 11:46:02 +02:00
committed by Christoffer Lerno
parent 12ffeeaad7
commit a5ce7c47ba
33 changed files with 1254 additions and 234 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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)

View File

@@ -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:

View File

@@ -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

View File

@@ -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);

View File

@@ -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.

View File

@@ -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);

View File

@@ -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");

View File

@@ -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 }
};

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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:

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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");

View File

@@ -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:

View File

@@ -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:

View File

@@ -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

View 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

View 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

View 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;
}

View File

@@ -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;
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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()

View File

@@ -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
}

View File

@@ -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()

View File

@@ -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
}
}

View File

@@ -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()

View File

@@ -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