diff --git a/releasenotes.md b/releasenotes.md index e7c75aab5..9cd57cfca 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -3,6 +3,8 @@ ## 0.7.6 Change list ### Changes / improvements +- Add lenof() compile time function #2439 +- Allow doc comments on individual struct members, faultdefs and enum values #2427. ### Fixes - Compiler assert with var x @noinit = 0 #2452 @@ -12,6 +14,8 @@ - Inlining location when accessing #foo symbols. - Improve inlined-at when checking generic code. - Fix codegen bug in expressions like `foo(x()) ?? io::EOF?` causing irregular crashes. +- Correctly silence "unsupported architecture" warning with `--quiet` #2465 +- Overloading &[] should be enough for foreach. #2466 ### Stdlib changes - Added generic `InterfaceList` to store a list of values that implement a specific interface @@ -56,7 +60,6 @@ - `$defined(#hash)` will not check the internal expression, just that `#hash` exists. Use `$defined((void)#hash)` for the old behaviour. - Added optional macro arguments using `macro foo(int x = ...)` which can be checked using `$defined(x)`. - Add compile time ternary `$val ??? : `. -- Allow doc comments on individual struct members, faultdefs and enum values #2427. ### Fixes - List.remove_at would incorrectly trigger ASAN. @@ -115,8 +118,6 @@ - Fix missing end of line when encountering errors in project creation. - Const enum methods are not being recognized. #2445 - $defined returns an error when assigning a struct initializer with an incorrect type #2449 -- Correctly silence "unsupported architecture" warning with `--quiet` #2465 -- Overloading &[] should be enough for foreach. #2466 ### Stdlib changes - Add `==` to `Pair`, `Triple` and TzDateTime. Add print to `Pair` and `Triple`. diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index aaa08edce..6b49e4fab 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -3713,6 +3713,7 @@ static inline void expr_set_span(Expr *expr, SourceSpan loc) case EXPR_VECTOR_FROM_ARRAY: case EXPR_ADDR_CONVERSION: case EXPR_RECAST: + case EXPR_LENOF: expr_set_span(expr->inner_expr, loc); return; case EXPR_EXPRESSION_LIST: diff --git a/src/compiler/copying.c b/src/compiler/copying.c index b6deefb2f..108ba3322 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -503,6 +503,7 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr) case EXPR_RVALUE: case EXPR_RECAST: case EXPR_ADDR_CONVERSION: + case EXPR_LENOF: MACRO_COPY_EXPR(expr->inner_expr); return expr; case EXPR_MAKE_ANY: diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 441c33ec4..69a9f2460 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -781,6 +781,7 @@ typedef enum EXPR_PTR_TO_INT, EXPR_LAMBDA, EXPR_LAST_FAULT, + EXPR_LENOF, EXPR_MACRO_BLOCK, EXPR_MACRO_BODY, EXPR_MACRO_BODY_EXPANSION, @@ -1227,6 +1228,7 @@ typedef enum TOKEN_IF, TOKEN_INLINE, TOKEN_IMPORT, + TOKEN_LENOF, TOKEN_MACRO, TOKEN_MODULE, TOKEN_NEXTCASE, @@ -1719,5 +1721,5 @@ case TYPE_U8: case TYPE_U16: case TYPE_U32: case TYPE_U64: case TYPE_U128 #define UNRESOLVED_EXPRS EXPR_TRY_UNRESOLVED: case EXPR_ACCESS_UNRESOLVED: \ case EXPR_CATCH_UNRESOLVED: case EXPR_UNRESOLVED_IDENTIFIER: case EXPR_CAST: \ case EXPR_TYPEID: case EXPR_EMBED: case EXPR_VASPLAT: case EXPR_OTHER_CONTEXT: \ - case EXPR_IOTA_DECL: \ + case EXPR_IOTA_DECL: case EXPR_LENOF: \ case EXPR_GENERIC_IDENT: case EXPR_COMPOUND_LITERAL: case EXPR_MACRO_BODY: case EXPR_CT_SUBSCRIPT diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 8b0ed36f5..063f866df 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -1237,6 +1237,22 @@ static Expr *parse_ct_embed(ParseContext *c, Expr *left, SourceSpan lhs_start UN return embed; } +/** + * lenof ::= LENOF '(' expr ')' + * flat_path ::= expr ('.' primary) | '[' expr ']')* + */ +static Expr *parse_lenof(ParseContext *c, Expr *left, SourceSpan lhs_start UNUSED) +{ + ASSERT(!left && "Unexpected left hand side"); + Expr *expr = EXPR_NEW_TOKEN(EXPR_LENOF); + advance(c); + CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); + ASSIGN_EXPR_OR_RET(expr->inner_expr, parse_expr(c), poisoned_expr); + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); + RANGE_EXTEND_PREV(expr); + return expr; +} + /** * ct_call ::= (CT_ALIGNOF | CT_FEATURE | CT_EXTNAMEOF | CT_OFFSETOF | CT_NAMEOF | CT_QNAMEOF) '(' flat_path ')' * flat_path ::= expr ('.' primary) | '[' expr ']')* @@ -2155,6 +2171,7 @@ ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_TRUE] = { parse_bool, NULL, PREC_NONE }, [TOKEN_FALSE] = { parse_bool, NULL, PREC_NONE }, [TOKEN_NULL] = { parse_null, NULL, PREC_NONE }, + [TOKEN_LENOF] = { parse_lenof, NULL, PREC_NONE }, [TOKEN_INTEGER] = { parse_integer, NULL, PREC_NONE }, [TOKEN_BUILTIN] = { parse_builtin, NULL, PREC_NONE }, [TOKEN_CHAR_LITERAL] = { parse_char_lit, NULL, PREC_NONE }, diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index 4d9218ce9..c51d5caa8 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -1409,6 +1409,7 @@ Ast *parse_stmt(ParseContext *c) case TOKEN_CT_VAEXPR: case TOKEN_FALSE: case TOKEN_INTEGER: + case TOKEN_LENOF: case TOKEN_LPAREN: case TOKEN_MINUS: case TOKEN_MINUSMINUS: diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 5c9c887dd..a8dd32bf9 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -10404,6 +10404,34 @@ static inline bool sema_expr_analyse_ct_is_const(SemaContext *context, Expr *exp expr_rewrite_const_bool(expr, type_bool, sema_cast_const(inner)); return true; } +static bool sema_expr_analyse_lenof(SemaContext *context, Expr *expr, bool *missing_ref) +{ + Expr *inner = expr->inner_expr; + if (!sema_analyse_expr(context, inner)) return false; + Type *canonical = inner->type->canonical; + Decl *len = sema_find_untyped_operator(canonical, OVERLOAD_LEN, NULL); + if (len) + { + return sema_insert_method_call(context, expr, len, inner, NULL, false); + } + switch (canonical->type_kind) + { + case TYPE_ARRAY: + case TYPE_VECTOR: + expr_rewrite_const_int(expr, type_isz, canonical->array.len); + return true; + case TYPE_SLICE: + expr_rewrite_slice_len(expr, inner, type_isz); + return true; + default: + if (missing_ref) + { + *missing_ref = true; + return false; + } + RETURN_SEMA_ERROR(inner, "%s does support lenof()", type_quoted_error_string(inner->type)); + } +} static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr) { @@ -10432,7 +10460,14 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr active_context->call_env.in_no_eval = true; main_expr = main_expr->expr_other_context.inner; goto RETRY; - case EXPR_ACCESS_UNRESOLVED: + case EXPR_LENOF: + if (!sema_expr_analyse_lenof(active_context, main_expr, &failed)) + { + if (!failed) goto FAIL; + success = false; + } + break; + case EXPR_ACCESS_UNRESOLVED: if (!sema_expr_analyse_access(active_context, main_expr, &failed, CHECK_VALUE, false)) { if (!failed) goto FAIL; @@ -11053,6 +11088,8 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr, case EXPR_MAKE_SLICE: case EXPR_CT_SUBSCRIPT: UNREACHABLE + case EXPR_LENOF: + return sema_expr_analyse_lenof(context, expr, NULL); case EXPR_IOTA_DECL: return sema_expr_analyse_iota_decl(context, expr); case EXPR_TWO: @@ -11601,7 +11638,7 @@ IDENT_CHECK:; case EXPR_INT_TO_FLOAT: case EXPR_INT_TO_PTR: case EXPR_PTR_TO_INT: - + case EXPR_LENOF: case EXPR_RETHROW: case EXPR_RETVAL: case EXPR_RVALUE: diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index f61003456..c28031274 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -235,6 +235,8 @@ const char *token_type_to_string(TokenType type) return "interface"; case TOKEN_IMPORT: return "import"; + case TOKEN_LENOF: + return "lenof"; case TOKEN_MACRO: return "macro"; case TOKEN_MODULE: diff --git a/test/unit/regression/lenof.c3 b/test/unit/regression/lenof.c3 new file mode 100644 index 000000000..af1c16153 --- /dev/null +++ b/test/unit/regression/lenof.c3 @@ -0,0 +1,20 @@ +module regression; +import std; + + +fn void lenof_test() @test +{ + List{int} l; + l.push(123); + l.push(222); + l.push(111); + test::eq(lenof(l), 3); + int[] x = { 1, 2 }; + test::eq(lenof(x), 2); + l.push(111); + test::eq(lenof(l), 4); + assert(!$defined(lenof(1))); + assert($defined(lenof(x))); + int* zz; + assert(!$defined(lenof(zz))); +} \ No newline at end of file