Skip to content

Commit 4b85f11

Browse files
authored
Fix gentrace assertions when tracing tuples containing type aliases (#5196)
PR #5145 stopped expanding type aliases during name resolution, so a type alias now persists as TK_TYPEALIASREF through codegen. Every codegen dispatch site was updated to unfold TK_TYPEALIASREF on demand, but two sites in gentrace.c were missed. Both only surface when a tuple is traced dynamically (e.g. as an element of a union), which is why ponylang/crdt's nightly started failing its build after #5145 landed: its `_Dot is (ID, U64)` alias wrapping a tuple exercises the first of these paths. trace_dynamic_tuple's TRACE_TUPLE branch duplicated the alias ref with ast_dup and passed it to the recursive trace_dynamic_tuple, which then iterated the alias ref's TK_ID / typeargs / cap / eph children as if they were tuple elements and tripped trace_type's default assertion. Unfold the alias once before the recursion so the callee sees the concrete TK_TUPLETYPE. trace_dynamic's TK_TYPEALIASREF case called typealias_unfold (which always returns a detached subtree) and passed the detached tree into the recursive trace_dynamic. When the detached subtree contained a tuple that reached trace_dynamic_tuple with tuple != NULL, the ast_swap(type, dontcare) at the top of trace_dynamic_tuple asserted on the NULL parent. Splice the unfolded form into the alias's position in its parent so downstream ast_swap always sees an attached tree; the rare detached-alias case is only reachable when tuple == NULL, so the in_tuple branch is never taken and the missing parent does not matter. A single regression test under test/full-program-tests exercises both scenarios: one field with an alias that expands directly to a tuple (bug 1) and another field with an alias that expands to a union containing a second alias-to-tuple (bug 2). Chained type aliases (alias of alias of tuple) are a known gap in the TRACE_TUPLE fix — typealias_unfold only unfolds one level. Constructing a test that reaches this site with a chained alias is currently blocked by other #5145 regressions elsewhere in codegen (the static trace_tuple path in gentrace.c and gen_digestof_value in genreference.c), which fire first for every configuration tried. That broader audit of every typealias_unfold call site is tracked in #5195. This is a regression against unreleased code, so no changelog or release notes entry.
1 parent 10b48a3 commit 4b85f11

File tree

2 files changed

+71
-2
lines changed

2 files changed

+71
-2
lines changed

src/libponyc/codegen/gentrace.c

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -814,7 +814,30 @@ static void trace_dynamic_tuple(compile_t* c, LLVMValueRef ctx,
814814
LLVMPositionBuilderAtEnd(c->builder, trace_block);
815815

816816
// If we are (A, B), turn (_, _) into (A, _).
817-
ast_t* swap = ast_dup(child);
817+
// If the element is a type alias for a tuple, unfold it once so the
818+
// recursive call sees the concrete TK_TUPLETYPE; iterating the alias
819+
// ref's children (TK_ID, typeargs, cap, eph) as tuple elements would
820+
// trip trace_type's default assertion. typealias_unfold is guaranteed
821+
// to succeed here because trace_type already unfolded the same child
822+
// to classify it as TRACE_TUPLE; reify is deterministic. Chained
823+
// aliases (alias of alias of tuple) are a known gap tracked in
824+
// ponylang/ponyc#5195 — typealias_unfold only unfolds one level, so
825+
// swap is still a TK_TYPEALIASREF and the recursive call hits the
826+
// same assertion. Constructing a test that reaches this site with a
827+
// chained alias is currently blocked by other PR #5145 regressions
828+
// elsewhere in codegen (the static trace_tuple path in this file and
829+
// gen_digestof_value in genreference.c), which fire first for every
830+
// configuration tried.
831+
ast_t* swap;
832+
833+
if(ast_id(child) == TK_TYPEALIASREF)
834+
{
835+
swap = typealias_unfold(child);
836+
pony_assert(swap != NULL);
837+
} else {
838+
swap = ast_dup(child);
839+
}
840+
818841
ast_swap(dc_child, swap);
819842

820843
// Get a pointer to the unboxed tuple and its descriptor.
@@ -950,7 +973,29 @@ static void trace_dynamic(compile_t* c, LLVMValueRef ctx, LLVMValueRef object,
950973

951974
if(unfolded != NULL)
952975
{
953-
trace_dynamic(c, ctx, object, unfolded, orig, tuple, next_block);
976+
// Splice the unfolded form into the alias's position in its parent.
977+
// This keeps the tree attached so that downstream code (e.g.
978+
// trace_dynamic_tuple's ast_swap into the enclosing tuple context)
979+
// always sees a non-NULL parent.
980+
//
981+
// If the alias itself has no parent, the only way to get here is
982+
// gentrace's own top-level TRACE_DYNAMIC case below, which always
983+
// passes tuple == NULL, so trace_dynamic_tuple's in_tuple branch
984+
// never runs and the missing parent does not matter. The other
985+
// callers of trace_dynamic (trace_dynamic_union_or_isect,
986+
// trace_dynamic_tuple's TRACE_MUT/VAL/TAG/DYNAMIC case) always pass
987+
// an attached type, and the recursive self-call below preserves
988+
// that invariant.
989+
if(ast_parent(type) != NULL)
990+
{
991+
ast_swap(type, unfolded);
992+
trace_dynamic(c, ctx, object, unfolded, orig, tuple, next_block);
993+
ast_swap(unfolded, type);
994+
} else {
995+
pony_assert(tuple == NULL);
996+
trace_dynamic(c, ctx, object, unfolded, orig, tuple, next_block);
997+
}
998+
954999
ast_free_unattached(unfolded);
9551000
}
9561001
break;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Regression test for type alias elements inside dynamically-traced tuples.
2+
//
3+
// Two related scenarios in gentrace's trace_dynamic path:
4+
//
5+
// 1. An outer tuple has a child that is a type alias expanding directly to a
6+
// tuple. trace_dynamic_tuple's TRACE_TUPLE case used to iterate the alias
7+
// ref as if it were a tuple, tripping an assertion on the TK_ID child.
8+
//
9+
// 2. An outer tuple has a child that is a type alias expanding to a union
10+
// which in turn contains another alias that expands to a tuple.
11+
// trace_dynamic's TK_TYPEALIASREF case unfolded the alias into a detached
12+
// subtree; when that subtree recursed into trace_dynamic_tuple with
13+
// tuple != NULL, ast_swap asserted on the detached type's NULL parent.
14+
15+
type _Pair is (U64, U64)
16+
type _Wrap is (_Pair | String)
17+
18+
actor Main
19+
var _direct: ((String, _Pair) | None) = None
20+
var _wrapped: ((String, _Wrap) | None) = None
21+
22+
new create(env: Env) =>
23+
_direct = ("hello", (U64(1), U64(2)))
24+
_wrapped = ("world", (U64(3), U64(4)))

0 commit comments

Comments
 (0)