diff --git a/releasenotes.md b/releasenotes.md index 3277343ad..a3fddb0c8 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -7,6 +7,7 @@ - Add `win-debug` setting to be able to pick dwarf for output #1855. - Error on switch case fallthough if there is more than one newline #1849. - Added flags to `c3c project view` to filter displayed properties +- VERY experimental `<[ ]>` syntax for generics. ### Fixes - Fix issue requiring prefix on a generic interface declaration. diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 13d99b1c9..1b64fbd0c 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -91,14 +91,20 @@ static bool parse_expr_list(ParseContext *c, Expr ***exprs_ref, TokenType end_to /** * generic_parameters ::= '(<' expr (',' expr) '>)' */ -bool parse_generic_parameters(ParseContext *c, Expr ***exprs_ref) +bool parse_generic_parameters(ParseContext *c, Expr ***exprs_ref, bool is_new_syntax) { - advance_and_verify(c, TOKEN_LGENPAR); + advance_and_verify(c, is_new_syntax ? TOKEN_LBRACKET : TOKEN_LGENPAR); while (true) { ASSIGN_EXPR_OR_RET(Expr *expr, parse_expr(c), false); vec_add(*exprs_ref, expr); if (try_consume(c, TOKEN_COMMA)) continue; + if (is_new_syntax) + { + CONSUME_OR_RET(TOKEN_RBRACKET, false); + CONSUME_OR_RET(TOKEN_GREATER, false); + return true; + } CONSUME_OR_RET(TOKEN_RGENPAR, false); return true; } @@ -1091,13 +1097,25 @@ static Expr *parse_subscript_expr(ParseContext *c, Expr *left) static Expr *parse_generic_expr(ParseContext *c, Expr *left) { ASSERT(left && expr_ok(left)); + bool is_new_syntax = tok_is(c, TOKEN_LESS); + if (is_new_syntax) advance(c); Expr *subs_expr = expr_new_expr(EXPR_GENERIC_IDENT, left); subs_expr->generic_ident_expr.parent = exprid(left); - if (!parse_generic_parameters(c, &subs_expr->generic_ident_expr.parmeters)) return poisoned_expr; + if (!parse_generic_parameters(c, &subs_expr->generic_ident_expr.parmeters, is_new_syntax)) return poisoned_expr; RANGE_EXTEND_PREV(subs_expr); return subs_expr; } +static Expr *parse_less(ParseContext *c, Expr *left) +{ + if (c->lexer.token_type == TOKEN_LBRACKET) + { + return parse_generic_expr(c, left); + } + return parse_binary(c, left); +} + + /** * access_expr ::= '.' primary_expr */ @@ -2057,7 +2075,7 @@ ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_NOT_EQUAL] = { NULL, parse_binary, PREC_RELATIONAL }, [TOKEN_GREATER] = { NULL, parse_binary, PREC_RELATIONAL }, [TOKEN_GREATER_EQ] = { NULL, parse_binary, PREC_RELATIONAL }, - [TOKEN_LESS] = { NULL, parse_binary, PREC_RELATIONAL }, + [TOKEN_LESS] = { NULL, parse_less, PREC_RELATIONAL }, [TOKEN_LESS_EQ] = { NULL, parse_binary, PREC_RELATIONAL }, [TOKEN_SHL] = { NULL, parse_binary, PREC_SHIFT }, [TOKEN_SHR] = { NULL, parse_binary, PREC_SHIFT }, diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 84dd7c5e2..4cbc63624 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -501,11 +501,12 @@ static inline TypeInfo *parse_base_type(ParseContext *c) /** * generic_type ::= type generic_parameters */ -static inline TypeInfo *parse_generic_type(ParseContext *c, TypeInfo *type) +static inline TypeInfo *parse_generic_type(ParseContext *c, TypeInfo *type, bool is_new_syntax) { ASSERT(type_info_ok(type)); TypeInfo *generic_type = type_info_new(TYPE_INFO_GENERIC, type->span); - if (!parse_generic_parameters(c, &generic_type->generic.params)) return poisoned_type_info; + if (is_new_syntax) advance(c); + if (!parse_generic_parameters(c, &generic_type->generic.params, is_new_syntax)) return poisoned_type_info; generic_type->generic.base = type; return generic_type; } @@ -613,8 +614,12 @@ TypeInfo *parse_type_with_base(ParseContext *c, TypeInfo *type_info) case TOKEN_LBRACKET: type_info = parse_array_type_index(c, type_info); break; + case TOKEN_LESS: + if (c->lexer.token_type != TOKEN_LBRACKET) break; + type_info = parse_generic_type(c, type_info, true); + break; case TOKEN_LGENPAR: - type_info = parse_generic_type(c, type_info); + type_info = parse_generic_type(c, type_info, false); break; case TOKEN_STAR: advance(c); diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index 8af704687..b05f4e5f1 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -55,7 +55,7 @@ Decl *parse_local_decl_after_type(ParseContext *c, TypeInfo *type); Decl *parse_var_decl(ParseContext *c); bool parse_current_is_expr(ParseContext *c); -bool parse_generic_parameters(ParseContext *c, Expr ***exprs_ref); +bool parse_generic_parameters(ParseContext *c, Expr ***exprs_ref, bool is_new_syntax); bool parse_parameters(ParseContext *c, Decl ***params_ref, Decl **body_params, Variadic *variadic, int *vararg_index_ref, ParameterParseKind parse_kind); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 12b256033..780c5fb0d 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -487,7 +487,6 @@ static bool sema_binary_is_expr_lvalue(SemaContext *context, Expr *top_expr, Exp case EXPR_EXT_TRUNC: case EXPR_INT_TO_BOOL: case EXPR_DISCARD: - if (failed_ref) goto FAILED_REF; goto ERR; case EXPR_IDENTIFIER: { @@ -545,15 +544,11 @@ static bool sema_binary_is_expr_lvalue(SemaContext *context, Expr *top_expr, Exp case EXPR_SLICE: case EXPR_SUBSCRIPT_ASSIGN: goto CHECK_OPTIONAL; - case EXPR_HASH_IDENT: - if (failed_ref) goto FAILED_REF; - RETURN_SEMA_ERROR(top_expr, "You cannot assign to an unevaluated expression."); - case EXPR_EXPRESSION_LIST: - if (!vec_size(expr->expression_list)) goto ERR; - return sema_binary_is_expr_lvalue(context, top_expr, VECLAST(expr->expression_list), failed_ref); case EXPR_CONST: if (failed_ref) goto FAILED_REF; RETURN_SEMA_ERROR(top_expr, "You cannot assign to a constant expression."); + case EXPR_CT_ARG: + case EXPR_HASH_IDENT: case EXPR_POISONED: case EXPR_ADDR_CONVERSION: case EXPR_ANYSWITCH: @@ -569,7 +564,6 @@ static bool sema_binary_is_expr_lvalue(SemaContext *context, Expr *top_expr, Exp case EXPR_COND: case EXPR_CT_AND_OR: case EXPR_CT_APPEND: - case EXPR_CT_ARG: case EXPR_CT_CALL: case EXPR_CT_CASTABLE: case EXPR_CT_CONCAT: @@ -623,6 +617,7 @@ static bool sema_binary_is_expr_lvalue(SemaContext *context, Expr *top_expr, Exp case EXPR_SLICE_TO_VEC_ARRAY: case EXPR_SCALAR_TO_VECTOR: case EXPR_SUBSCRIPT_ADDR: + case EXPR_EXPRESSION_LIST: goto ERR; } UNREACHABLE @@ -5702,7 +5697,7 @@ bool sema_expr_analyse_assign_right_side(SemaContext *context, Expr *expr, Type static bool sema_expr_analyse_ct_identifier_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right) { // Do regular lvalue evaluation of the identifier - if (!sema_analyse_expr_lvalue(context, left)) return false; + if (!sema_analyse_expr_lvalue(context, left, NULL)) return false; // Evaluate right side to using inference from last type. if (!sema_analyse_inferred_expr(context, left->type, right)) return false; @@ -5769,27 +5764,20 @@ static bool sema_expr_fold_hash(SemaContext *context, Expr *expr) */ static bool sema_expr_analyse_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right, bool *failed_ref) { -RETRY:; // 1. Evaluate left side - switch (left->expr_kind) + if (left->expr_kind == EXPR_TYPEINFO) { - case EXPR_HASH_IDENT: - if (!sema_expr_fold_hash(context, left)) return false; - goto RETRY; - case EXPR_CT_IDENT: - // $foo = ... - return sema_expr_analyse_ct_identifier_assign(context, expr, left, right); - case EXPR_TYPEINFO: - // $Foo = ... - return sema_expr_analyse_ct_type_identifier_assign(context, expr, left, right); - case EXPR_SUBSCRIPT: - // abc[...] = ... - if (!sema_analyse_expr_lvalue(context, left)) return false; - break; - default: - if (!sema_analyse_expr_lvalue(context, left)) return false; - break; + // Later make sure this can be handled in lvalue + // $Foo = ... + return sema_expr_analyse_ct_type_identifier_assign(context, expr, left, right); } + if (!sema_analyse_expr_lvalue(context, left, failed_ref)) return false; + if (left->expr_kind == EXPR_CT_IDENT) + { + // $foo = ... + return sema_expr_analyse_ct_identifier_assign(context, expr, left, right); + } + // 2. Check assignability if (!sema_expr_check_assign(context, left, failed_ref)) return false; @@ -5829,7 +5817,7 @@ static bool sema_binary_analyse_ct_common_assign(SemaContext *context, Expr *exp ASSERT_SPAN(left, left->expr_kind == EXPR_CT_IDENT); // 1. Analyse left side. - if (!sema_analyse_expr_lvalue(context, left)) return false; + if (!sema_analyse_expr_lvalue(context, left, NULL)) return false; Decl *left_var = left->ct_ident_expr.decl; if (!sema_cast_ct_ident_rvalue(context, left)) return false; @@ -5867,7 +5855,7 @@ static bool sema_expr_analyse_op_assign(SemaContext *context, Expr *expr, Expr * } // 1. Analyse left side. - if (!sema_analyse_expr_lvalue(context, left)) return false; + if (!sema_analyse_expr_lvalue(context, left, NULL)) return false; // 2. Verify that the left side is assignable. if (!sema_expr_check_assign(context, left, NULL)) return false; @@ -7491,7 +7479,7 @@ static inline bool sema_expr_analyse_incdec(SemaContext *context, Expr *expr) { // 1. Analyse the lvalue to update Expr *inner = expr->unary_expr.expr; - if (!sema_analyse_expr_lvalue(context, inner)) return false; + if (!sema_analyse_expr_lvalue(context, inner, NULL)) return false; // 2. Assert it's an l-value if (!sema_expr_check_assign(context, inner, NULL)) return false; @@ -9936,7 +9924,7 @@ bool sema_analyse_expr_address(SemaContext *context, Expr *expr) } -INLINE bool sema_analyse_expr_lvalue_dispatch(SemaContext *context, Expr *expr) +INLINE bool sema_analyse_expr_lvalue_dispatch(SemaContext *context, Expr *expr, bool *failed_ref) { RETRY: switch (expr->expr_kind) @@ -9958,24 +9946,184 @@ RETRY: ContextSwitchState state = context_switch_state_push(context, new_context); expr->resolve_status = RESOLVE_RUNNING; context = new_context; - bool success = sema_analyse_expr_lvalue_dispatch(new_context, expr); + bool success = sema_analyse_expr_lvalue_dispatch(new_context, expr, failed_ref); context_switch_stat_pop(new_context, state); return success; } - default: - if (expr->resolve_status == RESOLVE_RUNNING) + case EXPR_SWIZZLE: + if (failed_ref) goto FAILED_REF; + // Maybe this will work like foo.xy = ... + RETURN_SEMA_ERROR(expr, "You cannot use swizzling to assign to multiple elements, use element-wise assign instead."); + case EXPR_LAMBDA: + if (failed_ref) goto FAILED_REF; + RETURN_SEMA_ERROR(expr, "This expression is a value and cannot be assigned to."); + case EXPR_CONST: + if (failed_ref) goto FAILED_REF; + RETURN_SEMA_ERROR(expr, "You cannot assign to a constant expression."); + case EXPR_UNRESOLVED_IDENTIFIER: + if (!sema_analyse_expr_dispatch(context, expr, CHECK_VALUE)) return false; + FALLTHROUGH; + case EXPR_IDENTIFIER: + { + Decl *decl = expr->ident_expr; + if (decl->decl_kind != DECL_VAR) { - expr->resolve_status = RESOLVE_NOT_DONE; + if (failed_ref) goto FAILED_REF; + RETURN_SEMA_ERROR(expr, "You cannot assign a value to %s.", decl_to_a_name(decl)); } - return sema_analyse_expr_check(context, expr, CHECK_LVALUE); + if (decl->var.kind == VARDECL_CONST) + { + if (failed_ref) goto FAILED_REF; + RETURN_SEMA_ERROR(expr, "You cannot assign to a constant."); + } + decl = decl_raw(decl); + switch (decl->var.kind) + { + case VARDECL_LOCAL_CT: + case VARDECL_LOCAL_CT_TYPE: + case VARDECL_LOCAL: + case VARDECL_GLOBAL: + case VARDECL_PARAM: + case VARDECL_PARAM_CT: + case VARDECL_PARAM_CT_TYPE: + return true; + case VARDECL_MEMBER: + case VARDECL_BITMEMBER: + case VARDECL_PARAM_REF: + break; + case VARDECL_CONST: + case VARDECL_PARAM_EXPR: + case VARDECL_UNWRAPPED: + case VARDECL_ERASE: + case VARDECL_REWRAPPED: + UNREACHABLE + } + UNREACHABLE + } + case EXPR_POISONED: + return false; + case EXPR_UNARY: + if (!sema_analyse_expr_dispatch(context, expr, CHECK_VALUE)) return false; + if (expr->unary_expr.operator != UNARYOP_DEREF) break; + if (IS_OPTIONAL(expr)) + { + if (failed_ref) goto FAILED_REF; + RETURN_SEMA_ERROR(expr, "You cannot assign to a dereferenced optional."); + } + return true; + case EXPR_CT_ARG: + if (expr->ct_arg_expr.type == TOKEN_CT_VAEXPR) + { + if (!context->current_macro) + { + RETURN_SEMA_ERROR(expr, "'$vaexpr' can only be used inside of a macro."); + } + // An expr argument, this means we copy and evaluate. + ASSIGN_EXPR_OR_RET(Expr *arg_expr, sema_expr_analyse_ct_arg_index(context, exprptr(expr->ct_arg_expr.arg), NULL), false); + expr_replace(expr, copy_expr_single(arg_expr)); + return sema_analyse_expr_lvalue(context, expr, failed_ref); + } + break; + case EXPR_BITACCESS: + case EXPR_ACCESS_RESOLVED: + case EXPR_SLICE: + case EXPR_SUBSCRIPT_ASSIGN: + case EXPR_GENERIC_IDENT: + case EXPR_ACCESS_UNRESOLVED: + case EXPR_CT_EVAL: + return sema_analyse_expr_dispatch(context, expr, CHECK_LVALUE); + case EXPR_EXT_TRUNC: + case EXPR_INT_TO_BOOL: + case EXPR_DISCARD: + case EXPR_ADDR_CONVERSION: + case EXPR_ANYSWITCH: + case EXPR_ASM: + case EXPR_BENCHMARK_HOOK: + case EXPR_BINARY: + case EXPR_BITASSIGN: + case EXPR_BUILTIN: + case EXPR_BUILTIN_ACCESS: + case EXPR_CALL: + case EXPR_CATCH: + case EXPR_COMPILER_CONST: + case EXPR_COND: + case EXPR_CT_AND_OR: + case EXPR_CT_APPEND: + case EXPR_CT_CALL: + case EXPR_CT_CASTABLE: + case EXPR_CT_CONCAT: + case EXPR_CT_DEFINED: + case EXPR_CT_IS_CONST: + case EXPR_DECL: + case EXPR_DEFAULT_ARG: + case EXPR_DESIGNATED_INITIALIZER_LIST: + case EXPR_DESIGNATOR: + case EXPR_EXPR_BLOCK: + case EXPR_FORCE_UNWRAP: + case EXPR_INITIALIZER_LIST: + case EXPR_LAST_FAULT: + case EXPR_MACRO_BLOCK: + case EXPR_MACRO_BODY_EXPANSION: + case EXPR_MAKE_ANY: + case EXPR_MAKE_SLICE: + case EXPR_MEMBER_GET: + case EXPR_NAMED_ARGUMENT: + case EXPR_NOP: + case EXPR_OPERATOR_CHARS: + case EXPR_OPTIONAL: + case EXPR_POINTER_OFFSET: + case EXPR_POST_UNARY: + case EXPR_PTR_ACCESS: + case EXPR_ENUM_FROM_ORD: + case EXPR_SLICE_LEN: + case EXPR_FLOAT_TO_INT: + case EXPR_INT_TO_FLOAT: + case EXPR_INT_TO_PTR: + case EXPR_PTR_TO_INT: + case EXPR_RECAST: + case EXPR_RETHROW: + case EXPR_RETVAL: + case EXPR_RVALUE: + case EXPR_SLICE_ASSIGN: + case EXPR_SLICE_COPY: + case EXPR_SPLAT: + case EXPR_STRINGIFY: + case EXPR_TERNARY: + case EXPR_TEST_HOOK: + case EXPR_TRY: + case EXPR_TRY_UNWRAP_CHAIN: + case EXPR_TYPECALL: + case EXPR_TYPEID_INFO: + case EXPR_TYPEINFO: + case EXPR_ANYFAULT_TO_FAULT: + case EXPR_VECTOR_FROM_ARRAY: + case EXPR_VECTOR_TO_ARRAY: + case EXPR_SLICE_TO_VEC_ARRAY: + case EXPR_SCALAR_TO_VECTOR: + case EXPR_SUBSCRIPT_ADDR: + case EXPR_EXPRESSION_LIST: + case EXPR_MACRO_BODY: + case EXPR_CAST: + case EXPR_CATCH_UNRESOLVED: + case EXPR_COMPOUND_LITERAL: + case EXPR_EMBED: + case EXPR_TYPEID: + case EXPR_VASPLAT: + case EXPR_TRY_UNRESOLVED: + break; } + if (failed_ref) goto FAILED_REF; + RETURN_SEMA_ERROR(expr, "An assignable expression, like a variable, was expected here."); +FAILED_REF: + *failed_ref = true; + return false; } -bool sema_analyse_expr_lvalue(SemaContext *context, Expr *expr) +bool sema_analyse_expr_lvalue(SemaContext *context, Expr *expr, bool *failed_ref) { ASSERT(expr); - RESOLVE(expr, sema_analyse_expr_lvalue_dispatch(context, expr)); + RESOLVE(expr, sema_analyse_expr_lvalue_dispatch(context, expr, failed_ref)); } bool sema_expr_check_discard(SemaContext *context, Expr *expr) diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 51bd53798..fdeaadb0a 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -87,7 +87,8 @@ void sema_trace_liveness(void); Expr *sema_expr_resolve_access_child(SemaContext *context, Expr *child, bool *missing); bool sema_analyse_expr_address(SemaContext *context, Expr *expr); -bool sema_analyse_expr_lvalue(SemaContext *context, Expr *expr); + +bool sema_analyse_expr_lvalue(SemaContext *context, Expr *expr, bool *failed_ref); bool sema_analyse_expr_value(SemaContext *context, Expr *expr); Expr *expr_access_inline_member(Expr *parent, Decl *parent_decl); diff --git a/src/compiler/sema_liveness.c b/src/compiler/sema_liveness.c index 0d1778a5c..ec356dfec 100644 --- a/src/compiler/sema_liveness.c +++ b/src/compiler/sema_liveness.c @@ -262,8 +262,6 @@ RETRY: case EXPR_NAMED_ARGUMENT: case UNRESOLVED_EXPRS: UNREACHABLE - - UNREACHABLE case EXPR_DESIGNATOR: sema_trace_expr_liveness(expr->designator_expr.value); return; diff --git a/test/test_suite/compile_time/ct_vaexpr_assign.c3t b/test/test_suite/compile_time/ct_vaexpr_assign.c3t new file mode 100644 index 000000000..670c9d109 --- /dev/null +++ b/test/test_suite/compile_time/ct_vaexpr_assign.c3t @@ -0,0 +1,23 @@ +// #target: macos-x64 +module test; +import std; +macro foo(...) +{ + $vaexpr[0] = 2; +} + +fn void main() +{ + int $x; + foo($x); + int y = $x; +} + +/* #expect: test.ll + +define void @test.main() #0 { +entry: + %y = alloca i32, align 4 + store i32 2, ptr %y, align 4 + ret void +}