diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 93c07c482..ec8d71d3a 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -280,6 +280,8 @@ bool expr_is_pure(Expr *expr) if (!expr_is_pure(expr->expression_list[i])) return false; } return true; + case EXPR_TYPEOFANY: + return expr_is_pure(expr->inner_expr); case EXPR_LEN: return expr_is_pure(expr->len_expr.inner); case EXPR_SLICE: diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 505d65e78..2918c3f2f 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -304,7 +304,6 @@ struct Type_ struct TypeInfo_ { ResolveStatus resolve_status : 3; - bool virtual_type : 1; bool failable : 1; Type *type; TypeInfoKind kind; diff --git a/src/compiler/copying.c b/src/compiler/copying.c index 9f459fb71..4a7db0199 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -126,6 +126,7 @@ Expr *copy_expr(Expr *source_expr) case EXPR_CATCH: case EXPR_FAILABLE: case EXPR_GROUP: + case EXPR_TYPEOFANY: MACRO_COPY_EXPR(expr->inner_expr); return expr; case EXPR_COND: diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 66590addb..df54939b6 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -214,6 +214,7 @@ typedef enum EXPR_TRY_UNWRAP, EXPR_TRY_UNWRAP_CHAIN, EXPR_TYPEID, + EXPR_TYPEOFANY, EXPR_TYPEINFO, EXPR_UNARY, EXPR_UNDEF, @@ -385,7 +386,9 @@ typedef enum TOKEN_USHORT, TOKEN_USIZE, TOKEN_FLOAT128, + TOKEN_VARIANT, TOKEN_ANYERR, + TOKEN_FAULT, TOKEN_TYPEID, // Literals. @@ -458,8 +461,7 @@ typedef enum TOKEN_TRUE, TOKEN_TRY, TOKEN_UNION, - TOKEN_VAR, // Reserved - TOKEN_VIRTUAL, + TOKEN_VAR, TOKEN_WHILE, TOKEN_CT_ALIGNOF, // $alignof @@ -499,10 +501,10 @@ typedef enum case TOKEN_IPTR: case TOKEN_IPTRDIFF: case TOKEN_ISIZE: case TOKEN_LONG: \ case TOKEN_SHORT: case TOKEN_UINT128: case TOKEN_UINT: case TOKEN_ULONG: \ case TOKEN_UPTR: case TOKEN_UPTRDIFF: case TOKEN_USHORT: case TOKEN_USIZE: \ - case TOKEN_FLOAT128: case TOKEN_TYPEID: case TOKEN_ANYERR + case TOKEN_FLOAT128: case TOKEN_TYPEID: case TOKEN_ANYERR: case TOKEN_FAULT: case TOKEN_VARIANT #define TYPE_TOKENS NON_VOID_TYPE_TOKENS: case TOKEN_VOID #define TYPELIKE_TOKENS TYPE_TOKENS: case TOKEN_TYPE_IDENT: \ - case TOKEN_CT_TYPE_IDENT: case TOKEN_VIRTUAL: case TOKEN_CT_TYPEOF + case TOKEN_CT_TYPE_IDENT: case TOKEN_CT_TYPEOF // Note that ordering matters here. If ordering is changed, // So must type_find_max_type and friends. @@ -535,6 +537,8 @@ typedef enum TYPE_F64, TYPE_F128, TYPE_FLOAT_LAST = TYPE_F128, + TYPE_ANY, + TYPE_ANYERR, TYPE_TYPEID, TYPE_POINTER, TYPE_ENUM, @@ -543,7 +547,6 @@ typedef enum TYPE_UNION, TYPE_BITSTRUCT, TYPE_ERRTYPE, - TYPE_ANYERR, TYPE_TYPEDEF, TYPE_STRLIT, TYPE_DISTINCT, @@ -555,7 +558,6 @@ typedef enum TYPE_FAILABLE_ANY, TYPE_TYPEINFO, TYPE_VECTOR, - TYPE_ANY, TYPE_LAST = TYPE_ANY } TypeKind; diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 55bf35dc7..ab6faaa29 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -663,7 +663,7 @@ void llvm_codegen_setup() intrinsics_setup = true; } -static void llvm_set_linkage(GenContext *c, Decl *decl, LLVMValueRef value) +void llvm_set_linkage(GenContext *c, Decl *decl, LLVMValueRef value) { if (decl->module != c->code_module) { @@ -686,7 +686,7 @@ static void llvm_set_linkage(GenContext *c, Decl *decl, LLVMValueRef value) } } -void gencontext_emit_introspection_type(GenContext *c, Decl *decl) +void llvm_emit_introspection_type_from_decl(GenContext *c, Decl *decl) { llvm_get_type(c, decl->type); if (decl_is_struct_type(decl)) @@ -697,7 +697,7 @@ void gencontext_emit_introspection_type(GenContext *c, Decl *decl) Decl *member_decl = decls[i]; if (decl_is_struct_type(member_decl)) { - gencontext_emit_introspection_type(c, member_decl); + llvm_emit_introspection_type_from_decl(c, member_decl); } } } @@ -725,7 +725,6 @@ void gencontext_emit_introspection_type(GenContext *c, Decl *decl) LLVMSetInitializer(global_name, LLVMConstInt(llvm_get_type(c, type_char), 1, false)); decl->type->backend_typeid = LLVMConstPointerCast(global_name, llvm_get_type(c, type_typeid)); llvm_set_linkage(c, decl, global_name); - } @@ -856,7 +855,7 @@ void llvm_value_addr(GenContext *c, BEValue *value) else { LLVMValueRef temp = llvm_emit_alloca_aligned(c, value->type, "taddr"); - llvm_store_self_aligned(c, temp, value->value, value->type); + llvm_store_bevalue_dest_aligned(c, temp, value); llvm_value_set_address(value, temp, value->type); } } @@ -914,7 +913,7 @@ static void llvm_emit_type_decls(GenContext *context, Decl *decl) case DECL_ENUM: case DECL_ERRTYPE: case DECL_BITSTRUCT: - gencontext_emit_introspection_type(context, decl); + llvm_emit_introspection_type_from_decl(context, decl); break; case NON_TYPE_DECLS: UNREACHABLE diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index aa235b8a0..75aa3f350 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -3099,16 +3099,76 @@ static void llvm_emit_post_unary_expr(GenContext *context, BEValue *be_value, Ex false); } +void llvm_emit_derived_backend_type(GenContext *c, Type *type) +{ + llvm_get_type(c, type); + LLVMValueRef global_name = LLVMAddGlobal(c->module, llvm_get_type(c, type_char), type->name); + LLVMSetGlobalConstant(global_name, 1); + LLVMSetInitializer(global_name, LLVMConstInt(llvm_get_type(c, type_char), 1, false)); + type->backend_typeid = LLVMConstPointerCast(global_name, llvm_get_type(c, type_typeid)); + Decl *origin = NULL; + Type *original_type = type; + while (!origin) + { + switch (original_type->type_kind) + { + case TYPE_FAILABLE: + original_type = type->failable; + continue; + case TYPE_VECTOR: + original_type = type->vector.base; + continue; + case TYPE_ARRAY: + case TYPE_SUBARRAY: + original_type = original_type->array.base; + continue; + case TYPE_POINTER: + original_type = original_type->pointer; + continue; + case TYPE_ENUM: + case TYPE_FUNC: + case TYPE_STRUCT: + case TYPE_UNION: + case TYPE_BITSTRUCT: + case TYPE_ERRTYPE: + case TYPE_DISTINCT: + origin = type->decl; + continue; + case TYPE_TYPEDEF: + original_type = original_type->canonical; + continue; + case TYPE_STRLIT: + case TYPE_INFERRED_ARRAY: + case TYPE_UNTYPED_LIST: + case TYPE_FAILABLE_ANY: + case TYPE_TYPEINFO: + UNREACHABLE + default: + goto PRIMITIVE; + } + } + llvm_set_linkage(c, origin, global_name); + return; + + PRIMITIVE: + LLVMSetLinkage(global_name, LLVMWeakAnyLinkage); + LLVMSetVisibility(global_name, LLVMDefaultVisibility); +} + void llvm_emit_typeid(GenContext *c, BEValue *be_value, Type *type) { LLVMValueRef value; + type = type->canonical; if (type_is_builtin(type->type_kind)) { value = llvm_const_int(c, type_usize, type->type_kind); } else { - assert(type->backend_typeid); + if (!type->backend_typeid) + { + llvm_emit_derived_backend_type(c, type); + } value = type->backend_typeid; } llvm_value_set(be_value, value, type_typeid); @@ -3397,6 +3457,27 @@ static inline void llvm_emit_rethrow_expr(GenContext *c, BEValue *be_value, Expr } +static inline void llvm_emit_typeofany(GenContext *c, BEValue *be_value, Expr *expr) +{ + llvm_emit_expr(c, be_value, expr->inner_expr); + llvm_value_fold_failable(c, be_value); + if (llvm_value_is_addr(be_value)) + { + AlignSize alignment = 0; + LLVMValueRef pointer_addr = llvm_emit_struct_gep_raw(c, + be_value->value, + llvm_get_type(c, type_any), + 1, + be_value->alignment, + &alignment); + llvm_value_set_address_align(be_value, pointer_addr, type_typeid, alignment); + } + else + { + llvm_value_set(be_value, LLVMBuildExtractValue(c->builder, be_value->value, 1, ""), type_typeid); + } +} + /** * This is the foo? instruction. */ @@ -5176,6 +5257,9 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) case EXPR_RETHROW: llvm_emit_rethrow_expr(c, value, expr); return; + case EXPR_TYPEOFANY: + llvm_emit_typeofany(c, value, expr); + return; case EXPR_TYPEID: case EXPR_GROUP: // These are folded in the semantic analysis step. diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index 6716ff7e2..5960aa162 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -654,7 +654,7 @@ void llvm_emit_extern_decl(GenContext *context, Decl *decl) case DECL_ENUM: break; case DECL_ERRTYPE: - gencontext_emit_introspection_type(context, decl); + llvm_emit_introspection_type_from_decl(context, decl); // TODO // Fix typeid return; case DECL_TYPEDEF: diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index bdc86697e..206b5cc73 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -246,7 +246,7 @@ void llvm_emit_convert_value_from_coerced(GenContext *c, BEValue *result, LLVMTy void llvm_emit_coerce_store(GenContext *c, LLVMValueRef addr, AlignSize alignment, LLVMTypeRef coerced, LLVMValueRef value, LLVMTypeRef target_type); void llvm_emit_function_body(GenContext *context, Decl *decl); void llvm_emit_function_decl(GenContext *c, Decl *decl); -void gencontext_emit_introspection_type(GenContext *c, Decl *decl); +void llvm_emit_introspection_type_from_decl(GenContext *c, Decl *decl); LLVMValueRef llvm_emit_call_intrinsic(GenContext *c, unsigned intrinsic, LLVMTypeRef *types, unsigned type_count, LLVMValueRef *values, unsigned arg_count); void llvm_emit_cast(GenContext *c, CastKind cast_kind, BEValue *value, Type *to_type, Type *from_type); void llvm_emit_cond_br(GenContext *context, BEValue *value, LLVMBasicBlockRef then_block, LLVMBasicBlockRef else_block); @@ -306,7 +306,7 @@ static inline LLVMTypeRef llvm_get_ptr_type(GenContext *c, Type *type); LLVMTypeRef llvm_get_type(GenContext *c, Type *any_type); LLVMTypeRef llvm_get_pointee_type(GenContext *c, Type *any_type); static inline LLVMValueRef llvm_get_zero(GenContext *c, Type *type); - +void llvm_set_linkage(GenContext *c, Decl *decl, LLVMValueRef value); void llvm_debug_scope_push(GenContext *context, LLVMMetadataRef debug_scope); void llvm_debug_scope_pop(GenContext *context); void llvm_debug_push_lexical_scope(GenContext *context, SourceSpan location); diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index e7083b625..8413c80a5 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -1514,7 +1514,7 @@ Expr *parse_type_expression_with_path(Context *context, Path *path) { ASSIGN_TYPE_ELSE(type, parse_failable_type(context), poisoned_expr); } - if (!type->virtual_type && TOKEN_IS(TOKEN_LBRACE)) + if (TOKEN_IS(TOKEN_LBRACE)) { return parse_type_compound_literal_expr_after_type(context, type); } @@ -1546,7 +1546,6 @@ static Expr* parse_expr_block(Context *context, Expr *left) } ParseRule rules[TOKEN_EOF + 1] = { - [TOKEN_VIRTUAL] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_BOOL] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_CHAR] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_ICHAR] = { parse_type_identifier, NULL, PREC_NONE }, @@ -1571,6 +1570,8 @@ ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_VOID] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_TYPEID] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_ANYERR] = { parse_type_identifier, NULL, PREC_NONE }, + [TOKEN_FAULT] = { parse_type_identifier, NULL, PREC_NONE }, + [TOKEN_VARIANT] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_QUESTION] = { NULL, parse_ternary_expr, PREC_TERNARY }, [TOKEN_QUESTQUEST] = { NULL, parse_or_error_expr, PREC_OR_ERROR}, diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 39750a96c..796f91855 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -551,7 +551,6 @@ static inline TypeInfo *parse_base_type(Context *context) RANGE_EXTEND_PREV(type_info); return type_info; } - bool virtual = try_consume(context, TOKEN_VIRTUAL); SourceSpan range = source_span_from_token_id(context->tok.id); bool had_error; Path *path = parse_path_prefix(context, &had_error); @@ -561,9 +560,7 @@ static inline TypeInfo *parse_base_type(Context *context) TypeInfo *type_info = type_info_new(TYPE_INFO_IDENTIFIER, range); type_info->unresolved.path = path; type_info->unresolved.name_loc = context->tok.id; - type_info->virtual_type = virtual; if (!consume_type_name(context, "type")) return poisoned_type_info; - if (virtual) TRY_CONSUME_OR(TOKEN_STAR, "Expected '*' after virtual name.", poisoned_type_info); RANGE_EXTEND_PREV(type_info); return type_info; } @@ -581,36 +578,18 @@ static inline TypeInfo *parse_base_type(Context *context) type_found = type_from_token(context->tok.type); break; default: - // Special case: "virtual *" - if (virtual && context->tok.type == TOKEN_STAR) - { - type_info = type_info_new(TYPE_INFO_IDENTIFIER, source_span_from_token_id(context->prev_tok)); - advance(context); - type_info->resolve_status = RESOLVE_DONE; - type_info->type = type_any; - type_info->virtual_type = true; - RANGE_EXTEND_PREV(type_info); - return type_info; - } SEMA_TOKEN_ERROR(context->tok, "A type name was expected here."); return poisoned_type_info; } if (type_found) { - if (virtual) - { - SEMA_TOKEN_ERROR(context->tok, "Expected an interface name."); - advance(context); - return poisoned_type_info; - } assert(!type_info); type_info = type_info_new(TYPE_INFO_IDENTIFIER, source_span_from_token_id(context->tok.id)); type_info->resolve_status = RESOLVE_DONE; type_info->type = type_found; } - type_info->virtual_type = virtual; + assert(type_info); advance(context); - if (virtual) TRY_CONSUME_OR(TOKEN_STAR, "Expected '*' after virtual name.", poisoned_type_info); RANGE_EXTEND_PREV(type_info); return type_info; } diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index ff2b65af5..ad21d1869 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -818,6 +818,7 @@ Expr *recursive_may_narrow_float(Expr *expr, Type *type) case EXPR_TRY_UNWRAP: case EXPR_TRY_UNWRAP_CHAIN: case EXPR_SUBSCRIPT_ADDR: + case EXPR_TYPEOFANY: UNREACHABLE case EXPR_POST_UNARY: return recursive_may_narrow_float(expr->unary_expr.expr, type); @@ -969,6 +970,7 @@ Expr *recursive_may_narrow_int(Expr *expr, Type *type) case EXPR_TRY_UNWRAP: case EXPR_TRY_UNWRAP_CHAIN: case EXPR_SUBSCRIPT_ADDR: + case EXPR_TYPEOFANY: UNREACHABLE case EXPR_POST_UNARY: return recursive_may_narrow_int(expr->unary_expr.expr, type); @@ -1029,6 +1031,11 @@ bool cast_implicit(Expr *expr, Type *to_type) SEMA_ERROR(expr, "A failable %s cannot be converted to %s.", type_quoted_error_string(expr->type), type_quoted_error_string(to_type)); return false; } + if (to_canonical->type_kind == TYPE_ANY) + { + SEMA_ERROR(expr, "You can only convert pointers to 'variant', take the address of this expression first."); + return false; + } SEMA_ERROR(expr, "You cannot cast %s into %s even with an explicit cast, so this looks like an error.", type_quoted_error_string(expr->type), type_quoted_error_string(to_type)); return false; } @@ -1227,6 +1234,7 @@ static bool cast_inner(Expr *expr, Type *from_type, Type *to, Type *to_type) if (to->type_kind == TYPE_BOOL) return pointer_to_bool(expr, to_type); if (to->type_kind == TYPE_POINTER) return pointer_to_pointer(expr, to_type); if (to->type_kind == TYPE_SUBARRAY) return insert_cast(expr, CAST_APTSA, to_type); + if (to->type_kind == TYPE_ANY) return insert_cast(expr, CAST_PTRANY, to_type); break; case TYPE_ANY: if (to->type_kind == TYPE_POINTER) return insert_cast(expr, CAST_ANYPTR, to_type); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index c5468aec5..19637fdc8 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -349,6 +349,10 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) case EXPR_LEN: expr = expr->len_expr.inner; goto RETRY; + case EXPR_TYPEOFANY: + if (eval_kind != CONSTANT_EVAL_ANY) return false; + expr = expr->inner_expr; + goto RETRY; case EXPR_SLICE: if (expr->slice_expr.start && !expr_is_constant_eval(expr->slice_expr.start, CONSTANT_EVAL_FOLDABLE)) return false; if (expr->slice_expr.end && !expr_is_constant_eval(expr->slice_expr.end, CONSTANT_EVAL_FOLDABLE)) return false; @@ -2881,16 +2885,30 @@ static inline bool sema_expr_analyse_access(Context *context, Expr *expr) } // 8. Depending on parent type, we have some hard coded types - const char *kw = TOKSTR(identifier_token); + TokenType token_type = TOKTYPE(identifier_token); Expr *current_parent = parent; Type *type = type_no_fail(parent->type)->canonical; Type *flat_type = type_flatten(type); + if (!is_macro && token_type == TOKEN_TYPEID && flat_type->type_kind == TYPE_ANY) + { + expr->expr_kind = EXPR_TYPEOFANY; + expr->inner_expr = parent; + expr->type = type_typeid; + return true; + } + const char *kw = TOKSTR(identifier_token); + CHECK_DEEPER: // 9. Fix hard coded function `len` on subarrays and arrays if (!is_macro && kw == kw_len) { + if (flat_type->type_kind == TYPE_STRLIT) + { + expr_rewrite_to_int_const(expr, type_isize, parent->const_expr.string.len, true); + return true; + } if (flat_type->type_kind == TYPE_SUBARRAY) { expr->expr_kind = EXPR_LEN; @@ -5985,7 +6003,7 @@ static inline bool sema_analyse_identifier_path_string(Context *context, SourceS } if (!global_context.scratch_buffer_len) { - sema_error_range(span, "'%s' is not a valid identifier, did you misspell it?", chars); + sema_error_range(span, "A valid identifier was expected here, did you want to take the length of a string literal? If so use '.len'.", chars); return false; } TokenType token_type; @@ -6624,6 +6642,7 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Expr *expr) return false; case EXPR_LEN: case EXPR_SLICE_ASSIGN: + case EXPR_TYPEOFANY: // Created during semantic analysis UNREACHABLE case EXPR_MACRO_BLOCK: diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index 2cfddee35..0c88512e5 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -188,6 +188,8 @@ const char *token_type_to_string(TokenType type) return "alias"; case TOKEN_ANYERR: return "anyerr"; + case TOKEN_FAULT: + return "fault"; case TOKEN_AS: return "as"; case TOKEN_ASM: @@ -274,10 +276,10 @@ const char *token_type_to_string(TokenType type) return "while"; // Named types - case TOKEN_VIRTUAL: - return "virtual"; case TOKEN_VOID: return "void"; + case TOKEN_VARIANT: + return "variant"; case TOKEN_BOOL: return "bool"; case TOKEN_FLOAT128: @@ -396,7 +398,7 @@ bool token_is_type(TokenType type) bool token_is_any_type(TokenType type) { - return (type >= TOKEN_VOID && type <= TOKEN_TYPEID) || type == TOKEN_CT_TYPE_IDENT || type == TOKEN_TYPE_IDENT || type == TOKEN_VIRTUAL; + return (type >= TOKEN_VOID && type <= TOKEN_TYPEID) || type == TOKEN_CT_TYPE_IDENT || type == TOKEN_TYPE_IDENT; } bool token_is_ident_keyword(TokenType type) diff --git a/src/compiler/types.c b/src/compiler/types.c index d176a52b0..0984ab721 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -122,9 +122,8 @@ const char *type_to_error_string(Type *type) case TYPE_BITSTRUCT: case TYPE_ANYERR: case TYPE_UNTYPED_LIST: - return type->name; case TYPE_ANY: - return "any"; + return type->name; case TYPE_FUNC: return strcat_arena("func ", type->func.mangled_function_signature); case TYPE_VECTOR: @@ -1030,8 +1029,7 @@ void type_setup(PlatformTarget *target) create_type_cache(type_void); type_void->type_cache[0] = &t.voidstar; t.voidstar.pointer = type_void; - type_init("any", &t.any, TYPE_ANY, target->width_pointer * 2, target->align_pointer); - + type_init("variant", &t.any, TYPE_ANY, target->width_pointer * 2, target->align_pointer); type_create_alias("usize", &t.usz, type_int_unsigned_by_bitsize(target->width_pointer)); type_create_alias("isize", &t.isz, type_int_signed_by_bitsize(target->width_pointer)); @@ -1148,6 +1146,9 @@ Type *type_from_token(TokenType type) { switch (type) { + case TOKEN_VARIANT: + return type_any; + case TOKEN_FAULT: case TOKEN_ANYERR: return type_anyerr; case TOKEN_VOID: diff --git a/test/test_suite/compile_time_introspection/sizeof_errors.c3 b/test/test_suite/compile_time_introspection/sizeof_errors.c3 index e4f662868..1df948a18 100644 --- a/test/test_suite/compile_time_introspection/sizeof_errors.c3 +++ b/test/test_suite/compile_time_introspection/sizeof_errors.c3 @@ -19,7 +19,7 @@ fn void c() fn void c2() { - int x = $sizeof("#Baz"); // #error: '#Baz' is not a valid identifier, did you misspell it? + int x = $sizeof("#Baz"); // #error: A valid identifier was expected here, did you want to take the length of a string literal? If so use '.len'. } fn void d() @@ -29,7 +29,7 @@ fn void d() fn void e() { - int x = $sizeof(bar::Baze); // #error: 'Baze' could not be found, did you spell it right + int x = $sizeof(bar::Baze); // #error: } fn void f() @@ -39,7 +39,7 @@ fn void f() fn void g() { - int x = $sizeof("bar::"); // #error: 'bar::' is not a valid identifier, did you misspell it? + int x = $sizeof("bar::"); // #error: A valid identifier was expected here, did you want to take the length of a string literal? If so use '.len'. } fn void k() diff --git a/test/test_suite/strings/string_len.c3t b/test/test_suite/strings/string_len.c3t new file mode 100644 index 000000000..24068dd03 --- /dev/null +++ b/test/test_suite/strings/string_len.c3t @@ -0,0 +1,7 @@ +// #target: x64-darwin + +int i = "123".len; + +/* #expect: string_len.ll + +@string_len.i = global i32 3, align 4 \ No newline at end of file diff --git a/test/test_suite/variant/variant_test.c3t b/test/test_suite/variant/variant_test.c3t new file mode 100644 index 000000000..e32c431b7 --- /dev/null +++ b/test/test_suite/variant/variant_test.c3t @@ -0,0 +1,338 @@ +// #target: x64-darwin +module foo; +extern fn void printf(char*, ...); + +fn void test(variant x) +{ + switch (x.typeid) + { + case int: + printf("Was int\n"); + case double: + printf("Was double\n"); + case variant: + printf("Was variant\n"); + case int*: + printf("Was int*\n"); + default: + printf("Unknown type\n"); + } +} + +fn void test_all(variant... y) +{ + foreach (element : y) + { + test(element); + } +} + +fn void main() +{ + variant x = &&1; + int z; + variant y = &z; + typeid g = y.typeid; + typeid h = x.typeid; + if (y.typeid == int.typeid) + { + printf("y int match\n"); + } + if (x.typeid == int.typeid) + { + printf("x int match\n"); + } + y = &&1.0; + x = &x; + if (y.typeid == int.typeid) + { + printf("y int match\n"); + } + if (x.typeid == int.typeid) + { + printf("x int match\n"); + } + test(x); + test(&&1.0); + test(&&1); + test(&&true); + printf("----\n"); + int* df = null; + test_all(x, x, &&1.0, &x, &df); +} + +/* #expect: foo.ll + + +%variant = type { i8*, i64 } +%"variant[]" = type { %variant*, i64 } + +@"int*" = weak constant i8 1 + +define void @foo.test(i64 %0, i8* %1) #0 { +entry: + %x = alloca %variant, align 8 + %switch = alloca i64, align 8 + %pair = bitcast %variant* %x to { i64, i8* }* + %2 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %pair, i32 0, i32 0 + store i64 %0, i64* %2, align 8 + %3 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %pair, i32 0, i32 1 + store i8* %1, i8** %3, align 8 + %4 = getelementptr inbounds %variant, %variant* %x, i32 0, i32 1 + %5 = load i64, i64* %4, align 8 + store i64 %5, i64* %switch, align 8 + br label %switch.entry + +switch.entry: ; preds = %entry + %6 = load i64, i64* %switch, align 8 + %eq = icmp eq i64 5, %6 + br i1 %eq, label %switch.case, label %next_if + +switch.case: ; preds = %switch.entry + call void (i8*, ...) @printf(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str, i32 0, i32 0)) + br label %switch.exit + +next_if: ; preds = %switch.entry + %eq1 = icmp eq i64 15, %6 + br i1 %eq1, label %switch.case2, label %next_if3 + +switch.case2: ; preds = %next_if + call void (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str.1, i32 0, i32 0)) + br label %switch.exit + +next_if3: ; preds = %next_if + %eq4 = icmp eq i64 17, %6 + br i1 %eq4, label %switch.case5, label %next_if6 + +switch.case5: ; preds = %next_if3 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.2, i32 0, i32 0)) + br label %switch.exit + +next_if6: ; preds = %next_if3 + %eq7 = icmp eq i64 ptrtoint (i8* @"int*" to i64), %6 + br i1 %eq7, label %switch.case8, label %next_if9 + +switch.case8: ; preds = %next_if6 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.3, i32 0, i32 0)) + br label %switch.exit + +next_if9: ; preds = %next_if6 + br label %switch.default + +switch.default: ; preds = %next_if9 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str.4, i32 0, i32 0)) + br label %switch.exit + +switch.exit: ; preds = %switch.default, %switch.case8, %switch.case5, %switch.case2, %switch.case + ret void +} + +; Function Attrs: nounwind +define void @foo.test_all(i8* %0, i64 %1) #0 { +entry: + %y = alloca %"variant[]", align 8 + %"__idx$" = alloca i64, align 8 + %"__len$" = alloca i64, align 8 + %element = alloca %variant, align 8 + %pair = bitcast %"variant[]"* %y to { i8*, i64 }* + %2 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %pair, i32 0, i32 0 + store i8* %0, i8** %2, align 8 + %3 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %pair, i32 0, i32 1 + store i64 %1, i64* %3, align 8 + store i64 0, i64* %"__idx$", align 8 + %4 = getelementptr inbounds %"variant[]", %"variant[]"* %y, i32 0, i32 1 + %5 = load i64, i64* %4, align 8 + store i64 %5, i64* %"__len$", align 8 + br label %loop.cond + +loop.cond: ; preds = %loop.body, %entry + %6 = load i64, i64* %"__idx$", align 8 + %7 = load i64, i64* %"__len$", align 8 + %lt = icmp ult i64 %6, %7 + br i1 %lt, label %loop.body, label %loop.exit + +loop.body: ; preds = %loop.cond + %8 = getelementptr inbounds %"variant[]", %"variant[]"* %y, i32 0, i32 0 + %9 = load %variant*, %variant** %8, align 8 + %10 = load i64, i64* %"__idx$", align 8 + %ptroffset = getelementptr inbounds %variant, %variant* %9, i64 %10 + %11 = bitcast %variant* %element to i8* + %12 = bitcast %variant* %ptroffset to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %11, i8* align 8 %12, i32 16, i1 false) + %13 = bitcast %variant* %element to { i64, i8* }* + %14 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %13, i32 0, i32 0 + %lo = load i64, i64* %14, align 8 + %15 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %13, i32 0, i32 1 + %hi = load i8*, i8** %15, align 8 + call void @foo.test(i64 %lo, i8* %hi) + %16 = load i64, i64* %"__idx$", align 8 + %add = add i64 %16, 1 + store i64 %add, i64* %"__idx$", align 8 + br label %loop.cond + +loop.exit: ; preds = %loop.cond + ret void +} + +; Function Attrs: nounwind +define void @main() #0 { +entry: + %x = alloca %variant, align 8 + %taddr = alloca i32, align 4 + %z = alloca i32, align 4 + %y = alloca %variant, align 8 + %g = alloca i64, align 8 + %h = alloca i64, align 8 + %taddr4 = alloca double, align 8 + %taddr11 = alloca double, align 8 + %taddr12 = alloca %variant, align 8 + %taddr15 = alloca i32, align 4 + %taddr16 = alloca %variant, align 8 + %taddr19 = alloca i8, align 1 + %taddr20 = alloca %variant, align 8 + %df = alloca i32*, align 8 + %vararg = alloca %"variant[]", align 8 + %varargslots = alloca [5 x %variant], align 16 + %taddr23 = alloca double, align 8 + store i32 1, i32* %taddr, align 4 + %0 = bitcast i32* %taddr to i8* + %1 = insertvalue %variant undef, i8* %0, 0 + %2 = insertvalue %variant %1, i64 5, 1 + store %variant %2, %variant* %x, align 8 + store i32 0, i32* %z, align 4 + %3 = bitcast i32* %z to i8* + %4 = insertvalue %variant undef, i8* %3, 0 + %5 = insertvalue %variant %4, i64 5, 1 + store %variant %5, %variant* %y, align 8 + %6 = getelementptr inbounds %variant, %variant* %y, i32 0, i32 1 + %7 = load i64, i64* %6, align 8 + store i64 %7, i64* %g, align 8 + %8 = getelementptr inbounds %variant, %variant* %x, i32 0, i32 1 + %9 = load i64, i64* %8, align 8 + store i64 %9, i64* %h, align 8 + %10 = getelementptr inbounds %variant, %variant* %y, i32 0, i32 1 + %11 = load i64, i64* %10, align 8 + %eq = icmp eq i64 %11, 5 + br i1 %eq, label %if.then, label %if.exit + +if.then: ; preds = %entry + call void (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.5, i32 0, i32 0)) + br label %if.exit + +if.exit: ; preds = %if.then, %entry + %12 = getelementptr inbounds %variant, %variant* %x, i32 0, i32 1 + %13 = load i64, i64* %12, align 8 + %eq1 = icmp eq i64 %13, 5 + br i1 %eq1, label %if.then2, label %if.exit3 + +if.then2: ; preds = %if.exit + call void (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.6, i32 0, i32 0)) + br label %if.exit3 + +if.exit3: ; preds = %if.then2, %if.exit + store double 1.000000e+00, double* %taddr4, align 8 + %14 = bitcast double* %taddr4 to i8* + %15 = insertvalue %variant undef, i8* %14, 0 + %16 = insertvalue %variant %15, i64 15, 1 + store %variant %16, %variant* %y, align 8 + %17 = bitcast %variant* %x to i8* + %18 = insertvalue %variant undef, i8* %17, 0 + %19 = insertvalue %variant %18, i64 17, 1 + store %variant %19, %variant* %x, align 8 + %20 = getelementptr inbounds %variant, %variant* %y, i32 0, i32 1 + %21 = load i64, i64* %20, align 8 + %eq5 = icmp eq i64 %21, 5 + br i1 %eq5, label %if.then6, label %if.exit7 + +if.then6: ; preds = %if.exit3 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.7, i32 0, i32 0)) + br label %if.exit7 + +if.exit7: ; preds = %if.then6, %if.exit3 + %22 = getelementptr inbounds %variant, %variant* %x, i32 0, i32 1 + %23 = load i64, i64* %22, align 8 + %eq8 = icmp eq i64 %23, 5 + br i1 %eq8, label %if.then9, label %if.exit10 + +if.then9: ; preds = %if.exit7 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.8, i32 0, i32 0)) + br label %if.exit10 + +if.exit10: ; preds = %if.then9, %if.exit7 + %24 = bitcast %variant* %x to { i64, i8* }* + %25 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %24, i32 0, i32 0 + %lo = load i64, i64* %25, align 8 + %26 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %24, i32 0, i32 1 + %hi = load i8*, i8** %26, align 8 + call void @foo.test(i64 %lo, i8* %hi) + store double 1.000000e+00, double* %taddr11, align 8 + %27 = bitcast double* %taddr11 to i8* + %28 = insertvalue %variant undef, i8* %27, 0 + %29 = insertvalue %variant %28, i64 15, 1 + store %variant %29, %variant* %taddr12, align 8 + %30 = bitcast %variant* %taddr12 to { i64, i8* }* + %31 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %30, i32 0, i32 0 + %lo13 = load i64, i64* %31, align 8 + %32 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %30, i32 0, i32 1 + %hi14 = load i8*, i8** %32, align 8 + call void @foo.test(i64 %lo13, i8* %hi14) + store i32 1, i32* %taddr15, align 4 + %33 = bitcast i32* %taddr15 to i8* + %34 = insertvalue %variant undef, i8* %33, 0 + %35 = insertvalue %variant %34, i64 5, 1 + store %variant %35, %variant* %taddr16, align 8 + %36 = bitcast %variant* %taddr16 to { i64, i8* }* + %37 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %36, i32 0, i32 0 + %lo17 = load i64, i64* %37, align 8 + %38 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %36, i32 0, i32 1 + %hi18 = load i8*, i8** %38, align 8 + call void @foo.test(i64 %lo17, i8* %hi18) + store i8 1, i8* %taddr19, align 1 + %39 = insertvalue %variant undef, i8* %taddr19, 0 + %40 = insertvalue %variant %39, i64 2, 1 + store %variant %40, %variant* %taddr20, align 8 + %41 = bitcast %variant* %taddr20 to { i64, i8* }* + %42 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %41, i32 0, i32 0 + %lo21 = load i64, i64* %42, align 8 + %43 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %41, i32 0, i32 1 + %hi22 = load i8*, i8** %43, align 8 + call void @foo.test(i64 %lo21, i8* %hi22) + call void (i8*, ...) @printf(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.9, i32 0, i32 0)) + store i32* null, i32** %df, align 8 + %44 = getelementptr inbounds [5 x %variant], [5 x %variant]* %varargslots, i64 0, i64 0 + %45 = bitcast %variant* %44 to i8* + %46 = bitcast %variant* %x to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 16 %45, i8* align 8 %46, i32 16, i1 false) + %47 = getelementptr inbounds [5 x %variant], [5 x %variant]* %varargslots, i64 0, i64 1 + %48 = bitcast %variant* %47 to i8* + %49 = bitcast %variant* %x to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 16 %48, i8* align 8 %49, i32 16, i1 false) + store double 1.000000e+00, double* %taddr23, align 8 + %50 = bitcast double* %taddr23 to i8* + %51 = insertvalue %variant undef, i8* %50, 0 + %52 = insertvalue %variant %51, i64 15, 1 + %53 = getelementptr inbounds [5 x %variant], [5 x %variant]* %varargslots, i64 0, i64 2 + store %variant %52, %variant* %53, align 16 + %54 = bitcast %variant* %x to i8* + %55 = insertvalue %variant undef, i8* %54, 0 + %56 = insertvalue %variant %55, i64 17, 1 + %57 = getelementptr inbounds [5 x %variant], [5 x %variant]* %varargslots, i64 0, i64 3 + store %variant %56, %variant* %57, align 16 + %58 = bitcast i32** %df to i8* + %59 = insertvalue %variant undef, i8* %58, 0 + %60 = insertvalue %variant %59, i64 ptrtoint (i8* @"int*" to i64), 1 + %61 = getelementptr inbounds [5 x %variant], [5 x %variant]* %varargslots, i64 0, i64 4 + store %variant %60, %variant* %61, align 16 + %62 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg, i32 0, i32 1 + store i64 5, i64* %62, align 8 + %63 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg, i32 0, i32 0 + %64 = bitcast [5 x %variant]* %varargslots to %variant* + store %variant* %64, %variant** %63, align 8 + %65 = bitcast %"variant[]"* %vararg to { i8*, i64 }* + %66 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %65, i32 0, i32 0 + %lo24 = load i8*, i8** %66, align 8 + %67 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %65, i32 0, i32 1 + %hi25 = load i64, i64* %67, align 8 + call void @foo.test_all(i8* %lo24, i64 %hi25) + ret void +}