Skip to content

Commit 80a733b

Browse files
A field of a union of same-layout object types is read from the base type (#384)
`s.kind` / `s.r` on `s: { kind: "circle", r: number } | { kind: "square", r: number }` failed verification: "'ts.PropertyRef' op operand #0 must be ... tuple reference type ... but got '!ts.ref<!ts.union<...>>'". Such a union needs no tag and is stored as its base type, and property access switched to that type (actualType = baseType), but the value it accessed stayed the union, and MLIRPropertyAccessCodeLogic still held the original expression: the tuple accessor read the field through the union's reference. The value is now cast to the base type, and the access logic is given it (MLIRPropertyAccessCodeLogic::setExpression). Found in the #231 review. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 048c28f commit 80a733b

4 files changed

Lines changed: 58 additions & 2 deletions

File tree

‎tslang/include/TypeScript/MLIRLogic/MLIRCodeLogic.h‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1325,7 +1325,13 @@ class MLIRPropertyAccessCodeLogic
13251325
{
13261326
name = strAttr.getValue();
13271327
}
1328-
}
1328+
}
1329+
1330+
// the object is accessed as another value of it - a union as its base type
1331+
void setExpression(mlir::Value newExpression)
1332+
{
1333+
expression = newExpression;
1334+
}
13291335

13301336
mlir::Value Enum(mlir_ts::EnumType enumType)
13311337
{

‎tslang/lib/TypeScript/MLIRGenAccessCall.cpp‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,14 @@ namespace mlirgen
169169
{
170170
LLVM_DEBUG(llvm::dbgs() << "\n!! mlirGenPropertyAccessExpressionBaseLogic: union type " << baseType << "\n";);
171171
actualType = baseType;
172+
// The value is the base type as well: a union of object types sharing one layout
173+
// (`{ kind: "a", r: number } | { kind: "b", r: number }`) is stored as it. Left a
174+
// union, the field was read through the union's reference - a PropertyRef the
175+
// verifier rejects.
176+
objectValue = builder.create<mlir_ts::CastOp>(location, baseType, objectValue);
177+
cl.setExpression(objectValue);
172178
}
173-
}
179+
}
174180

175181
// class member access
176182
auto classAccessWithObject = [&](mlir_ts::ClassType classType, mlir::Value objectValue) {

‎tslang/test/tester/CMakeLists.txt‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,7 @@ tslang_add_test(NAME test-compile-00-safe-cast-while COMMAND test-runner "${PROJ
532532
tslang_add_test(NAME test-compile-01-safe-cast-while COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/01safe_cast_while.ts")
533533
tslang_add_test(NAME test-compile-00-safe-cast-field-access COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00safe_cast_field_access.ts")
534534
tslang_add_test(NAME test-compile-00-safe-cast-early-exit COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00safe_cast_early_exit.ts")
535+
tslang_add_test(NAME test-compile-00-union-object-types-access COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00union_object_types_access.ts")
535536
tslang_add_test(NAME test-compile-00-safe-cast-null-field COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00safe_cast_null_field.ts")
536537
tslang_add_test(NAME test-compile-00-safe-cast-else-scope COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00safe_cast_else_scope.ts")
537538
tslang_add_test(NAME test-compile-00-safe-cast-bug COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00safe_cast_bug.ts")
@@ -979,6 +980,7 @@ tslang_add_test(NAME test-jit-00-safe-cast-while COMMAND test-runner -jit "${PRO
979980
tslang_add_test(NAME test-jit-01-safe-cast-while COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/01safe_cast_while.ts")
980981
tslang_add_test(NAME test-jit-00-safe-cast-field-access COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00safe_cast_field_access.ts")
981982
tslang_add_test(NAME test-jit-00-safe-cast-early-exit COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00safe_cast_early_exit.ts")
983+
tslang_add_test(NAME test-jit-00-union-object-types-access COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00union_object_types_access.ts")
982984
tslang_add_test(NAME test-jit-00-safe-cast-null-field COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00safe_cast_null_field.ts")
983985
tslang_add_test(NAME test-jit-00-safe-cast-else-scope COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00safe_cast_else_scope.ts")
984986
tslang_add_test(NAME test-jit-00-safe-cast-bug COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00safe_cast_bug.ts")
@@ -1767,6 +1769,7 @@ set(TSLANG_CORPUS
17671769
00safe_cast_bug.ts
17681770
00safe_cast_field_access.ts
17691771
00safe_cast_early_exit.ts
1772+
00union_object_types_access.ts
17701773
00safe_cast_null_field.ts
17711774
00safe_cast_else_scope.ts
17721775
00safe_cast_typeof.ts
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// A field of a union of object types that share one layout. The union is stored as its base type,
2+
// but the field was read through the union's reference - a 'ts.PropertyRef' the verifier rejected
3+
// (#231).
4+
5+
type Shape = { kind: "circle", r: number } | { kind: "square", r: number };
6+
7+
function kind(s: Shape) {
8+
if (s.kind === "circle") {
9+
return 1;
10+
}
11+
12+
return 2;
13+
}
14+
15+
function size(s: Shape) {
16+
return s.r;
17+
}
18+
19+
type Sized = { kind: "circle", r: number | null } | { kind: "square", r: number | null };
20+
21+
function hasSize(s: Sized) {
22+
if (s.r !== null) {
23+
return true;
24+
}
25+
26+
return false;
27+
}
28+
29+
function main() {
30+
const c: Shape = { kind: "circle", r: 1 };
31+
const q: Shape = { kind: "square", r: 2 };
32+
assert(kind(c) == 1 && kind(q) == 2);
33+
assert(size(c) == 1 && size(q) == 2);
34+
35+
const c2: Sized = { kind: "circle", r: 3 };
36+
const q2: Sized = { kind: "square", r: null };
37+
assert(hasSize(c2));
38+
assert(!hasSize(q2));
39+
40+
print("done.");
41+
}

0 commit comments

Comments
 (0)