Skip to content

Commit 76b7f99

Browse files
committed
Implement emission of warning upon generic function/class export (#570)
1 parent 241a7f5 commit 76b7f99

9 files changed

+1741
-22
lines changed

NOTICE

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ under the licensing terms detailed in LICENSE:
1818
* Emil Laine <laine.emil@gmail.com>
1919
* Stephen Paul Weber <stephen.weber@shopify.com>
2020
* Jay Phelps <hello@jayphelps.com>
21+
* Joel Mun <hj923@hotmail.com>
2122

2223
Portions of this software are derived from third-party works licensed under
2324
the following terms:

src/compiler.ts

+12-2
Original file line numberDiff line numberDiff line change
@@ -680,13 +680,23 @@ export class Compiler extends DiagnosticEmitter {
680680
break;
681681
}
682682
case ElementKind.FUNCTION_PROTOTYPE: {
683-
if (!element.is(CommonFlags.GENERIC)) {
683+
if (element.is(CommonFlags.GENERIC)) {
684+
this.warning(
685+
DiagnosticCode.Generic_functions_or_classes_cannot_be_compiled_to_wasm,
686+
(<FunctionPrototype>element).identifierNode.range,
687+
);
688+
} else {
684689
this.compileFunctionUsingTypeArguments(<FunctionPrototype>element, []);
685690
}
686691
break;
687692
}
688693
case ElementKind.CLASS_PROTOTYPE: {
689-
if (!element.is(CommonFlags.GENERIC)) {
694+
if (element.is(CommonFlags.GENERIC)) {
695+
this.warning(
696+
DiagnosticCode.Generic_functions_or_classes_cannot_be_compiled_to_wasm,
697+
(<ClassPrototype>element).identifierNode.range,
698+
);
699+
} else {
690700
this.compileClassUsingTypeArguments(<ClassPrototype>element, []);
691701
}
692702
break;

src/diagnosticMessages.generated.ts

+2
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ export enum DiagnosticCode {
9999
Module_0_has_no_exported_member_1 = 2305,
100100
Generic_type_0_requires_1_type_argument_s = 2314,
101101
Type_0_is_not_generic = 2315,
102+
Generic_functions_or_classes_cannot_be_compiled_to_wasm = 2316,
102103
Type_0_is_not_assignable_to_type_1 = 2322,
103104
Index_signature_is_missing_in_type_0 = 2329,
104105
_this_cannot_be_referenced_in_current_location = 2332,
@@ -242,6 +243,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
242243
case 2305: return "Module '{0}' has no exported member '{1}'.";
243244
case 2314: return "Generic type '{0}' requires {1} type argument(s).";
244245
case 2315: return "Type '{0}' is not generic.";
246+
case 2316: return "Generic functions or classes cannot be compiled to wasm";
245247
case 2322: return "Type '{0}' is not assignable to type '{1}'.";
246248
case 2329: return "Index signature is missing in type '{0}'.";
247249
case 2332: return "'this' cannot be referenced in current location.";

src/diagnosticMessages.json

+1
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
"Module '{0}' has no exported member '{1}'.": 2305,
9595
"Generic type '{0}' requires {1} type argument(s).": 2314,
9696
"Type '{0}' is not generic.": 2315,
97+
"Generic functions or classes cannot be compiled to wasm": 2316,
9798
"Type '{0}' is not assignable to type '{1}'.": 2322,
9899
"Index signature is missing in type '{0}'.": 2329,
99100
"'this' cannot be referenced in current location.": 2332,

tests/compiler.js

+32-20
Original file line numberDiff line numberDiff line change
@@ -156,32 +156,44 @@ function runTest(basename) {
156156
}, err => {
157157
console.log();
158158

159-
// check expected stderr patterns in order
159+
// check expected stderr and stdout patterns in order
160160
let expectStderr = config.stderr;
161-
if (expectStderr) {
162-
const stderrString = stderr.toString();
163-
if (typeof expectStderr === "string") expectStderr = [ expectStderr ];
164-
let lastIndex = 0;
165-
let failed = false;
166-
expectStderr.forEach((substr, i) => {
167-
var index = stderrString.indexOf(substr, lastIndex);
168-
if (index < 0) {
169-
console.log("Missing pattern #" + (i + 1) + " '" + substr + "' in stderr at " + lastIndex + "+.");
161+
let expectStdout = config.stdout;
162+
let hasExpected = false;
163+
[
164+
{ expected: expectStderr, text: 'stderr' },
165+
{ expected: expectStdout, text: 'stdout' }
166+
].forEach((stdPatterns) => {
167+
let expected = stdPatterns.expected;
168+
if (expected) {
169+
hasExpected = true;
170+
const stderrString = stderr.toString();
171+
if (typeof expected === "string") expected = [ expected ];
172+
let lastIndex = 0;
173+
let failed = false;
174+
expected.forEach((substr, i) => {
175+
var index = stderrString.indexOf(substr, lastIndex);
176+
if (index < 0) {
177+
console.log("Missing pattern #" + (i + 1) + " '" + substr + "' in stderr at " + lastIndex + "+.");
178+
failedTests.add(basename);
179+
failed = true;
180+
} else {
181+
lastIndex = index + substr.length;
182+
}
183+
});
184+
let text = stdPatterns.text;
185+
if (failed) {
170186
failedTests.add(basename);
171-
failed = true;
187+
failedMessages.set(basename, + text + " mismatch");
188+
console.log("\n- " + colorsUtil.red(text + " MISMATCH") + "\n");
172189
} else {
173-
lastIndex = index + substr.length;
190+
console.log("- " + colorsUtil.green(text + " MATCH") + "\n");
174191
}
175-
});
176-
if (failed) {
177-
failedTests.add(basename);
178-
failedMessages.set(basename, "stderr mismatch");
179-
console.log("\n- " + colorsUtil.red("stderr MISMATCH") + "\n");
180-
} else {
181-
console.log("- " + colorsUtil.green("stderr MATCH") + "\n");
192+
return;
182193
}
194+
})
195+
if (hasExpected)
183196
return;
184-
}
185197

186198
if (err)
187199
stderr.write(err + os.EOL);

tests/compiler/generic-export.json

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"asc_flags": [
3+
"--runtime none"
4+
],
5+
"stdout": [
6+
"TS2316: Generic functions or classes cannot be compiled to wasm", "export function genericExample<T>(a: T, b: T): Array<T> {",
7+
"TS2316: Generic functions or classes cannot be compiled to wasm", "export class GenericNumber<T> {",
8+
"TS2316: Generic functions or classes cannot be compiled to wasm", "export class GenericNumber2<T> {"
9+
]
10+
}
+161
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
(module
2+
(type $FUNCSIG$iii (func (param i32 i32) (result i32)))
3+
(type $FUNCSIG$vi (func (param i32)))
4+
(type $FUNCSIG$ii (func (param i32) (result i32)))
5+
(type $FUNCSIG$i (func (result i32)))
6+
(type $FUNCSIG$v (func))
7+
(memory $0 0)
8+
(global $generic-export/okVariable i32 (i32.const 1))
9+
(global $~lib/rt/stub/startOffset (mut i32) (i32.const 0))
10+
(global $~lib/rt/stub/offset (mut i32) (i32.const 0))
11+
(global $generic-export/OkClass i32 (i32.const 4))
12+
(export "memory" (memory $0))
13+
(export "genericExample<i32>" (func $generic-export/okFunction))
14+
(export "okFunction" (func $generic-export/okFunction))
15+
(export "OkClass" (global $generic-export/OkClass))
16+
(export "OkClass#one" (func $generic-export/OkClass#one))
17+
(export "okVariable" (global $generic-export/okVariable))
18+
(export "functionThatUsesGenericFunction" (func $generic-export/functionThatUsesGenericFunction))
19+
(start $start)
20+
(func $~lib/rt/stub/maybeGrowMemory (; 0 ;) (type $FUNCSIG$vi) (param $0 i32)
21+
(local $1 i32)
22+
(local $2 i32)
23+
local.get $0
24+
memory.size
25+
local.tee $2
26+
i32.const 16
27+
i32.shl
28+
local.tee $1
29+
i32.gt_u
30+
if
31+
local.get $2
32+
local.get $0
33+
local.get $1
34+
i32.sub
35+
i32.const 65535
36+
i32.add
37+
i32.const -65536
38+
i32.and
39+
i32.const 16
40+
i32.shr_u
41+
local.tee $1
42+
local.get $2
43+
local.get $1
44+
i32.gt_s
45+
select
46+
memory.grow
47+
i32.const 0
48+
i32.lt_s
49+
if
50+
local.get $1
51+
memory.grow
52+
i32.const 0
53+
i32.lt_s
54+
if
55+
unreachable
56+
end
57+
end
58+
end
59+
local.get $0
60+
global.set $~lib/rt/stub/offset
61+
)
62+
(func $~lib/rt/stub/__alloc (; 1 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32)
63+
(local $2 i32)
64+
(local $3 i32)
65+
(local $4 i32)
66+
local.get $0
67+
i32.const 1073741808
68+
i32.gt_u
69+
if
70+
unreachable
71+
end
72+
global.get $~lib/rt/stub/offset
73+
i32.const 16
74+
i32.add
75+
local.tee $3
76+
local.get $0
77+
i32.const 15
78+
i32.add
79+
i32.const -16
80+
i32.and
81+
local.tee $2
82+
i32.const 16
83+
local.get $2
84+
i32.const 16
85+
i32.gt_u
86+
select
87+
local.tee $4
88+
i32.add
89+
call $~lib/rt/stub/maybeGrowMemory
90+
local.get $3
91+
i32.const 16
92+
i32.sub
93+
local.tee $2
94+
local.get $4
95+
i32.store
96+
local.get $2
97+
i32.const -1
98+
i32.store offset=4
99+
local.get $2
100+
local.get $1
101+
i32.store offset=8
102+
local.get $2
103+
local.get $0
104+
i32.store offset=12
105+
local.get $3
106+
)
107+
(func $~lib/rt/__allocArray (; 2 ;) (type $FUNCSIG$i) (result i32)
108+
(local $0 i32)
109+
(local $1 i32)
110+
i32.const 16
111+
i32.const 3
112+
call $~lib/rt/stub/__alloc
113+
local.tee $0
114+
i32.const 8
115+
i32.const 0
116+
call $~lib/rt/stub/__alloc
117+
local.tee $1
118+
i32.store
119+
local.get $0
120+
local.get $1
121+
i32.store offset=4
122+
local.get $0
123+
i32.const 8
124+
i32.store offset=8
125+
local.get $0
126+
i32.const 2
127+
i32.store offset=12
128+
local.get $0
129+
)
130+
(func $generic-export/okFunction (; 3 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32)
131+
(local $2 i32)
132+
(local $3 i32)
133+
call $~lib/rt/__allocArray
134+
local.tee $2
135+
i32.load offset=4
136+
local.tee $3
137+
local.get $0
138+
i32.store
139+
local.get $3
140+
local.get $1
141+
i32.store offset=4
142+
local.get $2
143+
)
144+
(func $generic-export/OkClass#one (; 4 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
145+
i32.const 1
146+
)
147+
(func $generic-export/functionThatUsesGenericFunction (; 5 ;) (type $FUNCSIG$i) (result i32)
148+
i32.const 1
149+
i32.const 1
150+
call $generic-export/okFunction
151+
)
152+
(func $start (; 6 ;) (type $FUNCSIG$v)
153+
i32.const 16
154+
global.set $~lib/rt/stub/startOffset
155+
i32.const 16
156+
global.set $~lib/rt/stub/offset
157+
)
158+
(func $null (; 7 ;) (type $FUNCSIG$v)
159+
nop
160+
)
161+
)

tests/compiler/generic-export.ts

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
export function genericExample<T>(a: T, b: T): Array<T> {
2+
return [a, b];
3+
}
4+
5+
export function okFunction(a: i32, b: i32): Array<i32> {
6+
return [a, b];
7+
}
8+
9+
export class OkClass {
10+
one(): i32 {
11+
return 1;
12+
}
13+
}
14+
15+
export class GenericNumber<T> {
16+
zeroValue: T;
17+
add: (x: T, y: T) => T;
18+
}
19+
20+
export namespace GenericNumberNamespace {
21+
export class GenericNumber2<T> {
22+
zeroValue: T;
23+
add: (x: T, y: T) => T;
24+
}
25+
}
26+
27+
export const okVariable: i32 = 1;
28+
29+
export function functionThatUsesGenericFunction(): Array<i32> {
30+
return genericExample<i32>(1, 1);
31+
}

0 commit comments

Comments
 (0)