Conversation
strptime is hidden by glibc unless _XOPEN_SOURCE is defined before <time.h>. On Ubuntu CI this caused an implicit declaration in stdlib/time.c: the char* return was truncated to int, leaving end pointing at garbage. time_parse crashed at runtime and the P0105 impossible-date test leaked a cc error instead of panicking. macOS declares strptime unconditionally, so all macOS tests passed.
…it (#2545) File-scope variable initializers that lower to runtime calls (negative integer overflow checks, struct literals with array/map fields, string interpolation) were emitted inline, which C rejects as non-constant file-scope initializers. Emit a zero-initialized global and defer the real initializer into the global-init buffer, matching the existing emit_vardecl_array/emit_vardecl_map handling.
…ence parameter (#2546) emit_mutable_call_argument() emitted `&<raw name>` for a bare identifier argument, skipping the name resolution that emit_label() applies. A module-level variable is declared under its mangled name, so the reference failed to compile with 'use of undeclared identifier'. Resolve the name through codegen_resolve_ref() before taking its address.
…own module (#2537) resolve_member_expr() only did a plain scope_lookup for a bare-label object and gave up on a miss, so a module-level struct variable referenced inside its declaring module resolved to null and addr(MODVAR.field) typed as ^unknown. Apply the same module-mangled symbol fallback the NODE_LABEL value path uses.
…unc signature positions (#2524) resolve_type_alias() only recursed into pointer (^T) and dynamic-array ([T]) type strings, so an alias used as a map key/value, a fixed-size array element (`[I,N]`), or a func signature parameter/return (`func(I)->I`) kept its alias spelling while the value's inferred type used the underlying name, producing spurious E3001/E3066 mismatches. Recurse into all three composite forms and re-resolve alias chains whose target is itself a map or func type.
Since mut became optional, 'foobar int' with no value compiles and silently zero-initializes, so a dropped '= value' has no signal. Emit W1004 on any mut-class declaration that has a type annotation but no value, formatting the type's zero literal into the message. const is unaffected (E2011 already requires a value).
…ps (#2498) Add pass tests for previously-uncovered builtins (char_count, char, sleep_s/ms/ns, cast to f32/f64/u32), defer execution semantics (LIFO, arg-eval-at-exit, nested activations), and bitwise ops on sized integer types. Move the @runtime introspection test into pass/stdlib/. Give 12 assertion-free pass/core files real assertions and a SOME TESTS FAILED footer so a wrong value is no longer invisible to the runner.
… on wrong output (#2498) 22 files incremented a failed counter but never surfaced it to the runner (no panic, no SOME TESTS FAILED, exit 0) — add the standard footer so a wrong value fails the run. Rewrite 7 nominal tests (all_imports_used, cast_array, char_byte_array_literals, const_arithmetic, escaped_interpolation, for_blank_identifier, local_const_array_size) with real assertions. cast_array is narrowed to same-width element casts; cross-width [int]->[u8] casts reinterpret the buffer instead of converting per element.
Rewrite 13 nominal pass/core tests (discard_attribute, doc_test, fixed_size_array_of_maps, function_scoped_using, map_field_ptr_deref, mixed_shared_type, pointer_correct_struct_arg, shared_type_named_return, struct_wildcard_monomorphize, type_param_generic, both keyword_alias_consistent files) with real assertions; the eprint/eprintln smoke tests assert only that every type runs. Add the standard SOME TESTS FAILED footer to 26 files that tracked a failed counter but never surfaced it to the runner. pass/core now has no test that passes silently on wrong output.
Extend existing per-module pass/stdlib tests to exercise every stdlib function that had no test: atomic and/or/xor, arrays/maps is_equal, binary 128/256-bit encode/decode (both byte orders), mem zero/fill/raw_copy, os set_env/unset_env, uuid generate_hyphenated, time tick/elapsed_ms/weekday, json parse/pretty_print, threads spawn_arg, io temp_dir/temp_file/write_bytes/append_bytes. Only net accept/receive/send/set_timeout remain — they need a live loopback socket pair the integration harness can't set up cleanly.
C promotes uint8_t/uint16_t to int before applying ~, producing a negative value that fails the narrowing range check on assignment. Codegen already cast byte results to uint8_t; extend the same mask to u8 and u16.
…te (#2554) spawn_thread never checked pthread_create's return, so an OS-level failure (thread limit, memory pressure) left a handle with an uninitialized pthread_t, a leaked state/ThreadArg, a stuck live count, and alive=1 forever. Now the return code is checked: on failure the live count and alive flag are unwound, both allocations freed, and P0108 is raised. The four mallocs go through a thread_alloc wrapper that raises P0109 on OOM.
… int64_t (#2555) cast(arr, [T]) stores its result packed at sizeof(T), but emit_index_expr and emit_foreach_array only special-cased byte among the sub-8-byte types; every other sized int (u8, u16, i16, i32, u32, ...) fell through to an int64_t read that strided past the buffer. Both element-type switches now resolve TK_INT/ TK_UINT element types through gray_type_to_c_codegen.
The char builtin branch only rejected a string argument when it was a string literal of length != 1. A length-1 literal or any non-literal string expression passed through as a char and reached codegen, which emitted (int32_t)(GrayString) and leaked a cc error. The branch now rejects any argument that is not an integer kind (or char) with E3001.
…ssign (#2550) m[key][i] and m[key][i] = v lowered to an address-of on the rvalue that the map lookup statement-expression produces, which cc rejected. Both the read (emit_index_expr) and the write (index assignment) paths now bind the lookup result to a GrayArray temp first; the header is a view over the stored buffer so element writes still land in the map's array. Covers plain and compound assignment including the sized-int overflow check.
…ompound assign The sized-int overflow-checked compound assignment path (a[i] += n) read the element with a hardcoded int64_t GRAY_ARRAY_GET_AT, over-reading a packed sub-8-byte array produced by cast(arr, [T]) and feeding garbage to the range check. GET now uses the element's C width; SET keeps int64_t since its memcpy length is the runtime elem_size, which stays safe for 8-byte literal slots.
…nteger (#2536) A string-backed enum is a GrayString at runtime, but the cast allowlist treated every TK_ENUM as int-backed, so cast(int, StringEnum) and cast(StringEnum, int) passed the typechecker and reached clang as a cast between an arithmetic type and a struct. Both int-backed enum rules now require the enum not be string-backed, and string <-> string-backed enum is allowed in their place (a pure GrayString reinterpretation in codegen).
Add benchmarks/ with a seven-workload suite timed end to end: JSON round-trip, ray tracer, lexer, Game of Life, KV store, SHA-256, and a compile-stress phase. run.sh builds a CLOCK_MONOTONIC stopwatch (runner.c), generates deterministic fixtures (gen.sh), times compile and run phases, prints a table, and writes benchmarks/results.json with machine info, commit, and timestamp. Grayscale-only absolute numbers - no baseline, no regression gate, no CI job. POSIX only; the make target is a no-op on Windows. BENCH_RUNS / BENCH_COMPILE_RUNS override iteration counts; fixtures are generated once and reused. Wire benchmark into .PHONY, KNOWN_TARGETS, and help; allow-list benchmarks/** in .gitignore while keeping .gen/ and results.json out; add a pointer row to CONTRIBUTING.md.
…#2547) Add 22 sections (28-49) to cli/tests.gray covering language features and stdlib that the verification suite never exercised: error handling, or_return (bare and fallback), ensure/defer LIFO, if/or/otherwise, bitwise operators, compound assignment, not_in, recursion, type aliases, nested composites, char<->int, scoped blocks, const locals, and higher-order functions; plus extended coverage of strings, arrays, maps, math, json (decode/round-trip/nested), strconv error slots, csv, and runtime. Notes: - Section 34 exercises not_in only; a file may use just one alias of a keyword (E2088). - Section 39 is 'Scoped blocks' rather than shadowing: real shadowing emits W2002 and the suite compiles warning-free. - arrays.deduplicate is left out: it does not typecheck in an assignment position right now. gray verify passes: 230 assertions, 0 failures.
) run.sh now wipes .gen/ and regenerates it every run instead of printing a 'delete it yourself' message; the fixtures are deterministic and rebuild in under a second. Each fixture-reading workload (json_roundtrip, lexer, kvstore, sha256) falls back to benchmarks/.gen next to its own source when BENCH_GEN is unset, so 'gray build benchmarks/workloads/lexer.gray && ./lexer' works from any directory instead of failing with 'cannot read fixture'. Document the suite: a Learn More link in README.md, and a 'Running one workload' section plus a BENCH_GEN row in benchmarks/README.md.
…#2556) test_stdlib.c gains 30 tests across the three priority modules: json (encode, decode, decode_result ok/err, round-trip, is_valid, pretty, split_array), io (write/read/append/bytes round-trips in a portable temp dir, read_lines, result-variant error slot, make/remove dir, and the pure path helpers), and regex (is_valid, match, find, find_all, replace, split, result-variant error slot). STDLIB_TEST_DEPS picks up json.o, io.o, regex.o. test_codegen.c gains 8 e2e cases for features with a codegen path but no end-to-end coverage: bitwise operators, the defer keyword, string stdlib (to_upper/to_lower/trim/replace/contains/split), a when multi-value arm, maps.remove_key, tagged (associated-data) enums, struct field defaults, and a generic <?> type parameter. Unit suites: 130 -> 160 stdlib tests. E2e: 121 -> 129.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR contains several bugfixes, major improvements to overall test coverage as well as a new
make benchmarkCLI command among docs imrpovementsBugfixes
cast()between a string-backed enum and an integer (bug: cast() between a string-backed enum and an integer leaks a C compiler error #2536)char()(bug: char() accepts a string argument and leaks a C compiler error #2552)cast(arr, [T])results with the element width, not int64_t (bug: cast(arr, [T]) to a sub-8-byte element type produces an array that indexes with the wrong stride #2555)bit_noton u8 and u16 back to type width (bug: bit_not on a u8 or u16 yields a negative value and panics P0019 on assignment #2553)pthread_createfailure instead of corrupting state (bug: threads.spawn ignores pthread_create failure, corrupting thread state #2554)strptimeon glibc fortime.parse(CI build)Performance, Optimizations & Refactors
make benchmarkreal-workload suite (perf: add make benchmark real-workload benchmark suite #2551)Tests
tests actually fail on wrong output; cover previously-untested stdlib
functions (tests: missing pass integration test coverage #2498)
gray verifycoverage for newer features and stdlib (tests: expandgray verifycoverage for newer language features and stdlib #2547)t{}(bug: lowercase type names are not parsed as struct literals, leaking a C compiler error #2467)Docs
.mdfiles