diff --git a/lib/std/core/env.c3 b/lib/std/core/env.c3 index f8525fb6d..65669fdc4 100644 --- a/lib/std/core/env.c3 +++ b/lib/std/core/env.c3 @@ -198,6 +198,7 @@ macro bool os_is_posix() @const return false; $endswitch } - +const String[] AUTHORS = $$AUTHORS; +const String[] AUTHOR_EMAILS = $$AUTHOR_EMAILS; const BUILTIN_EXPECT_IS_DISABLED = $feature(DISABLE_BUILTIN_EXPECT); const BUILTIN_PREFETCH_IS_DISABLED = $feature(DISABLE_BUILTIN_PREFETCH); diff --git a/releasenotes.md b/releasenotes.md index ff029ce9a..10418d70d 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -18,6 +18,7 @@ - `$is_const` is deprecated in favour of `@is_const` based on `$defined`. - Multiline contract comments #2113 - Removed the use of temp allocator in backtrace printing. +- `env::AUTHORS` and `env::AUTHOR_EMAILS` added. ### Fixes - mkdir/rmdir would not work properly with substring paths on non-windows platforms. diff --git a/resources/testproject/src/hello_world.c3 b/resources/testproject/src/hello_world.c3 index 495a8acdf..4fe003928 100644 --- a/resources/testproject/src/hello_world.c3 +++ b/resources/testproject/src/hello_world.c3 @@ -3,6 +3,8 @@ import std; import bar; import clib; import clib2; +import std::core::env; + fn int test_doubler(int x) @if(env::WIN32) => x * x; extern fn int test_doubler(int) @if(!env::WIN32); @@ -11,6 +13,11 @@ extern fn void printf(char *, ...); fn int main() { printf("Hello World!\n"); + printf("Authors:"); + io::printn(env::AUTHORS); + printf("Author emails:"); + io::printn(env::AUTHOR_EMAILS); + bar::test(); printf("Hello double: %d\n", test_doubler(11)); if ($feature(ABCD)) io::printn("ABCD"); diff --git a/src/build/build.h b/src/build/build.h index e271ac05f..00aa1438e 100644 --- a/src/build/build.h +++ b/src/build/build.h @@ -599,6 +599,12 @@ typedef struct BuildOptions_ bool testing; } BuildOptions; +typedef struct +{ + const char *author; + const char *email; +} AuthorEntry; + typedef struct { struct Library__ *parent; @@ -728,6 +734,7 @@ typedef struct const char **csources; const char **cinclude_dirs; const char **exec; + AuthorEntry *authors; const char **feature_list; const char *custom_linker_path; struct diff --git a/src/build/project.c b/src/build/project.c index 534c648e9..f3ffd72b4 100644 --- a/src/build/project.c +++ b/src/build/project.c @@ -184,7 +184,29 @@ static void load_into_build_target(BuildParseContext context, JSONObject *json, error_exit("Error reading %s: output extension '%s' must start with a '.'", context.file, target->extension); } } - + else + { + const char **authors = get_optional_string_array(context, json, "authors"); + AuthorEntry *author_list = NULL; + FOREACH(const char *, author, authors) + { + const char *email_start = strstr(author, "<"); + if (email_start) + { + const char *end = strstr(email_start + 1, ">"); + if (!end || end[1] != 0 || email_start + 1 == end) error_exit("Error reading %s: invalid author format '%s'", author); + const char *email = str_trim(str_copy(email_start + 1, end - email_start - 1)); + AuthorEntry entry = { str_trim(str_copy(author, email_start - author)), email }; + vec_add(author_list, entry); + } + else + { + AuthorEntry entry = { str_trim(str_dup(author)), NULL }; + vec_add(author_list, entry); + } + } + target->authors = author_list; + } // "Before compilation" execution APPEND_STRING_LIST(&target->exec, "exec"); diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 0a15ae676..f4178aa6d 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -1489,7 +1489,22 @@ void compile() setup_bool_define("THREAD_SANITIZER", compiler.build.feature.sanitize_thread); setup_string_define("BUILD_HASH", GIT_HASH); setup_string_define("BUILD_DATE", compiler_date_to_iso()); - + Expr *expr_names = expr_new(EXPR_CONST, INVALID_SPAN); + Expr *expr_emails = expr_new(EXPR_CONST, INVALID_SPAN); + expr_names->const_expr.const_kind = CONST_UNTYPED_LIST; + expr_emails->const_expr.const_kind = CONST_UNTYPED_LIST; + expr_names->type = type_untypedlist; + expr_emails->type = type_untypedlist; + expr_names->resolve_status = expr_names->resolve_status = RESOLVE_DONE; + FOREACH(AuthorEntry, entry, compiler.build.authors) + { + Expr *const_name = expr_new_const_string(INVALID_SPAN, entry.author); // NOLINT + Expr *const_email = expr_new_const_string(INVALID_SPAN, entry.email ? entry.email : ""); // NOLINT + vec_add(expr_names->const_expr.untyped_list, const_name); + vec_add(expr_emails->const_expr.untyped_list, const_email); + } + setup_define("AUTHORS", expr_names); + setup_define("AUTHOR_EMAILS", expr_emails); type_init_cint(); compiler_init_time = bench_mark(); diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 18df7b91e..ca245f3e3 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -710,8 +710,10 @@ typedef enum RangeType RANGE_CONST_END, RANGE_CONST_LEN, RANGE_CONST_RANGE, + RANGE_SINGLE_ELEMENT, } RangeType; + typedef struct { ResolveStatus status : 3; @@ -719,7 +721,6 @@ typedef struct bool start_from_end : 1; bool end_from_end : 1; bool is_len : 1; - bool is_range : 1; bool is_optional : 1; union { diff --git a/src/compiler/copying.c b/src/compiler/copying.c index bfa140b65..018fe2e43 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -195,6 +195,8 @@ void copy_range(CopyStruct *c, Range *range) { switch (range->range_type) { + case RANGE_SINGLE_ELEMENT: + UNREACHABLE case RANGE_CONST_LEN: case RANGE_CONST_END: MACRO_COPY_EXPRID(range->start); diff --git a/src/compiler/expr.c b/src/compiler/expr.c index e06c72c86..6e878a122 100644 --- a/src/compiler/expr.c +++ b/src/compiler/expr.c @@ -904,6 +904,8 @@ bool expr_is_pure(Expr *expr) if (!exprid_is_pure(expr->slice_expr.expr)) return false; switch (expr->slice_expr.range.range_type) { + case RANGE_SINGLE_ELEMENT: + UNREACHABLE case RANGE_CONST_RANGE: return true; case RANGE_CONST_END: diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index e21b0abd2..e99585f75 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -2611,6 +2611,8 @@ static void llvm_emit_slice_values(GenContext *c, Expr *slice, BEValue *parent_r BEValue start_index; switch (range.range_type) { + case RANGE_SINGLE_ELEMENT: + UNREACHABLE case RANGE_DYNAMIC: case RANGE_CONST_LEN: case RANGE_CONST_END: @@ -2680,6 +2682,8 @@ static void llvm_emit_slice_values(GenContext *c, Expr *slice, BEValue *parent_r // Get the index. switch (range.range_type) { + case RANGE_SINGLE_ELEMENT: + UNREACHABLE case RANGE_DYNAMIC: llvm_emit_exprid(c, &end_index, range.end); llvm_value_rvalue(c, &end_index); diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 0a580a1db..f6d8cd74c 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -50,12 +50,10 @@ bool parse_range(ParseContext *c, Range *range) bool is_len_range = range->is_len = try_consume(c, TOKEN_COLON); if (!is_len_range && !try_consume(c, TOKEN_DOTDOT)) { - // Otherwise this is not a range. - range->is_range = false; + // Otherwise this is a single element + range->range_type = RANGE_SINGLE_ELEMENT; return true; } - range->is_range = true; - // Is there an expression next? range->end_from_end = try_consume(c, TOKEN_BIT_XOR); if (range->end_from_end || parse_current_is_expr(c)) @@ -443,6 +441,11 @@ Expr *parse_vasplat(ParseContext *c) if (try_consume(c, TOKEN_LBRACKET)) { if (!parse_range(c, &expr->vasplat_expr)) return poisoned_expr; + if (expr->vasplat_expr.range_type == RANGE_SINGLE_ELEMENT) + { + PRINT_ERROR_AT(expr, "$vasplat expected a range."); + return poisoned_expr; + } CONSUME_OR_RET(TOKEN_RBRACKET, poisoned_expr); } RANGE_EXTEND_PREV(expr); @@ -1052,7 +1055,7 @@ static Expr *parse_subscript_expr(ParseContext *c, Expr *left, SourceSpan lhs_st Range range = { .range_type = RANGE_DYNAMIC }; if (!parse_range(c, &range)) return poisoned_expr; CONSUME_OR_RET(TOKEN_RBRACKET, poisoned_expr); - if (!range.is_range) + if (range.range_type == RANGE_SINGLE_ELEMENT) { subs_expr->subscript_expr.index = (SubscriptIndex) { .expr = range.start, diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index fe367f6c1..f197d0fb2 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1146,7 +1146,8 @@ static inline bool sema_identifier_find_possible_inferred(SemaContext *context, static inline bool sema_expr_analyse_identifier(SemaContext *context, Type *to, Expr *expr) { - ASSERT_SPAN(expr, expr && expr->unresolved_ident_expr.ident); + ASSERT(expr); + ASSERT_SPAN(expr, expr->unresolved_ident_expr.ident); DEBUG_LOG("Resolving identifier '%s'", expr->unresolved_ident_expr.ident); ASSERT_SPAN(expr, expr->resolve_status != RESOLVE_DONE); @@ -6212,11 +6213,6 @@ static Expr **sema_vasplat_insert(SemaContext *context, Expr **init_expressions, unsigned start_idx = 0; if (start) { - if (!range->is_range) - { - SEMA_ERROR(expr, "$vasplat expected a range."); - return NULL; - } if (!sema_analyse_expr(context, start)) return NULL; if (!expr_is_const_int(start)) { diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index aa1709fd2..23184cb6e 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -190,6 +190,8 @@ static inline IndexDiff range_const_len(Range *range) { switch (range->range_type) { + case RANGE_SINGLE_ELEMENT: + UNREACHABLE; case RANGE_CONST_LEN: return range->const_end; case RANGE_CONST_END: diff --git a/src/compiler/sema_liveness.c b/src/compiler/sema_liveness.c index 3a9148e0b..37364320d 100644 --- a/src/compiler/sema_liveness.c +++ b/src/compiler/sema_liveness.c @@ -453,6 +453,8 @@ RETRY: sema_trace_expr_liveness(exprptr(expr->slice_expr.expr)); switch (expr->slice_expr.range.range_type) { + case RANGE_SINGLE_ELEMENT: + UNREACHABLE case RANGE_CONST_RANGE: return; case RANGE_DYNAMIC: diff --git a/src/utils/lib.h b/src/utils/lib.h index aff8ad8a9..7e1d61bdb 100644 --- a/src/utils/lib.h +++ b/src/utils/lib.h @@ -157,6 +157,7 @@ bool str_is_integer(const char *string); bool str_has_no_uppercase(const char *string); bool str_is_valid_module_name(const char *name); char *str_copy(const char *start, size_t str_len); +char *str_dup(const char *str); StringSlice slice_next_token(StringSlice *slice, char separator); static inline bool slice_strcmp(StringSlice slice, const char *other); diff --git a/src/utils/stringutils.c b/src/utils/stringutils.c index e9c1c0953..ec39c0a82 100644 --- a/src/utils/stringutils.c +++ b/src/utils/stringutils.c @@ -332,6 +332,11 @@ char *str_cat(const char *a, const char *b) return buffer; } +char *str_dup(const char *str) +{ + return str_copy(str, strlen(str)); +} + char *str_copy(const char *start, size_t str_len) { char *dst = calloc_string(str_len + 1);