Skip to content

Commit 3d5e261

Browse files
committed
Fix compiler crash when passing an array literal to OutStream.writev
`detect_values_element_type` in the array literal element-type inference only stripped a top-level `this->` arrow from the element type extracted from an interface's `values()` method. When the element type came from a type alias that expanded into a union (such as `ByteSeq = (String | Array[U8] val)` in `ByteSeqIter`), the viewpoint pass distributed the `this->` across the union members. The top-level check then saw a `TK_UNIONTYPE` and left the arrows in place. The unstripped arrows reached code generation and tripped an assertion in `type_append` at `codegen/genname.c`. `detect_apply_element_type` already used the recursive `strip_this_arrow` helper for the same reason. The values-based strategy now does too, bringing the two paths into parity. Closes #2701 Closes #2790
1 parent ecf59a1 commit 3d5e261

File tree

4 files changed

+57
-5
lines changed

4 files changed

+57
-5
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
## Fix compiler crash when passing an array literal to `OutStream.writev`
2+
3+
Previously, the compiler would crash with an assertion failure when an array literal was passed to `env.out.writev` (or any other call expecting a `ByteSeqIter`):
4+
5+
```pony
6+
actor Main
7+
new create(env: Env) =>
8+
env.out.writev([])
9+
env.out.writev(["foo"; "bar"])
10+
```
11+
12+
The compiler's element-type inference for array literals, when the antecedent was an interface whose `values` method returned a type alias (such as `ByteSeqIter`, where `ByteSeq` is `(String | Array[U8] val)`), failed to fully strip viewpoint arrows from the inferred type. The leftover arrows eventually reached code generation and triggered an internal assertion.
13+
14+
This has been fixed. Code of the shape above now compiles and runs correctly.

src/libponyc/expr/array.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,9 @@ static ast_t* detect_values_element_type(pass_opt_t* opt, ast_t* ast,
120120
if(ast_id(typeparams) == TK_TYPEPARAMS)
121121
elem_type = reify(elem_type, typeparams, typeargs, opt, true);
122122

123-
if((ast_id(elem_type) == TK_ARROW) &&
124-
(ast_id(ast_child(elem_type)) == TK_THISTYPE))
125-
elem_type = ast_childidx(elem_type, 1);
126-
127-
return elem_type;
123+
// Strip `this->` viewpoint arrows, including any that were distributed
124+
// into a union/intersection/tuple when a type alias was expanded.
125+
return strip_this_arrow(opt, elem_type);
128126
}
129127

130128
static void find_possible_element_types(pass_opt_t* opt, ast_t* ast,
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
actor Main
2+
new create(env: Env) =>
3+
// Regression test for ponylang/ponyc#2701 and ponylang/ponyc#2790.
4+
//
5+
// `OutStream.writev` takes `ByteSeqIter`, an interface whose
6+
// `values()` method returns `Iterator[this->ByteSeq box]` where
7+
// `ByteSeq` is the type alias `(String | Array[U8] val)`. Array
8+
// literal element-type inference used to crash the compiler when
9+
// the element type was extracted from that shape, because a
10+
// `this->` arrow survived inside a union produced by alias
11+
// expansion and eventually reached code generation.
12+
env.out.writev([])
13+
env.out.writev(["foo"; "bar"])
14+
// Mixed-type array exercising both branches of `ByteSeq`.
15+
env.out.writev(["foo"; "bar".array(); "baz"])
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Regression test for ponylang/ponyc#2701 and ponylang/ponyc#2790.
2+
//
3+
// Mirrors `array-infer-from-values-interface-type-alias-element` with a
4+
// locally-declared interface and type alias so the regression is pinned
5+
// to the shape that triggered the crash, not to the stdlib definitions
6+
// of `ByteSeqIter`/`ByteSeq`. The shape: an interface whose `values()`
7+
// method returns `Iterator[this->Alias box]` where `Alias` is a type
8+
// alias that expands into a union. Element-type inference for array
9+
// literals with this antecedent must recursively strip `this->` arrows
10+
// from inside the alias-expanded union before the type reaches code
11+
// generation.
12+
13+
type _Elem is (String | Array[U8] val)
14+
15+
interface val _Elems
16+
fun values(): Iterator[this->_Elem box]
17+
18+
primitive _Sink
19+
fun apply(iter: _Elems) => None
20+
21+
actor Main
22+
new create(env: Env) =>
23+
_Sink([])
24+
_Sink(["foo"; "bar"])
25+
_Sink(["foo"; "bar".array(); "baz"])

0 commit comments

Comments
 (0)