Skip to content

Commit ee6a165

Browse files
authored
fix: Make field types invariant to guarantee soundness (#2539)
1 parent 7bc3b57 commit ee6a165

File tree

6 files changed

+109
-180
lines changed

6 files changed

+109
-180
lines changed

src/resolver.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3331,8 +3331,15 @@ export class Resolver extends DiagnosticEmitter {
33313331
}
33323332
}
33333333

3334-
// assignability
3335-
if (!fieldType.isStrictlyAssignableTo(existingField.type)) {
3334+
// assignability (to guarantee soundness, field types must be invariant)
3335+
// see also Wasm GC, where mutable fields are invariant for this reason
3336+
//
3337+
// class Animal { sibling: Animal; }
3338+
// class Cat extends Animal { sibling: Cat; } // covariance
3339+
// class Dog extends Animal { sibling: Dog; } // is unsound
3340+
// (<Animal>new Cat()).sibling = new Dog(); // → Cat with Dog sibling
3341+
//
3342+
if (fieldType != existingField.type) {
33363343
this.errorRelated(
33373344
DiagnosticCode.Property_0_in_type_1_is_not_assignable_to_the_same_property_in_base_type_2,
33383345
fieldPrototype.identifierNode.range, existingField.identifierNode.range,

tests/compiler/duplicate-field-errors.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"TS2325: Property 'protPriv' is private in type 'duplicate-field-errors/B' but not in type 'duplicate-field-errors/A'.",
99
"TS2325: Property 'pubPriv' is private in type 'duplicate-field-errors/B' but not in type 'duplicate-field-errors/A'.",
1010
"TS2444: Property 'pubProt' is protected in type 'duplicate-field-errors/B' but public in type 'duplicate-field-errors/A'.",
11-
"TS2300: Duplicate identifier 'method'."
11+
"TS2300: Duplicate identifier 'method'.",
12+
"Property 'sibling' in type 'duplicate-field-errors/Cat' is not assignable to the same property in base type 'duplicate-field-errors/Animal'."
1213
]
1314
}

tests/compiler/duplicate-field-errors.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,15 @@ class B extends A {
3636
public method: i32;
3737
}
3838

39+
class Animal {
40+
sibling: Animal | null;
41+
}
42+
class Cat extends Animal {
43+
sibling: Cat | null; // covariance is unsound
44+
}
45+
3946
export function test(): void {
4047
new A();
4148
new B();
49+
new Cat();
4250
}

tests/compiler/duplicate-fields.debug.wat

Lines changed: 32 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
(type $i32_=>_none (func (param i32)))
66
(type $none_=>_none (func))
77
(type $i32_i32_i32_=>_none (func (param i32 i32 i32)))
8-
(type $i32_i32_i32_=>_i32 (func (param i32 i32 i32) (result i32)))
98
(type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32)))
9+
(type $i32_i32_i32_=>_i32 (func (param i32 i32 i32) (result i32)))
1010
(type $none_=>_i32 (func (result i32)))
1111
(import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32)))
1212
(global $~lib/rt/itcms/total (mut i32) (i32.const 0))
@@ -26,9 +26,9 @@
2626
(global $duplicate-fields/foo (mut i32) (i32.const 0))
2727
(global $duplicate-fields/raz (mut i32) (i32.const 0))
2828
(global $~lib/rt/__rtti_base i32 (i32.const 480))
29-
(global $~lib/memory/__data_end i32 (i32.const 572))
30-
(global $~lib/memory/__stack_pointer (mut i32) (i32.const 16956))
31-
(global $~lib/memory/__heap_base i32 (i32.const 16956))
29+
(global $~lib/memory/__data_end i32 (i32.const 564))
30+
(global $~lib/memory/__stack_pointer (mut i32) (i32.const 16948))
31+
(global $~lib/memory/__heap_base i32 (i32.const 16948))
3232
(memory $0 1)
3333
(data (i32.const 12) "<\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00(\00\00\00A\00l\00l\00o\00c\00a\00t\00i\00o\00n\00 \00t\00o\00o\00 \00l\00a\00r\00g\00e\00\00\00\00\00")
3434
(data (i32.const 76) "<\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00 \00\00\00~\00l\00i\00b\00/\00r\00t\00/\00i\00t\00c\00m\00s\00.\00t\00s\00\00\00\00\00\00\00\00\00\00\00\00\00")
@@ -39,7 +39,7 @@
3939
(data (i32.const 320) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00")
4040
(data (i32.const 348) "<\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00")
4141
(data (i32.const 412) "<\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00&\00\00\00d\00u\00p\00l\00i\00c\00a\00t\00e\00-\00f\00i\00e\00l\00d\00s\00.\00t\00s\00\00\00\00\00\00\00")
42-
(data (i32.const 480) "\0b\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00\00\00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\03\00\00\00\00\00\00\00\00\00\00\00 \00\00\00\00\00\00\00\00\00\00\00\05\00\00\00 \00\00\00\06\00\00\00 \00\00\00\n\00\00\00 \00\00\00\00\00\00\00")
42+
(data (i32.const 480) "\n\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00\00\00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\03\00\00\00\00\00\00\00\00\00\00\00 \00\00\00\00\00\00\00\00\00\00\00\05\00\00\00 \00\00\00\t\00\00\00 \00\00\00\00\00\00\00")
4343
(table $0 1 1 funcref)
4444
(elem $0 (i32.const 1))
4545
(export "memory" (memory $0))
@@ -2217,11 +2217,6 @@
22172217
local.get $1
22182218
i32.store $0
22192219
)
2220-
(func $duplicate-fields/Bar#set:bar (param $0 i32) (param $1 i32)
2221-
local.get $0
2222-
local.get $1
2223-
i32.store $0 offset=4
2224-
)
22252220
(func $duplicate-fields/A3#set:protProt (param $0 i32) (param $1 i32)
22262221
local.get $0
22272222
local.get $1
@@ -2315,46 +2310,43 @@
23152310
block $invalid
23162311
block $duplicate-fields/A3
23172312
block $duplicate-fields/B3
2318-
block $duplicate-fields/Bar
2319-
block $duplicate-fields/B2
2320-
block $duplicate-fields/Foo
2321-
block $duplicate-fields/A2
2322-
block $duplicate-fields/B
2323-
block $duplicate-fields/A
2324-
block $~lib/arraybuffer/ArrayBufferView
2325-
block $~lib/string/String
2326-
block $~lib/arraybuffer/ArrayBuffer
2327-
local.get $0
2328-
i32.const 8
2329-
i32.sub
2330-
i32.load $0
2331-
br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $duplicate-fields/A $duplicate-fields/B $duplicate-fields/A2 $duplicate-fields/Foo $duplicate-fields/B2 $duplicate-fields/Bar $duplicate-fields/B3 $duplicate-fields/A3 $invalid
2332-
end
2333-
return
2313+
block $duplicate-fields/B2
2314+
block $duplicate-fields/Foo
2315+
block $duplicate-fields/A2
2316+
block $duplicate-fields/B
2317+
block $duplicate-fields/A
2318+
block $~lib/arraybuffer/ArrayBufferView
2319+
block $~lib/string/String
2320+
block $~lib/arraybuffer/ArrayBuffer
2321+
local.get $0
2322+
i32.const 8
2323+
i32.sub
2324+
i32.load $0
2325+
br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $duplicate-fields/A $duplicate-fields/B $duplicate-fields/A2 $duplicate-fields/Foo $duplicate-fields/B2 $duplicate-fields/B3 $duplicate-fields/A3 $invalid
23342326
end
23352327
return
23362328
end
2337-
local.get $0
2338-
local.get $1
2339-
call $~lib/arraybuffer/ArrayBufferView~visit
23402329
return
23412330
end
2331+
local.get $0
2332+
local.get $1
2333+
call $~lib/arraybuffer/ArrayBufferView~visit
23422334
return
23432335
end
23442336
return
23452337
end
2346-
local.get $0
2347-
local.get $1
2348-
call $duplicate-fields/A2~visit
23492338
return
23502339
end
2340+
local.get $0
2341+
local.get $1
2342+
call $duplicate-fields/A2~visit
23512343
return
23522344
end
2353-
local.get $0
2354-
local.get $1
2355-
call $duplicate-fields/B2~visit
23562345
return
23572346
end
2347+
local.get $0
2348+
local.get $1
2349+
call $duplicate-fields/B2~visit
23582350
return
23592351
end
23602352
return
@@ -2442,8 +2434,7 @@
24422434
i32.const 0
24432435
i32.const 0
24442436
i32.const 1
2445-
i32.const 2
2446-
call $duplicate-fields/Bar#constructor
2437+
call $duplicate-fields/Foo#constructor
24472438
local.set $0
24482439
global.get $~lib/memory/__stack_pointer
24492440
local.get $0
@@ -2453,8 +2444,8 @@
24532444
global.set $duplicate-fields/raz
24542445
global.get $duplicate-fields/raz
24552446
i32.load $0
2456-
i32.load $0 offset=4
2457-
i32.const 2
2447+
i32.load $0
2448+
i32.const 1
24582449
i32.eq
24592450
i32.eqz
24602451
if
@@ -2655,46 +2646,6 @@
26552646
global.set $~lib/memory/__stack_pointer
26562647
local.get $2
26572648
)
2658-
(func $duplicate-fields/Bar#constructor (param $this i32) (param $foo i32) (param $bar i32) (result i32)
2659-
(local $3 i32)
2660-
global.get $~lib/memory/__stack_pointer
2661-
i32.const 4
2662-
i32.sub
2663-
global.set $~lib/memory/__stack_pointer
2664-
call $~stack_check
2665-
global.get $~lib/memory/__stack_pointer
2666-
i32.const 0
2667-
i32.store $0
2668-
local.get $this
2669-
i32.eqz
2670-
if
2671-
global.get $~lib/memory/__stack_pointer
2672-
i32.const 8
2673-
i32.const 8
2674-
call $~lib/rt/itcms/__new
2675-
local.tee $this
2676-
i32.store $0
2677-
end
2678-
local.get $this
2679-
i32.const 0
2680-
call $duplicate-fields/Bar#set:bar
2681-
global.get $~lib/memory/__stack_pointer
2682-
local.get $this
2683-
local.get $foo
2684-
call $duplicate-fields/Foo#constructor
2685-
local.tee $this
2686-
i32.store $0
2687-
local.get $this
2688-
local.get $bar
2689-
call $duplicate-fields/Bar#set:bar
2690-
local.get $this
2691-
local.set $3
2692-
global.get $~lib/memory/__stack_pointer
2693-
i32.const 4
2694-
i32.add
2695-
global.set $~lib/memory/__stack_pointer
2696-
local.get $3
2697-
)
26982649
(func $duplicate-fields/A3#constructor (param $this i32) (result i32)
26992650
(local $1 i32)
27002651
global.get $~lib/memory/__stack_pointer
@@ -2710,7 +2661,7 @@
27102661
if
27112662
global.get $~lib/memory/__stack_pointer
27122663
i32.const 12
2713-
i32.const 10
2664+
i32.const 9
27142665
call $~lib/rt/itcms/__new
27152666
local.tee $this
27162667
i32.store $0
@@ -2747,7 +2698,7 @@
27472698
if
27482699
global.get $~lib/memory/__stack_pointer
27492700
i32.const 12
2750-
i32.const 9
2701+
i32.const 8
27512702
call $~lib/rt/itcms/__new
27522703
local.tee $this
27532704
i32.store $0

0 commit comments

Comments
 (0)